[geany/geany] ad1deb: Sync ctags parsers with big changes with Geany (#2991)

Jiří Techet git-noreply at xxxxx
Tue Dec 7 00:17:33 UTC 2021


Branch:      refs/heads/master
Author:      Jiří Techet <techet at gmail.com>
Committer:   GitHub <noreply at github.com>
Date:        Tue, 07 Dec 2021 00:17:33 UTC
Commit:      ad1debc0ff45b1ee2dac117e7a494e43523723aa
             https://github.com/geany/geany/commit/ad1debc0ff45b1ee2dac117e7a494e43523723aa

Log Message:
-----------
Sync ctags parsers with big changes with Geany (#2991)

* Remove the geany_ prefix from selected parsers and use the corresponding uctags version

* Update tag mappings in tm_parser.c

* Update PHP and zephir context separator to "\\" which is used in uctags now

* Update ruby unit tests, the tests only differ in added "()" to all functions in generated tags.

* Update nsis unit tests, the section name seems to be parsed correctly now.

* Update go unit tests, the new go tags contain extra scope information.

* Update php and zephir unit tests, PHP and zephir now use "\\" as the context separator instead of "::".
For zephir, return values of methods are now parsed.

* Update Objective-C unit tests, the new parser parses also method arguments.

* Update lua unit tests, Scope information seems to be correctly parsed now.

* Add uctags iniconf parser

* Update tag mappings for the iniconf parser

* Add a way to modify scope information if needed and use it to unify php separators

This patch introduces a new function tm_parser_update_scope() which
can be used to modify scope information when the provided value from
ctags doesn't suit our needs.

This patch uses it to replace php scope separator \ with :: so there's
a single scope separator for this language.

* Remove comment regarding ruby scope separator

"." is used as the context separator in ruby now, the comment is
probably just some historic artifact.

* Add comment explaining why we modify PHP scope separators

* Add a mechanism to enable/disable roles for certain kinds

This patch allows us to enable/disable ctags roles for certain languages
and kinds. Roles are currently disabled only for the Go kind 'p'
for the reasons mentioned in the comment message in the patch.

* Add a mechanism to enable/disable some ctags kinds

This patch is similar to the patch allowing us to enable/disable roles,
it just does this for ctags kinds. This can be useful when there is
a bug in a ctags parser causing incorrect parsing when certain kind is
enabled - which is what happens when parsing cython source files
with the 'z' flag enabled.

* Add LUA to the list of parsers returning full scope

* Enable/disable kinds in ctags based on whether we use them or not

Instead of enabling all kinds and then manually disabling them if
there are problems with them, we can enable/disable them based on whether
we actually use them - i.e., when they are mapped to something else
than tm_tag_undef_t.


Modified Paths:
--------------
    ctags/Makefile.am
    ctags/parsers/basic.c
    ctags/parsers/diff.c
    ctags/parsers/geany_basic.c
    ctags/parsers/geany_go.c
    ctags/parsers/geany_iniconf.c
    ctags/parsers/geany_lua.c
    ctags/parsers/geany_nsis.c
    ctags/parsers/geany_perl.c
    ctags/parsers/geany_ruby.c
    ctags/parsers/go.c
    ctags/parsers/html.c
    ctags/parsers/iniconf.c
    ctags/parsers/iniconf.h
    ctags/parsers/jscript.c
    ctags/parsers/lua.c
    ctags/parsers/make.c
    ctags/parsers/make.h
    ctags/parsers/nsis.c
    ctags/parsers/objc.c
    ctags/parsers/perl.c
    ctags/parsers/perl.h
    ctags/parsers/php.c
    ctags/parsers/ruby.c
    ctags/parsers/rust.c
    ctags/parsers/sql.c
    src/tagmanager/tm_ctags.c
    src/tagmanager/tm_parser.c
    src/tagmanager/tm_parser.h
    src/tagmanager/tm_parsers.h
    tests/ctags/bug1742588.rb.tags
    tests/ctags/bug2781264.rb.tags
    tests/ctags/geany.nsi.tags
    tests/ctags/namespaces.php.tags
    tests/ctags/namespaces2.php.tags
    tests/ctags/objectivec_implementation.mm.tags
    tests/ctags/objectivec_interface.mm.tags
    tests/ctags/objectivec_property.mm.tags
    tests/ctags/objectivec_protocol.mm.tags
    tests/ctags/return-hint.zep.tags
    tests/ctags/return-types.go.tags
    tests/ctags/ruby-block-call.rb.tags
    tests/ctags/ruby-doc.rb.tags
    tests/ctags/ruby-scope-after-anonymous-class.rb.tags
    tests/ctags/ruby-sf-bug-364.rb.tags
    tests/ctags/simple.lua.tags
    tests/ctags/simple.rb.tags
    tests/ctags/strings.rb.tags
    tests/ctags/test.go.tags

Modified: ctags/Makefile.am
33 lines changed, 18 insertions(+), 15 deletions(-)
===================================================================
@@ -15,43 +15,46 @@ parsers = \
 	parsers/abc.c \
 	parsers/asciidoc.c \
 	parsers/geany_asm.c \
-	parsers/geany_basic.c \
+	parsers/basic.c \
 	parsers/bibtex.c \
 	parsers/geany_c.c \
 	parsers/cobol.c \
-	parsers/geany_iniconf.c \
+	parsers/iniconf.c \
+	parsers/iniconf.h \
 	parsers/css.c \
-	parsers/geany_diff.c \
+	parsers/diff.c \
 	parsers/geany_docbook.c \
 	parsers/erlang.c \
 	parsers/flex.c \
 	parsers/geany_fortran.c \
-	parsers/geany_go.c \
+	parsers/go.c \
 	parsers/haskell.c \
 	parsers/haxe.c \
-	parsers/geany_html.c \
-	parsers/geany_jscript.c \
+	parsers/html.c \
+	parsers/jscript.c \
 	parsers/json.c \
 	parsers/julia.c \
 	parsers/geany_lcpp.c \
 	parsers/geany_lcpp.h \
-	parsers/geany_lua.c \
-	parsers/geany_make.c \
+	parsers/lua.c \
+	parsers/make.c \
+	parsers/make.h \
 	parsers/geany_markdown.c \
 	parsers/geany_matlab.c \
-	parsers/geany_nsis.c \
-	parsers/geany_objc.c \
+	parsers/nsis.c \
+	parsers/objc.c \
 	parsers/geany_pascal.c \
-	parsers/geany_perl.c \
-	parsers/geany_php.c \
+	parsers/perl.c \
+	parsers/perl.h \
+	parsers/php.c \
 	parsers/powershell.c \
 	parsers/geany_python.c \
 	parsers/geany_r.c \
 	parsers/rst.c \
-	parsers/geany_ruby.c \
-	parsers/geany_rust.c \
+	parsers/ruby.c \
+	parsers/rust.c \
 	parsers/geany_sh.c \
-	parsers/geany_sql.c \
+	parsers/sql.c \
 	parsers/geany_tcl.c \
 	parsers/geany_tex.c \
 	parsers/txt2tags.c \


Modified: ctags/parsers/basic.c
206 lines changed, 206 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,206 @@
+/*
+ *   Copyright (c) 2000-2006, Darren Hiebert, Elias Pschernig
+ *
+ *   This source code is released for free distribution under the terms of the
+ *   GNU General Public License version 2 or (at your option) any later version.
+ *
+ *   This module contains functions for generating tags for BlitzBasic
+ *   (BlitzMax), PureBasic and FreeBasic language files. For now, this is kept
+ *   quite simple - but feel free to ask for more things added any time -
+ *   patches are of course most welcome.
+ */
+
+/*
+ *   INCLUDE FILES
+ */
+#include "general.h" /* must always come first */
+
+#include <string.h>
+
+#include "parse.h"
+#include "read.h"
+#include "routines.h"
+#include "vstring.h"
+
+/*
+ *   DATA DEFINITIONS
+ */
+typedef enum {
+	K_CONST,
+	K_FUNCTION,
+	K_LABEL,
+	K_TYPE,
+	K_VARIABLE,
+	K_ENUM
+} BasicKind;
+
+typedef struct {
+	char const *token;
+	BasicKind kind;
+	int skip;
+} KeyWord;
+
+static kindDefinition BasicKinds[] = {
+	{true, 'c', "constant", "constants"},
+	{true, 'f', "function", "functions"},
+	{true, 'l', "label", "labels"},
+	{true, 't', "type", "types"},
+	{true, 'v', "variable", "variables"},
+	{true, 'g', "enum", "enumerations"}
+};
+
+static KeyWord blitzbasic_keywords[] = {
+	{"const", K_CONST, 0},
+	{"global", K_VARIABLE, 0},
+	{"dim", K_VARIABLE, 0},
+	{"function", K_FUNCTION, 0},
+	{"type", K_TYPE, 0},
+	{NULL, 0, 0}
+};
+
+static KeyWord purebasic_keywords[] = {
+	{"newlist", K_VARIABLE, 0},
+	{"global", K_VARIABLE, 0},
+	{"dim", K_VARIABLE, 0},
+	{"procedure", K_FUNCTION, 0},
+	{"interface", K_TYPE, 0},
+	{"structure", K_TYPE, 0},
+	{NULL, 0, 0}
+};
+
+static KeyWord freebasic_keywords[] = {
+	{"const", K_CONST, 0},
+	{"dim as", K_VARIABLE, 1},
+	{"dim", K_VARIABLE, 0},
+	{"common", K_VARIABLE, 0},
+	{"function", K_FUNCTION, 0},
+	{"sub", K_FUNCTION, 0},
+	{"private sub", K_FUNCTION, 0},
+	{"public sub", K_FUNCTION, 0},
+	{"private function", K_FUNCTION, 0},
+	{"public function", K_FUNCTION, 0},
+	{"type", K_TYPE, 0},
+	{"enum", K_ENUM, 0},
+	{NULL, 0, 0}
+};
+
+/*
+ *   FUNCTION DEFINITIONS
+ */
+
+/* Match the name of a tag (function, variable, type, ...) starting at pos. */
+static char const *extract_name (char const *pos, vString * name)
+{
+	while (isspace (*pos))
+		pos++;
+	vStringClear (name);
+	for (; *pos && !isspace (*pos) && *pos != '(' && *pos != ','; pos++)
+		vStringPut (name, *pos);
+	return pos;
+}
+
+/* Match a keyword starting at p (case insensitive). */
+static int match_keyword (const char *p, KeyWord const *kw)
+{
+	vString *name;
+	size_t i;
+	int j;
+	for (i = 0; i < strlen (kw->token); i++)
+	{
+		if (tolower (p[i]) != kw->token[i])
+			return 0;
+	}
+	name = vStringNew ();
+	p += i;
+	for (j = 0; j < 1 + kw->skip; j++)
+	{
+		p = extract_name (p, name);
+	}
+	makeSimpleTag (name, kw->kind);
+	vStringDelete (name);
+	return 1;
+}
+
+/* Match a "label:" style label. */
+static void match_colon_label (char const *p)
+{
+	char const *end = p + strlen (p) - 1;
+	while (isspace (*end))
+		end--;
+	if (*end == ':')
+	{
+		vString *name = vStringNew ();
+		vStringNCatS (name, p, end - p);
+		makeSimpleTag (name, K_LABEL);
+		vStringDelete (name);
+	}
+}
+
+/* Match a ".label" style label. */
+static void match_dot_label (char const *p)
+{
+	if (*p == '.')
+	{
+		vString *name = vStringNew ();
+		extract_name (p + 1, name);
+		makeSimpleTag (name, K_LABEL);
+		vStringDelete (name);
+	}
+}
+
+static void findBasicTags (void)
+{
+	const char *line;
+	const char *extension = fileExtension (getInputFileName ());
+	KeyWord *keywords;
+
+	if (strcmp (extension, "bb") == 0)
+		keywords = blitzbasic_keywords;
+	else if (strcmp (extension, "pb") == 0)
+		keywords = purebasic_keywords;
+	else
+		keywords = freebasic_keywords;
+
+	while ((line = (const char *) readLineFromInputFile ()) != NULL)
+	{
+		const char *p = line;
+		KeyWord const *kw;
+
+		while (isspace (*p))
+			p++;
+
+		/* Empty line? */
+		if (!*p)
+			continue;
+
+		/* REM comment? */
+		if (strncasecmp (p, "REM", 3) == 0  &&
+			(isspace (*(p + 3)) || *(p + 3) == '\0'))
+			continue;
+
+		/* Single-quote comment? */
+		if (*p == '\'')
+			continue;
+
+		/* In Basic, keywords always are at the start of the line. */
+		for (kw = keywords; kw->token; kw++)
+			if (match_keyword (p, kw)) break;
+
+		/* Is it a label? */
+		if (strcmp (extension, "bb") == 0)
+			match_dot_label (p);
+		else
+			match_colon_label (p);
+	}
+}
+
+parserDefinition *BasicParser (void)
+{
+	static char const *extensions[] = { "bas", "bi", "bm", "bb", "pb", NULL };
+	parserDefinition *def = parserNew ("Basic");
+	def->kindTable = BasicKinds;
+	def->kindCount = ARRAY_SIZE (BasicKinds);
+	def->extensions = extensions;
+	def->parser = findBasicTags;
+	return def;
+}


Modified: ctags/parsers/diff.c
93 lines changed, 86 insertions(+), 7 deletions(-)
===================================================================
@@ -16,6 +16,7 @@
 #include <ctype.h>
 #include <string.h>
 
+#include "entry.h"
 #include "parse.h"
 #include "routines.h"
 #include "read.h"
@@ -25,11 +26,17 @@
 *   DATA DEFINITIONS
 */
 typedef enum {
-	K_FUNCTION
+	K_MODIFIED_FILE,
+	K_NEW_FILE,
+	K_DELETED_FILE,
+	K_HUNK,
 } diffKind;
 
 static kindDefinition DiffKinds [] = {
-	{ true, 'f', "function", "functions"}
+	{ true, 'm', "modifiedFile",  "modified files"},
+	{ true, 'n', "newFile",       "newly created files"},
+	{ true, 'd', "deletedFile",   "deleted files"},
+	{ true, 'h', "hunk",          "hunks"},
 };
 
 enum {
@@ -42,6 +49,11 @@ static const char *DiffDelims[2] = {
 	"+++ "
 };
 
+static const char *HunkDelim[2] = {
+	"@@ ",
+	" @@",
+};
+
 /*
 *   FUNCTION DEFINITIONS
 */
@@ -76,18 +88,66 @@ static const unsigned char *stripAbsolute (const unsigned char *filename)
 	return tmp;
 }
 
+static int parseHunk (const unsigned char* cp, vString *hunk, int scope_index)
+{
+	/*
+	   example input: @@ -0,0 +1,134 @@
+	   expected output: -0,0 +1,134
+	*/
+
+	const char *next_delim;
+	const char *start, *end;
+	const char *c;
+	int i = CORK_NIL;
+
+	cp += 3;
+	start = (const char*)cp;
+
+	if (*start != '-')
+		return i;
+
+	next_delim = strstr ((const char*)cp, HunkDelim[1]);
+	if ((next_delim == NULL)
+	    || (! (start < next_delim )))
+		return i;
+	end = next_delim;
+	if (! ( '0' <= *( end - 1 ) && *( end - 1 ) <= '9'))
+		return i;
+	for (c = start; c < end; c++)
+		if (*c == '\t')
+			return i;
+	vStringNCopyS (hunk, start, end - start);
+	i = makeSimpleTag (hunk, K_HUNK);
+	tagEntryInfo *e =  getEntryInCorkQueue (i);
+	if (e && scope_index > CORK_NIL)
+		e->extensionFields.scopeIndex = scope_index;
+	return i;
+}
+
+static void markTheLastTagAsDeletedFile (int scope_index)
+{
+	tagEntryInfo *e =  getEntryInCorkQueue (scope_index);
+
+	if (e)
+		e->kindIndex = K_DELETED_FILE;
+}
+
 static void findDiffTags (void)
 {
 	vString *filename = vStringNew ();
+	vString *hunk = vStringNew ();
 	const unsigned char *line, *tmp;
 	int delim = DIFF_DELIM_MINUS;
+	diffKind kind;
+	int scope_index = CORK_NIL;
 
 	while ((line = readLineFromInputFile ()) != NULL)
 	{
 		const unsigned char* cp = line;
 
 		if (strncmp ((const char*) cp, DiffDelims[delim], 4u) == 0)
 		{
+			scope_index = CORK_NIL;
 			cp += 4;
 			if (isspace ((int) *cp)) continue;
 			/* when original filename is /dev/null use the new one instead */
@@ -109,26 +169,45 @@ static void findDiffTags (void)
 					tmp++;
 				}
 
-				makeSimpleTag (filename, K_FUNCTION);
+				if (delim == DIFF_DELIM_PLUS)
+					kind = K_NEW_FILE;
+				else
+					kind = K_MODIFIED_FILE;
+				scope_index = makeSimpleTag (filename, kind);
 				vStringClear (filename);
 			}
 
 			/* restore default delim */
 			delim = DIFF_DELIM_MINUS;
 		}
+		else if ((scope_index > CORK_NIL)
+			 && (strncmp ((const char*) cp, DiffDelims[1], 4u) == 0))
+		{
+			cp += 4;
+			if (isspace ((int) *cp)) continue;
+			/* when modified filename is /dev/null, the original name is deleted. */
+			if (strncmp ((const char*) cp, "/dev/null", 9u) == 0 &&
+			    (cp[9] == 0 || isspace (cp[9])))
+				markTheLastTagAsDeletedFile (scope_index);
+		}
+		else if (strncmp ((const char*) cp, HunkDelim[0], 3u) == 0)
+		{
+			if (parseHunk (cp, hunk, scope_index) != CORK_NIL)
+				vStringClear (hunk);
+		}
 	}
+	vStringDelete (hunk);
 	vStringDelete (filename);
 }
 
 extern parserDefinition* DiffParser (void)
 {
-	static const char *const patterns [] = { "*.diff", "*.patch", NULL };
-	static const char *const extensions [] = { "diff", NULL };
+	static const char *const extensions [] = { "diff", "patch", NULL };
 	parserDefinition* const def = parserNew ("Diff");
-	def->kindTable  = DiffKinds;
+	def->kindTable      = DiffKinds;
 	def->kindCount  = ARRAY_SIZE (DiffKinds);
-	def->patterns   = patterns;
 	def->extensions = extensions;
 	def->parser     = findDiffTags;
+	def->useCork    = CORK_QUEUE;
 	return def;
 }


