Merge pull request #482 from philippwiesemann/fix-typos-po-de
[geany-mirror.git] / tagmanager / ctags / go.c
blob1e5c2d76f40751c5ccfac90d70ff253c3c0cf9c5
1 /*
2 * INCLUDE FILES
3 */
4 #include "general.h" /* must always come first */
6 #include "entry.h"
7 #include "keyword.h"
8 #include "read.h"
9 #include "main.h"
10 #include "vstring.h"
11 #include "options.h"
14 * MACROS
16 #define isType(token,t) (boolean) ((token)->type == (t))
17 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
20 * DATA DECLARATIONS
23 typedef enum eKeywordId {
24 KEYWORD_NONE = -1,
25 KEYWORD_package,
26 KEYWORD_import,
27 KEYWORD_const,
28 KEYWORD_type,
29 KEYWORD_var,
30 KEYWORD_func,
31 KEYWORD_struct,
32 KEYWORD_interface,
33 KEYWORD_map,
34 KEYWORD_chan
35 } keywordId;
37 /* Used to determine whether keyword is valid for the current language and
38 * what its ID is.
40 typedef struct sKeywordDesc {
41 const char *name;
42 keywordId id;
43 } keywordDesc;
45 typedef enum eTokenType {
46 TOKEN_NONE = -1,
47 // Token not important for top-level Go parsing
48 TOKEN_OTHER,
49 TOKEN_KEYWORD,
50 TOKEN_IDENTIFIER,
51 TOKEN_STRING,
52 TOKEN_OPEN_PAREN,
53 TOKEN_CLOSE_PAREN,
54 TOKEN_OPEN_CURLY,
55 TOKEN_CLOSE_CURLY,
56 TOKEN_OPEN_SQUARE,
57 TOKEN_CLOSE_SQUARE,
58 TOKEN_SEMICOLON,
59 TOKEN_STAR,
60 TOKEN_LEFT_ARROW,
61 TOKEN_DOT,
62 TOKEN_COMMA,
63 TOKEN_EOF
64 } tokenType;
66 typedef struct sTokenInfo {
67 tokenType type;
68 keywordId keyword;
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 */
72 } tokenInfo;
75 * DATA DEFINITIONS
78 static int Lang_go;
79 static vString *scope;
81 typedef enum {
82 GOTAG_UNDEFINED = -1,
83 GOTAG_PACKAGE,
84 GOTAG_FUNCTION,
85 GOTAG_CONST,
86 GOTAG_TYPE,
87 GOTAG_VAR,
88 } goKind;
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
115 // XXX UTF-8
116 static boolean isStartIdentChar (const int c)
118 return (boolean)
119 (isalpha (c) || c == '_' || c > 128);
122 static boolean isIdentChar (const int c)
124 return (boolean)
125 (isStartIdentChar (c) || isdigit (c));
128 static void initialize (const langType language)
130 size_t i;
131 const size_t count =
132 sizeof (GoKeywordTable) / sizeof (GoKeywordTable[0]);
133 Lang_go = language;
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 ();
149 return token;
152 static void deleteToken (tokenInfo * const token)
154 if (token != NULL)
156 vStringDelete (token->string);
157 eFree (token);
162 * Parsing functions
165 static void parseString (vString *const string, const int delimiter)
167 boolean end = FALSE;
168 while (!end)
170 int c = fileGetc ();
171 if (c == EOF)
172 end = TRUE;
173 else if (c == '\\' && delimiter != '`')
175 c = fileGetc ();
176 if (c != '\'' && c != '\"')
177 vStringPut (string, '\\');
178 vStringPut (string, c);
180 else if (c == delimiter)
181 end = TRUE;
182 else
183 vStringPut (string, c);
185 vStringTerminate (string);
188 static void parseIdentifier (vString *const string, const int firstChar)
190 int c = firstChar;
193 vStringPut (string, c);
194 c = fileGetc ();
195 } while (isIdentChar (c));
196 vStringTerminate (string);
197 fileUngetc (c); /* always unget, LF might add a semicolon */
200 static void readToken (tokenInfo *const token)
202 int c;
203 static tokenType lastTokenType = TOKEN_NONE;
205 token->type = TOKEN_NONE;
206 token->keyword = KEYWORD_NONE;
207 vStringClear (token->string);
209 getNextChar:
212 c = fileGetc ();
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;
223 goto done;
226 while (c == '\t' || c == ' ' || c == '\r' || c == '\n');
228 switch (c)
230 case EOF:
231 token->type = TOKEN_EOF;
232 break;
234 case ';':
235 token->type = TOKEN_SEMICOLON;
236 break;
238 case '/':
240 boolean hasNewline = FALSE;
241 int d = fileGetc ();
242 switch (d)
244 case '/':
245 fileSkipToCharacter ('\n');
246 /* Line comments start with the
247 * character sequence // and
248 * continue through the next
249 * newline. A line comment acts
250 * like a newline. */
251 fileUngetc ('\n');
252 goto getNextChar;
253 case '*':
258 d = fileGetc ();
259 if (d == '\n')
261 hasNewline = TRUE;
263 } while (d != EOF && d != '*');
265 c = fileGetc ();
266 if (c == '/')
267 break;
268 else
269 fileUngetc (c);
270 } while (c != EOF && c != '\0');
272 fileUngetc (hasNewline ? '\n' : ' ');
273 goto getNextChar;
274 default:
275 token->type = TOKEN_OTHER;
276 fileUngetc (d);
277 break;
280 break;
282 case '"':
283 case '\'':
284 case '`':
285 token->type = TOKEN_STRING;
286 parseString (token->string, c);
287 token->lineNumber = getSourceLineNumber ();
288 token->filePosition = getInputFilePosition ();
289 break;
291 case '<':
293 int d = fileGetc ();
294 if (d == '-')
295 token->type = TOKEN_LEFT_ARROW;
296 else
298 fileUngetc (d);
299 token->type = TOKEN_OTHER;
302 break;
304 case '(':
305 token->type = TOKEN_OPEN_PAREN;
306 break;
308 case ')':
309 token->type = TOKEN_CLOSE_PAREN;
310 break;
312 case '{':
313 token->type = TOKEN_OPEN_CURLY;
314 break;
316 case '}':
317 token->type = TOKEN_CLOSE_CURLY;
318 break;
320 case '[':
321 token->type = TOKEN_OPEN_SQUARE;
322 break;
324 case ']':
325 token->type = TOKEN_CLOSE_SQUARE;
326 break;
328 case '*':
329 token->type = TOKEN_STAR;
330 break;
332 case '.':
333 token->type = TOKEN_DOT;
334 break;
336 case ',':
337 token->type = TOKEN_COMMA;
338 break;
340 default:
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;
349 else
350 token->type = TOKEN_KEYWORD;
352 else
353 token->type = TOKEN_OTHER;
354 break;
357 done:
358 lastTokenType = token->type;
361 static void skipToMatched (tokenInfo *const token)
363 int nest_level = 0;
364 tokenType open_token;
365 tokenType close_token;
367 switch (token->type)
369 case TOKEN_OPEN_PAREN:
370 open_token = TOKEN_OPEN_PAREN;
371 close_token = TOKEN_CLOSE_PAREN;
372 break;
373 case TOKEN_OPEN_CURLY:
374 open_token = TOKEN_OPEN_CURLY;
375 close_token = TOKEN_CLOSE_CURLY;
376 break;
377 case TOKEN_OPEN_SQUARE:
378 open_token = TOKEN_OPEN_SQUARE;
379 close_token = TOKEN_CLOSE_SQUARE;
380 break;
381 default:
382 return;
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))
392 nest_level++;
393 while (!(isType (token, close_token) && (nest_level == 0)) &&
394 !isType (token, TOKEN_EOF))
396 readToken (token);
397 if (isType (token, open_token))
399 nest_level++;
401 if (isType (token, close_token))
403 if (nest_level > 0)
405 nest_level--;
409 readToken (token);
413 static void skipType (tokenInfo *const token)
415 again:
416 // Type = TypeName | TypeLit | "(" Type ")" .
417 // Skips also function multiple return values "(" Type {"," Type} ")"
418 if (isType (token, TOKEN_OPEN_PAREN))
420 skipToMatched (token);
421 return;
424 // TypeName = QualifiedIdent.
425 // QualifiedIdent = [ PackageName "." ] identifier .
426 // PackageName = identifier .
427 if (isType (token, TOKEN_IDENTIFIER))
429 readToken (token);
430 if (isType (token, TOKEN_DOT))
432 readToken (token);
433 if (isType (token, TOKEN_IDENTIFIER))
434 readToken (token);
436 return;
439 // StructType = "struct" "{" { FieldDecl ";" } "}"
440 // InterfaceType = "interface" "{" { MethodSpec ";" } "}" .
441 if (isKeyword (token, KEYWORD_struct) || isKeyword (token, KEYWORD_interface))
443 readToken (token);
444 // skip over "{}"
445 skipToMatched (token);
446 return;
449 // ArrayType = "[" ArrayLength "]" ElementType .
450 // SliceType = "[" "]" ElementType .
451 // ElementType = Type .
452 if (isType (token, TOKEN_OPEN_SQUARE))
454 skipToMatched (token);
455 goto again;
458 // PointerType = "*" BaseType .
459 // BaseType = Type .
460 // ChannelType = ( "chan" [ "<-" ] | "<-" "chan" ) ElementType .
461 if (isType (token, TOKEN_STAR) || isKeyword (token, KEYWORD_chan) || isType (token, TOKEN_LEFT_ARROW))
463 readToken (token);
464 goto again;
467 // MapType = "map" "[" KeyType "]" ElementType .
468 // KeyType = Type .
469 if (isKeyword (token, KEYWORD_map))
471 readToken (token);
472 // skip over "[]"
473 skipToMatched (token);
474 goto again;
477 // FunctionType = "func" Signature .
478 // Signature = Parameters [ Result ] .
479 // Result = Parameters | Type .
480 // Parameters = "(" [ ParameterList [ "," ] ] ")" .
481 if (isKeyword (token, KEYWORD_func))
483 readToken (token);
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.
489 goto again;
493 static void makeTag (tokenInfo *const token, const goKind kind)
495 const char *const name = vStringValue (token->string);
497 tagEntryInfo e;
498 initTagEntry (&e, name);
500 if (!GoKinds [kind].enabled)
501 return;
503 e.lineNumber = token->lineNumber;
504 e.filePosition = token->filePosition;
505 e.kindName = GoKinds [kind].name;
506 e.kind = GoKinds [kind].letter;
508 makeTagEntry (&e);
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);
517 makeTagEntry (&e);
518 vStringDelete (qualifiedName);
522 static void parsePackage (tokenInfo *const token)
524 readToken (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 ] .
539 // Body = Block.
541 // MethodDecl = "func" Receiver MethodName Signature [ Body ] .
542 // Receiver = "(" [ identifier ] [ "*" ] BaseTypeName ")" .
543 // BaseTypeName = identifier .
545 // Skip over receiver.
546 readToken (token);
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.
555 readToken (token);
556 skipToMatched (token);
558 // Skip over result.
559 skipType (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;
579 readToken (token);
581 if (isType (token, TOKEN_OPEN_PAREN))
583 usesParens = TRUE;
584 readToken (token);
589 while (!isType (token, TOKEN_EOF))
591 if (isType (token, TOKEN_IDENTIFIER))
593 makeTag (token, kind);
594 readToken (token);
596 if (!isType (token, TOKEN_COMMA))
597 break;
598 readToken (token);
601 skipType (token);
602 while (!isType (token, TOKEN_SEMICOLON) && !isType (token, TOKEN_CLOSE_PAREN)
603 && !isType (token, TOKEN_EOF))
605 readToken (token);
606 skipToMatched (token);
609 if (usesParens && !isType (token, TOKEN_CLOSE_PAREN))
611 // we are at TOKEN_SEMICOLON
612 readToken (token);
615 while (!isType (token, TOKEN_EOF) &&
616 usesParens && !isType (token, TOKEN_CLOSE_PAREN));
619 static void parseGoFile (tokenInfo *const token)
623 readToken (token);
625 if (isType (token, TOKEN_KEYWORD))
627 switch (token->keyword)
629 case KEYWORD_package:
630 parsePackage (token);
631 break;
632 case KEYWORD_func:
633 parseFunctionOrMethod (token);
634 break;
635 case KEYWORD_const:
636 parseConstTypeVar (token, GOTAG_CONST);
637 break;
638 case KEYWORD_type:
639 parseConstTypeVar (token, GOTAG_TYPE);
640 break;
641 case KEYWORD_var:
642 parseConstTypeVar (token, GOTAG_VAR);
643 break;
644 default:
645 break;
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 ();
660 parseGoFile (token);
662 deleteToken (token);
663 vStringDelete (scope);
664 scope = NULL;
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;
676 return def;