4 #include "general.h" /* must always come first */
16 #define isType(token,t) (boolean) ((token)->type == (t))
17 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
23 typedef enum eKeywordId
{
37 /* Used to determine whether keyword is valid for the current language and
40 typedef struct sKeywordDesc
{
45 typedef enum eTokenType
{
47 // Token not important for top-level Go parsing
66 typedef struct sTokenInfo
{
69 vString
*string
; /* the name of the token */
70 unsigned long lineNumber
; /* line number of tag */
71 MIOPos filePosition
; /* file position of line containing name */
79 static vString
*scope
;
90 static kindOption GoKinds
[] = {
91 {TRUE
, 'p', "namespace", "packages"},
92 {TRUE
, 'f', "function", "functions"},
93 {TRUE
, 'c', "macro", "constants"},
94 {TRUE
, 't', "typedef", "types"},
95 {TRUE
, 'v', "variable", "variables"}
98 static keywordDesc GoKeywordTable
[] = {
99 {"package", KEYWORD_package
},
100 {"import", KEYWORD_import
},
101 {"const", KEYWORD_const
},
102 {"type", KEYWORD_type
},
103 {"var", KEYWORD_var
},
104 {"func", KEYWORD_func
},
105 {"struct", KEYWORD_struct
},
106 {"interface", KEYWORD_interface
},
107 {"map", KEYWORD_map
},
108 {"chan", KEYWORD_chan
}
112 * FUNCTION DEFINITIONS
116 static boolean
isStartIdentChar (const int c
)
119 (isalpha (c
) || c
== '_' || c
> 128);
122 static boolean
isIdentChar (const int c
)
125 (isStartIdentChar (c
) || isdigit (c
));
128 static void initialize (const langType language
)
132 sizeof (GoKeywordTable
) / sizeof (GoKeywordTable
[0]);
134 for (i
= 0; i
< count
; ++i
)
136 const keywordDesc
*const p
= &GoKeywordTable
[i
];
137 addKeyword (p
->name
, language
, (int) p
->id
);
141 static tokenInfo
*newToken (void)
143 tokenInfo
*const token
= xMalloc (1, tokenInfo
);
144 token
->type
= TOKEN_NONE
;
145 token
->keyword
= KEYWORD_NONE
;
146 token
->string
= vStringNew ();
147 token
->lineNumber
= getSourceLineNumber ();
148 token
->filePosition
= getInputFilePosition ();
152 static void deleteToken (tokenInfo
* const token
)
156 vStringDelete (token
->string
);
165 static void parseString (vString
*const string
, const int delimiter
)
173 else if (c
== '\\' && delimiter
!= '`')
176 if (c
!= '\'' && c
!= '\"')
177 vStringPut (string
, '\\');
178 vStringPut (string
, c
);
180 else if (c
== delimiter
)
183 vStringPut (string
, c
);
185 vStringTerminate (string
);
188 static void parseIdentifier (vString
*const string
, const int firstChar
)
193 vStringPut (string
, c
);
195 } while (isIdentChar (c
));
196 vStringTerminate (string
);
197 fileUngetc (c
); /* always unget, LF might add a semicolon */
200 static void readToken (tokenInfo
*const token
)
203 static tokenType lastTokenType
= TOKEN_NONE
;
205 token
->type
= TOKEN_NONE
;
206 token
->keyword
= KEYWORD_NONE
;
207 vStringClear (token
->string
);
213 token
->lineNumber
= getSourceLineNumber ();
214 token
->filePosition
= getInputFilePosition ();
215 if (c
== '\n' && (lastTokenType
== TOKEN_IDENTIFIER
||
216 lastTokenType
== TOKEN_STRING
||
217 lastTokenType
== TOKEN_OTHER
||
218 lastTokenType
== TOKEN_CLOSE_PAREN
||
219 lastTokenType
== TOKEN_CLOSE_CURLY
||
220 lastTokenType
== TOKEN_CLOSE_SQUARE
))
222 token
->type
= TOKEN_SEMICOLON
;
226 while (c
== '\t' || c
== ' ' || c
== '\r' || c
== '\n');
231 token
->type
= TOKEN_EOF
;
235 token
->type
= TOKEN_SEMICOLON
;
240 boolean hasNewline
= FALSE
;
245 fileSkipToCharacter ('\n');
246 /* Line comments start with the
247 * character sequence // and
248 * continue through the next
249 * newline. A line comment acts
263 } while (d
!= EOF
&& d
!= '*');
270 } while (c
!= EOF
&& c
!= '\0');
272 fileUngetc (hasNewline
? '\n' : ' ');
275 token
->type
= TOKEN_OTHER
;
285 token
->type
= TOKEN_STRING
;
286 parseString (token
->string
, c
);
287 token
->lineNumber
= getSourceLineNumber ();
288 token
->filePosition
= getInputFilePosition ();
295 token
->type
= TOKEN_LEFT_ARROW
;
299 token
->type
= TOKEN_OTHER
;
305 token
->type
= TOKEN_OPEN_PAREN
;
309 token
->type
= TOKEN_CLOSE_PAREN
;
313 token
->type
= TOKEN_OPEN_CURLY
;
317 token
->type
= TOKEN_CLOSE_CURLY
;
321 token
->type
= TOKEN_OPEN_SQUARE
;
325 token
->type
= TOKEN_CLOSE_SQUARE
;
329 token
->type
= TOKEN_STAR
;
333 token
->type
= TOKEN_DOT
;
337 token
->type
= TOKEN_COMMA
;
341 if (isStartIdentChar (c
))
343 parseIdentifier (token
->string
, c
);
344 token
->lineNumber
= getSourceLineNumber ();
345 token
->filePosition
= getInputFilePosition ();
346 token
->keyword
= lookupKeyword (vStringValue (token
->string
), Lang_go
);
347 if (isKeyword (token
, KEYWORD_NONE
))
348 token
->type
= TOKEN_IDENTIFIER
;
350 token
->type
= TOKEN_KEYWORD
;
353 token
->type
= TOKEN_OTHER
;
358 lastTokenType
= token
->type
;
361 static void skipToMatched (tokenInfo
*const token
)
364 tokenType open_token
;
365 tokenType close_token
;
369 case TOKEN_OPEN_PAREN
:
370 open_token
= TOKEN_OPEN_PAREN
;
371 close_token
= TOKEN_CLOSE_PAREN
;
373 case TOKEN_OPEN_CURLY
:
374 open_token
= TOKEN_OPEN_CURLY
;
375 close_token
= TOKEN_CLOSE_CURLY
;
377 case TOKEN_OPEN_SQUARE
:
378 open_token
= TOKEN_OPEN_SQUARE
;
379 close_token
= TOKEN_CLOSE_SQUARE
;
386 * This routine will skip to a matching closing token.
387 * It will also handle nested tokens like the (, ) below.
388 * ( name varchar(30), text binary(10) )
390 if (isType (token
, open_token
))
393 while (!(isType (token
, close_token
) && (nest_level
== 0)) &&
394 !isType (token
, TOKEN_EOF
))
397 if (isType (token
, open_token
))
401 if (isType (token
, close_token
))
413 static void skipType (tokenInfo
*const token
)
416 // Type = TypeName | TypeLit | "(" Type ")" .
417 // Skips also function multiple return values "(" Type {"," Type} ")"
418 if (isType (token
, TOKEN_OPEN_PAREN
))
420 skipToMatched (token
);
424 // TypeName = QualifiedIdent.
425 // QualifiedIdent = [ PackageName "." ] identifier .
426 // PackageName = identifier .
427 if (isType (token
, TOKEN_IDENTIFIER
))
430 if (isType (token
, TOKEN_DOT
))
433 if (isType (token
, TOKEN_IDENTIFIER
))
439 // StructType = "struct" "{" { FieldDecl ";" } "}"
440 // InterfaceType = "interface" "{" { MethodSpec ";" } "}" .
441 if (isKeyword (token
, KEYWORD_struct
) || isKeyword (token
, KEYWORD_interface
))
445 skipToMatched (token
);
449 // ArrayType = "[" ArrayLength "]" ElementType .
450 // SliceType = "[" "]" ElementType .
451 // ElementType = Type .
452 if (isType (token
, TOKEN_OPEN_SQUARE
))
454 skipToMatched (token
);
458 // PointerType = "*" BaseType .
460 // ChannelType = ( "chan" [ "<-" ] | "<-" "chan" ) ElementType .
461 if (isType (token
, TOKEN_STAR
) || isKeyword (token
, KEYWORD_chan
) || isType (token
, TOKEN_LEFT_ARROW
))
467 // MapType = "map" "[" KeyType "]" ElementType .
469 if (isKeyword (token
, KEYWORD_map
))
473 skipToMatched (token
);
477 // FunctionType = "func" Signature .
478 // Signature = Parameters [ Result ] .
479 // Result = Parameters | Type .
480 // Parameters = "(" [ ParameterList [ "," ] ] ")" .
481 if (isKeyword (token
, KEYWORD_func
))
484 // Parameters, skip over "()"
485 skipToMatched (token
);
486 // Result is parameters or type or nothing. skipType treats anything
487 // surrounded by parentheses as a type, and does nothing if what
488 // follows is not a type.
493 static void makeTag (tokenInfo
*const token
, const goKind kind
)
495 const char *const name
= vStringValue (token
->string
);
498 initTagEntry (&e
, name
);
500 if (!GoKinds
[kind
].enabled
)
503 e
.lineNumber
= token
->lineNumber
;
504 e
.filePosition
= token
->filePosition
;
505 e
.kindName
= GoKinds
[kind
].name
;
506 e
.kind
= GoKinds
[kind
].letter
;
510 if (scope
&& Option
.include
.qualifiedTags
)
512 vString
*qualifiedName
= vStringNew ();
513 vStringCopy (qualifiedName
, scope
);
514 vStringCatS (qualifiedName
, ".");
515 vStringCat (qualifiedName
, token
->string
);
516 e
.name
= vStringValue (qualifiedName
);
518 vStringDelete (qualifiedName
);
522 static void parsePackage (tokenInfo
*const token
)
525 if (isType (token
, TOKEN_IDENTIFIER
))
527 makeTag (token
, GOTAG_PACKAGE
);
528 if (!scope
&& Option
.include
.qualifiedTags
)
530 scope
= vStringNew ();
531 vStringCopy (scope
, token
->string
);
536 static void parseFunctionOrMethod (tokenInfo
*const token
)
538 // FunctionDecl = "func" identifier Signature [ Body ] .
541 // MethodDecl = "func" Receiver MethodName Signature [ Body ] .
542 // Receiver = "(" [ identifier ] [ "*" ] BaseTypeName ")" .
543 // BaseTypeName = identifier .
545 // Skip over receiver.
547 if (isType (token
, TOKEN_OPEN_PAREN
))
548 skipToMatched (token
);
550 if (isType (token
, TOKEN_IDENTIFIER
))
552 makeTag (token
, GOTAG_FUNCTION
);
554 // Skip over parameters.
556 skipToMatched (token
);
561 // Skip over function body.
562 if (isType (token
, TOKEN_OPEN_CURLY
))
563 skipToMatched (token
);
567 static void parseConstTypeVar (tokenInfo
*const token
, goKind kind
)
569 // ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
570 // ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
571 // IdentifierList = identifier { "," identifier } .
572 // ExpressionList = Expression { "," Expression } .
573 // TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
574 // TypeSpec = identifier Type .
575 // VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
576 // VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
577 boolean usesParens
= FALSE
;
581 if (isType (token
, TOKEN_OPEN_PAREN
))
589 while (!isType (token
, TOKEN_EOF
))
591 if (isType (token
, TOKEN_IDENTIFIER
))
593 makeTag (token
, kind
);
596 if (!isType (token
, TOKEN_COMMA
))
602 while (!isType (token
, TOKEN_SEMICOLON
) && !isType (token
, TOKEN_CLOSE_PAREN
)
603 && !isType (token
, TOKEN_EOF
))
606 skipToMatched (token
);
609 if (usesParens
&& !isType (token
, TOKEN_CLOSE_PAREN
))
611 // we are at TOKEN_SEMICOLON
615 while (!isType (token
, TOKEN_EOF
) &&
616 usesParens
&& !isType (token
, TOKEN_CLOSE_PAREN
));
619 static void parseGoFile (tokenInfo
*const token
)
625 if (isType (token
, TOKEN_KEYWORD
))
627 switch (token
->keyword
)
629 case KEYWORD_package
:
630 parsePackage (token
);
633 parseFunctionOrMethod (token
);
636 parseConstTypeVar (token
, GOTAG_CONST
);
639 parseConstTypeVar (token
, GOTAG_TYPE
);
642 parseConstTypeVar (token
, GOTAG_VAR
);
648 else if (isType (token
, TOKEN_OPEN_PAREN
) || isType (token
, TOKEN_OPEN_CURLY
) ||
649 isType (token
, TOKEN_OPEN_SQUARE
))
651 skipToMatched (token
);
653 } while (token
->type
!= TOKEN_EOF
);
656 static void findGoTags (void)
658 tokenInfo
*const token
= newToken ();
663 vStringDelete (scope
);
667 extern parserDefinition
*GoParser (void)
669 static const char *const extensions
[] = { "go", NULL
};
670 parserDefinition
*def
= parserNew ("Go");
671 def
->kinds
= GoKinds
;
672 def
->kindCount
= KIND_COUNT (GoKinds
);
673 def
->extensions
= extensions
;
674 def
->parser
= findGoTags
;
675 def
->initialize
= initialize
;