Modified: ctags/parsers/geany_basic.c
252 lines changed, 0 insertions(+), 252 deletions(-)
===================================================================
@@ -1,252 +0,0 @@
-/*
- *   Copyright (c) 2000-2006, Darren Hiebert, Elias Pschernig
- *
- *   This source code is released for free distribution under the terms of the
- *   GNU General Public License version 2 or (at your option) any later version.
- *
- *   This module contains functions for generating tags for BlitzBasic
- *   (BlitzMax), PureBasic and FreeBasic language files. For now, this is kept
- *   quite simple - but feel free to ask for more things added any time -
- *   patches are of course most welcome.
- */
-
-/*
- *   INCLUDE FILES
- */
-#include "general.h" /* must always come first */
-
-#include <string.h>
-
-#include "parse.h"
-#include "read.h"
-#include "routines.h"
-#include "vstring.h"
-
-/*
- *   DATA DEFINITIONS
- */
-typedef enum {
-	K_CONST,
-	K_FUNCTION,
-	K_LABEL,
-	K_TYPE,
-	K_VARIABLE,
-	K_ENUM
-} BasicKind;
-
-typedef struct {
-	char const *token;
-	BasicKind kind;
-} KeyWord;
-
-static kindDefinition BasicKinds[] = {
-	{true, 'c', "constant", "constants"},
-	{true, 'f', "function", "functions"},
-	{true, 'l', "label", "labels"},
-	{true, 't', "type", "types"},
-	{true, 'v', "variable", "variables"},
-	{true, 'g', "enum", "enumerations"}
-};
-
-static KeyWord freebasic_keywords[] = {
-	{"dim", K_VARIABLE}, /* must always be the first */
-	{"common", K_VARIABLE}, /* must always be the second */
-	{"const", K_CONST}, /* must always be the third */
-	{"function", K_FUNCTION},
-	{"sub", K_FUNCTION},
-	{"property", K_FUNCTION},
-	{"constructor", K_FUNCTION},
-	{"destructor", K_FUNCTION},
-	{"private sub", K_FUNCTION},
-	{"public sub", K_FUNCTION},
-	{"private function", K_FUNCTION},
-	{"public function", K_FUNCTION},
-	{"type", K_TYPE},
-	{"enum", K_ENUM},
-	{NULL, 0}
-};
-
-/*
- *   FUNCTION DEFINITIONS
- */
-
-/* Match the name of a dim or const starting at pos. */
-static int extract_dim (char const *pos, vString * name, BasicKind kind)
-{
-	const char *old_pos = pos;
-	while (isspace (*pos))
-		pos++;
-
-	/* create tags only if there is some space between the keyword and the identifier */
-	if (old_pos == pos)
-		return 0;
-
-	vStringClear (name);
-
-	if (strncasecmp (pos, "shared", 6) == 0)
-		pos += 6; /* skip keyword "shared" */
-
-	while (isspace (*pos))
-		pos++;
-
-	/* capture "dim as String str" */
-	if (strncasecmp (pos, "as", 2) == 0)
-	{
-			pos += 2; /* skip keyword "as" */
-
-		while (isspace (*pos))
-			pos++;
-		while (!isspace (*pos)) /* skip next part which is a type */
-			pos++;
-		while (isspace (*pos))
-			pos++;
-		/* now we are at the name */
-	}
-	/* capture "dim as foo ptr bar" */
-	if (strncasecmp (pos, "ptr", 3) == 0 && isspace(*(pos+4)))
-	{
-		pos += 3; /* skip keyword "ptr" */
-		while (isspace (*pos))
-			pos++;
-	}
-	/*	capture "dim as string * 4096 chunk" */
-	if (strncmp (pos, "*", 1) == 0)
-	{
-		pos += 1; /* skip "*" */
-		while (isspace (*pos) || isdigit(*pos) || ispunct(*pos))
-			pos++;
-	}
-
-	for (; *pos && !isspace (*pos) && *pos != '(' && *pos != ',' && *pos != '='; pos++)
-		vStringPut (name, *pos);
-	makeSimpleTag (name, kind);
-
-	/* if the line contains a ',', we have multiple declarations */
-	while (*pos && strchr (pos, ','))
-	{
-		/* skip all we don't need(e.g. "..., new_array(5), " we skip "(5)") */
-		while (*pos != ',' && *pos != '\'')
-			pos++;
-
-		if (*pos == '\'')
-			return 0; /* break if we are in a comment */
-
-		while (isspace (*pos) || *pos == ',')
-			pos++;
-
-		if (*pos == '\'')
-			return 0; /* break if we are in a comment */
-
-		vStringClear (name);
-		for (; *pos && !isspace (*pos) && *pos != '(' && *pos != ',' && *pos != '='; pos++)
-			vStringPut (name, *pos);
-		makeSimpleTag (name, kind);
-	}
-
-	vStringDelete (name);
-	return 1;
-}
-
-/* Match the name of a tag (function, variable, type, ...) starting at pos. */
-static char const *extract_name (char const *pos, vString * name)
-{
-	while (isspace (*pos))
-		pos++;
-	vStringClear (name);
-	for (; *pos && !isspace (*pos) && *pos != '(' && *pos != ',' && *pos != '='; pos++)
-		vStringPut (name, *pos);
-	return pos;
-}
-
-/* Match a keyword starting at p (case insensitive). */
-static int match_keyword (const char *p, KeyWord const *kw)
-{
-	vString *name;
-	size_t i;
-	int j;
-	const char *old_p;
-	for (i = 0; i < strlen (kw->token); i++)
-	{
-		if (tolower (p[i]) != kw->token[i])
-			return 0;
-	}
-	name = vStringNew ();
-	p += i;
-	if (kw == &freebasic_keywords[0] ||
-		kw == &freebasic_keywords[1] ||
-		kw == &freebasic_keywords[2])
-		return extract_dim (p, name, kw->kind); /* extract_dim adds the found tag(s) */
-
-	old_p = p;
-	while (isspace (*p))
-		p++;
-
-	/* create tags only if there is some space between the keyword and the identifier */
-	if (old_p == p)
-	{
-		vStringDelete (name);
-		return 0;
-	}
-
-	for (j = 0; j < 1; j++)
-	{
-		p = extract_name (p, name);
-	}	
-	makeSimpleTag (name, kw->kind);
-	vStringDelete (name);
-	return 1;
-}
-
-/* Match a "label:" style label. */
-static void match_colon_label (char const *p)
-{
-	char const *end = p + strlen (p) - 1;
-	while (isspace (*end))
-		end--;
-	if (*end == ':')
-	{
-		vString *name = vStringNew ();
-		vStringNCatS (name, p, end - p);
-		makeSimpleTag (name, K_LABEL);
-		vStringDelete (name);
-	}
-}
-
-static void findBasicTags (void)
-{
-	const char *line;
-	KeyWord *keywords;
-
-	keywords = freebasic_keywords;
-
-	while ((line = (const char *) readLineFromInputFile ()) != NULL)
-	{
-		const char *p = line;
-		KeyWord const *kw;
-
-		while (isspace (*p))
-			p++;
-
-		/* Empty line or comment? */
-		if (!*p || *p == '\'')
-			continue;
-
-		/* In Basic, keywords always are at the start of the line. */
-		for (kw = keywords; kw->token; kw++)
-			if (match_keyword (p, kw)) break;
-
-		/* Is it a label? */
-		match_colon_label (p);
-	}
-}
-
-parserDefinition *BasicParser (void)
-{
-	static char const *extensions[] = { "bas", "bi", "bb", "pb", NULL };
-	parserDefinition *def = parserNew ("FreeBasic");
-	def->kindTable = BasicKinds;
-	def->kindCount = ARRAY_SIZE (BasicKinds);
-	def->extensions = extensions;
-	def->parser = findBasicTags;
-	return def;
-}


