1
Description: Add Go support
2
Bug-Debian: http://bugs.debian.org/634166
3
Author: Alexey Marinichev <amarinichev@chromium.org>
4
Origin: other, https://github.com/lyosha/ctags-go/commit/ca097bd639e35470a9abccbf348016b7cc44f811
5
Last-Update: 2011-07-20
8
===================================================================
15
+#include "general.h" /* must always come first */
23
+#include "routines.h"
30
+#define isType(token,t) (boolean) ((token)->type == (t))
31
+#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
37
+typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
39
+typedef enum eKeywordId {
53
+/* Used to determine whether keyword is valid for the current language and
56
+typedef struct sKeywordDesc {
61
+typedef enum eTokenType {
64
+ // Don't need TOKEN_FORWARD_SLASH
65
+ TOKEN_FORWARD_SLASH,
82
+typedef struct sTokenInfo {
85
+ vString *string; /* the name of the token */
86
+ unsigned long lineNumber; /* line number of tag */
87
+ fpos_t filePosition; /* file position of line containing name */
95
+static jmp_buf Exception;
96
+static vString *scope;
99
+ GOTAG_UNDEFINED = -1,
107
+static kindOption GoKinds[] = {
108
+ {TRUE, 'p', "package", "packages"},
109
+ {TRUE, 'f', "func", "functions"},
110
+ {TRUE, 'c', "const", "constants"},
111
+ {TRUE, 't', "type", "types"},
112
+ {TRUE, 'v', "var", "variables"}
115
+static keywordDesc GoKeywordTable[] = {
116
+ {"package", KEYWORD_package},
117
+ {"import", KEYWORD_import},
118
+ {"const", KEYWORD_const},
119
+ {"type", KEYWORD_type},
120
+ {"var", KEYWORD_var},
121
+ {"func", KEYWORD_func},
122
+ {"struct", KEYWORD_struct},
123
+ {"interface", KEYWORD_interface},
124
+ {"map", KEYWORD_map},
125
+ {"chan", KEYWORD_chan}
129
+* FUNCTION DEFINITIONS
133
+static boolean isIdentChar (const int c)
136
+ (isalpha (c) || isdigit (c) || c == '$' ||
137
+ c == '@' || c == '_' || c == '#' || c > 128);
140
+static void initialize (const langType language)
143
+ const size_t count =
144
+ sizeof (GoKeywordTable) / sizeof (GoKeywordTable[0]);
145
+ Lang_go = language;
146
+ for (i = 0; i < count; ++i)
148
+ const keywordDesc *const p = &GoKeywordTable[i];
149
+ addKeyword (p->name, language, (int) p->id);
153
+static tokenInfo *newToken (void)
155
+ tokenInfo *const token = xMalloc (1, tokenInfo);
156
+ token->type = TOKEN_NONE;
157
+ token->keyword = KEYWORD_NONE;
158
+ token->string = vStringNew ();
159
+ token->lineNumber = getSourceLineNumber ();
160
+ token->filePosition = getInputFilePosition ();
164
+static void deleteToken (tokenInfo * const token)
168
+ vStringDelete (token->string);
174
+ * Parsing functions
177
+static void parseString (vString *const string, const int delimiter)
179
+ boolean end = FALSE;
182
+ int c = fileGetc ();
185
+ else if (c == '\\' && delimiter != '`')
187
+ c = fileGetc (); /* This maybe a ' or ". */
188
+ vStringPut (string, c);
190
+ else if (c == delimiter)
193
+ vStringPut (string, c);
195
+ vStringTerminate (string);
198
+static void parseIdentifier (vString *const string, const int firstChar)
201
+ //Assert (isIdentChar (c));
204
+ vStringPut (string, c);
206
+ } while (isIdentChar (c));
207
+ vStringTerminate (string);
208
+ fileUngetc (c); /* always unget, LF might add a semicolon */
211
+static void readToken (tokenInfo *const token)
214
+ static tokenType lastTokenType = TOKEN_NONE;
216
+ token->type = TOKEN_NONE;
217
+ token->keyword = KEYWORD_NONE;
218
+ vStringClear (token->string);
224
+ token->lineNumber = getSourceLineNumber ();
225
+ token->filePosition = getInputFilePosition ();
226
+ if (c == '\n' && (lastTokenType == TOKEN_IDENTIFIER ||
227
+ lastTokenType == TOKEN_STRING ||
228
+ lastTokenType == TOKEN_CLOSE_PAREN ||
229
+ lastTokenType == TOKEN_CLOSE_CURLY ||
230
+ lastTokenType == TOKEN_CLOSE_SQUARE))
232
+ token->type = TOKEN_SEMICOLON;
236
+ while (c == '\t' || c == ' ' || c == '\r' || c == '\n');
241
+ longjmp (Exception, (int)ExceptionEOF);
246
+ boolean hasNewline = FALSE;
247
+ int d = fileGetc ();
251
+ fileSkipToCharacter ('\n');
252
+ /* Line comments start with the
253
+ * character sequence // and
254
+ * continue through the next
255
+ * newline. A line comment acts
256
+ * like a newline. */
270
+ } while (d != EOF && d != '*');
277
+ } while (c != EOF && c != '\0');
279
+ fileUngetc (hasNewline ? '\n' : ' ');
282
+ token->type = TOKEN_FORWARD_SLASH;
292
+ token->type = TOKEN_STRING;
293
+ parseString (token->string, c);
294
+ token->lineNumber = getSourceLineNumber ();
295
+ token->filePosition = getInputFilePosition ();
300
+ int d = fileGetc ();
303
+ token->type = TOKEN_LEFT_ARROW;
311
+ token->type = TOKEN_OPEN_PAREN;
315
+ token->type = TOKEN_CLOSE_PAREN;
319
+ token->type = TOKEN_OPEN_CURLY;
323
+ token->type = TOKEN_CLOSE_CURLY;
327
+ token->type = TOKEN_OPEN_SQUARE;
331
+ token->type = TOKEN_CLOSE_SQUARE;
335
+ token->type = TOKEN_STAR;
339
+ token->type = TOKEN_DOT;
343
+ token->type = TOKEN_COMMA;
347
+ parseIdentifier (token->string, c);
348
+ token->lineNumber = getSourceLineNumber ();
349
+ token->filePosition = getInputFilePosition ();
350
+ token->keyword = lookupKeyword (vStringValue (token->string), Lang_go);
351
+ if (isKeyword (token, KEYWORD_NONE))
352
+ token->type = TOKEN_IDENTIFIER;
354
+ token->type = TOKEN_KEYWORD;
359
+ lastTokenType = token->type;
362
+static void skipToMatched (tokenInfo *const token)
364
+ int nest_level = 0;
365
+ tokenType open_token;
366
+ tokenType close_token;
368
+ switch (token->type)
370
+ case TOKEN_OPEN_PAREN:
371
+ open_token = TOKEN_OPEN_PAREN;
372
+ close_token = TOKEN_CLOSE_PAREN;
374
+ case TOKEN_OPEN_CURLY:
375
+ open_token = TOKEN_OPEN_CURLY;
376
+ close_token = TOKEN_CLOSE_CURLY;
378
+ case TOKEN_OPEN_SQUARE:
379
+ open_token = TOKEN_OPEN_SQUARE;
380
+ close_token = TOKEN_CLOSE_SQUARE;
387
+ * This routine will skip to a matching closing token.
388
+ * It will also handle nested tokens like the (, ) below.
389
+ * ( name varchar(30), text binary(10) )
391
+ if (isType (token, open_token))
394
+ while (!(isType (token, close_token) && (nest_level == 0)))
397
+ if (isType (token, open_token))
401
+ if (isType (token, close_token))
403
+ if (nest_level > 0)
413
+static void skipType (tokenInfo *const token)
416
+ // Type = TypeName | TypeLit | "(" Type ")" .
417
+ if (isType (token, TOKEN_OPEN_PAREN))
419
+ skipToMatched (token);
423
+ // TypeName = QualifiedIdent.
424
+ // QualifiedIdent = [ PackageName "." ] identifier .
425
+ // PackageName = identifier .
426
+ if (isType (token, TOKEN_IDENTIFIER))
429
+ if (isType (token, TOKEN_DOT))
432
+ Assert (isType (token, TOKEN_IDENTIFIER));
438
+ // StructType = "struct" "{" { FieldDecl ";" } "}"
439
+ // InterfaceType = "interface" "{" { MethodSpec ";" } "}" .
440
+ if (isKeyword (token, KEYWORD_struct) || isKeyword (token, KEYWORD_interface))
443
+ Assert (isType (token, TOKEN_OPEN_CURLY));
444
+ skipToMatched (token);
448
+ // ArrayType = "[" ArrayLength "]" ElementType .
449
+ // SliceType = "[" "]" ElementType .
450
+ // ElementType = Type .
451
+ if (isType (token, TOKEN_OPEN_SQUARE))
453
+ skipToMatched (token);
457
+ // PointerType = "*" BaseType .
458
+ // BaseType = Type .
459
+ // ChannelType = ( "chan" [ "<-" ] | "<-" "chan" ) ElementType .
460
+ if (isType (token, TOKEN_STAR) || isKeyword (token, KEYWORD_chan) || isType (token, TOKEN_LEFT_ARROW))
466
+ // MapType = "map" "[" KeyType "]" ElementType .
467
+ // KeyType = Type .
468
+ if (isKeyword (token, KEYWORD_map))
471
+ Assert (isType (token, TOKEN_OPEN_SQUARE));
472
+ skipToMatched (token);
476
+ // FunctionType = "func" Signature .
477
+ // Signature = Parameters [ Result ] .
478
+ // Result = Parameters | Type .
479
+ // Parameters = "(" [ ParameterList [ "," ] ] ")" .
480
+ if (isKeyword (token, KEYWORD_func))
483
+ Assert (isType (token, TOKEN_OPEN_PAREN));
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
+// Skip to the next semicolon, skipping over matching brackets.
494
+static void skipToTopLevelSemicolon (tokenInfo *const token)
496
+ while (!isType (token, TOKEN_SEMICOLON))
499
+ skipToMatched (token);
503
+static void makeTag (tokenInfo *const token, const goKind kind)
505
+ const char *const name = vStringValue (token->string);
508
+ initTagEntry (&e, name);
510
+ if (!GoKinds [kind].enabled)
513
+ e.lineNumber = token->lineNumber;
514
+ e.filePosition = token->filePosition;
515
+ e.kindName = GoKinds [kind].name;
516
+ e.kind = GoKinds [kind].letter;
520
+ if (scope && Option.include.qualifiedTags)
522
+ vString *qualifiedName = vStringNew ();
523
+ vStringCopy (qualifiedName, scope);
524
+ vStringCatS (qualifiedName, ".");
525
+ vStringCat (qualifiedName, token->string);
526
+ e.name = vStringValue (qualifiedName);
528
+ vStringDelete (qualifiedName);
532
+static void parsePackage (tokenInfo *const token)
534
+ tokenInfo *const name = newToken ();
537
+ Assert (isType (name, TOKEN_IDENTIFIER));
538
+ makeTag (name, GOTAG_PACKAGE);
539
+ if (!scope && Option.include.qualifiedTags)
541
+ scope = vStringNew ();
542
+ vStringCopy (scope, name->string);
545
+ deleteToken (name);
548
+static void parseFunctionOrMethod (tokenInfo *const token)
550
+ // FunctionDecl = "func" identifier Signature [ Body ] .
553
+ // MethodDecl = "func" Receiver MethodName Signature [ Body ] .
554
+ // Receiver = "(" [ identifier ] [ "*" ] BaseTypeName ")" .
555
+ // BaseTypeName = identifier .
556
+ tokenInfo *const name = newToken ();
558
+ // Skip over receiver.
560
+ if (isType (name, TOKEN_OPEN_PAREN))
561
+ skipToMatched (name);
563
+ Assert (isType (name, TOKEN_IDENTIFIER));
565
+ // Skip over parameters.
567
+ skipToMatched (token);
569
+ // Skip over result.
572
+ // Skip over function body.
573
+ if (isType (token, TOKEN_OPEN_CURLY))
574
+ skipToMatched (token);
576
+ makeTag (name, GOTAG_FUNCTION);
578
+ deleteToken (name);
581
+static void parseConstTypeVar (tokenInfo *const token, goKind kind)
583
+ // ConstDecl = "const" ( ConstSpec | "(" { ConstSpec ";" } ")" ) .
584
+ // ConstSpec = IdentifierList [ [ Type ] "=" ExpressionList ] .
585
+ // IdentifierList = identifier { "," identifier } .
586
+ // ExpressionList = Expression { "," Expression } .
587
+ // TypeDecl = "type" ( TypeSpec | "(" { TypeSpec ";" } ")" ) .
588
+ // TypeSpec = identifier Type .
589
+ // VarDecl = "var" ( VarSpec | "(" { VarSpec ";" } ")" ) .
590
+ // VarSpec = IdentifierList ( Type [ "=" ExpressionList ] | "=" ExpressionList ) .
591
+ tokenInfo *const name = newToken ();
592
+ boolean usesParens = FALSE;
596
+ if (isType (name, TOKEN_OPEN_PAREN))
605
+ makeTag (name, kind);
607
+ if (!isType (token, TOKEN_COMMA) && !isType (token, TOKEN_CLOSE_PAREN))
613
+ skipToTopLevelSemicolon (token);
618
+ if (!isType (name, TOKEN_CLOSE_PAREN))
622
+ deleteToken (name);
625
+static void parseGoFile (tokenInfo *const token)
631
+ if (isType (token, TOKEN_KEYWORD))
633
+ switch (token->keyword)
635
+ case KEYWORD_package:
636
+ parsePackage (token);
639
+ parseFunctionOrMethod (token);
641
+ case KEYWORD_const:
642
+ parseConstTypeVar (token, GOTAG_CONST);
645
+ parseConstTypeVar (token, GOTAG_TYPE);
648
+ parseConstTypeVar (token, GOTAG_VAR);
657
+static void findGoTags (void)
659
+ tokenInfo *const token = newToken ();
660
+ exception_t exception;
662
+ exception = (exception_t) (setjmp (Exception));
663
+ while (exception == ExceptionNone)
664
+ parseGoFile (token);
666
+ deleteToken (token);
667
+ vStringDelete (scope);
671
+extern parserDefinition *GoParser (void)
673
+ static const char *const extensions[] = { "go", NULL };
674
+ parserDefinition *def = parserNew ("Go");
675
+ def->kinds = GoKinds;
676
+ def->kindCount = KIND_COUNT (GoKinds);
677
+ def->extensions = extensions;
678
+ def->parser = findGoTags;
679
+ def->initialize = initialize;
683
===================================================================
695
===================================================================