xref: /Universal-ctags/parsers/go.c (revision aaaac7eeac8399141aa8e6d9e6ec0379931848b2)
1 /*
2 *   This source code is released for free distribution under the terms of the
3 *   GNU General Public License version 2 or (at your option) any later version.
4 *
5 *   Reference:
6 *     https://golang.org/ref/spec
7 */
8 
9 
10 /*
11  *   INCLUDE FILES
12  */
13 #include "general.h"        /* must always come first */
14 
15 #include "debug.h"
16 #include "entry.h"
17 #include "keyword.h"
18 #include "read.h"
19 #include "numarray.h"
20 #include "objpool.h"
21 #include "parse.h"
22 #include "routines.h"
23 #include "vstring.h"
24 #include "xtag.h"
25 #include "field.h"
26 #include "htable.h"
27 
28 #include <string.h>
29 
30 /*
31  *	 MACROS
32  */
33 #define MAX_COLLECTOR_LENGTH 512
34 #define isType(token,t) (bool) ((token)->type == (t))
35 #define isKeyword(token,k) (bool) ((token)->keyword == (k))
36 #define isStartIdentChar(c) (isalpha (c) ||  (c) == '_' || (c) > 128) /* XXX UTF-8 */
37 #define isIdentChar(c) (isStartIdentChar (c) || isdigit (c))
38 #define newToken() (objPoolGet (TokenPool))
39 #define deleteToken(t) (objPoolPut (TokenPool, (t)))
40 
41 /*
42  *	 DATA DECLARATIONS
43  */
44 
45 enum eKeywordId {
46 	KEYWORD_package,
47 	KEYWORD_import,
48 	KEYWORD_const,
49 	KEYWORD_type,
50 	KEYWORD_var,
51 	KEYWORD_func,
52 	KEYWORD_struct,
53 	KEYWORD_interface,
54 	KEYWORD_map,
55 	KEYWORD_chan
56 };
57 typedef int keywordId; /* to allow KEYWORD_NONE */
58 
59 typedef enum eTokenType {
60 	TOKEN_NONE = -1,
61 	// Token not important for top-level Go parsing
62 	TOKEN_OTHER,
63 	TOKEN_KEYWORD,
64 	TOKEN_IDENTIFIER,
65 	TOKEN_STRING,
66 	TOKEN_OPEN_PAREN,
67 	TOKEN_CLOSE_PAREN,
68 	TOKEN_OPEN_CURLY,
69 	TOKEN_CLOSE_CURLY,
70 	TOKEN_OPEN_SQUARE,
71 	TOKEN_CLOSE_SQUARE,
72 	TOKEN_SEMICOLON,
73 	TOKEN_STAR,
74 	TOKEN_LEFT_ARROW,
75 	TOKEN_DOT,
76 	TOKEN_COMMA,
77 	TOKEN_EQUAL,
78 	TOKEN_3DOTS,
79 	TOKEN_EOF
80 } tokenType;
81 
82 typedef struct sTokenInfo {
83 	tokenType type;
84 	keywordId keyword;
85 	vString *string;		/* the name of the token */
86 	unsigned long lineNumber;	/* line number of tag */
87 	MIOPos filePosition;		/* file position of line containing name */
88 	int c;						/* Used in AppendTokenToVString */
89 } tokenInfo;
90 
91 typedef struct sCollector {
92 	vString *str;
93 	size_t last_len;
94 } collector;
95 
96 /*
97 *   DATA DEFINITIONS
98 */
99 
100 static int Lang_go;
101 static objPool *TokenPool = NULL;
102 
103 typedef enum {
104 	GOTAG_UNDEFINED = -1,
105 	GOTAG_PACKAGE,
106 	GOTAG_FUNCTION,
107 	GOTAG_CONST,
108 	GOTAG_TYPE,
109 	GOTAG_VAR,
110 	GOTAG_STRUCT,
111 	GOTAG_INTERFACE,
112 	GOTAG_MEMBER,
113 	GOTAG_ANONMEMBER,
114 	GOTAG_METHODSPEC,
115 	GOTAG_UNKNOWN,
116 	GOTAG_PACKAGE_NAME,
117 	GOTAG_TALIAS,
118 	GOTAG_RECEIVER,
119 } goKind;
120 
121 typedef enum {
122 	R_GOTAG_PACKAGE_IMPORTED,
123 } GoPackageRole;
124 
125 static roleDefinition GoPackageRoles [] = {
126 	{ true, "imported", "imported package" },
127 };
128 
129 typedef enum {
130 	R_GOTAG_UNKNOWN_RECEIVER,
131 } GoUnknownRole;
132 
133 static roleDefinition GoUnknownRoles [] = {
134 	{ true, "receiverType", "receiver type" },
135 };
136 
137 static kindDefinition GoKinds[] = {
138 	{true, 'p', "package", "packages",
139 	  .referenceOnly = false, ATTACH_ROLES (GoPackageRoles)},
140 	{true, 'f', "func", "functions"},
141 	{true, 'c', "const", "constants"},
142 	{true, 't', "type", "types"},
143 	{true, 'v', "var", "variables"},
144 	{true, 's', "struct", "structs"},
145 	{true, 'i', "interface", "interfaces"},
146 	{true, 'm', "member", "struct members"},
147 	{true, 'M', "anonMember", "struct anonymous members"},
148 	{true, 'n', "methodSpec", "interface method specification"},
149 	{true, 'u', "unknown", "unknown",
150 	 .referenceOnly = true, ATTACH_ROLES (GoUnknownRoles)},
151 	{true, 'P', "packageName", "name for specifying imported package"},
152 	{true, 'a', "talias", "type aliases"},
153 	{false,'R', "receiver", "receivers"},
154 };
155 
156 static const keywordTable GoKeywordTable[] = {
157 	{"package", KEYWORD_package},
158 	{"import", KEYWORD_import},
159 	{"const", KEYWORD_const},
160 	{"type", KEYWORD_type},
161 	{"var", KEYWORD_var},
162 	{"func", KEYWORD_func},
163 	{"struct", KEYWORD_struct},
164 	{"interface", KEYWORD_interface},
165 	{"map", KEYWORD_map},
166 	{"chan", KEYWORD_chan}
167 };
168 
169 typedef enum {
170 	F_PACKAGE,
171 	F_PACKAGE_NAME,
172 	F_HOW_IMPORTED,
173 } goField;
174 
175 static fieldDefinition GoFields [] = {
176 	{
177 		.name = "package",
178 		.description = "the real package specified by the package name",
179 		.enabled = true,
180 	},
181 	{
182 		.name = "packageName",
183 		.description = "the name for referring the package",
184 		.enabled = true,
185 	},
186 	{
187 		.name = "howImported",
188 		.description = "how the package is imported (\"inline\" for `.' or \"init\" for `_')",
189 		.enabled = false,
190 	},
191 };
192 
193 
194 /*
195 *   FUNCTION DEFINITIONS
196 */
197 
newPoolToken(void * createArg CTAGS_ATTR_UNUSED)198 static void *newPoolToken (void *createArg CTAGS_ATTR_UNUSED)
199 {
200 	tokenInfo *const token = xMalloc (1, tokenInfo);
201 	token->string = vStringNew ();
202 	return token;
203 }
204 
clearPoolToken(void * data)205 static void clearPoolToken (void *data)
206 {
207 	tokenInfo *token = data;
208 
209 	token->type = TOKEN_NONE;
210 	token->keyword = KEYWORD_NONE;
211 	token->lineNumber   = getInputLineNumber ();
212 	token->filePosition = getInputFilePosition ();
213 	vStringClear (token->string);
214 }
215 
copyToken(tokenInfo * const dest,const tokenInfo * const other)216 static void copyToken (tokenInfo *const dest, const tokenInfo *const other)
217 {
218 	dest->type = other->type;
219 	dest->keyword = other->keyword;
220 	vStringCopy(dest->string, other->string);
221 	dest->lineNumber = other->lineNumber;
222 	dest->filePosition = other->filePosition;
223 }
224 
deletePoolToken(void * data)225 static void deletePoolToken (void* data)
226 {
227 	tokenInfo * const token = data;
228 
229 	vStringDelete (token->string);
230 	eFree (token);
231 }
232 
initialize(const langType language)233 static void initialize (const langType language)
234 {
235 	Lang_go = language;
236 	TokenPool = objPoolNew (16, newPoolToken, deletePoolToken, clearPoolToken, NULL);
237 }
238 
finalize(const langType language,bool initialized)239 static void finalize (const langType language, bool initialized)
240 {
241 	if (!initialized)
242 		return;
243 
244 	objPoolDelete (TokenPool);
245 }
246 
247 /*
248  *   Parsing functions
249  */
250 
parseString(vString * const string,const int delimiter)251 static void parseString (vString *const string, const int delimiter)
252 {
253 	bool end = false;
254 	while (!end)
255 	{
256 		int c = getcFromInputFile ();
257 		if (c == EOF)
258 			end = true;
259 		else if (c == '\\' && delimiter != '`')
260 		{
261 			c = getcFromInputFile ();
262 			if (c != '\'' && c != '\"')
263 				vStringPut (string, '\\');
264 			vStringPut (string, c);
265 		}
266 		else if (c == delimiter)
267 			end = true;
268 		else
269 			vStringPut (string, c);
270 	}
271 }
272 
parseIdentifier(vString * const string,const int firstChar)273 static void parseIdentifier (vString *const string, const int firstChar)
274 {
275 	int c = firstChar;
276 	do
277 	{
278 		vStringPut (string, c);
279 		c = getcFromInputFile ();
280 	} while (isIdentChar (c));
281 	ungetcToInputFile (c);		/* always unget, LF might add a semicolon */
282 }
283 
collectorIsEmpty(collector * collector)284 static bool collectorIsEmpty(collector *collector)
285 {
286 	return !vStringLength(collector->str);
287 }
288 
collectorPut(collector * collector,char c)289 static void collectorPut (collector *collector, char c)
290 {
291 	if ((vStringLength(collector->str) > 2)
292 		&& strcmp (vStringValue (collector->str) + (vStringLength(collector->str) - 3),
293 				  "...") == 0
294 		&& c == ' ')
295 		return;
296 	else if (vStringLength(collector->str) > 0)
297 	{
298 		if (vStringLast(collector->str) == '(' && c == ' ')
299 			return;
300 		else if (vStringLast(collector->str) == ' ' && c == ')')
301 			vStringChop(collector->str);
302 	}
303 
304 	collector->last_len = vStringLength (collector->str);
305 	vStringPut (collector->str, c);
306 }
307 
collectorCatS(collector * collector,char * cstr)308 static void collectorCatS (collector *collector, char *cstr)
309 {
310 	collector->last_len = vStringLength (collector->str);
311 	vStringCatS (collector->str, cstr);
312 }
313 
collectorCat(collector * collector,vString * str)314 static void collectorCat (collector *collector, vString *str)
315 {
316 	collector->last_len = vStringLength (collector->str);
317 	vStringCat (collector->str, str);
318 }
319 
collectorAppendToken(collector * collector,const tokenInfo * const token)320 static void collectorAppendToken (collector *collector, const tokenInfo *const token)
321 {
322 	if (token->type == TOKEN_LEFT_ARROW)
323 		collectorCatS (collector, "<-");
324 	else if (token->type == TOKEN_STRING)
325 	{
326 		// only struct member annotations can appear in function prototypes
327 		// so only `` type strings are possible
328 		collector->last_len = vStringLength (collector->str);
329 		vStringPut(collector->str, '`');
330 		vStringCat(collector->str, token->string);
331 		vStringPut(collector->str, '`');
332 	}
333 	else if (token->type == TOKEN_IDENTIFIER || token->type == TOKEN_KEYWORD)
334 		collectorCat (collector, token->string);
335 	else if (token->type == TOKEN_3DOTS)
336 	{
337 		if ((vStringLength (collector->str) > 0)
338 			&& vStringLast(collector->str) != ' ')
339 			collectorPut (collector, ' ');
340 		collectorCatS (collector, "...");
341 	}
342 	else if (token->c != EOF)
343 		collectorPut (collector, token->c);
344 }
345 
collectorTruncate(collector * collector,bool dropLast)346 static void collectorTruncate (collector *collector, bool dropLast)
347 {
348 	if (dropLast)
349 		vStringTruncate (collector->str, collector->last_len);
350 
351 	vStringStripLeading (collector->str);
352 	vStringStripTrailing (collector->str);
353 }
354 
readTokenFull(tokenInfo * const token,collector * collector)355 static void readTokenFull (tokenInfo *const token, collector *collector)
356 {
357 	int c;
358 	static tokenType lastTokenType = TOKEN_NONE;
359 	bool firstWhitespace = true;
360 	bool whitespace;
361 
362 	token->c = EOF;
363 	token->type = TOKEN_NONE;
364 	token->keyword = KEYWORD_NONE;
365 	vStringClear (token->string);
366 
367 getNextChar:
368 	do
369 	{
370 		c = getcFromInputFile ();
371 		token->lineNumber = getInputLineNumber ();
372 		token->filePosition = getInputFilePosition ();
373 		if (c == '\n' && (lastTokenType == TOKEN_IDENTIFIER ||
374 						  lastTokenType == TOKEN_STRING ||
375 						  lastTokenType == TOKEN_OTHER ||
376 						  lastTokenType == TOKEN_CLOSE_PAREN ||
377 						  lastTokenType == TOKEN_CLOSE_CURLY ||
378 						  lastTokenType == TOKEN_CLOSE_SQUARE))
379 		{
380 			c = ';';  // semicolon injection
381 		}
382 		whitespace = c == '\t'  ||  c == ' ' ||  c == '\r' || c == '\n';
383 		if (collector && whitespace && firstWhitespace && vStringLength (collector->str) < MAX_COLLECTOR_LENGTH)
384 		{
385 			firstWhitespace = false;
386 			collectorPut (collector, ' ');
387 		}
388 	}
389 	while (whitespace);
390 
391 	switch (c)
392 	{
393 		case EOF:
394 			token->type = TOKEN_EOF;
395 			break;
396 
397 		case ';':
398 			token->type = TOKEN_SEMICOLON;
399 			break;
400 
401 		case '/':
402 			{
403 				bool hasNewline = false;
404 				int d = getcFromInputFile ();
405 				switch (d)
406 				{
407 					case '/':
408 						skipToCharacterInInputFile ('\n');
409 						/* Line comments start with the
410 						 * character sequence // and
411 						 * continue through the next
412 						 * newline. A line comment acts
413 						 * like a newline.  */
414 						ungetcToInputFile ('\n');
415 						goto getNextChar;
416 					case '*':
417 						do
418 						{
419 							do
420 							{
421 								d = getcFromInputFile ();
422 								if (d == '\n')
423 								{
424 									hasNewline = true;
425 								}
426 							} while (d != EOF && d != '*');
427 
428 							c = getcFromInputFile ();
429 							if (c == '/')
430 								break;
431 							else
432 								ungetcToInputFile (c);
433 						} while (c != EOF && c != '\0');
434 
435 						ungetcToInputFile (hasNewline ? '\n' : ' ');
436 						goto getNextChar;
437 					default:
438 						token->type = TOKEN_OTHER;
439 						ungetcToInputFile (d);
440 						break;
441 				}
442 			}
443 			break;
444 
445 		case '"':
446 		case '\'':
447 		case '`':
448 			token->type = TOKEN_STRING;
449 			parseString (token->string, c);
450 			token->lineNumber = getInputLineNumber ();
451 			token->filePosition = getInputFilePosition ();
452 			break;
453 
454 		case '<':
455 			{
456 				int d = getcFromInputFile ();
457 				if (d == '-')
458 					token->type = TOKEN_LEFT_ARROW;
459 				else
460 				{
461 					ungetcToInputFile (d);
462 					token->type = TOKEN_OTHER;
463 				}
464 			}
465 			break;
466 
467 		case '(':
468 			token->type = TOKEN_OPEN_PAREN;
469 			break;
470 
471 		case ')':
472 			token->type = TOKEN_CLOSE_PAREN;
473 			break;
474 
475 		case '{':
476 			token->type = TOKEN_OPEN_CURLY;
477 			break;
478 
479 		case '}':
480 			token->type = TOKEN_CLOSE_CURLY;
481 			break;
482 
483 		case '[':
484 			token->type = TOKEN_OPEN_SQUARE;
485 			break;
486 
487 		case ']':
488 			token->type = TOKEN_CLOSE_SQUARE;
489 			break;
490 
491 		case '*':
492 			token->type = TOKEN_STAR;
493 			break;
494 
495 		case '.':
496 			{
497 				int d, e;
498 				d = getcFromInputFile ();
499 				if (d == '.')
500 				{
501 					e = getcFromInputFile ();
502 					if (e == '.')
503 					{
504 						token->type = TOKEN_3DOTS;
505 						break;
506 					}
507 					else
508 					{
509 						ungetcToInputFile (e);
510 						ungetcToInputFile (d);
511 					}
512 				}
513 				else
514 					ungetcToInputFile (d);
515 			}
516 			token->type = TOKEN_DOT;
517 			break;
518 
519 		case ',':
520 			token->type = TOKEN_COMMA;
521 			break;
522 
523 		case '=':
524 			token->type = TOKEN_EQUAL;
525 			break;
526 
527 		default:
528 			if (isStartIdentChar (c))
529 			{
530 				parseIdentifier (token->string, c);
531 				token->lineNumber = getInputLineNumber ();
532 				token->filePosition = getInputFilePosition ();
533 				token->keyword = lookupKeyword (vStringValue (token->string), Lang_go);
534 				if (isKeyword (token, KEYWORD_NONE))
535 					token->type = TOKEN_IDENTIFIER;
536 				else
537 					token->type = TOKEN_KEYWORD;
538 			}
539 			else
540 				token->type = TOKEN_OTHER;
541 			break;
542 	}
543 
544 	token->c = c;
545 
546 	if (collector && vStringLength (collector->str) < MAX_COLLECTOR_LENGTH)
547 		collectorAppendToken (collector, token);
548 
549 	lastTokenType = token->type;
550 }
551 
readToken(tokenInfo * const token)552 static void readToken (tokenInfo *const token)
553 {
554 	readTokenFull (token, NULL);
555 }
556 
skipToMatchedNoRead(tokenInfo * const token,collector * collector)557 static bool skipToMatchedNoRead (tokenInfo *const token, collector *collector)
558 {
559 	int nest_level = 0;
560 	tokenType open_token = token->type;
561 	tokenType close_token;
562 
563 	switch (open_token)
564 	{
565 		case TOKEN_OPEN_PAREN:
566 			close_token = TOKEN_CLOSE_PAREN;
567 			break;
568 		case TOKEN_OPEN_CURLY:
569 			close_token = TOKEN_CLOSE_CURLY;
570 			break;
571 		case TOKEN_OPEN_SQUARE:
572 			close_token = TOKEN_CLOSE_SQUARE;
573 			break;
574 		default:
575 			return false;
576 	}
577 
578 	/*
579 	 * This routine will skip to a matching closing token.
580 	 * It will also handle nested tokens.
581 	 */
582 	nest_level++;
583 	while (nest_level > 0 && !isType (token, TOKEN_EOF))
584 	{
585 		readTokenFull (token, collector);
586 		if (isType (token, open_token))
587 			nest_level++;
588 		else if (isType (token, close_token))
589 			nest_level--;
590 	}
591 
592 	return true;
593 }
594 
skipToMatched(tokenInfo * const token,collector * collector)595 static void skipToMatched (tokenInfo *const token, collector *collector)
596 {
597 	if (skipToMatchedNoRead (token, collector))
598 		readTokenFull (token, collector);
599 }
600 
skipType(tokenInfo * const token,collector * collector)601 static bool skipType (tokenInfo *const token, collector *collector)
602 {
603 	// Type      = TypeName | TypeLit | "(" Type ")" .
604 	// Skips also function multiple return values "(" Type {"," Type} ")"
605 	if (isType (token, TOKEN_OPEN_PAREN))
606 	{
607 		skipToMatched (token, collector);
608 		return true;
609 	}
610 
611 	// TypeName  = QualifiedIdent.
612 	// QualifiedIdent = [ PackageName "." ] identifier .
613 	// PackageName    = identifier .
614 	if (isType (token, TOKEN_IDENTIFIER))
615 	{
616 		readTokenFull (token, collector);
617 		if (isType (token, TOKEN_DOT))
618 		{
619 			readTokenFull (token, collector);
620 			if (isType (token, TOKEN_IDENTIFIER))
621 				readTokenFull (token, collector);
622 		}
623 		return true;
624 	}
625 
626 	// StructType     = "struct" "{" { FieldDecl ";" } "}"
627 	// InterfaceType      = "interface" "{" { MethodSpec ";" } "}" .
628 	if (isKeyword (token, KEYWORD_struct) || isKeyword (token, KEYWORD_interface))
629 	{
630 		readTokenFull (token, collector);
631 		// skip over "{}"
632 		skipToMatched (token, collector);
633 		return true;
634 	}
635 
636 	// ArrayType   = "[" ArrayLength "]" ElementType .
637 	// SliceType = "[" "]" ElementType .
638 	// ElementType = Type .
639 	if (isType (token, TOKEN_OPEN_SQUARE))
640 	{
641 		skipToMatched (token, collector);
642 		return skipType (token, collector);
643 	}
644 
645 	// PointerType = "*" BaseType .
646 	// BaseType = Type .
647 	// ChannelType = ( "chan" [ "<-" ] | "<-" "chan" ) ElementType .
648 	if (isType (token, TOKEN_STAR) || isKeyword (token, KEYWORD_chan) || isType (token, TOKEN_LEFT_ARROW))
649 	{
650 		readTokenFull (token, collector);
651 		return skipType (token, collector);
652 	}
653 
654 	// MapType     = "map" "[" KeyType "]" ElementType .
655 	// KeyType     = Type .
656 	if (isKeyword (token, KEYWORD_map))
657 	{
658 		readTokenFull (token, collector);
659 		// skip over "[]"
660 		skipToMatched (token, collector);
661 		return skipType (token, collector);
662 	}
663 
664 	// FunctionType   = "func" Signature .
665 	// Signature      = Parameters [ Result ] .
666 	// Result         = Parameters | Type .
667 	// Parameters     = "(" [ ParameterList [ "," ] ] ")" .
668 	if (isKeyword (token, KEYWORD_func))
669 	{
670 		readTokenFull (token, collector);
671 		// Parameters, skip over "()"
672 		skipToMatched (token, collector);
673 		// Result is parameters or type or nothing.  skipType treats anything
674 		// surrounded by parentheses as a type, and does nothing if what
675 		// follows is not a type.
676 		return skipType (token, collector);
677 	}
678 
679 	return false;
680 }
681 
makeTagFull(tokenInfo * const token,const goKind kind,const int scope,const char * argList,const char * typeref,const int role)682 static int makeTagFull (tokenInfo *const token, const goKind kind,
683 						const int scope, const char *argList, const char *typeref,
684 						const int role)
685 {
686 	const char *const name = vStringValue (token->string);
687 
688 	tagEntryInfo e;
689 
690 	/* Don't record `_' placeholder variable  */
691 	if (kind == GOTAG_VAR && name[0] == '_' && name[1] == '\0')
692 		return CORK_NIL;
693 
694 	initRefTagEntry (&e, name, kind, role);
695 
696 	e.lineNumber = token->lineNumber;
697 	e.filePosition = token->filePosition;
698 	if (argList)
699 		e.extensionFields.signature = argList;
700 	if (typeref)
701 	{
702 		/* Follows Cxx parser convention */
703 		e.extensionFields.typeRef [0] = "typename";
704 		e.extensionFields.typeRef [1] = typeref;
705 	}
706 
707 	e.extensionFields.scopeIndex = scope;
708 	return makeTagEntry (&e);
709 }
710 
makeTag(tokenInfo * const token,const goKind kind,const int scope,const char * argList,const char * typeref)711 static int makeTag (tokenInfo *const token, const goKind kind,
712 					const int scope, const char *argList, const char *typeref)
713 {
714 	return makeTagFull (token, kind, scope, argList, typeref,
715 						ROLE_DEFINITION_INDEX);
716 }
717 
makeRefTag(tokenInfo * const token,const goKind kind,const int role)718 static int makeRefTag (tokenInfo *const token, const goKind kind,
719 					   const int role)
720 {
721 	return makeTagFull (token, kind, CORK_NIL, NULL, NULL, role);
722 }
723 
parsePackage(tokenInfo * const token)724 static int parsePackage (tokenInfo *const token)
725 {
726 	readToken (token);
727 	if (isType (token, TOKEN_IDENTIFIER))
728 	{
729 		return makeTag (token, GOTAG_PACKAGE, CORK_NIL, NULL, NULL);
730 	}
731 	else
732 		return CORK_NIL;
733 }
734 
parseReceiver(tokenInfo * const token,int * corkIndex)735 static tokenInfo * parseReceiver (tokenInfo *const token, int *corkIndex)
736 {
737 	tokenInfo *receiver_type_token = NULL;
738 	int nest_level = 1;
739 
740 	*corkIndex = CORK_NIL;
741 
742 	/* Looking for an identifier before ')'. */
743 	while (nest_level > 0 && !isType (token, TOKEN_EOF))
744 	{
745 		if (isType (token, TOKEN_IDENTIFIER))
746 		{
747 			if (*corkIndex == CORK_NIL)
748 				*corkIndex = makeTag (token, GOTAG_RECEIVER, CORK_NIL, NULL, NULL);
749 			if (!receiver_type_token)
750 				receiver_type_token = newToken ();
751 			copyToken (receiver_type_token, token);
752 		}
753 
754 		readToken (token);
755 		if (isType (token, TOKEN_OPEN_PAREN))
756 			nest_level++;
757 		else if (isType (token, TOKEN_CLOSE_PAREN))
758 			nest_level--;
759 	}
760 
761 	if (nest_level > 0 && receiver_type_token)
762 	{
763 		deleteToken (receiver_type_token);
764 		receiver_type_token = NULL;
765 	}
766 
767 	if (receiver_type_token)
768 	{
769 		tagEntryInfo *e = getEntryInCorkQueue (*corkIndex);
770 		if (e)
771 		{
772 			e->extensionFields.typeRef [0] = eStrdup ("typename");
773 			e->extensionFields.typeRef [1] = vStringStrdup (receiver_type_token->string);
774 		}
775 	}
776 	readToken (token);
777 	return receiver_type_token;
778 }
779 
parseFunctionOrMethod(tokenInfo * const token,const int scope)780 static void parseFunctionOrMethod (tokenInfo *const token, const int scope)
781 {
782 	int receiver_cork = CORK_NIL;
783 	tokenInfo *receiver_type_token = NULL;
784 
785 	// FunctionDecl = "func" identifier Signature [ Body ] .
786 	// Body         = Block.
787 	//
788 	// MethodDecl   = "func" Receiver MethodName Signature [ Body ] .
789 	// Receiver     = "(" [ identifier ] [ "*" ] BaseTypeName ")" .
790 	// BaseTypeName = identifier .
791 
792 	// Pick up receiver type.
793 	readToken (token);
794 	if (isType (token, TOKEN_OPEN_PAREN))
795 		receiver_type_token = parseReceiver (token, &receiver_cork);
796 
797 	if (isType (token, TOKEN_IDENTIFIER))
798 	{
799 		int cork;
800 		tagEntryInfo *e = NULL;
801 		tokenInfo *functionToken = newToken ();
802 		int func_scope;
803 
804 		copyToken (functionToken, token);
805 
806 		// Start recording signature
807 		vString *buffer = vStringNew ();
808 		collector collector = { .str = buffer, .last_len = 0, };
809 
810 		// Skip over parameters.
811 		readTokenFull (token, &collector);
812 		skipToMatchedNoRead (token, &collector);
813 
814 		collectorTruncate (&collector, false);
815 		if (receiver_type_token)
816 		{
817 			func_scope = anyEntryInScope (scope, vStringValue (receiver_type_token->string), false);
818 			if (func_scope == CORK_NIL)
819 				func_scope = makeTagFull(receiver_type_token, GOTAG_UNKNOWN,
820 										 scope, NULL, NULL,
821 										 R_GOTAG_UNKNOWN_RECEIVER);
822 		}
823 		else
824 			func_scope = scope;
825 
826 		cork = makeTag (functionToken, GOTAG_FUNCTION,
827 						func_scope, vStringValue (buffer), NULL);
828 		if ((e = getEntryInCorkQueue (cork)))
829 		{
830 			tagEntryInfo *receiver = getEntryInCorkQueue (receiver_cork);
831 			if (receiver)
832 				receiver->extensionFields.scopeIndex = cork;
833 		}
834 
835 		deleteToken (functionToken);
836 
837 		vStringClear (collector.str);
838 		collector.last_len = 0;
839 
840 		readTokenFull (token, &collector);
841 
842 		// Skip over result.
843 		skipType (token, &collector);
844 
845 		// Neither "{" nor " {".
846 		if (!(isType (token, TOKEN_OPEN_CURLY) && collector.last_len < 2))
847 		{
848 			collectorTruncate(&collector, isType (token, TOKEN_OPEN_CURLY));
849 			if (e)
850 			{
851 				e->extensionFields.typeRef [0] = eStrdup ("typename");
852 				e->extensionFields.typeRef [1] = vStringDeleteUnwrap (buffer);
853 				buffer = NULL;
854 			}
855 		}
856 
857 		if (buffer)
858 			vStringDelete (buffer);
859 
860 		// Skip over function body.
861 		if (isType (token, TOKEN_OPEN_CURLY))
862 		{
863 			skipToMatched (token, NULL);
864 			if (e)
865 				e->extensionFields.endLine = getInputLineNumber ();
866 		}
867 	}
868 
869 	if (receiver_type_token)
870 		deleteToken(receiver_type_token);
871 }
872 
attachTypeRefField(int scope,intArray * corks,const char * const type)873 static void attachTypeRefField (int scope, intArray *corks, const char *const type)
874 {
875 	int type_cork = anyEntryInScope (scope, type, false);
876 	tagEntryInfo *type_e = getEntryInCorkQueue (type_cork);
877 
878 	for (unsigned int i = 0; i < intArrayCount (corks); i++)
879 	{
880 		int cork = intArrayItem (corks, i);
881 		tagEntryInfo *e = getEntryInCorkQueue (cork);
882 		if (!e)
883 			continue;
884 		e->extensionFields.typeRef [0] = eStrdup (type_e
885 												  ?GoKinds[type_e->kindIndex].name
886 												  :"typename");
887 		e->extensionFields.typeRef [1] = eStrdup (type);
888 	}
889 }
890 
parseInterfaceMethods(tokenInfo * const token,const int scope)891 static void parseInterfaceMethods (tokenInfo *const token, const int scope)
892 {
893 	// InterfaceType      = "interface" "{" { MethodSpec ";" } "}" .
894 	// MethodSpec         = MethodName Signature | InterfaceTypeName .
895 	// MethodName         = identifier .
896 	// InterfaceTypeName  = TypeName .
897 
898 	vString *inheritsBuf = vStringNew ();
899 	collector inherits = { .str = inheritsBuf, .last_len = 0, };
900 
901 	readToken (token);
902 	if (!isType (token, TOKEN_OPEN_CURLY))
903 		return;
904 
905 	readToken (token);
906 	while (!isType (token, TOKEN_EOF) && !isType (token, TOKEN_CLOSE_CURLY))
907 	{
908 		if (isType (token, TOKEN_IDENTIFIER))
909 		{
910 			tokenInfo * headToken = newToken();
911 			copyToken (headToken, token);
912 
913 			readToken (token);
914 			if(isType (token, TOKEN_DOT))
915 			{
916 				if (!collectorIsEmpty(&inherits))
917 					collectorPut (&inherits, ',');
918 				collectorAppendToken (&inherits, headToken);
919 				readTokenFull (token, NULL);
920 				if (isType (token, TOKEN_IDENTIFIER))
921 				{
922 					collectorPut (&inherits, '.');
923 					collectorAppendToken (&inherits, token);
924 					readToken (token);
925 				}
926 				/* If the token is not an identifier, the input
927 				   may be wrong. */
928 			}
929 			else if (isType (token, TOKEN_SEMICOLON))
930 			{
931 				if (!collectorIsEmpty(&inherits))
932 					collectorPut (&inherits, ',');
933 				collectorAppendToken (&inherits, headToken);
934 				readToken (token);
935 			}
936 			else if (isType (token, TOKEN_OPEN_PAREN))
937 			{
938 				// => Signature
939 				// Signature      = Parameters [ Result ] .
940 				vString *pbuf = vStringNew ();
941 				collector pcol = { .str = pbuf, .last_len = 0, };
942 				vString *rbuf = NULL;
943 				collector rcol = { .str = NULL, .last_len = 0, };
944 
945 				// Parameters
946 				collectorPut (&pcol, '(');
947 				skipToMatched (token, &pcol);
948 				collectorTruncate(&pcol, true);
949 
950 				if (!isType (token, TOKEN_SEMICOLON))
951 				{
952 					rbuf = vStringNew ();
953 					rcol.str = rbuf;
954 
955 					collectorAppendToken (&rcol, token);
956 					skipType (token, &rcol);
957 					collectorTruncate(&rcol, true);
958 				}
959 
960 				makeTag (headToken, GOTAG_METHODSPEC, scope,
961 						 vStringValue (pbuf),
962 						 rbuf? vStringValue(rbuf): NULL);
963 
964 				if (rbuf)
965 					vStringDelete (rbuf);
966 				vStringDelete (pbuf);
967 			}
968 			deleteToken (headToken);
969 		}
970 		else
971 			readToken (token);
972 	}
973 
974 	if (!collectorIsEmpty(&inherits))
975 	{
976 		tagEntryInfo *e = getEntryInCorkQueue (scope);
977 		if (e)
978 		{
979 			e->extensionFields.inheritance = vStringDeleteUnwrap (inheritsBuf);
980 			inheritsBuf = NULL;
981 		}
982 	}
983 	vStringDelete (inheritsBuf);
984 }
985 
parseStructMembers(tokenInfo * const token,const int scope)986 static void parseStructMembers (tokenInfo *const token, const int scope)
987 {
988 	// StructType     = "struct" "{" { FieldDecl ";" } "}" .
989 	// FieldDecl      = (IdentifierList Type | AnonymousField) [ Tag ] .
990 	// AnonymousField = [ "*" ] TypeName .
991 	// Tag            = string_lit .
992 
993 	readToken (token);
994 	if (!isType (token, TOKEN_OPEN_CURLY))
995 		return;
996 
997 	vString *typeForAnonMember = vStringNew ();
998 	intArray *corkForFields = intArrayNew ();
999 
1000 	readToken (token);
1001 	while (!isType (token, TOKEN_EOF) && !isType (token, TOKEN_CLOSE_CURLY))
1002 	{
1003 		tokenInfo *memberCandidate = NULL;
1004 		bool first = true;
1005 
1006 		while (!isType (token, TOKEN_EOF))
1007 		{
1008 			if (isType (token, TOKEN_IDENTIFIER))
1009 			{
1010 				if (first)
1011 				{
1012 					// could be anonymous field like in 'struct {int}' - we don't know yet
1013 					memberCandidate = newToken ();
1014 					copyToken (memberCandidate, token);
1015 					first = false;
1016 				}
1017 				else
1018 				{
1019 					int cork;
1020 					if (memberCandidate)
1021 					{
1022 						// if we are here, there was a comma and memberCandidate isn't an anonymous field
1023 						cork = makeTag (memberCandidate, GOTAG_MEMBER, scope, NULL, NULL);
1024 						deleteToken (memberCandidate);
1025 						memberCandidate = NULL;
1026 						intArrayAdd (corkForFields, cork);
1027 					}
1028 					cork = makeTag (token, GOTAG_MEMBER, scope, NULL, NULL);
1029 					intArrayAdd (corkForFields, cork);
1030 				}
1031 				readToken (token);
1032 			}
1033 			if (!isType (token, TOKEN_COMMA))
1034 				break;
1035 			readToken (token);
1036 		}
1037 
1038 		if (first && isType (token, TOKEN_STAR))
1039 		{
1040 			vStringPut (typeForAnonMember, '*');
1041 			readToken (token);
1042 		}
1043 		else if (memberCandidate &&
1044 				 (isType (token, TOKEN_DOT) ||
1045 				  isType (token, TOKEN_STRING) ||
1046 				  isType (token, TOKEN_SEMICOLON)))
1047 			// memberCandidate is part of anonymous type
1048 			vStringCat (typeForAnonMember, memberCandidate->string);
1049 
1050 		// the above two cases that set typeForAnonMember guarantee
1051 		// this is an anonymous member
1052 		if (vStringLength (typeForAnonMember) > 0)
1053 		{
1054 			tokenInfo *anonMember = NULL;
1055 
1056 			if (memberCandidate)
1057 			{
1058 				anonMember = newToken ();
1059 				copyToken (anonMember, memberCandidate);
1060 			}
1061 
1062 			// TypeName of AnonymousField has a dot like package"."type.
1063 			// Pick up the last package component, and store it to
1064 			// memberCandidate.
1065 			while (isType (token, TOKEN_IDENTIFIER) ||
1066 				   isType (token, TOKEN_DOT))
1067 			{
1068 				if (isType (token, TOKEN_IDENTIFIER))
1069 				{
1070 					if (!anonMember)
1071 						anonMember = newToken ();
1072 					copyToken (anonMember, token);
1073 					vStringCat (typeForAnonMember, anonMember->string);
1074 				}
1075 				else if (isType (token, TOKEN_DOT))
1076 					vStringPut (typeForAnonMember, '.');
1077 				readToken (token);
1078 			}
1079 
1080 			// optional tag
1081 			if (isType (token, TOKEN_STRING))
1082 				readToken (token);
1083 
1084 			if (anonMember)
1085 			{
1086 				makeTag (anonMember, GOTAG_ANONMEMBER, scope, NULL,
1087 						 vStringValue (typeForAnonMember));
1088 				deleteToken (anonMember);
1089 			}
1090 		}
1091 		else
1092 		{
1093 			vString *typeForMember = vStringNew ();
1094 			collector collector = { .str = typeForMember, .last_len = 0, };
1095 
1096 			collectorAppendToken (&collector, token);
1097 			skipType (token, &collector);
1098 			collectorTruncate (&collector, true);
1099 
1100 			if (memberCandidate)
1101 				makeTag (memberCandidate, GOTAG_MEMBER, scope, NULL,
1102 						 vStringValue (typeForMember));
1103 
1104 			attachTypeRefField (scope, corkForFields, vStringValue (typeForMember));
1105 			intArrayClear (corkForFields);
1106 			vStringDelete (typeForMember);
1107 		}
1108 
1109 		if (memberCandidate)
1110 			deleteToken (memberCandidate);
1111 
1112 		vStringClear (typeForAnonMember);
1113 
1114 		while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_CURLY)
1115 				&& !isType (token, TOKEN_EOF))
1116 		{
1117 			readToken (token);
1118 			skipToMatched (token, NULL);
1119 		}
1120 
1121 		if (!isType (token, TOKEN_CLOSE_CURLY))
1122 		{
1123 			// we are at TOKEN_SEMICOLON
1124 			readToken (token);
1125 		}
1126 	}
1127 
1128 	intArrayDelete (corkForFields);
1129 	vStringDelete (typeForAnonMember);
1130 }
1131 
parseConstTypeVar(tokenInfo * const token,goKind kind,const int scope)1132 static void parseConstTypeVar (tokenInfo *const token, goKind kind, const int scope)
1133 {
1134 	// ConstDecl      = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
1135 	// ConstSpec      = IdentifierList [ [ Type ] "=" ExpressionList ] .
1136 	// IdentifierList = identifier { "," identifier } .
1137 	// ExpressionList = Expression { "," Expression } .
1138 	// TypeDecl     = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
1139 	// TypeSpec     = identifier [ "=" ] Type .
1140 	// VarDecl     = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
1141 	// VarSpec     = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
1142 	bool usesParens = false;
1143 	intArray *corks
1144 		= (kind == GOTAG_VAR || kind == GOTAG_CONST)? intArrayNew (): NULL;
1145 
1146 	readToken (token);
1147 
1148 	if (isType (token, TOKEN_OPEN_PAREN))
1149 	{
1150 		usesParens = true;
1151 		readToken (token);
1152 	}
1153 
1154 	do
1155 	{
1156 		tokenInfo *typeToken = NULL;
1157 		int member_scope = scope;
1158 
1159 		while (!isType (token, TOKEN_EOF))
1160 		{
1161 			if (isType (token, TOKEN_IDENTIFIER))
1162 			{
1163 				if (kind == GOTAG_TYPE)
1164 				{
1165 					typeToken = newToken ();
1166 					copyToken (typeToken, token);
1167 					readToken (token);
1168 					if (isType (token, TOKEN_EQUAL))
1169 					{
1170 						kind = GOTAG_TALIAS;
1171 						readToken (token);
1172 					}
1173 
1174 					if (isKeyword (token, KEYWORD_struct))
1175 						member_scope = makeTag (typeToken, GOTAG_STRUCT,
1176 												scope, NULL, NULL);
1177 					else if (isKeyword (token, KEYWORD_interface))
1178 						member_scope = makeTag (typeToken, GOTAG_INTERFACE,
1179 												scope, NULL, NULL);
1180 					else
1181 						member_scope = makeTag (typeToken, kind,
1182 												scope, NULL, NULL);
1183 
1184 					if (member_scope != CORK_NIL)
1185 						registerEntry (member_scope);
1186 					break;
1187 				}
1188 				else
1189 				{
1190 					int c = makeTag (token, kind, scope, NULL, NULL);
1191 					if (c != CORK_NIL && corks)
1192 						intArrayAdd (corks, c);
1193 				}
1194 				readToken (token);
1195 			}
1196 			if (!isType (token, TOKEN_COMMA))
1197 				break;
1198 			readToken (token);
1199 		}
1200 
1201 		if (typeToken)
1202 		{
1203 			if (isKeyword (token, KEYWORD_struct))
1204 				parseStructMembers (token, member_scope);
1205 			else if (isKeyword (token, KEYWORD_interface))
1206 				parseInterfaceMethods (token, member_scope);
1207 			else
1208 			{
1209 				/* Filling "typeref:" field of typeToken. */
1210 				vString *buffer = vStringNew ();
1211 				collector collector = { .str = buffer, .last_len = 0, };
1212 
1213 				collectorAppendToken (&collector, token);
1214 				skipType (token, &collector);
1215 				collectorTruncate (&collector, true);
1216 
1217 				if ((member_scope != CORK_NIL) && !vStringIsEmpty (buffer))
1218 				{
1219 					tagEntryInfo *e = getEntryInCorkQueue (member_scope);
1220 					if (e)
1221 					{
1222 						e->extensionFields.typeRef [0] = eStrdup ("typename");
1223 						e->extensionFields.typeRef [1] = vStringDeleteUnwrap (buffer);
1224 					}
1225 				}
1226 				else
1227 					vStringDelete (buffer);
1228 			}
1229 			deleteToken (typeToken);
1230 		}
1231 		else if (corks)
1232 		{
1233 			vString *buffer = vStringNew ();
1234 			collector collector = { .str = buffer, .last_len = 0, };
1235 
1236 			collectorAppendToken (&collector, token);
1237 			skipType (token, &collector);
1238 			collectorTruncate (&collector, true);
1239 
1240 			if (!vStringIsEmpty (buffer))
1241 				attachTypeRefField (scope, corks, vStringValue (buffer));
1242 			vStringDelete (buffer);
1243 			intArrayClear (corks);
1244 		}
1245 		else
1246 			skipType (token, NULL);
1247 
1248 		while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_PAREN)
1249 				&& !isType (token, TOKEN_EOF))
1250 		{
1251 			readToken (token);
1252 			skipToMatched (token, NULL);
1253 		}
1254 
1255 		if (member_scope != scope && member_scope != CORK_NIL)
1256 		{
1257 			tagEntryInfo *e = getEntryInCorkQueue (member_scope);
1258 			if (e)
1259 				e->extensionFields.endLine = getInputLineNumber ();
1260 		}
1261 
1262 		if (usesParens && !isType (token, TOKEN_CLOSE_PAREN))
1263 		{
1264 			// we are at TOKEN_SEMICOLON
1265 			readToken (token);
1266 		}
1267 	}
1268 	while (!isType (token, TOKEN_EOF) &&
1269 			usesParens && !isType (token, TOKEN_CLOSE_PAREN));
1270 
1271 	intArrayDelete (corks);
1272 }
1273 
parseImportSpec(tokenInfo * const token)1274 static void parseImportSpec (tokenInfo *const token)
1275 {
1276 	// ImportSpec       = [ "." | PackageName ] ImportPath .
1277 	// ImportPath       = string_lit .
1278 
1279 	int packageName_cork = CORK_NIL;
1280 	char *how_imported = NULL;
1281 	if (isType (token, TOKEN_IDENTIFIER))
1282 	{
1283 		if (strcmp(vStringValue (token->string), "_") == 0)
1284 			how_imported = "init";
1285 		else
1286 		{
1287 			packageName_cork = makeTag (token, GOTAG_PACKAGE_NAME,
1288 										CORK_NIL, NULL, NULL);
1289 		}
1290 		readToken (token);
1291 	}
1292 	else if (isType (token, TOKEN_DOT))
1293 	{
1294 		how_imported = "inline";
1295 		readToken (token);
1296 	}
1297 
1298 	if (isType (token, TOKEN_STRING))
1299 	{
1300 		int package_cork =
1301 			makeRefTag (token, GOTAG_PACKAGE, R_GOTAG_PACKAGE_IMPORTED);
1302 
1303 		if (package_cork != CORK_NIL && how_imported)
1304 			attachParserFieldToCorkEntry (package_cork,
1305 										  GoFields [F_HOW_IMPORTED].ftype,
1306 										  how_imported);
1307 
1308 		if (packageName_cork != CORK_NIL)
1309 		{
1310 			attachParserFieldToCorkEntry (packageName_cork,
1311 										  GoFields [F_PACKAGE].ftype,
1312 										  vStringValue (token->string));
1313 			if (package_cork != CORK_NIL)
1314 			{
1315 				tagEntryInfo *e = getEntryInCorkQueue (packageName_cork);
1316 				if (e)
1317 					attachParserFieldToCorkEntry (package_cork,
1318 												  GoFields [F_PACKAGE_NAME].ftype,
1319 												  e->name);
1320 			}
1321 		}
1322 	}
1323 }
1324 
parseImport(tokenInfo * const token)1325 static void parseImport (tokenInfo *const token)
1326 {
1327 	// ImportDecl       = "import" ( ImportSpec | "(" { ImportSpec ";" } ")" ) .
1328 
1329 	readToken (token);
1330 	if (isType (token, TOKEN_EOF))
1331 		return;
1332 
1333 	if (isType (token, TOKEN_OPEN_PAREN))
1334 	{
1335 		do
1336 		{
1337 			parseImportSpec (token);
1338 			readToken (token);
1339 		} while (!isType (token, TOKEN_EOF) &&
1340 				 !isType (token, TOKEN_CLOSE_PAREN));
1341 	}
1342 	else
1343 	{
1344 		parseImportSpec (token);
1345 		return;
1346 	}
1347 }
1348 
parseGoFile(tokenInfo * const token)1349 static void parseGoFile (tokenInfo *const token)
1350 {
1351 	int scope = CORK_NIL;
1352 	do
1353 	{
1354 		readToken (token);
1355 
1356 		if (isType (token, TOKEN_KEYWORD))
1357 		{
1358 			switch (token->keyword)
1359 			{
1360 				case KEYWORD_package:
1361 					scope = parsePackage (token);
1362 					break;
1363 				case KEYWORD_func:
1364 					parseFunctionOrMethod (token, scope);
1365 					break;
1366 				case KEYWORD_const:
1367 					parseConstTypeVar (token, GOTAG_CONST, scope);
1368 					break;
1369 				case KEYWORD_type:
1370 					parseConstTypeVar (token, GOTAG_TYPE, scope);
1371 					break;
1372 				case KEYWORD_var:
1373 					parseConstTypeVar (token, GOTAG_VAR, scope);
1374 					break;
1375 				case KEYWORD_import:
1376 					parseImport (token);
1377 					break;
1378 				default:
1379 					break;
1380 			}
1381 		}
1382 		else if (isType (token, TOKEN_OPEN_PAREN) || isType (token, TOKEN_OPEN_CURLY) ||
1383 			isType (token, TOKEN_OPEN_SQUARE))
1384 		{
1385 			skipToMatched (token, NULL);
1386 		}
1387 	} while (token->type != TOKEN_EOF);
1388 }
1389 
findGoTags(void)1390 static void findGoTags (void)
1391 {
1392 	tokenInfo *const token = newToken ();
1393 
1394 	parseGoFile (token);
1395 
1396 	deleteToken (token);
1397 }
1398 
GoParser(void)1399 extern parserDefinition *GoParser (void)
1400 {
1401 	static const char *const extensions[] = { "go", NULL };
1402 	parserDefinition *def = parserNew ("Go");
1403 	def->kindTable = GoKinds;
1404 	def->kindCount = ARRAY_SIZE (GoKinds);
1405 	def->extensions = extensions;
1406 	def->parser = findGoTags;
1407 	def->initialize = initialize;
1408 	def->finalize = finalize;
1409 	def->keywordTable = GoKeywordTable;
1410 	def->keywordCount = ARRAY_SIZE (GoKeywordTable);
1411 	def->fieldTable = GoFields;
1412 	def->fieldCount = ARRAY_SIZE (GoFields);
1413 	def->useCork = CORK_QUEUE | CORK_SYMTAB;
1414 	def->requestAutomaticFQTag = true;
1415 	return def;
1416 }
1417