Modified: ctags/parsers/geany_go.c
831 lines changed, 0 insertions(+), 831 deletions(-)
===================================================================
@@ -1,831 +0,0 @@
-/*
-*   This source code is released for free distribution under the terms of the
-*   GNU General Public License version 2 or (at your option) any later version.
-*
-*   INCLUDE FILES
-*/
-#include "general.h"        /* must always come first */
-
-#include "debug.h"
-#include "entry.h"
-#include "keyword.h"
-#include "read.h"
-#include "parse.h"
-#include "routines.h"
-#include "vstring.h"
-#include "options.h"
-#include "xtag.h"
-
-/*
- *	 MACROS
- */
-#define MAX_SIGNATURE_LENGTH 512
-#define isType(token,t) (bool) ((token)->type == (t))
-#define isKeyword(token,k) (bool) ((token)->keyword == (k))
-
-/*
- *	 DATA DECLARATIONS
- */
-
-enum eKeywordId {
-	KEYWORD_package,
-	KEYWORD_import,
-	KEYWORD_const,
-	KEYWORD_type,
-	KEYWORD_var,
-	KEYWORD_func,
-	KEYWORD_struct,
-	KEYWORD_interface,
-	KEYWORD_map,
-	KEYWORD_chan
-};
-typedef int keywordId; /* to allow KEYWORD_NONE */
-
-typedef enum eTokenType {
-	TOKEN_NONE = -1,
-	// Token not important for top-level Go parsing
-	TOKEN_OTHER,
-	TOKEN_KEYWORD,
-	TOKEN_IDENTIFIER,
-	TOKEN_STRING,
-	TOKEN_OPEN_PAREN,
-	TOKEN_CLOSE_PAREN,
-	TOKEN_OPEN_CURLY,
-	TOKEN_CLOSE_CURLY,
-	TOKEN_OPEN_SQUARE,
-	TOKEN_CLOSE_SQUARE,
-	TOKEN_SEMICOLON,
-	TOKEN_STAR,
-	TOKEN_LEFT_ARROW,
-	TOKEN_DOT,
-	TOKEN_COMMA,
-	TOKEN_EOF
-} tokenType;
-
-typedef struct sTokenInfo {
-	tokenType type;
-	keywordId keyword;
-	vString *string;		/* the name of the token */
-	unsigned long lineNumber;	/* line number of tag */
-	MIOPos filePosition;		/* file position of line containing name */
-} tokenInfo;
-
-/*
-*   DATA DEFINITIONS
-*/
-
-static int Lang_go;
-static vString *scope;
-static vString *signature = NULL;
-
-typedef enum {
-	GOTAG_UNDEFINED = -1,
-	GOTAG_PACKAGE,
-	GOTAG_FUNCTION,
-	GOTAG_CONST,
-	GOTAG_TYPE,
-	GOTAG_VAR,
-	GOTAG_STRUCT,
-	GOTAG_INTERFACE,
-	GOTAG_MEMBER
-} goKind;
-
-static kindDefinition GoKinds[] = {
-	{true, 'p', "package", "packages"},
-	{true, 'f', "func", "functions"},
-	{true, 'c', "const", "constants"},
-	{true, 't', "type", "types"},
-	{true, 'v', "var", "variables"},
-	{true, 's', "struct", "structs"},
-	{true, 'i', "interface", "interfaces"},
-	{true, 'm', "member", "struct members"}
-};
-
-static const keywordTable GoKeywordTable[] = {
-	{"package", KEYWORD_package},
-	{"import", KEYWORD_import},
-	{"const", KEYWORD_const},
-	{"type", KEYWORD_type},
-	{"var", KEYWORD_var},
-	{"func", KEYWORD_func},
-	{"struct", KEYWORD_struct},
-	{"interface", KEYWORD_interface},
-	{"map", KEYWORD_map},
-	{"chan", KEYWORD_chan}
-};
-
-/*
-*   FUNCTION DEFINITIONS
-*/
-
-// XXX UTF-8
-static bool isStartIdentChar (const int c)
-{
-	return (bool)
-		(isalpha (c) ||  c == '_' || c > 128);
-}
-
-static bool isIdentChar (const int c)
-{
-	return (bool)
-		(isStartIdentChar (c) || isdigit (c));
-}
-
-static void initialize (const langType language)
-{
-	Lang_go = language;
-}
-
-static tokenInfo *newToken (void)
-{
-	tokenInfo *const token = xMalloc (1, tokenInfo);
-	token->type = TOKEN_NONE;
-	token->keyword = KEYWORD_NONE;
-	token->string = vStringNew ();
-	token->lineNumber = getInputLineNumber ();
-	token->filePosition = getInputFilePosition ();
-	return token;
-}
-
-static tokenInfo *copyToken (tokenInfo *other)
-{
-	tokenInfo *const token = xMalloc (1, tokenInfo);
-	token->type = other->type;
-	token->keyword = other->keyword;
-	token->string = vStringNewCopy (other->string);
-	token->lineNumber = other->lineNumber;
-	token->filePosition = other->filePosition;
-	return token;
-}
-
-static void deleteToken (tokenInfo * const token)
-{
-	if (token != NULL)
-	{
-		vStringDelete (token->string);
-		eFree (token);
-	}
-}
-
-/*
- *   Parsing functions
- */
-
-static void parseString (vString *const string, const int delimiter)
-{
-	bool end = false;
-	while (!end)
-	{
-		int c = getcFromInputFile ();
-		if (c == EOF)
-			end = true;
-		else if (c == '\\' && delimiter != '`')
-		{
-			c = getcFromInputFile ();
-			if (c != '\'' && c != '\"')
-				vStringPut (string, '\\');
-			vStringPut (string, c);
-		}
-		else if (c == delimiter)
-			end = true;
-		else
-			vStringPut (string, c);
-	}
-}
-
-static void parseIdentifier (vString *const string, const int firstChar)
-{
-	int c = firstChar;
-	do
-	{
-		vStringPut (string, c);
-		c = getcFromInputFile ();
-	} while (isIdentChar (c));
-	ungetcToInputFile (c);		/* always unget, LF might add a semicolon */
-}
-
-static void readToken (tokenInfo *const token)
-{
-	int c;
-	static tokenType lastTokenType = TOKEN_NONE;
-	bool firstWhitespace = true;
-	bool whitespace;
-
-	token->type = TOKEN_NONE;
-	token->keyword = KEYWORD_NONE;
-	vStringClear (token->string);
-
-getNextChar:
-	do
-	{
-		c = getcFromInputFile ();
-		token->lineNumber = getInputLineNumber ();
-		token->filePosition = getInputFilePosition ();
-		if (c == '\n' && (lastTokenType == TOKEN_IDENTIFIER ||
-						  lastTokenType == TOKEN_STRING ||
-						  lastTokenType == TOKEN_OTHER ||
-						  lastTokenType == TOKEN_CLOSE_PAREN ||
-						  lastTokenType == TOKEN_CLOSE_CURLY ||
-						  lastTokenType == TOKEN_CLOSE_SQUARE))
-		{
-			c = ';';  // semicolon injection
-		}
-		whitespace = c == '\t'  ||  c == ' ' ||  c == '\r' || c == '\n';
-		if (signature && whitespace && firstWhitespace && vStringLength (signature) < MAX_SIGNATURE_LENGTH)
-		{
-			firstWhitespace = false;
-			vStringPut(signature, ' ');
-		}
-	}
-	while (whitespace);
-
-	switch (c)
-	{
-		case EOF:
-			token->type = TOKEN_EOF;
-			break;
-
-		case ';':
-			token->type = TOKEN_SEMICOLON;
-			break;
-
-		case '/':
-			{
-				bool hasNewline = false;
-				int d = getcFromInputFile ();
-				switch (d)
-				{
-					case '/':
-						skipToCharacterInInputFile ('\n');
-						/* Line comments start with the
-						 * character sequence // and
-						 * continue through the next
-						 * newline. A line comment acts
-						 * like a newline.  */
-						ungetcToInputFile ('\n');
-						goto getNextChar;
-					case '*':
-						do
-						{
-							do
-							{
-								d = getcFromInputFile ();
-								if (d == '\n')
-								{
-									hasNewline = true;
-								}
-							} while (d != EOF && d != '*');
-
-							c = getcFromInputFile ();
-							if (c == '/')
-								break;
-							else
-								ungetcToInputFile (c);
-						} while (c != EOF && c != '\0');
-
-						ungetcToInputFile (hasNewline ? '\n' : ' ');
-						goto getNextChar;
-					default:
-						token->type = TOKEN_OTHER;
-						ungetcToInputFile (d);
-						break;
-				}
-			}
-			break;
-
-		case '"':
-		case '\'':
-		case '`':
-			token->type = TOKEN_STRING;
-			parseString (token->string, c);
-			token->lineNumber = getInputLineNumber ();
-			token->filePosition = getInputFilePosition ();
-			break;
-
-		case '<':
-			{
-				int d = getcFromInputFile ();
-				if (d == '-')
-					token->type = TOKEN_LEFT_ARROW;
-				else
-				{
-					ungetcToInputFile (d);
-					token->type = TOKEN_OTHER;
-				}
-			}
-			break;
-
-		case '(':
-			token->type = TOKEN_OPEN_PAREN;
-			break;
-
-		case ')':
-			token->type = TOKEN_CLOSE_PAREN;
-			break;
-
-		case '{':
-			token->type = TOKEN_OPEN_CURLY;
-			break;
-
-		case '}':
-			token->type = TOKEN_CLOSE_CURLY;
-			break;
-
-		case '[':
-			token->type = TOKEN_OPEN_SQUARE;
-			break;
-
-		case ']':
-			token->type = TOKEN_CLOSE_SQUARE;
-			break;
-
-		case '*':
-			token->type = TOKEN_STAR;
-			break;
-
-		case '.':
-			token->type = TOKEN_DOT;
-			break;
-
-		case ',':
-			token->type = TOKEN_COMMA;
-			break;
-
-		default:
-			if (isStartIdentChar (c))
-			{
-				parseIdentifier (token->string, c);
-				token->lineNumber = getInputLineNumber ();
-				token->filePosition = getInputFilePosition ();
-				token->keyword = lookupKeyword (vStringValue (token->string), Lang_go);
-				if (isKeyword (token, KEYWORD_NONE))
-					token->type = TOKEN_IDENTIFIER;
-				else
-					token->type = TOKEN_KEYWORD;
-			}
-			else
-				token->type = TOKEN_OTHER;
-			break;
-	}
-
-	if (signature && vStringLength (signature) < MAX_SIGNATURE_LENGTH)
-	{
-		if (token->type == TOKEN_LEFT_ARROW)
-			vStringCatS(signature, "<-");
-		else if (token->type == TOKEN_STRING)
-		{
-			// only struct member annotations can appear in function prototypes
-			// so only `` type strings are possible
-			vStringPut(signature, '`');
-			vStringCat(signature, token->string);
-			vStringPut(signature, '`');
-		}
-		else if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_KEYWORD)
-			vStringCat(signature, token->string);
-		else if (c != EOF)
-			vStringPut(signature, c);
-	}
-
-	lastTokenType = token->type;
-}
-
-static bool skipToMatchedNoRead (tokenInfo *const token)
-{
-	int nest_level = 0;
-	tokenType open_token = token->type;
-	tokenType close_token;
-
-	switch (open_token)
-	{
-		case TOKEN_OPEN_PAREN:
-			close_token = TOKEN_CLOSE_PAREN;
-			break;
-		case TOKEN_OPEN_CURLY:
-			close_token = TOKEN_CLOSE_CURLY;
-			break;
-		case TOKEN_OPEN_SQUARE:
-			close_token = TOKEN_CLOSE_SQUARE;
-			break;
-		default:
-			return false;
-	}
-
-	/*
-	 * This routine will skip to a matching closing token.
-	 * It will also handle nested tokens.
-	 */
-	nest_level++;
-	while (nest_level > 0 && !isType (token, TOKEN_EOF))
-	{
-		readToken (token);
-		if (isType (token, open_token))
-			nest_level++;
-		else if (isType (token, close_token))
-			nest_level--;
-	}
-
-	return true;
-}
-
-static void skipToMatched (tokenInfo *const token)
-{
-	if (skipToMatchedNoRead (token))
-		readToken (token);
-}
-
-static bool skipType (tokenInfo *const token)
-{
-	// Type      = TypeName | TypeLit | "(" Type ")" .
-	// Skips also function multiple return values "(" Type {"," Type} ")"
-	if (isType (token, TOKEN_OPEN_PAREN))
-	{
-		skipToMatched (token);
-		return true;
-	}
-
-	// TypeName  = QualifiedIdent.
-	// QualifiedIdent = [ PackageName "." ] identifier .
-	// PackageName    = identifier .
-	if (isType (token, TOKEN_IDENTIFIER))
-	{
-		readToken (token);
-		if (isType (token, TOKEN_DOT))
-		{
-			readToken (token);
-			if (isType (token, TOKEN_IDENTIFIER))
-				readToken (token);
-		}
-		return true;
-	}
-
-	// StructType     = "struct" "{" { FieldDecl ";" } "}"
-	// InterfaceType      = "interface" "{" { MethodSpec ";" } "}" .
-	if (isKeyword (token, KEYWORD_struct) || isKeyword (token, KEYWORD_interface))
-	{
-		readToken (token);
-		// skip over "{}"
-		skipToMatched (token);
-		return true;
-	}
-
-	// ArrayType   = "[" ArrayLength "]" ElementType .
-	// SliceType = "[" "]" ElementType .
-	// ElementType = Type .
-	if (isType (token, TOKEN_OPEN_SQUARE))
-	{
-		skipToMatched (token);
-		return skipType (token);
-	}
-
-	// PointerType = "*" BaseType .
-	// BaseType = Type .
-	// ChannelType = ( "chan" [ "<-" ] | "<-" "chan" ) ElementType .
-	if (isType (token, TOKEN_STAR) || isKeyword (token, KEYWORD_chan) || isType (token, TOKEN_LEFT_ARROW))
-	{
-		readToken (token);
-		return skipType (token);
-	}
-
-	// MapType     = "map" "[" KeyType "]" ElementType .
-	// KeyType     = Type .
-	if (isKeyword (token, KEYWORD_map))
-	{
-		readToken (token);
-		// skip over "[]"
-		skipToMatched (token);
-		return skipType (token);
-	}
-
-	// FunctionType   = "func" Signature .
-	// Signature      = Parameters [ Result ] .
-	// Result         = Parameters | Type .
-	// Parameters     = "(" [ ParameterList [ "," ] ] ")" .
-	if (isKeyword (token, KEYWORD_func))
-	{
-		readToken (token);
-		// Parameters, skip over "()"
-		skipToMatched (token);
-		// Result is parameters or type or nothing.  skipType treats anything
-		// surrounded by parentheses as a type, and does nothing if what
-		// follows is not a type.
-		return skipType (token);
-	}
-
-	return false;
-}
-
-static void makeTag (tokenInfo *const token, const goKind kind,
-	tokenInfo *const parent_token, const goKind parent_kind,
-	const char *argList, const char *varType)
-{
-	const char *const name = vStringValue (token->string);
-
-	tagEntryInfo e;
-	initTagEntry (&e, name, kind);
-
-	if (!GoKinds [kind].enabled)
-		return;
-
-	e.lineNumber = token->lineNumber;
-	e.filePosition = token->filePosition;
-	if (argList)
-		e.extensionFields.signature = argList;
-	if (varType)
-		e.extensionFields.typeRef[1] = varType;
-
-	if (parent_kind != GOTAG_UNDEFINED && parent_token != NULL)
-	{
-		e.extensionFields.scopeKindIndex = parent_kind;
-		e.extensionFields.scopeName = vStringValue (parent_token->string);
-	}
-	makeTagEntry (&e);
-
-	if (scope && isXtagEnabled(XTAG_QUALIFIED_TAGS))
-	{
-		vString *qualifiedName = vStringNew ();
-		vStringCopy (qualifiedName, scope);
-		vStringCatS (qualifiedName, ".");
-		vStringCat (qualifiedName, token->string);
-		e.name = vStringValue (qualifiedName);
-		makeTagEntry (&e);
-		vStringDelete (qualifiedName);
-	}
-}
-
-static void parsePackage (tokenInfo *const token)
-{
-	readToken (token);
-	if (isType (token, TOKEN_IDENTIFIER))
-	{
-		makeTag (token, GOTAG_PACKAGE, NULL, GOTAG_UNDEFINED, NULL, NULL);
-		if (!scope && isXtagEnabled(XTAG_QUALIFIED_TAGS))
-		{
-			scope = vStringNew ();
-			vStringCopy (scope, token->string);
-		}
-	}
-}
-
-static void parseFunctionOrMethod (tokenInfo *const token)
-{
-	// FunctionDecl = "func" identifier Signature [ Body ] .
-	// Body         = Block.
-	//
-	// MethodDecl   = "func" Receiver MethodName Signature [ Body ] .
-	// Receiver     = "(" [ identifier ] [ "*" ] BaseTypeName ")" .
-	// BaseTypeName = identifier .
-
-	// Skip over receiver.
-	readToken (token);
-	if (isType (token, TOKEN_OPEN_PAREN))
-		skipToMatched (token);
-
-	if (isType (token, TOKEN_IDENTIFIER))
-	{
-		vString *argList;
-		tokenInfo *functionToken = copyToken (token);
-
-		// Start recording signature
-		signature = vStringNew ();
-
-		// Skip over parameters.
-		readToken (token);
-		skipToMatchedNoRead (token);
-
-		vStringStripLeading (signature);
-		vStringStripTrailing (signature);
-		argList = signature;
-		signature = vStringNew ();
-
-		readToken (token);
-
-		// Skip over result.
-		skipType (token);
-
-		// Remove the extra { we have just read
-		vStringStripTrailing (signature);
-		vStringChop (signature);
-
-		vStringStripLeading (signature);
-		vStringStripTrailing (signature);
-		makeTag (functionToken, GOTAG_FUNCTION, NULL, GOTAG_UNDEFINED, argList->buffer, signature->buffer);
-		deleteToken (functionToken);
-		vStringDelete(signature);
-		vStringDelete(argList);
-
-		// Stop recording signature
-		signature = NULL;
-
-		// Skip over function body.
-		if (isType (token, TOKEN_OPEN_CURLY))
-			skipToMatched (token);
-	}
-}
-
-static void parseStructMembers (tokenInfo *const token, tokenInfo *const parent_token)
-{
-	// StructType     = "struct" "{" { FieldDecl ";" } "}" .
-	// FieldDecl      = (IdentifierList Type | AnonymousField) [ Tag ] .
-	// AnonymousField = [ "*" ] TypeName .
-	// Tag            = string_lit .
-
-	readToken (token);
-	if (!isType (token, TOKEN_OPEN_CURLY))
-		return;
-
-	readToken (token);
-	while (!isType (token, TOKEN_EOF) && !isType (token, TOKEN_CLOSE_CURLY))
-	{
-		tokenInfo *memberCandidate = NULL;
-		bool first = true;
-
-		while (!isType (token, TOKEN_EOF))
-		{
-			if (isType (token, TOKEN_IDENTIFIER))
-			{
-				if (first)
-				{
-					// could be anonymous field like in 'struct {int}' - we don't know yet
-					memberCandidate = copyToken (token);
-					first = false;
-				}
-				else
-				{
-					if (memberCandidate)
-					{
-						// if we are here, there was a comma and memberCandidate isn't an anonymous field
-						makeTag (memberCandidate, GOTAG_MEMBER, parent_token, GOTAG_STRUCT, NULL, NULL);
-						deleteToken (memberCandidate);
-						memberCandidate = NULL;
-					}
-					makeTag (token, GOTAG_MEMBER, parent_token, GOTAG_STRUCT, NULL, NULL);
-				}
-				readToken (token);
-			}
-			if (!isType (token, TOKEN_COMMA))
-				break;
-			readToken (token);
-		}
-
-		// in the case of  an anonymous field, we already read part of the
-		// type into memberCandidate and skipType() should return false so no tag should
-		// be generated in this case.
-		if (skipType (token) && memberCandidate)
-			makeTag (memberCandidate, GOTAG_MEMBER, parent_token, GOTAG_STRUCT, NULL, NULL);
-
-		if (memberCandidate)
-			deleteToken (memberCandidate);
-
-		while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_CURLY)
-				&& !isType (token, TOKEN_EOF))
-		{
-			readToken (token);
-			skipToMatched (token);
-		}
-
-		if (!isType (token, TOKEN_CLOSE_CURLY))
-		{
-			// we are at TOKEN_SEMICOLON
-			readToken (token);
-		}
-	}
-}
-
-static void parseConstTypeVar (tokenInfo *const token, goKind kind)
-{
-	// ConstDecl      = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
-	// ConstSpec      = IdentifierList [ [ Type ] "=" ExpressionList ] .
-	// IdentifierList = identifier { "," identifier } .
-	// ExpressionList = Expression { "," Expression } .
-	// TypeDecl     = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
-	// TypeSpec     = identifier Type .
-	// VarDecl     = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
-	// VarSpec     = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
-	bool usesParens = false;
-
-	readToken (token);
-
-	if (isType (token, TOKEN_OPEN_PAREN))
-	{
-		usesParens = true;
-		readToken (token);
-	}
-
-	do
-	{
-		tokenInfo *typeToken = NULL;
-
-		while (!isType (token, TOKEN_EOF))
-		{
-			if (isType (token, TOKEN_IDENTIFIER))
-			{
-				if (kind == GOTAG_TYPE)
-				{
-					typeToken = copyToken (token);
-					readToken (token);
-					if (isKeyword (token, KEYWORD_struct))
-						makeTag (typeToken, GOTAG_STRUCT, NULL, GOTAG_UNDEFINED, NULL, NULL);
-					else if (isKeyword (token, KEYWORD_interface))
-						makeTag (typeToken, GOTAG_INTERFACE, NULL, GOTAG_UNDEFINED, NULL, NULL);
-					else
-						makeTag (typeToken, kind, NULL, GOTAG_UNDEFINED, NULL, NULL);
-					break;
-				}
-				else
-					makeTag (token, kind, NULL, GOTAG_UNDEFINED, NULL, NULL);
-				readToken (token);
-			}
-			if (!isType (token, TOKEN_COMMA))
-				break;
-			readToken (token);
-		}
-
-		if (typeToken)
-		{
-			if (isKeyword (token, KEYWORD_struct))
-				parseStructMembers (token, typeToken);
-			else
-				skipType (token);
-			deleteToken (typeToken);
-		}
-		else
-			skipType (token);
-
-		while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_PAREN)
-				&& !isType (token, TOKEN_EOF))
-		{
-			readToken (token);
-			skipToMatched (token);
-		}
-
-		if (usesParens && !isType (token, TOKEN_CLOSE_PAREN))
-		{
-			// we are at TOKEN_SEMICOLON
-			readToken (token);
-		}
-	}
-	while (!isType (token, TOKEN_EOF) &&
-			usesParens && !isType (token, TOKEN_CLOSE_PAREN));
-}
-
-static void parseGoFile (tokenInfo *const token)
-{
-	do
-	{
-		readToken (token);
-
-		if (isType (token, TOKEN_KEYWORD))
-		{
-			switch (token->keyword)
-			{
-				case KEYWORD_package:
-					parsePackage (token);
-					break;
-				case KEYWORD_func:
-					parseFunctionOrMethod (token);
-					break;
-				case KEYWORD_const:
-					parseConstTypeVar (token, GOTAG_CONST);
-					break;
-				case KEYWORD_type:
-					parseConstTypeVar (token, GOTAG_TYPE);
-					break;
-				case KEYWORD_var:
-					parseConstTypeVar (token, GOTAG_VAR);
-					break;
-				default:
-					break;
-			}
-		}
-		else if (isType (token, TOKEN_OPEN_PAREN) || isType (token, TOKEN_OPEN_CURLY) ||
-			isType (token, TOKEN_OPEN_SQUARE))
-		{
-			skipToMatched (token);
-		}
-	} while (token->type != TOKEN_EOF);
-}
-
-static void findGoTags (void)
-{
-	tokenInfo *const token = newToken ();
-
-	parseGoFile (token);
-
-	deleteToken (token);
-	vStringDelete (scope);
-	scope = NULL;
-}
-
-extern parserDefinition *GoParser (void)
-{
-	static const char *const extensions[] = { "go", NULL };
-	parserDefinition *def = parserNew ("Go");
-	def->kindTable = GoKinds;
-	def->kindCount = ARRAY_SIZE (GoKinds);
-	def->extensions = extensions;
-	def->parser = findGoTags;
-	def->initialize = initialize;
-	def->keywordTable = GoKeywordTable;
-	def->keywordCount = ARRAY_SIZE (GoKeywordTable);
-	return def;
-}


Modified: ctags/parsers/geany_iniconf.c
128 lines changed, 0 insertions(+), 128 deletions(-)
===================================================================
@@ -1,128 +0,0 @@
-/*
-*
-*   Copyright (c) 2000-2001, Darren Hiebert
-*
-*   This source code is released for free distribution under the terms of the
-*   GNU General Public License.
-*
-*   This module contains functions for generating tags for config files.
-*/
-
-/*
-*   INCLUDE FILES
-*/
-#include "general.h"	/* must always come first */
-
-#include <ctype.h>
-
-#include "parse.h"
-#include "read.h"
-#include "vstring.h"
-#include "routines.h"
-#include "entry.h"
-
-/*
-*   DATA DEFINITIONS
-*/
-typedef enum {
-	K_SECTION,
-	K_KEY
-} confKind;
-
-static kindDefinition ConfKinds [] = {
-	{ true, 'n', "namespace",  "sections"},
-	{ true, 'm', "macro", "keys"}
-};
-
-/*
-*   FUNCTION DEFINITIONS
-*/
-
-static bool isIdentifier (int c)
-{
-	/* allow whitespace within keys and sections */
-	return (bool)(isalnum (c) || isspace (c) ||  c == '_');
-}
-
-static void findConfTags (void)
-{
-	vString *name = vStringNew ();
-	vString *scope = vStringNew ();
-	const unsigned char *line;
-
-	while ((line = readLineFromInputFile ()) != NULL)
-	{
-		const unsigned char* cp = line;
-		bool possible = true;
-
-		if (isspace ((int) *cp) || *cp == '#' || (*cp != '\0' && *cp == '/' && *(cp+1) == '/'))
-			continue;
-
-		/* look for a section */
-		if (*cp != '\0' && *cp == '[')
-		{
-			++cp;
-			while (*cp != '\0' && *cp != ']')
-			{
-				vStringPut (name, (int) *cp);
-				++cp;
-			}
-			makeSimpleTag (name, K_SECTION);
-			/* remember section name */
-			vStringCopy (scope, name);
-			vStringClear (name);
-			continue;
-		}
-
-		while (*cp != '\0')
-		{
-			/*  We look for any sequence of identifier characters following a white space */
-			if (possible && isIdentifier ((int) *cp))
-			{
-				while (isIdentifier ((int) *cp))
-				{
-					vStringPut (name, (int) *cp);
-					++cp;
-				}
-				vStringStripTrailing (name);
-				while (isspace ((int) *cp))
-					++cp;
-				if (*cp == '=')
-				{
-					tagEntryInfo e;
-					initTagEntry (&e, vStringValue (name), K_KEY);
-
-					if (vStringLength (scope) > 0)
-					{
-						e.extensionFields.scopeKindIndex = K_SECTION;
-						e.extensionFields.scopeName = vStringValue(scope);
-					}
-					makeTagEntry (&e);
-				}
-				vStringClear (name);
-			}
-			else if (isspace ((int) *cp))
-				possible = true;
-			else
-				possible = false;
-
-			if (*cp != '\0')
-				++cp;
-		}
-	}
-	vStringDelete (name);
-	vStringDelete (scope);
-}
-
-extern parserDefinition* ConfParser (void)
-{
-	static const char *const patterns [] = { "*.ini", "*.conf", NULL };
-	static const char *const extensions [] = { "conf", NULL };
-	parserDefinition* const def = parserNew ("Conf");
-	def->kindTable  = ConfKinds;
-	def->kindCount  = ARRAY_SIZE (ConfKinds);
-	def->patterns   = patterns;
-	def->extensions = extensions;
-	def->parser     = findConfTags;
-	return def;
-}


Modified: ctags/parsers/geany_lua.c
120 lines changed, 0 insertions(+), 120 deletions(-)
===================================================================
@@ -1,120 +0,0 @@
-/*
-*   Copyright (c) 2000-2001, Max Ischenko <mfi at ukr.net>.
-*
-*   This source code is released for free distribution under the terms of the
-*   GNU General Public License version 2 or (at your option) any later version.
-*
-*   This module contains functions for generating tags for Lua language.
-*/
-
-/*
-*   INCLUDE FILES
-*/
-#include "general.h"  /* must always come first */
-
-#include <string.h>
-
-#include "parse.h"
-#include "read.h"
-#include "routines.h"
-#include "vstring.h"
-
-/*
-*   DATA DEFINITIONS
-*/
-typedef enum {
-	K_FUNCTION
-} luaKind;
-
-static kindDefinition LuaKinds [] = {
-	{ true, 'f', "function", "functions" }
-};
-
-/*
-*   FUNCTION DEFINITIONS
-*/
-
-/*
- * Helper function.
- * Returns 1 if line looks like a line of Lua code.
- *
- * TODO: Recognize UNIX bang notation.
- * (Lua treat first line as a comment if it starts with #!)
- *
- */
-static bool is_a_code_line (const unsigned char *line)
-{
-	bool result;
-	const unsigned char *p = line;
-	while (isspace ((int) *p))
-		p++;
-	if (p [0] == '\0')
-		result = false;
-	else if (p [0] == '-' && p [1] == '-')
-		result = false;
-	else
-		result = true;
-	return result;
-}
-
-static void extract_name (const char *begin, const char *end, vString *name)
-{
-	if (begin != NULL  &&  end != NULL  &&  begin < end)
-	{
-		const char *cp;
-
-		while (isspace ((int) *begin))
-			begin++;
-		while (isspace ((int) *end))
-			end--;
-		if (begin < end)
-		{
-			for (cp = begin ; cp != end; cp++)
-				vStringPut (name, (int) *cp);
-
-			makeSimpleTag (name, K_FUNCTION);
-			vStringClear (name);
-		}
-	}
-}
-
-static void findLuaTags (void)
-{
-	vString *name = vStringNew ();
-	const unsigned char *line;
-
-	while ((line = readLineFromInputFile ()) != NULL)
-	{
-		const char *p, *q;
-
-		if (! is_a_code_line (line))
-			continue;
-
-		p = (const char*) strstr ((const char*) line, "function");
-		if (p == NULL)
-			continue;
-
-		q = strchr ((const char*) line, '=');
-
-		if (q == NULL) {
-			p = p + 9;  /* skip the `function' word */
-			q = strchr ((const char*) p, '(');
-			extract_name (p, q, name);
-		} else if (*(q+1) != '=') { /* ignore `if type(v) == "function" then ...' */
-			p = (const char*) &line[0];
-			extract_name (p, q, name);
-		}
-	}
-	vStringDelete (name);
-}
-
-extern parserDefinition* LuaParser (void)
-{
-	static const char* const extensions [] = { "lua", NULL };
-	parserDefinition* def = parserNew ("Lua");
-	def->kindTable  = LuaKinds;
-	def->kindCount  = ARRAY_SIZE (LuaKinds);
-	def->extensions = extensions;
-	def->parser     = findLuaTags;
-	return def;
-}


Modified: ctags/parsers/geany_nsis.c
142 lines changed, 0 insertions(+), 142 deletions(-)
===================================================================
@@ -1,142 +0,0 @@
-/*
-*   Copyright (c) 2000-2002, Darren Hiebert
-*   Copyright (c) 2009-2011, Enrico Tröger
-*
-*   This source code is released for free distribution under the terms of the
-*   GNU General Public License.
-*
-*   This module contains functions for generating tags for NSIS scripts (based on sh.c).
-*/
-
-/*
-*   INCLUDE FILES
-*/
-#include "general.h"	/* must always come first */
-
-#include <string.h>
-
-#include "parse.h"
-#include "read.h"
-#include "vstring.h"
-#include "routines.h"
-
-/*
-*   DATA DEFINITIONS
-*/
-typedef enum {
-	K_SECTION,
-	K_FUNCTION,
-	K_VARIABLE
-} NsisKind;
-
-static kindDefinition NsisKinds [] = {
-	{ true, 'n', "namespace", "sections"},
-	{ true, 'f', "function", "functions"},
-	{ true, 'v', "variable", "variables"}
-};
-
-/*
-*   FUNCTION DEFINITIONS
-*/
-
-static void findNsisTags (void)
-{
-	vString *name = vStringNew ();
-	const unsigned char *line;
-
-	while ((line = readLineFromInputFile ()) != NULL)
-	{
-		const unsigned char* cp = line;
-
-		while (isspace (*cp))
-			cp++;
-
-		if (*cp == '#' || *cp == ';')
-			continue;
-
-		/* functions */
-		if (strncasecmp ((const char*) cp, "function", (size_t) 8) == 0 &&
-			isspace ((int) cp [8]))
-		{
-			cp += 8;
-			/* skip all whitespace */
-			while (isspace ((int) *cp))
-				++cp;
-			while (isalnum ((int) *cp) || *cp == '_' || *cp == '-' || *cp == '.' || *cp == '!')
-			{
-				vStringPut (name, (int) *cp);
-				++cp;
-			}
-			makeSimpleTag (name, K_FUNCTION);
-			vStringClear (name);
-		}
-		/* variables */
-		else if (strncasecmp ((const char*) cp, "var", (size_t) 3) == 0 &&
-			isspace ((int) cp [3]))
-		{
-			cp += 3;
-			/* skip all whitespace */
-			while (isspace ((int) *cp))
-				++cp;
-			/* skip any flags */
-			while (*cp == '/')
-			{
-				++cp;
-				while (! isspace ((int) *cp))
-					++cp;
-				while (isspace ((int) *cp))
-					++cp;
-			}
-			while (isalnum ((int) *cp) || *cp == '_')
-			{
-				vStringPut (name, (int) *cp);
-				++cp;
-			}
-			makeSimpleTag (name, K_VARIABLE);
-			vStringClear (name);
-		}
-		/* sections */
-		else if (strncasecmp ((const char*) cp, "section", (size_t) 7) == 0  &&
-				 isspace ((int) cp [7]))
-		{
-			bool in_quotes = false;
-			cp += 7;
-			/* skip all whitespace */
-			while (isspace ((int) *cp))
-			++cp;
-			while (isalnum ((int) *cp) || isspace ((int) *cp) ||
-				   *cp == '_' || *cp == '-' || *cp == '.' || *cp == '!' || *cp == '"')
-			{
-				if (*cp == '"')
-				{
-					if (in_quotes)
-						break;
-					else
-					{
-						in_quotes = true;
-						++cp;
-						continue;
-					}
-				}
-				vStringPut (name, (int) *cp);
-				++cp;
-			}
-			makeSimpleTag (name, K_SECTION);
-			vStringClear (name);
-		}
-	}
-	vStringDelete (name);
-}
-
-extern parserDefinition* NsisParser (void)
-{
-	static const char *const extensions [] = {
-		"nsi", "nsh", NULL
-	};
-	parserDefinition* def = parserNew ("NSIS");
-	def->kindTable  = NsisKinds;
-	def->kindCount  = ARRAY_SIZE (NsisKinds);
-	def->extensions = extensions;
-	def->parser     = findNsisTags;
-	return def;
-}


Modified: ctags/parsers/geany_perl.c
380 lines changed, 0 insertions(+), 380 deletions(-)
===================================================================
@@ -1,380 +0,0 @@
-/*
-*   Copyright (c) 2000-2003, Darren Hiebert
-*
-*   This source code is released for free distribution under the terms of the
-*   GNU General Public License version 2 or (at your option) any later version.
-*
-*   This module contains functions for generating tags for PERL language
-*   files.
-*/
-
-/*
-*   INCLUDE FILES
-*/
-#include "general.h"  /* must always come first */
-#include "debug.h"
-
-#include <string.h>
-
-#include "entry.h"
-#include "promise.h"
-#include "options.h"
-#include "read.h"
-#include "routines.h"
-#include "vstring.h"
-#include "xtag.h"
-
-#define TRACE_PERL_C 0
-#define TRACE if (TRACE_PERL_C) printf("perl.c:%d: ", __LINE__), printf
-
-/*
-*   DATA DEFINITIONS
-*/
-typedef enum {
-	K_NONE = -1,
-	K_CONSTANT,
-	K_FORMAT,
-	K_LABEL,
-	K_PACKAGE,
-	K_SUBROUTINE,
-	K_SUBROUTINE_DECLARATION
-} perlKind;
-
-static kindDefinition PerlKinds [] = {
-	{ true,  'c', "constant",               "constants" },
-	{ true,  'f', "format",                 "formats" },
-	{ true,  'l', "label",                  "labels" },
-	{ true,  'p', "package",                "packages" },
-	{ true,  's', "subroutine",             "subroutines" },
-	{ false, 'd', "subroutineDeclaration",  "subroutine declarations" },
-};
-
-/*
-*   FUNCTION DEFINITIONS
-*/
-
-static bool isIdentifier1 (int c)
-{
-	return (bool) (isalpha (c) || c == '_');
-}
-
-static bool isIdentifier (int c)
-{
-	return (bool) (isalnum (c) || c == '_');
-}
-
-static bool isPodWord (const char *word)
-{
-	bool result = false;
-	if (isalpha (*word))
-	{
-		const char *const pods [] = {
-			"head1", "head2", "head3", "head4", "over", "item", "back",
-			"pod", "begin", "end", "for"
-		};
-		const size_t count = ARRAY_SIZE (pods);
-		const char *white = strpbrk (word, " \t");
-		const size_t len = (white!=NULL) ? (size_t)(white-word) : strlen (word);
-		char *const id = (char*) eMalloc (len + 1);
-		size_t i;
-		strncpy (id, word, len);
-		id [len] = '\0';
-		for (i = 0  ;  i < count  &&  ! result  ;  ++i)
-		{
-			if (strcmp (id, pods [i]) == 0)
-				result = true;
-		}
-		eFree (id);
-	}
-	return result;
-}
-
-/*
- * Perl subroutine declaration may look like one of the following:
- *
- *  sub abc;
- *  sub abc :attr;
- *  sub abc (proto);
- *  sub abc (proto) :attr;
- *
- * Note that there may be more than one attribute.  Attributes may
- * have things in parentheses (they look like arguments).  Anything
- * inside of those parentheses goes.  Prototypes may contain semi-colons.
- * The matching end when we encounter (outside of any parentheses) either
- * a semi-colon (that'd be a declaration) or an left curly brace
- * (definition).
- *
- * This is pretty complicated parsing (plus we all know that only perl can
- * parse Perl), so we are only promising best effort here.
- *
- * If we can't determine what this is (due to a file ending, for example),
- * we will return false.
- */
-static bool isSubroutineDeclaration (const unsigned char *cp)
-{
-	bool attr = false;
-	int nparens = 0;
-
-	do {
-		for ( ; *cp; ++cp) {
-SUB_DECL_SWITCH:
-			switch (*cp) {
-				case ':':
-					if (nparens)
-						break;
-					else if (true == attr)
-						return false;    /* Invalid attribute name */
-					else
-						attr = true;
-					break;
-				case '(':
-					++nparens;
-					break;
-				case ')':
-					--nparens;
-					break;
-				case ' ':
-				case '\t':
-					break;
-				case ';':
-					if (!nparens)
-						return true;
-					/* fall through */
-				case '{':
-					if (!nparens)
-						return false;
-					/* fall through */
-				default:
-					if (attr) {
-						if (isIdentifier1(*cp)) {
-							cp++;
-							while (isIdentifier (*cp))
-								cp++;
-							attr = false;
-							goto SUB_DECL_SWITCH; /* Instead of --cp; */
-						} else {
-							return false;
-						}
-					} else if (nparens) {
-						break;
-					} else {
-						return false;
-					}
-			}
-		}
-	} while (NULL != (cp = readLineFromInputFile ()));
-
-	return false;
-}
-
-/* Algorithm adapted from from GNU etags.
- * Perl support by Bart Robinson <lomew at cs.utah.edu>
- * Perl sub names: look for /^ [ \t\n]sub [ \t\n]+ [^ \t\n{ (]+/
- */
-static void findPerlTags (void)
-{
-	vString *name = vStringNew ();
-	vString *package = NULL;
-	bool skipPodDoc = false;
-	const unsigned char *line;
-
-	while ((line = readLineFromInputFile ()) != NULL)
-	{
-		bool spaceRequired = false;
-		bool qualified = false;
-		const unsigned char *cp = line;
-		perlKind kind = K_NONE;
-		tagEntryInfo e;
-
-		if (skipPodDoc)
-		{
-			if (strncmp ((const char*) line, "=cut", (size_t) 4) == 0)
-				skipPodDoc = false;
-			continue;
-		}
-		else if (line [0] == '=')
-		{
-			skipPodDoc = isPodWord ((const char*)line + 1);
-			continue;
-		}
-		else if (strcmp ((const char*) line, "__DATA__") == 0)
-			break;
-		else if (strcmp ((const char*) line, "__END__") == 0)
-			break;
-		else if (line [0] == '#')
-			continue;
-
-		while (isspace (*cp))
-			cp++;
-
-		if (strncmp((const char*) cp, "sub", (size_t) 3) == 0)
-		{
-			TRACE("this looks like a sub\n");
-			cp += 3;
-			kind = K_SUBROUTINE;
-			spaceRequired = true;
-			qualified = true;
-		}
-		else if (strncmp((const char*) cp, "use", (size_t) 3) == 0)
-		{
-			cp += 3;
-			if (!isspace(*cp))
-				continue;
-			while (*cp && isspace (*cp))
-				++cp;
-			if (strncmp((const char*) cp, "constant", (size_t) 8) != 0)
-				continue;
-			cp += 8;
-			kind = K_CONSTANT;
-			spaceRequired = true;
-			qualified = true;
-		}
-		else if (strncmp((const char*) cp, "package", (size_t) 7) == 0)
-		{
-			/* This will point to space after 'package' so that a tag
-			   can be made */
-			const unsigned char *space = cp += 7;
-
-			if (package == NULL)
-				package = vStringNew ();
-			else
-				vStringClear (package);
-			while (isspace (*cp))
-				cp++;
-			while ((int) *cp != ';'  &&  !isspace ((int) *cp))
-			{
-				vStringPut (package, (int) *cp);
-				cp++;
-			}
-			vStringCatS (package, "::");
-
-			cp = space;	 /* Rewind */
-			kind = K_PACKAGE;
-			spaceRequired = true;
-			qualified = true;
-		}
-		else if (strncmp((const char*) cp, "format", (size_t) 6) == 0)
-		{
-			cp += 6;
-			kind = K_FORMAT;
-			spaceRequired = true;
-			qualified = true;
-		}
-		else
-		{
-			if (isIdentifier1 (*cp))
-			{
-				const unsigned char *p = cp;
-				while (isIdentifier (*p))
-					++p;
-				while (isspace (*p))
-					++p;
-				if ((int) *p == ':' && (int) *(p + 1) != ':')
-					kind = K_LABEL;
-			}
-		}
-		if (kind != K_NONE)
-		{
-			TRACE("cp0: %s\n", (const char *) cp);
-			if (spaceRequired && *cp && !isspace (*cp))
-				continue;
-
-			TRACE("cp1: %s\n", (const char *) cp);
-			while (isspace (*cp))
-				cp++;
-
-			while (!*cp || '#' == *cp) { /* Gobble up empty lines
-				                            and comments */
-				cp = readLineFromInputFile ();
-				if (!cp)
-					goto END_MAIN_WHILE;
-				while (isspace (*cp))
-					cp++;
-			}
-
-			while (isIdentifier (*cp) || (K_PACKAGE == kind && ':' == *cp))
-			{
-				vStringPut (name, (int) *cp);
-				cp++;
-			}
-
-			if (K_FORMAT == kind &&
-				vStringLength (name) == 0 && /* cp did not advance */
-				'=' == *cp)
-			{
-				/* format's name is optional.  If it's omitted, 'STDOUT'
-				   is assumed. */
-				vStringCatS (name, "STDOUT");
-			}
-
-			TRACE("name: %s\n", name->buffer);
-
-			if (0 == vStringLength(name)) {
-				vStringClear(name);
-				continue;
-			}
-
-			if (K_SUBROUTINE == kind)
-			{
-				/*
-				 * isSubroutineDeclaration() may consume several lines.  So
-				 * we record line positions.
-				 */
-				initTagEntry(&e, vStringValue(name), kind);
-
-				if (true == isSubroutineDeclaration(cp)) {
-					if (true == PerlKinds[K_SUBROUTINE_DECLARATION].enabled) {
-						kind = K_SUBROUTINE_DECLARATION;
-						e.kindIndex = kind;
-					} else {
-						vStringClear (name);
-						continue;
-					}
-				}
-
-				makeTagEntry(&e);
-
-				if (isXtagEnabled(XTAG_QUALIFIED_TAGS) && qualified &&
-					package != NULL  && vStringLength (package) > 0)
-				{
-					vString *const qualifiedName = vStringNew ();
-					vStringCopy (qualifiedName, package);
-					vStringCat (qualifiedName, name);
-					e.name = vStringValue(qualifiedName);
-					makeTagEntry(&e);
-					vStringDelete (qualifiedName);
-				}
-			} else if (vStringLength (name) > 0)
-			{
-				makeSimpleTag (name, kind);
-				if (isXtagEnabled(XTAG_QUALIFIED_TAGS) && qualified &&
-					K_PACKAGE != kind &&
-					package != NULL  && vStringLength (package) > 0)
-				{
-					vString *const qualifiedName = vStringNew ();
-					vStringCopy (qualifiedName, package);
-					vStringCat (qualifiedName, name);
-					makeSimpleTag (qualifiedName, kind);
-					vStringDelete (qualifiedName);
-				}
-			}
-			vStringClear (name);
-		}
-	}
-
-END_MAIN_WHILE:
-	vStringDelete (name);
-	if (package != NULL)
-		vStringDelete (package);
-}
-
-extern parserDefinition* PerlParser (void)
-{
-	static const char *const extensions [] = { "pl", "pm", "plx", "perl", NULL };
-	parserDefinition* def = parserNew ("Perl");
-	def->kindTable  = PerlKinds;
-	def->kindCount  = ARRAY_SIZE (PerlKinds);
-	def->extensions = extensions;
-	def->parser     = findPerlTags;
-	return def;
-}


Modified: ctags/parsers/geany_ruby.c
564 lines changed, 0 insertions(+), 564 deletions(-)
===================================================================
@@ -1,564 +0,0 @@
-/*
-*   Copyright (c) 2000-2001, Thaddeus Covert <sahuagin at mediaone.net>
-*   Copyright (c) 2002 Matthias Veit <matthias_veit at yahoo.de>
-*   Copyright (c) 2004 Elliott Hughes <enh at acm.org>
-*
-*   This source code is released for free distribution under the terms of the
-*   GNU General Public License version 2 or (at your option) any later version.
-*
-*   This module contains functions for generating tags for Ruby language
-*   files.
-*/
-
-/*
-*   INCLUDE FILES
-*/
-#include "general.h"  /* must always come first */
-
-#include <string.h>
-
-#include "debug.h"
-#include "entry.h"
-#include "parse.h"
-#include "nestlevel.h"
-#include "read.h"
-#include "routines.h"
-#include "vstring.h"
-
-/*
-*   DATA DECLARATIONS
-*/
-typedef enum {
-	K_UNDEFINED = -1, K_CLASS, K_METHOD, K_MODULE, K_SINGLETON,
-} rubyKind;
-
-/*
-*   DATA DEFINITIONS
-*/
-static kindDefinition RubyKinds [] = {
-	{ true, 'c', "class",  "classes" },
-	{ true, 'f', "method", "methods" },
-	{ true, 'm', "module", "modules" },
-	{ true, 'S', "singletonMethod", "singleton methods" },
-#if 0
-	/* Following two kinds are reserved. */
-	{ true, 'd', "describe", "describes and contexts for Rspec" },
-	{ true, 'C', "constant", "constants" },
-#endif
-};
-
-static NestingLevels* nesting = NULL;
-
-#define SCOPE_SEPARATOR '.'
-
-/*
-*   FUNCTION DEFINITIONS
-*/
-
-static void enterUnnamedScope (void);
-
-/*
-* Returns a string describing the scope in 'nls'.
-* We record the current scope as a list of entered scopes.
-* Scopes corresponding to 'if' statements and the like are
-* represented by empty strings. Scopes corresponding to
-* modules and classes are represented by the name of the
-* module or class.
-*/
-static vString* nestingLevelsToScope (const NestingLevels* nls)
-{
-	int i;
-	unsigned int chunks_output = 0;
-	vString* result = vStringNew ();
-	for (i = 0; i < nls->n; ++i)
-	{
-	    NestingLevel *nl = nestingLevelsGetNthFromRoot (nls, i);
-	    tagEntryInfo *e = getEntryOfNestingLevel (nl);
-	    if (e && strlen (e->name) > 0 && (!e->placeholder))
-	    {
-	        if (chunks_output++ > 0)
-	            vStringPut (result, SCOPE_SEPARATOR);
-	        vStringCatS (result, e->name);
-	    }
-	}
-	return result;
-}
-
-/*
-* Attempts to advance 's' past 'literal'.
-* Returns true if it did, false (and leaves 's' where
-* it was) otherwise.
-*/
-static bool canMatch (const unsigned char** s, const char* literal,
-                         bool (*end_check) (int))
-{
-	const int literal_length = strlen (literal);
-	const int s_length = strlen ((const char *)*s);
-
-	if (s_length < literal_length)
-		return false;
-
-	const unsigned char next_char = *(*s + literal_length);
-	if (strncmp ((const char*) *s, literal, literal_length) != 0)
-	{
-	    return false;
-	}
-	/* Additionally check that we're at the end of a token. */
-	if (! end_check (next_char))
-	{
-	    return false;
-	}
-	*s += literal_length;
-	return true;
-}
-
-static bool isIdentChar (int c)
-{
-	return (isalnum (c) || c == '_');
-}
-
-static bool notIdentChar (int c)
-{
-	return ! isIdentChar (c);
-}
-
-static bool notOperatorChar (int c)
-{
-	return ! (c == '[' || c == ']' ||
-	          c == '=' || c == '!' || c == '~' ||
-	          c == '+' || c == '-' ||
-	          c == '@' || c == '*' || c == '/' || c == '%' ||
-	          c == '<' || c == '>' ||
-	          c == '&' || c == '^' || c == '|');
-}
-
-static bool isWhitespace (int c)
-{
-	return c == 0 || isspace (c);
-}
-
-static bool canMatchKeyword (const unsigned char** s, const char* literal)
-{
-	return canMatch (s, literal, notIdentChar);
-}
-
-/*
-* Attempts to advance 'cp' past a Ruby operator method name. Returns
-* true if successful (and copies the name into 'name'), false otherwise.
-*/
-static bool parseRubyOperator (vString* name, const unsigned char** cp)
-{
-	static const char* RUBY_OPERATORS[] = {
-	    "[]", "[]=",
-	    "**",
-	    "!", "~", "+@", "-@",
-	    "*", "/", "%",
-	    "+", "-",
-	    ">>", "<<",
-	    "&",
-	    "^", "|",
-	    "<=", "<", ">", ">=",
-	    "<=>", "==", "===", "!=", "=~", "!~",
-	    "`",
-	    NULL
-	};
-	int i;
-	for (i = 0; RUBY_OPERATORS[i] != NULL; ++i)
-	{
-	    if (canMatch (cp, RUBY_OPERATORS[i], notOperatorChar))
-	    {
-	        vStringCatS (name, RUBY_OPERATORS[i]);
-	        return true;
-	    }
-	}
-	return false;
-}
-
-/*
-* Emits a tag for the given 'name' of kind 'kind' at the current nesting.
-*/
-static void emitRubyTag (vString* name, rubyKind kind)
-{
-	tagEntryInfo tag;
-	vString* scope;
-	tagEntryInfo *parent;
-	rubyKind parent_kind = K_UNDEFINED;
-	NestingLevel *lvl;
-	const char *unqualified_name;
-	const char *qualified_name;
-	int r;
-
-	if (!RubyKinds[kind].enabled) {
-		return;
-	}
-
-	scope = nestingLevelsToScope (nesting);
-	lvl = nestingLevelsGetCurrent (nesting);
-	parent = getEntryOfNestingLevel (lvl);
-	if (parent)
-		parent_kind =  parent->kindIndex;
-
-	qualified_name = vStringValue (name);
-	unqualified_name = strrchr (qualified_name, SCOPE_SEPARATOR);
-	if (unqualified_name && unqualified_name[1])
-	{
-		if (unqualified_name > qualified_name)
-		{
-			if (vStringLength (scope) > 0)
-				vStringPut (scope, SCOPE_SEPARATOR);
-			vStringNCatS (scope, qualified_name,
-			              unqualified_name - qualified_name);
-			/* assume module parent type for a lack of a better option */
-			parent_kind = K_MODULE;
-		}
-		unqualified_name++;
-	}
-	else
-		unqualified_name = qualified_name;
-
-	initTagEntry (&tag, unqualified_name, kind);
-	if (vStringLength (scope) > 0) {
-		Assert (0 <= parent_kind &&
-		        (size_t) parent_kind < (ARRAY_SIZE (RubyKinds)));
-
-		tag.extensionFields.scopeKindIndex = parent_kind;
-		tag.extensionFields.scopeName = vStringValue (scope);
-	}
-	r = makeTagEntry (&tag);
-
-	nestingLevelsPush (nesting, r);
-
-	vStringClear (name);
-	vStringDelete (scope);
-}
-
-/* Tests whether 'ch' is a character in 'list'. */
-static bool charIsIn (char ch, const char* list)
-{
-	return (strchr (list, ch) != NULL);
-}
-
-/* Advances 'cp' over leading whitespace. */
-static void skipWhitespace (const unsigned char** cp)
-{
-	while (isspace (**cp))
-	{
-	    ++*cp;
-	}
-}
-
-/*
-* Copies the characters forming an identifier from *cp into
-* name, leaving *cp pointing to the character after the identifier.
-*/
-static rubyKind parseIdentifier (
-		const unsigned char** cp, vString* name, rubyKind kind)
-{
-	/* Method names are slightly different to class and variable names.
-	 * A method name may optionally end with a question mark, exclamation
-	 * point or equals sign. These are all part of the name.
-	 * A method name may also contain a period if it's a singleton method.
-	 */
-	bool had_sep = false;
-	const char* also_ok;
-	if (kind == K_METHOD)
-	{
-		also_ok = ".?!=";
-	}
-	else if (kind == K_SINGLETON)
-	{
-		also_ok = "?!=";
-	}
-	else
-	{
-		also_ok = "";
-	}
-
-	skipWhitespace (cp);
-
-	/* Check for an anonymous (singleton) class such as "class << HTTP". */
-	if (kind == K_CLASS && **cp == '<' && *(*cp + 1) == '<')
-	{
-		return K_UNDEFINED;
-	}
-
-	/* Check for operators such as "def []=(key, val)". */
-	if (kind == K_METHOD || kind == K_SINGLETON)
-	{
-		if (parseRubyOperator (name, cp))
-		{
-			return kind;
-		}
-	}
-
-	/* Copy the identifier into 'name'. */
-	while (**cp != 0 && (**cp == ':' || isIdentChar (**cp) || charIsIn (**cp, also_ok)))
-	{
-		char last_char = **cp;
-
-		if (last_char == ':')
-			had_sep = true;
-		else
-		{
-			if (had_sep)
-			{
-				vStringPut (name, SCOPE_SEPARATOR);
-				had_sep = false;
-			}
-			vStringPut (name, last_char);
-		}
-		++*cp;
-
-		if (kind == K_METHOD)
-		{
-			/* Recognize singleton methods. */
-			if (last_char == '.')
-			{
-				vStringClear (name);
-				return parseIdentifier (cp, name, K_SINGLETON);
-			}
-		}
-
-		if (kind == K_METHOD || kind == K_SINGLETON)
-		{
-			/* Recognize characters which mark the end of a method name. */
-			if (charIsIn (last_char, "?!="))
-			{
-				break;
-			}
-		}
-	}
-	return kind;
-}
-
-static void readAndEmitTag (const unsigned char** cp, rubyKind expected_kind)
-{
-	if (isspace (**cp))
-	{
-		vString *name = vStringNew ();
-		rubyKind actual_kind = parseIdentifier (cp, name, expected_kind);
-
-		if (actual_kind == K_UNDEFINED || vStringLength (name) == 0)
-		{
-			/*
-			* What kind of tags should we create for code like this?
-			*
-			*    %w(self.clfloor clfloor).each do |name|
-			*        module_eval <<-"end;"
-			*            def #{name}(x, y=1)
-			*                q, r = x.divmod(y)
-			*                q = q.to_i
-			*                return q, r
-			*            end
-			*        end;
-			*    end
-			*
-			* Or this?
-			*
-			*    class << HTTP
-			*
-			* For now, we don't create any.
-			*/
-			enterUnnamedScope ();
-		}
-		else
-		{
-			emitRubyTag (name, actual_kind);
-		}
-		vStringDelete (name);
-	}
-}
-
-static void enterUnnamedScope (void)
-{
-	int r = CORK_NIL;
-	NestingLevel *parent = nestingLevelsGetCurrent (nesting);
-	tagEntryInfo *e_parent = getEntryOfNestingLevel (parent);
-
-	if (e_parent)
-	{
-		tagEntryInfo e;
-		initTagEntry (&e, "", e_parent->kindIndex);
-		e.placeholder = 1;
-		r = makeTagEntry (&e);
-	}
-	nestingLevelsPush (nesting, r);
-}
-
-static void findRubyTags (void)
-{
-	const unsigned char *line;
-	bool inMultiLineComment = false;
-
-	nesting = nestingLevelsNew (0);
-
-	/* FIXME: this whole scheme is wrong, because Ruby isn't line-based.
-	* You could perfectly well write:
-	*
-	*  def
-	*  method
-	*   puts("hello")
-	*  end
-	*
-	* if you wished, and this function would fail to recognize anything.
-	*/
-	while ((line = readLineFromInputFile ()) != NULL)
-	{
-		const unsigned char *cp = line;
-		/* if we expect a separator after a while, for, or until statement
-		 * separators are "do", ";" or newline */
-		bool expect_separator = false;
-
-		if (canMatch (&cp, "=begin", isWhitespace))
-		{
-			inMultiLineComment = true;
-			continue;
-		}
-		if (canMatch (&cp, "=end", isWhitespace))
-		{
-			inMultiLineComment = false;
-			continue;
-		}
-		if (inMultiLineComment)
-			continue;
-
-		skipWhitespace (&cp);
-
-		/* Avoid mistakenly starting a scope for modifiers such as
-		*
-		*   return if <exp>
-		*
-		* FIXME: this is fooled by code such as
-		*
-		*   result = if <exp>
-		*               <a>
-		*            else
-		*               <b>
-		*            end
-		*
-		* FIXME: we're also fooled if someone does something heinous such as
-		*
-		*   puts("hello") \
-		*       unless <exp>
-		*/
-		if (canMatchKeyword (&cp, "for") ||
-		    canMatchKeyword (&cp, "until") ||
-		    canMatchKeyword (&cp, "while"))
-		{
-			expect_separator = true;
-			enterUnnamedScope ();
-		}
-		else if (canMatchKeyword (&cp, "case") ||
-		         canMatchKeyword (&cp, "if") ||
-		         canMatchKeyword (&cp, "unless"))
-		{
-			enterUnnamedScope ();
-		}
-
-		/*
-		* "module M", "class C" and "def m" should only be at the beginning
-		* of a line.
-		*/
-		if (canMatchKeyword (&cp, "module"))
-		{
-			readAndEmitTag (&cp, K_MODULE);
-		}
-		else if (canMatchKeyword (&cp, "class"))
-		{
-			readAndEmitTag (&cp, K_CLASS);
-		}
-		else if (canMatchKeyword (&cp, "def"))
-		{
-			rubyKind kind = K_METHOD;
-			NestingLevel *nl = nestingLevelsGetCurrent (nesting);
-			tagEntryInfo *e  = getEntryOfNestingLevel (nl);
-
-			/* if the def is inside an unnamed scope at the class level, assume
-			 * it's from a singleton from a construct like this:
-			 *
-			 * class C
-			 *   class << self
-			 *     def singleton
-			 *       ...
-			 *     end
-			 *   end
-			 * end
-			 */
-			if (e && e->kindIndex == K_CLASS && strlen (e->name) == 0)
-				kind = K_SINGLETON;
-			readAndEmitTag (&cp, kind);
-		}
-		while (*cp != '\0')
-		{
-			/* FIXME: we don't cope with here documents,
-			* or regular expression literals, or ... you get the idea.
-			* Hopefully, the restriction above that insists on seeing
-			* definitions at the starts of lines should keep us out of
-			* mischief.
-			*/
-			if (inMultiLineComment || isspace (*cp))
-			{
-				++cp;
-			}
-			else if (*cp == '#')
-			{
-				/* FIXME: this is wrong, but there *probably* won't be a
-				* definition after an interpolated string (where # doesn't
-				* mean 'comment').
-				*/
-				break;
-			}
-			else if (canMatchKeyword (&cp, "begin"))
-			{
-				enterUnnamedScope ();
-			}
-			else if (canMatchKeyword (&cp, "do"))
-			{
-				if (! expect_separator)
-					enterUnnamedScope ();
-				else
-					expect_separator = false;
-			}
-			else if (canMatchKeyword (&cp, "end") && nesting->n > 0)
-			{
-				/* Leave the most recent scope. */
-				nestingLevelsPop (nesting);
-			}
-			else if (*cp == '"')
-			{
-				/* Skip string literals.
-				 * FIXME: should cope with escapes and interpolation.
-				 */
-				do {
-					++cp;
-				} while (*cp != 0 && *cp != '"');
-				if (*cp == '"')
-					cp++; /* skip the last found '"' */
-			}
-			else if (*cp == ';')
-			{
-				++cp;
-				expect_separator = false;
-			}
-			else if (*cp != '\0')
-			{
-				do
-					++cp;
-				while (isIdentChar (*cp));
-			}
-		}
-	}
-	nestingLevelsFree (nesting);
-}
-
-extern parserDefinition* RubyParser (void)
-{
-	static const char *const extensions [] = { "rb", "ruby", NULL };
-	parserDefinition* def = parserNew ("Ruby");
-	def->kindTable  = RubyKinds;
-	def->kindCount  = ARRAY_SIZE (RubyKinds);
-	def->extensions = extensions;
-	def->parser     = findRubyTags;
-	def->useCork    = CORK_QUEUE;
-	return def;
-}


Modified: ctags/parsers/go.c
1416 lines changed, 1416 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,1416 @@
+/*
+*   This source code is released for free distribution under the terms of the
+*   GNU General Public License version 2 or (at your option) any later version.
+*
+*   Reference:
+*     https://golang.org/ref/spec
+*/
+
+
+/*
+ *   INCLUDE FILES
+ */
+#include "general.h"        /* must always come first */
+
+#include "debug.h"
+#include "entry.h"
+#include "keyword.h"
+#include "read.h"
+#include "numarray.h"
+#include "objpool.h"
+#include "parse.h"
+#include "routines.h"
+#include "vstring.h"
+#include "xtag.h"
+#include "field.h"
+#include "htable.h"
+
+#include <string.h>
+
+/*
+ *	 MACROS
+ */
+#define MAX_COLLECTOR_LENGTH 512
+#define isType(token,t) (bool) ((token)->type == (t))
+#define isKeyword(token,k) (bool) ((token)->keyword == (k))
+#define isStartIdentChar(c) (isalpha (c) ||  (c) == '_' || (c) > 128) /* XXX UTF-8 */
+#define isIdentChar(c) (isStartIdentChar (c) || isdigit (c))
+#define newToken() (objPoolGet (TokenPool))
+#define deleteToken(t) (objPoolPut (TokenPool, (t)))
+
+/*
+ *	 DATA DECLARATIONS
+ */
+
+enum eKeywordId {
+	KEYWORD_package,
+	KEYWORD_import,
+	KEYWORD_const,
+	KEYWORD_type,
+	KEYWORD_var,
+	KEYWORD_func,
+	KEYWORD_struct,
+	KEYWORD_interface,
+	KEYWORD_map,
+	KEYWORD_chan
+};
+typedef int keywordId; /* to allow KEYWORD_NONE */
+
+typedef enum eTokenType {
+	TOKEN_NONE = -1,
+	// Token not important for top-level Go parsing
+	TOKEN_OTHER,
+	TOKEN_KEYWORD,
+	TOKEN_IDENTIFIER,
+	TOKEN_STRING,
+	TOKEN_OPEN_PAREN,
+	TOKEN_CLOSE_PAREN,
+	TOKEN_OPEN_CURLY,
+	TOKEN_CLOSE_CURLY,
+	TOKEN_OPEN_SQUARE,
+	TOKEN_CLOSE_SQUARE,
+	TOKEN_SEMICOLON,
+	TOKEN_STAR,
+	TOKEN_LEFT_ARROW,
+	TOKEN_DOT,
+	TOKEN_COMMA,
+	TOKEN_EQUAL,
+	TOKEN_3DOTS,
+	TOKEN_EOF
+} tokenType;
+
+typedef struct sTokenInfo {
+	tokenType type;
+	keywordId keyword;
+	vString *string;		/* the name of the token */
+	unsigned long lineNumber;	/* line number of tag */
+	MIOPos filePosition;		/* file position of line containing name */
+	int c;						/* Used in AppendTokenToVString */
+} tokenInfo;
+
+typedef struct sCollector {
+	vString *str;
+	size_t last_len;
+} collector;
+
+/*
+*   DATA DEFINITIONS
+*/
+
+static int Lang_go;
+static objPool *TokenPool = NULL;
+
+typedef enum {
+	GOTAG_UNDEFINED = -1,
+	GOTAG_PACKAGE,
+	GOTAG_FUNCTION,
+	GOTAG_CONST,
+	GOTAG_TYPE,
+	GOTAG_VAR,
+	GOTAG_STRUCT,
+	GOTAG_INTERFACE,
+	GOTAG_MEMBER,
+	GOTAG_ANONMEMBER,
+	GOTAG_METHODSPEC,
+	GOTAG_UNKNOWN,
+	GOTAG_PACKAGE_NAME,
+	GOTAG_TALIAS,
+	GOTAG_RECEIVER,
+} goKind;
+
+typedef enum {
+	R_GOTAG_PACKAGE_IMPORTED,
+} GoPackageRole;
+
+static roleDefinition GoPackageRoles [] = {
+	{ true, "imported", "imported package" },
+};
+
+typedef enum {
+	R_GOTAG_UNKNOWN_RECEIVER,
+} GoUnknownRole;
+
+static roleDefinition GoUnknownRoles [] = {
+	{ true, "receiverType", "receiver type" },
+};
+
+static kindDefinition GoKinds[] = {
+	{true, 'p', "package", "packages",
+	  .referenceOnly = false, ATTACH_ROLES (GoPackageRoles)},
+	{true, 'f', "func", "functions"},
+	{true, 'c', "const", "constants"},
+	{true, 't', "type", "types"},
+	{true, 'v', "var", "variables"},
+	{true, 's', "struct", "structs"},
+	{true, 'i', "interface", "interfaces"},
+	{true, 'm', "member", "struct members"},
+	{true, 'M', "anonMember", "struct anonymous members"},
+	{true, 'n', "methodSpec", "interface method specification"},
+	{true, 'u', "unknown", "unknown",
+	 .referenceOnly = true, ATTACH_ROLES (GoUnknownRoles)},
+	{true, 'P', "packageName", "name for specifying imported package"},
+	{true, 'a', "talias", "type aliases"},
+	{false,'R', "receiver", "receivers"},
+};
+
+static const keywordTable GoKeywordTable[] = {
+	{"package", KEYWORD_package},
+	{"import", KEYWORD_import},
+	{"const", KEYWORD_const},
+	{"type", KEYWORD_type},
+	{"var", KEYWORD_var},
+	{"func", KEYWORD_func},
+	{"struct", KEYWORD_struct},
+	{"interface", KEYWORD_interface},
+	{"map", KEYWORD_map},
+	{"chan", KEYWORD_chan}
+};
+
+typedef enum {
+	F_PACKAGE,
+	F_PACKAGE_NAME,
+	F_HOW_IMPORTED,
+} goField;
+
+static fieldDefinition GoFields [] = {
+	{
+		.name = "package",
+		.description = "the real package specified by the package name",
+		.enabled = true,
+	},
+	{
+		.name = "packageName",
+		.description = "the name for referring the package",
+		.enabled = true,
+	},
+	{
+		.name = "howImported",
+		.description = "how the package is imported (\"inline\" for `.' or \"init\" for `_')",
+		.enabled = false,
+	},
+};
+
+
+/*
+*   FUNCTION DEFINITIONS
+*/
+
+static void *newPoolToken (void *createArg CTAGS_ATTR_UNUSED)
+{
+	tokenInfo *const token = xMalloc (1, tokenInfo);
+	token->string = vStringNew ();
+	return token;
+}
+
+static void clearPoolToken (void *data)
+{
+	tokenInfo *token = data;
+
+	token->type = TOKEN_NONE;
+	token->keyword = KEYWORD_NONE;
+	token->lineNumber   = getInputLineNumber ();
+	token->filePosition = getInputFilePosition ();
+	vStringClear (token->string);
+}
+
+static void copyToken (tokenInfo *const dest, const tokenInfo *const other)
+{
+	dest->type = other->type;
+	dest->keyword = other->keyword;
+	vStringCopy(dest->string, other->string);
+	dest->lineNumber = other->lineNumber;
+	dest->filePosition = other->filePosition;
+}
+
+static void deletePoolToken (void* data)
+{
+	tokenInfo * const token = data;
+
+	vStringDelete (token->string);
+	eFree (token);
+}
+
+static void initialize (const langType language)
+{
+	Lang_go = language;
+	TokenPool = objPoolNew (16, newPoolToken, deletePoolToken, clearPoolToken, NULL);
+}
+
+static void finalize (const langType language, bool initialized)
+{
+	if (!initialized)
+		return;
+
+	objPoolDelete (TokenPool);
+}
+
+/*
+ *   Parsing functions
+ */
+
+static void parseString (vString *const string, const int delimiter)
+{
+	bool end = false;
+	while (!end)
+	{
+		int c = getcFromInputFile ();
+		if (c == EOF)
+			end = true;
+		else if (c == '\\' && delimiter != '`')
+		{
+			c = getcFromInputFile ();
+			if (c != '\'' && c != '\"')
+				vStringPut (string, '\\');
+			vStringPut (string, c);
+		}
+		else if (c == delimiter)
+			end = true;
+		else
+			vStringPut (string, c);
+	}
+}
+
+static void parseIdentifier (vString *const string, const int firstChar)
+{
+	int c = firstChar;
+	do
+	{
+		vStringPut (string, c);
+		c = getcFromInputFile ();
+	} while (isIdentChar (c));
+	ungetcToInputFile (c);		/* always unget, LF might add a semicolon */
+}
+
+static bool collectorIsEmpty(collector *collector)
+{
+	return !vStringLength(collector->str);
+}
+
+static void collectorPut (collector *collector, char c)
+{
+	if ((vStringLength(collector->str) > 2)
+		&& strcmp (vStringValue (collector->str) + (vStringLength(collector->str) - 3),
+				  "...") == 0
+		&& c == ' ')
+		return;
+	else if (vStringLength(collector->str) > 0)
+	{
+		if (vStringLast(collector->str) == '(' && c == ' ')
+			return;
+		else if (vStringLast(collector->str) == ' ' && c == ')')
+			vStringChop(collector->str);
+	}
+
+	collector->last_len = vStringLength (collector->str);
+	vStringPut (collector->str, c);
+}
+
+static void collectorCatS (collector *collector, char *cstr)
+{
+	collector->last_len = vStringLength (collector->str);
+	vStringCatS (collector->str, cstr);
+}
+
+static void collectorCat (collector *collector, vString *str)
+{
+	collector->last_len = vStringLength (collector->str);
+	vStringCat (collector->str, str);
+}
+
+static void collectorAppendToken (collector *collector, const tokenInfo *const token)
+{
+	if (token->type == TOKEN_LEFT_ARROW)
+		collectorCatS (collector, "<-");
+	else if (token->type == TOKEN_STRING)
+	{
+		// only struct member annotations can appear in function prototypes
+		// so only `` type strings are possible
+		collector->last_len = vStringLength (collector->str);
+		vStringPut(collector->str, '`');
+		vStringCat(collector->str, token->string);
+		vStringPut(collector->str, '`');
+	}
+	else if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_KEYWORD)
+		collectorCat (collector, token->string);
+	else if (token->type == TOKEN_3DOTS)
+	{
+		if ((vStringLength (collector->str) > 0)
+			&& vStringLast(collector->str) != ' ')
+			collectorPut (collector, ' ');
+		collectorCatS (collector, "...");
+	}
+	else if (token->c != EOF)
+		collectorPut (collector, token->c);
+}
+
+static void collectorTruncate (collector *collector, bool dropLast)
+{
+	if (dropLast)
+		vStringTruncate (collector->str, collector->last_len);
+
+	vStringStripLeading (collector->str);
+	vStringStripTrailing (collector->str);
+}
+
+static void readTokenFull (tokenInfo *const token, collector *collector)
+{
+	int c;
+	static tokenType lastTokenType = TOKEN_NONE;
+	bool firstWhitespace = true;
+	bool whitespace;
+
+	token->c = EOF;
+	token->type = TOKEN_NONE;
+	token->keyword = KEYWORD_NONE;
+	vStringClear (token->string);
+
+getNextChar:
+	do
+	{
+		c = getcFromInputFile ();
+		token->lineNumber = getInputLineNumber ();
+		token->filePosition = getInputFilePosition ();
+		if (c == '\n' && (lastTokenType == TOKEN_IDENTIFIER ||
+						  lastTokenType == TOKEN_STRING ||
+						  lastTokenType == TOKEN_OTHER ||
+						  lastTokenType == TOKEN_CLOSE_PAREN ||
+						  lastTokenType == TOKEN_CLOSE_CURLY ||
+						  lastTokenType == TOKEN_CLOSE_SQUARE))
+		{
+			c = ';';  // semicolon injection
+		}
+		whitespace = c == '\t'  ||  c == ' ' ||  c == '\r' || c == '\n';
+		if (collector && whitespace && firstWhitespace && vStringLength (collector->str) < MAX_COLLECTOR_LENGTH)
+		{
+			firstWhitespace = false;
+			collectorPut (collector, ' ');
+		}
+	}
+	while (whitespace);
+
+	switch (c)
+	{
+		case EOF:
+			token->type = TOKEN_EOF;
+			break;
+
+		case ';':
+			token->type = TOKEN_SEMICOLON;
+			break;
+
+		case '/':
+			{
+				bool hasNewline = false;
+				int d = getcFromInputFile ();
+				switch (d)
+				{
+					case '/':
+						skipToCharacterInInputFile ('\n');
+						/* Line comments start with the
+						 * character sequence // and
+						 * continue through the next
+						 * newline. A line comment acts
+						 * like a newline.  */
+						ungetcToInputFile ('\n');
+						goto getNextChar;
+					case '*':
+						do
+						{
+							do
+							{
+								d = getcFromInputFile ();
+								if (d == '\n')
+								{
+									hasNewline = true;
+								}
+							} while (d != EOF && d != '*');
+
+							c = getcFromInputFile ();
+							if (c == '/')
+								break;
+							else
+								ungetcToInputFile (c);
+						} while (c != EOF && c != '\0');
+
+						ungetcToInputFile (hasNewline ? '\n' : ' ');
+						goto getNextChar;
+					default:
+						token->type = TOKEN_OTHER;
+						ungetcToInputFile (d);
+						break;
+				}
+			}
+			break;
+
+		case '"':
+		case '\'':
+		case '`':
+			token->type = TOKEN_STRING;
+			parseString (token->string, c);
+			token->lineNumber = getInputLineNumber ();
+			token->filePosition = getInputFilePosition ();
+			break;
+
+		case '<':
+			{
+				int d = getcFromInputFile ();
+				if (d == '-')
+					token->type = TOKEN_LEFT_ARROW;
+				else
+				{
+					ungetcToInputFile (d);
+					token->type = TOKEN_OTHER;
+				}
+			}
+			break;
+
+		case '(':
+			token->type = TOKEN_OPEN_PAREN;
+			break;
+
+		case ')':
+			token->type = TOKEN_CLOSE_PAREN;
+			break;
+
+		case '{':
+			token->type = TOKEN_OPEN_CURLY;
+			break;
+
+		case '}':
+			token->type = TOKEN_CLOSE_CURLY;
+			break;
+
+		case '[':
+			token->type = TOKEN_OPEN_SQUARE;
+			break;
+
+		case ']':
+			token->type = TOKEN_CLOSE_SQUARE;
+			break;
+
+		case '*':
+			token->type = TOKEN_STAR;
+			break;
+
+		case '.':
+			{
+				int d, e;
+				d = getcFromInputFile ();
+				if (d == '.')
+				{
+					e = getcFromInputFile ();
+					if (e == '.')
+					{
+						token->type = TOKEN_3DOTS;
+						break;
+					}
+					else
+					{
+						ungetcToInputFile (e);
+						ungetcToInputFile (d);
+					}
+				}
+				else
+					ungetcToInputFile (d);
+			}
+			token->type = TOKEN_DOT;
+			break;
+
+		case ',':
+			token->type = TOKEN_COMMA;
+			break;
+
+		case '=':
+			token->type = TOKEN_EQUAL;
+			break;
+
+		default:
+			if (isStartIdentChar (c))
+			{
+				parseIdentifier (token->string, c);
+				token->lineNumber = getInputLineNumber ();
+				token->filePosition = getInputFilePosition ();
+				token->keyword = lookupKeyword (vStringValue (token->string), Lang_go);
+				if (isKeyword (token, KEYWORD_NONE))
+					token->type = TOKEN_IDENTIFIER;
+				else
+					token->type = TOKEN_KEYWORD;
+			}
+			else
+				token->type = TOKEN_OTHER;
+			break;
+	}
+
+	token->c = c;
+
+	if (collector && vStringLength (collector->str) < MAX_COLLECTOR_LENGTH)
+		collectorAppendToken (collector, token);
+
+	lastTokenType = token->type;
+}
+
+static void readToken (tokenInfo *const token)
+{
+	readTokenFull (token, NULL);
+}
+
+static bool skipToMatchedNoRead (tokenInfo *const token, collector *collector)
+{
+	int nest_level = 0;
+	tokenType open_token = token->type;
+	tokenType close_token;
+
+	switch (open_token)
+	{
+		case TOKEN_OPEN_PAREN:
+			close_token = TOKEN_CLOSE_PAREN;
+			break;
+		case TOKEN_OPEN_CURLY:
+			close_token = TOKEN_CLOSE_CURLY;
+			break;
+		case TOKEN_OPEN_SQUARE:
+			close_token = TOKEN_CLOSE_SQUARE;
+			break;
+		default:
+			return false;
+	}
+
+	/*
+	 * This routine will skip to a matching closing token.
+	 * It will also handle nested tokens.
+	 */
+	nest_level++;
+	while (nest_level > 0 && !isType (token, TOKEN_EOF))
+	{
+		readTokenFull (token, collector);
+		if (isType (token, open_token))
+			nest_level++;
+		else if (isType (token, close_token))
+			nest_level--;
+	}
+
+	return true;
+}
+
+static void skipToMatched (tokenInfo *const token, collector *collector)
+{
+	if (skipToMatchedNoRead (token, collector))
+		readTokenFull (token, collector);
+}
+
+static bool skipType (tokenInfo *const token, collector *collector)
+{
+	// Type      = TypeName | TypeLit | "(" Type ")" .
+	// Skips also function multiple return values "(" Type {"," Type} ")"
+	if (isType (token, TOKEN_OPEN_PAREN))
+	{
+		skipToMatched (token, collector);
+		return true;
+	}
+
+	// TypeName  = QualifiedIdent.
+	// QualifiedIdent = [ PackageName "." ] identifier .
+	// PackageName    = identifier .
+	if (isType (token, TOKEN_IDENTIFIER))
+	{
+		readTokenFull (token, collector);
+		if (isType (token, TOKEN_DOT))
+		{
+			readTokenFull (token, collector);
+			if (isType (token, TOKEN_IDENTIFIER))
+				readTokenFull (token, collector);
+		}
+		return true;
+	}
+
+	// StructType     = "struct" "{" { FieldDecl ";" } "}"
+	// InterfaceType      = "interface" "{" { MethodSpec ";" } "}" .
+	if (isKeyword (token, KEYWORD_struct) || isKeyword (token, KEYWORD_interface))
+	{
+		readTokenFull (token, collector);
+		// skip over "{}"
+		skipToMatched (token, collector);
+		return true;
+	}
+
+	// ArrayType   = "[" ArrayLength "]" ElementType .
+	// SliceType = "[" "]" ElementType .
+	// ElementType = Type .
+	if (isType (token, TOKEN_OPEN_SQUARE))
+	{
+		skipToMatched (token, collector);
+		return skipType (token, collector);
+	}
+
+	// PointerType = "*" BaseType .
+	// BaseType = Type .
+	// ChannelType = ( "chan" [ "<-" ] | "<-" "chan" ) ElementType .
+	if (isType (token, TOKEN_STAR) || isKeyword (token, KEYWORD_chan) || isType (token, TOKEN_LEFT_ARROW))
+	{
+		readTokenFull (token, collector);
+		return skipType (token, collector);
+	}
+
+	// MapType     = "map" "[" KeyType "]" ElementType .
+	// KeyType     = Type .
+	if (isKeyword (token, KEYWORD_map))
+	{
+		readTokenFull (token, collector);
+		// skip over "[]"
+		skipToMatched (token, collector);
+		return skipType (token, collector);
+	}
+
+	// FunctionType   = "func" Signature .
+	// Signature      = Parameters [ Result ] .
+	// Result         = Parameters | Type .
+	// Parameters     = "(" [ ParameterList [ "," ] ] ")" .
+	if (isKeyword (token, KEYWORD_func))
+	{
+		readTokenFull (token, collector);
+		// Parameters, skip over "()"
+		skipToMatched (token, collector);
+		// Result is parameters or type or nothing.  skipType treats anything
+		// surrounded by parentheses as a type, and does nothing if what
+		// follows is not a type.
+		return skipType (token, collector);
+	}
+
+	return false;
+}
+
+static int makeTagFull (tokenInfo *const token, const goKind kind,
+						const int scope, const char *argList, const char *typeref,
+						const int role)
+{
+	const char *const name = vStringValue (token->string);
+
+	tagEntryInfo e;
+
+	/* Don't record `_' placeholder variable  */
+	if (kind == GOTAG_VAR && name[0] == '_' && name[1] == '\0')
+		return CORK_NIL;
+
+	initRefTagEntry (&e, name, kind, role);
+
+	e.lineNumber = token->lineNumber;
+	e.filePosition = token->filePosition;
+	if (argList)
+		e.extensionFields.signature = argList;
+	if (typeref)
+	{
+		/* Follows Cxx parser convention */
+		e.extensionFields.typeRef [0] = "typename";
+		e.extensionFields.typeRef [1] = typeref;
+	}
+
+	e.extensionFields.scopeIndex = scope;
+	return makeTagEntry (&e);
+}
+
+static int makeTag (tokenInfo *const token, const goKind kind,
+					const int scope, const char *argList, const char *typeref)
+{
+	return makeTagFull (token, kind, scope, argList, typeref,
+						ROLE_DEFINITION_INDEX);
+}
+
+static int makeRefTag (tokenInfo *const token, const goKind kind,
+					   const int role)
+{
+	return makeTagFull (token, kind, CORK_NIL, NULL, NULL, role);
+}
+
+static int parsePackage (tokenInfo *const token)
+{
+	readToken (token);
+	if (isType (token, TOKEN_IDENTIFIER))
+	{
+		return makeTag (token, GOTAG_PACKAGE, CORK_NIL, NULL, NULL);
+	}
+	else
+		return CORK_NIL;
+}
+
+static tokenInfo * parseReceiver (tokenInfo *const token, int *corkIndex)
+{
+	tokenInfo *receiver_type_token = NULL;
+	int nest_level = 1;
+
+	*corkIndex = CORK_NIL;
+
+	/* Looking for an identifier before ')'. */
+	while (nest_level > 0 && !isType (token, TOKEN_EOF))
+	{
+		if (isType (token, TOKEN_IDENTIFIER))
+		{
+			if (*corkIndex == CORK_NIL)
+				*corkIndex = makeTag (token, GOTAG_RECEIVER, CORK_NIL, NULL, NULL);
+			if (!receiver_type_token)
+				receiver_type_token = newToken ();
+			copyToken (receiver_type_token, token);
+		}
+
+		readToken (token);
+		if (isType (token, TOKEN_OPEN_PAREN))
+			nest_level++;
+		else if (isType (token, TOKEN_CLOSE_PAREN))
+			nest_level--;
+	}
+
+	if (nest_level > 0 && receiver_type_token)
+	{
+		deleteToken (receiver_type_token);
+		receiver_type_token = NULL;
+	}
+
+	if (receiver_type_token)
+	{
+		tagEntryInfo *e = getEntryInCorkQueue (*corkIndex);
+		if (e)
+		{
+			e->extensionFields.typeRef [0] = eStrdup ("typename");
+			e->extensionFields.typeRef [1] = vStringStrdup (receiver_type_token->string);
+		}
+	}
+	readToken (token);
+	return receiver_type_token;
+}
+
+static void parseFunctionOrMethod (tokenInfo *const token, const int scope)
+{
+	int receiver_cork = CORK_NIL;
+	tokenInfo *receiver_type_token = NULL;
+
+	// FunctionDecl = "func" identifier Signature [ Body ] .
+	// Body         = Block.
+	//
+	// MethodDecl   = "func" Receiver MethodName Signature [ Body ] .
+	// Receiver     = "(" [ identifier ] [ "*" ] BaseTypeName ")" .
+	// BaseTypeName = identifier .
+
+	// Pick up receiver type.
+	readToken (token);
+	if (isType (token, TOKEN_OPEN_PAREN))
+		receiver_type_token = parseReceiver (token, &receiver_cork);
+
+	if (isType (token, TOKEN_IDENTIFIER))
+	{
+		int cork;
+		tagEntryInfo *e = NULL;
+		tokenInfo *functionToken = newToken ();
+		int func_scope;
+
+		copyToken (functionToken, token);
+
+		// Start recording signature
+		vString *buffer = vStringNew ();
+		collector collector = { .str = buffer, .last_len = 0, };
+
+		// Skip over parameters.
+		readTokenFull (token, &collector);
+		skipToMatchedNoRead (token, &collector);
+
+		collectorTruncate (&collector, false);
+		if (receiver_type_token)
+		{
+			func_scope = anyEntryInScope (scope, vStringValue (receiver_type_token->string));
+			if (func_scope == CORK_NIL)
+				func_scope = makeTagFull(receiver_type_token, GOTAG_UNKNOWN,
+										 scope, NULL, NULL,
+										 R_GOTAG_UNKNOWN_RECEIVER);
+		}
+		else
+			func_scope = scope;
+
+		cork = makeTag (functionToken, GOTAG_FUNCTION,
+						func_scope, vStringValue (buffer), NULL);
+		if ((e = getEntryInCorkQueue (cork)))
+		{
+			tagEntryInfo *receiver = getEntryInCorkQueue (receiver_cork);
+			if (receiver)
+				receiver->extensionFields.scopeIndex = cork;
+		}
+
+		deleteToken (functionToken);
+
+		vStringClear (collector.str);
+		collector.last_len = 0;
+
+		readTokenFull (token, &collector);
+
+		// Skip over result.
+		skipType (token, &collector);
+
+		// Neither "{" nor " {".
+		if (!(isType (token, TOKEN_OPEN_CURLY) && collector.last_len < 2))
+		{
+			collectorTruncate(&collector, isType (token, TOKEN_OPEN_CURLY));
+			if (e)
+			{
+				e->extensionFields.typeRef [0] = eStrdup ("typename");
+				e->extensionFields.typeRef [1] = vStringDeleteUnwrap (buffer);
+				buffer = NULL;
+			}
+		}
+
+		if (buffer)
+			vStringDelete (buffer);
+
+		// Skip over function body.
+		if (isType (token, TOKEN_OPEN_CURLY))
+		{
+			skipToMatched (token, NULL);
+			if (e)
+				e->extensionFields.endLine = getInputLineNumber ();
+		}
+	}
+
+	if (receiver_type_token)
+		deleteToken(receiver_type_token);
+}
+
+static void attachTypeRefField (int scope, intArray *corks, const char *const type)
+{
+	int type_cork = anyEntryInScope (scope, type);
+	tagEntryInfo *type_e = getEntryInCorkQueue (type_cork);
+
+	for (unsigned int i = 0; i < intArrayCount (corks); i++)
+	{
+		int cork = intArrayItem (corks, i);
+		tagEntryInfo *e = getEntryInCorkQueue (cork);
+		if (!e)
+			continue;
+		e->extensionFields.typeRef [0] = eStrdup (type_e
+												  ?GoKinds[type_e->kindIndex].name
+												  :"typename");
+		e->extensionFields.typeRef [1] = eStrdup (type);
+	}
+}
+
+static void parseInterfaceMethods (tokenInfo *const token, const int scope)
+{
+	// InterfaceType      = "interface" "{" { MethodSpec ";" } "}" .
+	// MethodSpec         = MethodName Signature | InterfaceTypeName .
+	// MethodName         = identifier .
+	// InterfaceTypeName  = TypeName .
+
+	vString *inheritsBuf = vStringNew ();
+	collector inherits = { .str = inheritsBuf, .last_len = 0, };
+
+	readToken (token);
+	if (!isType (token, TOKEN_OPEN_CURLY))
+		return;
+
+	readToken (token);
+	while (!isType (token, TOKEN_EOF) && !isType (token, TOKEN_CLOSE_CURLY))
+	{
+		if (isType (token, TOKEN_IDENTIFIER))
+		{
+			tokenInfo * headToken = newToken();
+			copyToken (headToken, token);
+
+			readToken (token);
+			if(isType (token, TOKEN_DOT))
+			{
+				if (!collectorIsEmpty(&inherits))
+					collectorPut (&inherits, ',');
+				collectorAppendToken (&inherits, headToken);
+				readTokenFull (token, NULL);
+				if (isType (token, TOKEN_IDENTIFIER))
+				{
+					collectorPut (&inherits, '.');
+					collectorAppendToken (&inherits, token);
+					readToken (token);
+				}
+				/* If the token is not an identifier, the input
+				   may be wrong. */
+			}
+			else if (isType (token, TOKEN_SEMICOLON))
+			{
+				if (!collectorIsEmpty(&inherits))
+					collectorPut (&inherits, ',');
+				collectorAppendToken (&inherits, headToken);
+				readToken (token);
+			}
+			else if (isType (token, TOKEN_OPEN_PAREN))
+			{
+				// => Signature
+				// Signature      = Parameters [ Result ] .
+				vString *pbuf = vStringNew ();
+				collector pcol = { .str = pbuf, .last_len = 0, };
+				vString *rbuf = NULL;
+				collector rcol = { .str = NULL, .last_len = 0, };
+
+				// Parameters
+				collectorPut (&pcol, '(');
+				skipToMatched (token, &pcol);
+				collectorTruncate(&pcol, true);
+
+				if (!isType (token, TOKEN_SEMICOLON))
+				{
+					rbuf = vStringNew ();
+					rcol.str = rbuf;
+
+					collectorAppendToken (&rcol, token);
+					skipType (token, &rcol);
+					collectorTruncate(&rcol, true);
+				}
+
+				makeTag (headToken, GOTAG_METHODSPEC, scope,
+						 vStringValue (pbuf),
+						 rbuf? vStringValue(rbuf): NULL);
+
+				if (rbuf)
+					vStringDelete (rbuf);
+				vStringDelete (pbuf);
+			}
+			deleteToken (headToken);
+		}
+		else
+			readToken (token);
+	}
+
+	if (!collectorIsEmpty(&inherits))
+	{
+		tagEntryInfo *e = getEntryInCorkQueue (scope);
+		if (e)
+		{
+			e->extensionFields.inheritance = vStringDeleteUnwrap (inheritsBuf);
+			inheritsBuf = NULL;
+		}
+	}
+	vStringDelete (inheritsBuf);
+}
+
+static void parseStructMembers (tokenInfo *const token, const int scope)
+{
+	// StructType     = "struct" "{" { FieldDecl ";" } "}" .
+	// FieldDecl      = (IdentifierList Type | AnonymousField) [ Tag ] .
+	// AnonymousField = [ "*" ] TypeName .
+	// Tag            = string_lit .
+
+	readToken (token);
+	if (!isType (token, TOKEN_OPEN_CURLY))
+		return;
+
+	vString *typeForAnonMember = vStringNew ();
+	intArray *corkForFields = intArrayNew ();
+
+	readToken (token);
+	while (!isType (token, TOKEN_EOF) && !isType (token, TOKEN_CLOSE_CURLY))
+	{
+		tokenInfo *memberCandidate = NULL;
+		bool first = true;
+
+		while (!isType (token, TOKEN_EOF))
+		{
+			if (isType (token, TOKEN_IDENTIFIER))
+			{
+				if (first)
+				{
+					// could be anonymous field like in 'struct {int}' - we don't know yet
+					memberCandidate = newToken ();
+					copyToken (memberCandidate, token);
+					first = false;
+				}
+				else
+				{
+					int cork;
+					if (memberCandidate)
+					{
+						// if we are here, there was a comma and memberCandidate isn't an anonymous field
+						cork = makeTag (memberCandidate, GOTAG_MEMBER, scope, NULL, NULL);
+						deleteToken (memberCandidate);
+						memberCandidate = NULL;
+						intArrayAdd (corkForFields, cork);
+					}
+					cork = makeTag (token, GOTAG_MEMBER, scope, NULL, NULL);
+					intArrayAdd (corkForFields, cork);
+				}
+				readToken (token);
+			}
+			if (!isType (token, TOKEN_COMMA))
+				break;
+			readToken (token);
+		}
+
+		if (first && isType (token, TOKEN_STAR))
+		{
+			vStringPut (typeForAnonMember, '*');
+			readToken (token);
+		}
+		else if (memberCandidate &&
+				 (isType (token, TOKEN_DOT) ||
+				  isType (token, TOKEN_STRING) ||
+				  isType (token, TOKEN_SEMICOLON)))
+			// memberCandidate is part of anonymous type
+			vStringCat (typeForAnonMember, memberCandidate->string);
+
+		// the above two cases that set typeForAnonMember guarantee
+		// this is an anonymous member
+		if (vStringLength (typeForAnonMember) > 0)
+		{
+			tokenInfo *anonMember = NULL;
+
+			if (memberCandidate)
+			{
+				anonMember = newToken ();
+				copyToken (anonMember, memberCandidate);
+			}
+
+			// TypeName of AnonymousField has a dot like package"."type.
+			// Pick up the last package component, and store it to
+			// memberCandidate.
+			while (isType (token, TOKEN_IDENTIFIER) ||
+				   isType (token, TOKEN_DOT))
+			{
+				if (isType (token, TOKEN_IDENTIFIER))
+				{
+					if (!anonMember)
+						anonMember = newToken ();
+					copyToken (anonMember, token);
+					vStringCat (typeForAnonMember, anonMember->string);
+				}
+				else if (isType (token, TOKEN_DOT))
+					vStringPut (typeForAnonMember, '.');
+				readToken (token);
+			}
+
+			// optional tag
+			if (isType (token, TOKEN_STRING))
+				readToken (token);
+
+			if (anonMember)
+			{
+				makeTag (anonMember, GOTAG_ANONMEMBER, scope, NULL,
+						 vStringValue (typeForAnonMember));
+				deleteToken (anonMember);
+			}
+		}
+		else
+		{
+			vString *typeForMember = vStringNew ();
+			collector collector = { .str = typeForMember, .last_len = 0, };
+
+			collectorAppendToken (&collector, token);
+			skipType (token, &collector);
+			collectorTruncate (&collector, true);
+
+			if (memberCandidate)
+				makeTag (memberCandidate, GOTAG_MEMBER, scope, NULL,
+						 vStringValue (typeForMember));
+
+			attachTypeRefField (scope, corkForFields, vStringValue (typeForMember));
+			intArrayClear (corkForFields);
+			vStringDelete (typeForMember);
+		}
+
+		if (memberCandidate)
+			deleteToken (memberCandidate);
+
+		vStringClear (typeForAnonMember);
+
+		while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_CURLY)
+				&& !isType (token, TOKEN_EOF))
+		{
+			readToken (token);
+			skipToMatched (token, NULL);
+		}
+
+		if (!isType (token, TOKEN_CLOSE_CURLY))
+		{
+			// we are at TOKEN_SEMICOLON
+			readToken (token);
+		}
+	}
+
+	intArrayDelete (corkForFields);
+	vStringDelete (typeForAnonMember);
+}
+
+static void parseConstTypeVar (tokenInfo *const token, goKind kind, const int scope)
+{
+	// ConstDecl      = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
+	// ConstSpec      = IdentifierList [ [ Type ] "=" ExpressionList ] .
+	// IdentifierList = identifier { "," identifier } .
+	// ExpressionList = Expression { "," Expression } .
+	// TypeDecl     = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
+	// TypeSpec     = identifier [ "=" ] Type .
+	// VarDecl     = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
+	// VarSpec     = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
+	bool usesParens = false;
+	intArray *corks
+		= (kind == GOTAG_VAR || kind == GOTAG_CONST)? intArrayNew (): NULL;
+
+	readToken (token);
+
+	if (isType (token, TOKEN_OPEN_PAREN))
+	{
+		usesParens = true;
+		readToken (token);
+	}
+
+	do
+	{
+		tokenInfo *typeToken = NULL;
+		int member_scope = scope;
+
+		while (!isType (token, TOKEN_EOF))
+		{
+			if (isType (token, TOKEN_IDENTIFIER))
+			{
+				if (kind == GOTAG_TYPE)
+				{
+					typeToken = newToken ();
+					copyToken (typeToken, token);
+					readToken (token);
+					if (isType (token, TOKEN_EQUAL))
+					{
+						kind = GOTAG_TALIAS;
+						readToken (token);
+					}
+
+					if (isKeyword (token, KEYWORD_struct))
+						member_scope = makeTag (typeToken, GOTAG_STRUCT,
+												scope, NULL, NULL);
+					else if (isKeyword (token, KEYWORD_interface))
+						member_scope = makeTag (typeToken, GOTAG_INTERFACE,
+												scope, NULL, NULL);
+					else
+						member_scope = makeTag (typeToken, kind,
+												scope, NULL, NULL);
+
+					if (member_scope != CORK_NIL)
+						registerEntry (member_scope);
+					break;
+				}
+				else
+				{
+					int c = makeTag (token, kind, scope, NULL, NULL);
+					if (c != CORK_NIL && corks)
+						intArrayAdd (corks, c);
+				}
+				readToken (token);
+			}
+			if (!isType (token, TOKEN_COMMA))
+				break;
+			readToken (token);
+		}
+
+		if (typeToken)
+		{
+			if (isKeyword (token, KEYWORD_struct))
+				parseStructMembers (token, member_scope);
+			else if (isKeyword (token, KEYWORD_interface))
+				parseInterfaceMethods (token, member_scope);
+			else
+			{
+				/* Filling "typeref:" field of typeToken. */
+				vString *buffer = vStringNew ();
+				collector collector = { .str = buffer, .last_len = 0, };
+
+				collectorAppendToken (&collector, token);
+				skipType (token, &collector);
+				collectorTruncate (&collector, true);
+
+				if ((member_scope != CORK_NIL) && !vStringIsEmpty (buffer))
+				{
+					tagEntryInfo *e = getEntryInCorkQueue (member_scope);
+					if (e)
+					{
+						e->extensionFields.typeRef [0] = eStrdup ("typename");
+						e->extensionFields.typeRef [1] = vStringDeleteUnwrap (buffer);
+					}
+				@@ Diff output truncated at 100000 characters. @@

--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list