Remove unused variable
[geany-mirror.git] / ctags / parsers / gdscript.c
blob2de92d9f26754ae425f8bb608111d22625b126d0
1 /*
2 * Copyright (c) 2000-2003, Darren Hiebert
3 * Copyright (c) 2014-2016, Colomban Wendling <ban@herbesfolles.org>
4 * Copyright (c) 2021, David Yang <davidyang6us@gmail.com>
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License version 2 or (at your option) any later version.
9 * This module contains functions for generating tags for GDScript language
10 * files. This module is derived from the Python module.
12 * GDScript language reference:
13 * https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/gdscript_basics.html
14 * https://docs.godotengine.org/en/stable/development/file_formats/gdscript_grammar.html#doc-gdscript-grammar
15 * https://godotengine.org/article/gdscript-progress-report-new-gdscript-now-merged
19 #include "general.h" /* must always come first */
21 #include <string.h>
23 #include "entry.h"
24 #include "nestlevel.h"
25 #include "read.h"
26 #include "parse.h"
27 #include "vstring.h"
28 #include "keyword.h"
29 #include "routines.h"
30 #include "debug.h"
31 #include "xtag.h"
32 #include "objpool.h"
33 #include "strlist.h"
35 #define isIdentifierChar(c) \
36 (isalnum (c) || (c) == '_' || (c) >= 0x80)
37 #define newToken() (objPoolGet (TokenPool))
38 #define deleteToken(t) (objPoolPut (TokenPool, (t)))
40 enum {
41 KEYWORD_class,
42 KEYWORD_func,
43 KEYWORD_extends,
44 KEYWORD_pass,
45 KEYWORD_return,
46 KEYWORD_lambda,
47 KEYWORD_variable,
48 KEYWORD_const,
49 KEYWORD_enum,
50 KEYWORD_class_name,
51 KEYWORD_signal,
52 KEYWORD_modifier,
54 typedef int keywordId; /* to allow KEYWORD_NONE */
56 typedef enum {
57 ACCESS_PRIVATE,
58 ACCESS_PROTECTED,
59 ACCESS_PUBLIC,
60 COUNT_ACCESS
61 } accessType;
63 static const char *const GDScriptAccesses[COUNT_ACCESS] = {
64 "private",
65 "protected",
66 "public"
69 typedef enum {
70 F_ANNOTATIONS,
71 COUNT_FIELD
72 } gdscriptField;
74 static fieldDefinition GDScriptFields[COUNT_FIELD] = {
75 { .name = "annotations",
76 .description = "annotations on functions and variables",
77 .enabled = true },
80 typedef enum {
81 K_CLASS,
82 K_METHOD,
83 K_VARIABLE,
84 K_CONST,
85 K_ENUM,
86 K_ENUMERATOR,
87 K_PARAMETER,
88 K_LOCAL_VARIABLE,
89 K_SIGNAL,
90 COUNT_KIND
91 } gdscriptKind;
93 typedef enum {
94 GDSCRIPT_CLASS_EXTENDED,
95 } gdscriptClassRole;
97 static roleDefinition GDScriptClassRoles [] = {
98 { true, "extended", "used as a base class for extending" },
101 static kindDefinition GDScriptKinds[COUNT_KIND] = {
102 {true, 'c', "class", "classes",
103 .referenceOnly = false, ATTACH_ROLES(GDScriptClassRoles)},
104 {true, 'm', "method", "methods"},
105 {true, 'v', "variable", "variables"},
106 {true, 'C', "const", "constants"},
107 {true, 'g', "enum", "enumeration names"},
108 {true, 'e', "enumerator", "enumerated values"},
109 {false,'z', "parameter", "function parameters"},
110 {false,'l', "local", "local variables"},
111 {true, 's', "signal", "signals"},
114 typedef enum {
115 X_IMPLICIT_CLASS,
116 } gdscriptXtag;
118 static xtagDefinition GDScriptXtagTable [] = {
120 .enabled = false,
121 .name = "implicitClass",
122 .description = "Include tag for the implicitly defined unnamed class",
126 static const keywordTable GDScriptKeywordTable[] = {
127 /* keyword keyword ID */
128 { "class", KEYWORD_class },
129 { "func", KEYWORD_func },
130 { "extends", KEYWORD_extends },
131 { "lambda", KEYWORD_lambda }, // Future GDScript lambda currently uses func, may change
132 { "pass", KEYWORD_pass },
133 { "return", KEYWORD_return },
134 { "var", KEYWORD_variable },
135 { "const", KEYWORD_const },
136 { "enum", KEYWORD_enum },
137 { "class_name", KEYWORD_class_name },
138 { "signal", KEYWORD_signal },
142 const static struct keywordGroup modifierKeywords = {
143 .value = KEYWORD_modifier,
144 .addingUnlessExisting = false,
145 .keywords = {
146 "static",
147 "remote", "remotesync",
148 "master", "mastersycn",
149 "puppet", "puppetsync",
150 NULL,
154 typedef enum eTokenType {
155 /* 0..255 are the byte's value */
156 TOKEN_EOF = 256,
157 TOKEN_UNDEFINED,
158 TOKEN_INDENT,
159 TOKEN_KEYWORD,
160 TOKEN_OPERATOR,
161 TOKEN_IDENTIFIER,
162 TOKEN_STRING,
163 TOKEN_ARROW, /* -> */
164 TOKEN_WHITESPACE,
165 } tokenType;
167 typedef struct {
168 int type;
169 keywordId keyword;
170 vString * string;
171 int indent;
172 unsigned long lineNumber;
173 MIOPos filePosition;
174 } tokenInfo;
176 struct gdscriptNestingLevelUserData {
177 int indentation;
179 #define GDS_NL(nl) ((struct gdscriptNestingLevelUserData *) nestingLevelGetUserData (nl))
181 static langType Lang_gdscript;
182 static unsigned int TokenContinuationDepth = 0;
183 static tokenInfo *NextToken = NULL;
184 static NestingLevels *GDScriptNestingLevels = NULL;
185 static objPool *TokenPool = NULL;
188 // Always reports single-underscores as protected
189 static accessType accessFromIdentifier (const vString *const ident, int parentKind)
191 const char *const p = vStringValue (ident);
192 const size_t len = vStringLength (ident);
194 /* inside a function/method, private */
195 if (parentKind != -1 && parentKind != K_CLASS)
196 return ACCESS_PRIVATE;
197 /* not starting with "_", public */
198 else if (len < 1 || p[0] != '_')
199 return ACCESS_PUBLIC;
200 /* "_...": suggested as non-public, but easily accessible */
201 else
202 return ACCESS_PROTECTED;
205 static void initGDScriptEntry (tagEntryInfo *const e, const tokenInfo *const token,
206 const gdscriptKind kind)
208 accessType access;
209 int parentKind = -1;
210 NestingLevel *nl;
212 initTagEntry (e, vStringValue (token->string), kind);
214 e->lineNumber = token->lineNumber;
215 e->filePosition = token->filePosition;
217 nl = nestingLevelsGetCurrent (GDScriptNestingLevels);
218 if (nl)
220 tagEntryInfo *nlEntry = getEntryOfNestingLevel (nl);
222 e->extensionFields.scopeIndex = nl->corkIndex;
224 /* nlEntry can be NULL if a kind was disabled. But what can we do
225 * here? Even disabled kinds should count for the hierarchy I
226 * guess -- as it'd otherwise be wrong -- but with cork we're
227 * fucked up as there's nothing to look up. Damn. */
228 if (nlEntry)
229 parentKind = nlEntry->kindIndex;
232 access = accessFromIdentifier (token->string, parentKind);
233 e->extensionFields.access = GDScriptAccesses[access];
234 /* FIXME: should we really set isFileScope in addition to access? */
235 if (access == ACCESS_PRIVATE)
236 e->isFileScope = true;
239 static int makeClassTag (const tokenInfo *const token,
240 const vString *const inheritance)
242 if (GDScriptKinds[K_CLASS].enabled)
244 tagEntryInfo e;
246 initGDScriptEntry (&e, token, K_CLASS);
248 e.extensionFields.inheritance = inheritance ? vStringValue (inheritance) : "";
250 return makeTagEntry (&e);
253 return CORK_NIL;
256 static vString *makeDecoratorString (const stringList *const strlist)
258 vString *vstr = vStringNew ();
260 for (unsigned int i = 0; i < stringListCount (strlist); i++)
262 vString *elt = stringListItem (strlist, i);
263 if (i != 0 && (vStringValue (elt) > 0
264 && vStringValue (elt)[0] != '('))
265 vStringPut (vstr, ',');
266 vStringCat (vstr, elt);
268 return vstr;
271 static int makeFunctionTag (const tokenInfo *const token,
272 int kind,
273 const vString *const arglist,
274 const stringList *const decorators)
276 if (GDScriptKinds[kind].enabled)
278 tagEntryInfo e;
279 vString *vstr = NULL;
280 int r;
282 initGDScriptEntry (&e, token, kind);
284 if (arglist)
285 e.extensionFields.signature = vStringValue (arglist);
286 if (decorators && stringListCount (decorators) > 0)
288 vstr = makeDecoratorString (decorators);
289 attachParserField (&e, false, GDScriptFields[F_ANNOTATIONS].ftype,
290 vStringValue (vstr));
293 r = makeTagEntry (&e);
294 vStringDelete (vstr); /* NULL is ignored. */
296 return r;
299 return CORK_NIL;
302 static int makeSimpleGDScriptTag (const tokenInfo *const token, gdscriptKind const kind)
304 if (GDScriptKinds[kind].enabled)
306 tagEntryInfo e;
308 initGDScriptEntry (&e, token, kind);
309 return makeTagEntry (&e);
312 return CORK_NIL;
315 static int makeSimpleGDScriptRefTag (const tokenInfo *const token,
316 gdscriptKind const kind,
317 int roleIndex, xtagType xtag)
319 if (isXtagEnabled (XTAG_REFERENCE_TAGS))
321 tagEntryInfo e;
323 initRefTagEntry (&e, vStringValue (token->string),
324 kind, roleIndex);
326 e.lineNumber = token->lineNumber;
327 e.filePosition = token->filePosition;
329 if (xtag != XTAG_UNKNOWN)
330 markTagExtraBit (&e, xtag);
332 return makeTagEntry (&e);
335 return CORK_NIL;
338 static void *newPoolToken (void *createArg CTAGS_ATTR_UNUSED)
340 tokenInfo *token = xMalloc (1, tokenInfo);
341 token->string = vStringNew ();
342 return token;
345 static void deletePoolToken (void *data)
347 tokenInfo *token = data;
348 vStringDelete (token->string);
349 eFree (token);
352 static void clearPoolToken (void *data)
354 tokenInfo *token = data;
356 token->type = TOKEN_UNDEFINED;
357 token->keyword = KEYWORD_NONE;
358 token->indent = 0;
359 token->lineNumber = getInputLineNumber ();
360 token->filePosition = getInputFilePosition ();
361 vStringClear (token->string);
364 static void copyToken (tokenInfo *const dest, const tokenInfo *const src)
366 dest->lineNumber = src->lineNumber;
367 dest->filePosition = src->filePosition;
368 dest->type = src->type;
369 dest->keyword = src->keyword;
370 dest->indent = src->indent;
371 vStringCopy(dest->string, src->string);
374 /* Skip a single or double quoted string. */
375 static void readString (vString *const string, const int delimiter)
377 int escaped = 0;
378 int c;
380 while ((c = getcFromInputFile ()) != EOF)
382 if (escaped)
384 vStringPut (string, c);
385 escaped--;
387 else if (c == '\\')
388 escaped++;
389 else if (c == delimiter || c == '\n' || c == '\r')
391 if (c != delimiter)
392 ungetcToInputFile (c);
393 break;
395 else
396 vStringPut (string, c);
400 /* Skip a single or double triple quoted string. */
401 static void readTripleString (vString *const string, const int delimiter)
403 int c;
404 int escaped = 0;
405 int n = 0;
406 while ((c = getcFromInputFile ()) != EOF)
408 if (c == delimiter && ! escaped)
410 if (++n >= 3)
411 break;
413 else
415 for (; n > 0; n--)
416 vStringPut (string, delimiter);
417 if (c != '\\' || escaped)
418 vStringPut (string, c);
419 n = 0;
422 if (escaped)
423 escaped--;
424 else if (c == '\\')
425 escaped++;
429 static void readIdentifier (vString *const string, const int firstChar)
431 int c = firstChar;
434 vStringPut (string, c);
435 c = getcFromInputFile ();
437 while (isIdentifierChar (c));
438 ungetcToInputFile (c);
441 static void ungetToken (tokenInfo *const token)
443 Assert (NextToken == NULL);
444 NextToken = newToken ();
445 copyToken (NextToken, token);
448 static void readTokenFull (tokenInfo *const token, bool inclWhitespaces)
450 int c;
451 int n;
453 /* if we've got a token held back, emit it */
454 if (NextToken)
456 copyToken (token, NextToken);
457 deleteToken (NextToken);
458 NextToken = NULL;
459 return;
462 token->type = TOKEN_UNDEFINED;
463 token->keyword = KEYWORD_NONE;
464 vStringClear (token->string);
466 getNextChar:
468 n = 0;
471 c = getcFromInputFile ();
472 n++;
474 while (c == ' ' || c == '\t' || c == '\f');
476 token->lineNumber = getInputLineNumber ();
477 token->filePosition = getInputFilePosition ();
479 if (inclWhitespaces && n > 1 && c != '\r' && c != '\n')
481 ungetcToInputFile (c);
482 vStringPut (token->string, ' ');
483 token->type = TOKEN_WHITESPACE;
484 return;
487 switch (c)
489 case EOF:
490 token->type = TOKEN_EOF;
491 break;
493 case '\'':
494 case '"':
496 int d = getcFromInputFile ();
497 token->type = TOKEN_STRING;
498 vStringPut (token->string, c);
499 if (d != c)
501 ungetcToInputFile (d);
502 readString (token->string, c);
504 else if ((d = getcFromInputFile ()) == c)
505 readTripleString (token->string, c);
506 else /* empty string */
507 ungetcToInputFile (d);
508 vStringPut (token->string, c);
509 token->lineNumber = getInputLineNumber ();
510 token->filePosition = getInputFilePosition ();
511 break;
514 case '=':
516 int d = getcFromInputFile ();
517 vStringPut (token->string, c);
518 if (d == c)
520 vStringPut (token->string, d);
521 token->type = TOKEN_OPERATOR;
523 else
525 ungetcToInputFile (d);
526 token->type = c;
528 break;
531 case '-':
533 int d = getcFromInputFile ();
534 if (d == '>')
536 vStringPut (token->string, c);
537 vStringPut (token->string, d);
538 token->type = TOKEN_ARROW;
539 break;
541 ungetcToInputFile (d);
542 /* fall through */
544 case '+':
545 case '*':
546 case '%':
547 case '<':
548 case '>':
549 case '/':
551 int d = getcFromInputFile ();
552 vStringPut (token->string, c);
553 if (d != '=')
555 ungetcToInputFile (d);
556 token->type = c;
558 else
560 vStringPut (token->string, d);
561 token->type = TOKEN_OPERATOR;
563 break;
566 /* eats newline to implement line continuation */
567 case '\\':
569 int d = getcFromInputFile ();
570 if (d == '\r')
571 d = getcFromInputFile ();
572 if (d != '\n')
573 ungetcToInputFile (d);
574 goto getNextChar;
577 case '#': /* comment */
578 case '\r': /* newlines for indent */
579 case '\n':
581 int indent = 0;
584 if (c == '#')
587 c = getcFromInputFile ();
588 while (c != EOF && c != '\r' && c != '\n');
590 if (c == '\r')
592 int d = getcFromInputFile ();
593 if (d != '\n')
594 ungetcToInputFile (d);
596 indent = 0;
597 while ((c = getcFromInputFile ()) == ' ' || c == '\t' || c == '\f')
599 if (c == '\t')
600 indent += 8 - (indent % 8);
601 else if (c == '\f') /* yeah, it's weird */
602 indent = 0;
603 else
604 indent++;
606 } /* skip completely empty lines, so retry */
607 while (c == '\r' || c == '\n' || c == '#');
608 ungetcToInputFile (c);
609 if (TokenContinuationDepth > 0)
611 if (inclWhitespaces)
613 vStringPut (token->string, ' ');
614 token->type = TOKEN_WHITESPACE;
616 else
617 goto getNextChar;
619 else
621 token->type = TOKEN_INDENT;
622 token->indent = indent;
624 break;
627 default:
628 if (! isIdentifierChar (c))
630 vStringPut (token->string, c);
631 token->type = c;
633 else
635 /* FIXME: handle U, B, R and F string prefixes? */
636 readIdentifier (token->string, c);
637 token->keyword = lookupKeyword (vStringValue (token->string), Lang_gdscript);
638 if (token->keyword == KEYWORD_NONE)
639 token->type = TOKEN_IDENTIFIER;
640 else
641 token->type = TOKEN_KEYWORD;
643 break;
646 // handle implicit continuation lines not to emit INDENT inside brackets
647 if (token->type == '(' ||
648 token->type == '{' ||
649 token->type == '[')
651 TokenContinuationDepth ++;
653 else if (TokenContinuationDepth > 0 &&
654 (token->type == ')' ||
655 token->type == '}' ||
656 token->type == ']'))
658 TokenContinuationDepth --;
662 static void readToken (tokenInfo *const token)
664 readTokenFull (token, false);
667 /*================================= parsing =================================*/
670 static void reprCat (vString *const repr, const tokenInfo *const token)
672 if (token->type != TOKEN_INDENT &&
673 token->type != TOKEN_WHITESPACE)
675 vStringCat (repr, token->string);
677 else if (vStringLength (repr) > 0 && vStringLast (repr) != ' ')
679 vStringPut (repr, ' ');
683 static bool skipOverPair (tokenInfo *const token, int tOpen, int tClose,
684 vString *const repr, bool reprOuterPair)
686 if (token->type == tOpen)
688 int depth = 1;
690 if (repr && reprOuterPair)
691 reprCat (repr, token);
694 readTokenFull (token, true);
695 if (repr && (reprOuterPair || token->type != tClose || depth > 1))
697 reprCat (repr, token);
699 if (token->type == tOpen)
700 depth ++;
701 else if (token->type == tClose)
702 depth --;
704 while (token->type != TOKEN_EOF && depth > 0);
707 return token->type == tClose;
710 static void readQualifiedName (tokenInfo *const nameToken)
712 readToken (nameToken);
714 if (nameToken->type == TOKEN_IDENTIFIER ||
715 nameToken->type == '.')
717 vString *qualifiedName = vStringNew ();
718 tokenInfo *token = newToken ();
720 while (nameToken->type == TOKEN_IDENTIFIER ||
721 nameToken->type == '.')
723 vStringCat (qualifiedName, nameToken->string);
724 copyToken (token, nameToken);
726 readToken (nameToken);
728 /* put the last, non-matching, token back */
729 ungetToken (nameToken);
731 copyToken (nameToken, token);
732 nameToken->type = TOKEN_IDENTIFIER;
733 vStringCopy (nameToken->string, qualifiedName);
735 deleteToken (token);
736 vStringDelete (qualifiedName);
740 static vString *parseParamTypeAnnotation (tokenInfo *const token,
741 vString *arglist)
743 readToken (token);
744 if (token->type != ':')
746 ungetToken (token);
747 return NULL;
750 reprCat (arglist, token);
751 int depth = 0;
752 vString *t = vStringNew ();
753 while (true)
755 readTokenFull (token, true);
756 if (token->type == TOKEN_WHITESPACE)
758 reprCat (arglist, token);
759 continue;
761 else if (token->type == TOKEN_EOF)
762 break;
764 if (token->type == '(' ||
765 token->type == '[' ||
766 token->type == '{')
767 depth ++;
768 else if (token->type == ')' ||
769 token->type == ']' ||
770 token->type == '}')
771 depth --;
773 if (depth < 0
774 || (depth == 0 && (token->type == '='
775 || token->type == ',')))
777 ungetToken (token);
778 return t;
780 reprCat (arglist, token);
781 reprCat (t, token);
783 vStringDelete (t);
784 return NULL;
787 static vString *parseReturnTypeAnnotation (tokenInfo *const token)
789 readToken (token);
790 if (token->type != TOKEN_ARROW)
792 return NULL;
795 int depth = 0;
796 vString *t = vStringNew ();
797 while (true)
799 readToken (token);
800 if (token->type == TOKEN_EOF)
801 break;
803 if (token->type == '(' ||
804 token->type == '[' ||
805 token->type == '{')
806 depth ++;
807 else if (token->type == ')' ||
808 token->type == ']' ||
809 token->type == '}')
810 depth --;
811 if (depth == 0 && token->type == ':')
813 ungetToken (token);
814 return t;
816 else
817 reprCat (t, token);
819 vStringDelete (t);
820 return NULL;
823 static bool parseClassOrDef (tokenInfo *const token,
824 const stringList *const decorators,
825 gdscriptKind kind, bool isCDef)
827 vString *arglist = NULL;
828 tokenInfo *name = NULL;
829 tokenInfo *parameterTokens[16] = { NULL };
830 vString *parameterTypes [ARRAY_SIZE(parameterTokens)] = { NULL };
831 unsigned int parameterCount = 0;
832 NestingLevel *lv;
833 int corkIndex;
835 readToken (token);
836 if (token->type != TOKEN_IDENTIFIER)
837 return false;
839 name = newToken ();
840 copyToken (name, token);
842 readToken (token);
843 /* collect parameters or inheritance */
844 if (token->type == '(')
846 int prevTokenType = token->type;
847 int depth = 1;
849 arglist = vStringNew ();
850 if (kind != K_CLASS)
851 reprCat (arglist, token);
855 if (token->type != TOKEN_WHITESPACE &&
856 token->type != '*')
858 prevTokenType = token->type;
861 readTokenFull (token, true);
862 if (kind != K_CLASS || token->type != ')' || depth > 1)
863 reprCat (arglist, token);
865 if (token->type == '(' ||
866 token->type == '[' ||
867 token->type == '{')
868 depth ++;
869 else if (token->type == ')' ||
870 token->type == ']' ||
871 token->type == '}')
872 depth --;
873 else if (kind != K_CLASS && depth == 1 &&
874 token->type == TOKEN_IDENTIFIER &&
875 (prevTokenType == '(' || prevTokenType == ',') &&
876 parameterCount < ARRAY_SIZE (parameterTokens) &&
877 GDScriptKinds[K_PARAMETER].enabled)
879 tokenInfo *parameterName = newToken ();
881 copyToken (parameterName, token);
882 parameterTokens[parameterCount] = parameterName;
883 parameterTypes [parameterCount++] = parseParamTypeAnnotation (token, arglist);
886 while (token->type != TOKEN_EOF && depth > 0);
888 else if (token->type == TOKEN_KEYWORD && token->keyword == KEYWORD_extends)
890 readToken (token);
891 if (token->type == TOKEN_IDENTIFIER)
893 makeSimpleGDScriptRefTag (token, K_CLASS, GDSCRIPT_CLASS_EXTENDED, XTAG_UNKNOWN);
894 arglist = vStringNewCopy (token->string);
897 else if (kind == K_SIGNAL)
899 /* signal can be defined with no parameter list. */
900 ungetToken (token);
903 if (kind == K_CLASS)
904 corkIndex = makeClassTag (name, arglist);
905 else
906 corkIndex = makeFunctionTag (name, kind, arglist, decorators);
908 lv = nestingLevelsPush (GDScriptNestingLevels, corkIndex);
909 GDS_NL (lv)->indentation = token->indent;
911 deleteToken (name);
912 vStringDelete (arglist);
914 if (parameterCount > 0)
916 unsigned int i;
918 for (i = 0; i < parameterCount; i++)
920 int paramCorkIndex = makeSimpleGDScriptTag (parameterTokens[i], K_PARAMETER);
921 deleteToken (parameterTokens[i]);
922 tagEntryInfo *e = getEntryInCorkQueue (paramCorkIndex);
923 if (e && parameterTypes[i])
925 e->extensionFields.typeRef [0] = eStrdup ("typename");
926 e->extensionFields.typeRef [1] = vStringDeleteUnwrap (parameterTypes[i]);
927 parameterTypes[i] = NULL;
929 vStringDelete (parameterTypes[i]); /* NULL is acceptable. */
933 tagEntryInfo *e;
934 vString *t;
935 if (kind != K_CLASS
936 && (e = getEntryInCorkQueue (corkIndex))
937 && (t = parseReturnTypeAnnotation (token)))
939 e->extensionFields.typeRef [0] = eStrdup ("typename");
940 e->extensionFields.typeRef [1] = vStringDeleteUnwrap (t);
943 if (kind == K_SIGNAL)
944 nestingLevelsPop (GDScriptNestingLevels);
946 return true;
949 static bool parseEnum (tokenInfo *const token)
951 int corkIndex;
953 readToken (token);
955 if (token->type == '{')
957 tokenInfo *name = newToken ();
958 copyToken (name, token);
959 vStringClear (name->string);
960 anonGenerate (name->string, "anon_enum_", K_ENUM);
961 name->type = TOKEN_IDENTIFIER;
962 corkIndex = makeSimpleGDScriptTag (name, K_ENUM);
963 deleteToken (name);
964 tagEntryInfo *e = getEntryInCorkQueue (corkIndex);
965 if (e)
966 markTagExtraBit (e, XTAG_ANONYMOUS);
968 else if (token->type == TOKEN_IDENTIFIER)
970 corkIndex = makeSimpleGDScriptTag(token, K_ENUM);
971 readToken (token);
973 else
974 return false;
976 if (token->type != '{')
977 return false;
979 readToken (token);
980 nestingLevelsPush (GDScriptNestingLevels, corkIndex);
982 while (token->type != '}' && token->type != TOKEN_EOF)
984 if (token->type == TOKEN_IDENTIFIER)
985 makeSimpleGDScriptTag(token, K_ENUMERATOR);
986 else if (token->type == '=')
988 /* Skip the right value. */
990 readToken (token);
991 while (token->type != ','
992 && token->type != '}'
993 && token->type != TOKEN_EOF);
994 if (token->type != ',')
995 continue;
997 readToken (token);
1000 tagEntryInfo *e;
1001 vString *t;
1002 if ((e = getEntryInCorkQueue (corkIndex))
1003 && (t = parseReturnTypeAnnotation (token)))
1005 e->extensionFields.typeRef [0] = eStrdup ("typename");
1006 e->extensionFields.typeRef [1] = vStringDeleteUnwrap (t);
1009 nestingLevelsPop (GDScriptNestingLevels);
1010 return true;
1013 static bool parseClassName (tokenInfo *const token)
1015 readToken (token);
1016 if (token->type == TOKEN_IDENTIFIER)
1018 /* A class name is explicitly given with "class_name" keyword.
1019 * Let's overwrite the anonymous tag for the class
1021 NestingLevel *nl = nestingLevelsGetNthFromRoot (GDScriptNestingLevels, 0);
1022 tagEntryInfo *klass = nl? getEntryInCorkQueue (nl->corkIndex): NULL;
1024 tagEntryInfo e;
1025 char *name = vStringStrdup (token->string);
1026 initTagEntry (&e, klass? "UNUSED": name, K_CLASS);
1028 if (klass)
1030 eFree ((void *)klass->name);
1031 klass->name = name;
1032 name = NULL;
1033 unmarkTagExtraBit(klass, XTAG_ANONYMOUS);
1035 /* Adjust the position. */
1036 setTagPositionFromTag (klass, &e);
1039 /* Extract B in class_name C extends B */
1040 readToken (token);
1041 if (token->type == TOKEN_KEYWORD
1042 && token->keyword == KEYWORD_extends)
1044 readToken (token);
1045 if (token->type == TOKEN_IDENTIFIER)
1047 makeSimpleGDScriptRefTag (token, K_CLASS,
1048 GDSCRIPT_CLASS_EXTENDED,
1049 XTAG_UNKNOWN);
1050 if (klass)
1052 if (klass->extensionFields.inheritance)
1053 eFree ((void *)klass->extensionFields.inheritance);
1054 klass->extensionFields.inheritance = vStringStrdup (token->string);
1056 else
1057 e.extensionFields.inheritance = vStringValue(token->string);
1061 if (!klass)
1062 makeTagEntry (&e);
1064 if (name)
1065 eFree (name);
1068 while (token->type != TOKEN_EOF &&
1069 token->type != ';' &&
1070 token->type != TOKEN_INDENT)
1071 readToken (token);
1073 return false;
1076 static bool parseExtends (tokenInfo *const token)
1078 if (token->keyword == KEYWORD_extends)
1080 readQualifiedName (token);
1081 if (token->type == TOKEN_IDENTIFIER)
1083 makeSimpleGDScriptRefTag (token, K_CLASS, GDSCRIPT_CLASS_EXTENDED, XTAG_UNKNOWN);
1084 NestingLevel *nl = nestingLevelsGetCurrent (GDScriptNestingLevels);
1085 if (nl)
1087 tagEntryInfo *klass = getEntryInCorkQueue (nl->corkIndex);
1088 if (klass)
1090 if (klass->extensionFields.inheritance)
1091 eFree ((void *)klass->extensionFields.inheritance);
1092 klass->extensionFields.inheritance = vStringStrdup(token->string);
1097 readToken (token);
1098 return false;
1101 /* this only handles the most common cases, but an annotation can be any
1102 * expression in theory.
1103 * this function assumes there must be an annotation, and doesn't do any check
1104 * on the token on which it is called: the caller should do that part. */
1105 static bool skipVariableTypeAnnotation (tokenInfo *const token, vString *const repr)
1107 bool readNext = true;
1109 readToken (token);
1110 switch (token->type)
1112 case '[': readNext = skipOverPair (token, '[', ']', repr, true); break;
1113 case '(': readNext = skipOverPair (token, '(', ')', repr, true); break;
1114 case '{': readNext = skipOverPair (token, '{', '}', repr, true); break;
1115 default: reprCat (repr, token);
1117 if (readNext)
1118 readToken (token);
1119 /* skip subscripts and calls */
1120 while (token->type == '[' || token->type == '(' || token->type == '.' || token->type == '|')
1122 switch (token->type)
1124 case '[': readNext = skipOverPair (token, '[', ']', repr, true); break;
1125 case '(': readNext = skipOverPair (token, '(', ')', repr, true); break;
1126 case '|':
1127 reprCat (repr, token);
1128 skipVariableTypeAnnotation (token, repr);
1129 readNext = false;
1130 break;
1131 case '.':
1132 reprCat (repr, token);
1133 readToken (token);
1134 readNext = token->type == TOKEN_IDENTIFIER;
1135 if (readNext)
1136 reprCat (repr, token);
1137 break;
1138 default: readNext = false; break;
1140 if (readNext)
1141 readToken (token);
1144 return false;
1147 static bool parseVariable (tokenInfo *const token, const gdscriptKind kind,
1148 const stringList *const decorators,
1149 const int keyword)
1151 readToken(token);
1152 vString *type = vStringNew();
1153 tokenInfo *name = newToken ();
1154 copyToken (name, token);
1155 if (!name)
1156 return false;
1158 readToken (token);
1159 // Variable declarations with dotted names are illegal
1160 if (token->type == '.')
1161 return false;
1163 /* (parse and) skip annotations. we need not to be too permissive because we
1164 * aren't yet sure we're actually parsing a variable. */
1165 if (token->type == ':' && skipVariableTypeAnnotation (token, type))
1166 readToken (token);
1168 int index = makeSimpleGDScriptTag (name, kind);
1169 deleteToken(name);
1170 tagEntryInfo *e = getEntryInCorkQueue (index);
1172 if (e && decorators && stringListCount (decorators) > 0)
1174 vString *vstr = makeDecoratorString (decorators);
1175 attachParserField (e, true, GDScriptFields[F_ANNOTATIONS].ftype,
1176 vStringValue (vstr));
1177 vStringDelete (vstr);
1180 vString *vtype = vStringNew();
1181 char * stype = vStringValue (type);
1182 if (strcmp(stype, "=") && strcmp(stype, ""))
1184 vStringCatS(vtype, stype);
1186 vStringDelete(type);
1188 if (e && vStringLength(vtype) > 0) /// TODO: Fix types away
1190 e->extensionFields.typeRef [0] = eStrdup ("typename");
1191 e->extensionFields.typeRef [1] = vStringDeleteUnwrap (vtype);
1193 else
1195 vStringDelete(vtype);
1199 while ((TokenContinuationDepth > 0 || token->type != ',') &&
1200 token->type != TOKEN_EOF &&
1201 token->type != ';' &&
1202 token->type != TOKEN_INDENT)
1204 readToken (token);
1208 return false;
1211 /* pops any level >= to indent */
1212 static void setIndent (tokenInfo *const token)
1214 NestingLevel *lv = nestingLevelsGetCurrent (GDScriptNestingLevels);
1216 while (lv && GDS_NL (lv)->indentation >= token->indent)
1218 tagEntryInfo *e = getEntryInCorkQueue (lv->corkIndex);
1219 if (e)
1220 e->extensionFields.endLine = token->lineNumber;
1222 nestingLevelsPop (GDScriptNestingLevels);
1223 lv = nestingLevelsGetCurrent (GDScriptNestingLevels);
1227 static int prepareUnnamedClass (struct NestingLevels *nls)
1230 /* Ugly: we need a "position" on the input stream for making a tag.
1231 * At the begining of parsing, the position is undefined.
1232 * By reading a byte, the position is defined.
1234 int c = getcFromInputFile ();
1235 if (c == EOF)
1236 return CORK_NIL;
1237 ungetcToInputFile (c);
1240 vString * tmp_class = anonGenerateNew ("anon_class_", K_CLASS);
1241 int corkIndex = makeSimpleTag (tmp_class, K_CLASS);
1242 vStringDelete (tmp_class);
1244 tagEntryInfo *e = getEntryInCorkQueue (corkIndex);
1245 if (e)
1246 markTagExtraBit (e, XTAG_ANONYMOUS);
1248 /* This virtual scope should not be poped. */
1249 NestingLevel *lv = nestingLevelsPush (nls, corkIndex);
1250 GDS_NL (lv)->indentation = -1;
1252 return corkIndex;
1255 static void findGDScriptTags (void)
1257 tokenInfo *const token = newToken ();
1258 stringList *decorators = stringListNew();
1259 bool atStatementStart = true;
1261 TokenContinuationDepth = 0;
1262 NextToken = NULL;
1263 GDScriptNestingLevels = nestingLevelsNew (sizeof (struct gdscriptNestingLevelUserData));
1265 if (isXtagEnabled (GDScriptXtagTable[X_IMPLICIT_CLASS].xtype))
1266 prepareUnnamedClass (GDScriptNestingLevels);
1268 readToken (token);
1269 while (token->type != TOKEN_EOF)
1271 tokenType iterationTokenType = token->type;
1272 int iterationTokenKeyword = token->keyword;
1273 bool readNext = true;
1275 if (token->type == TOKEN_INDENT)
1276 setIndent (token);
1277 else if (token->keyword == KEYWORD_class ||
1278 token->keyword == KEYWORD_func ||
1279 token->keyword == KEYWORD_signal)
1281 gdscriptKind kind = K_METHOD;
1282 switch (token->keyword)
1284 case KEYWORD_class: kind = K_CLASS; break;
1285 case KEYWORD_func: kind = K_METHOD; break;
1286 case KEYWORD_signal: kind = K_SIGNAL; break;
1287 default:
1288 AssertNotReached();
1290 readNext = parseClassOrDef (token, decorators, kind, false);
1292 else if (token->keyword == KEYWORD_extends)
1294 readNext = parseExtends (token);
1296 else if (token->type == '(')
1297 { /* skip parentheses to avoid finding stuff inside them */
1298 readNext = skipOverPair (token, '(', ')', NULL, false);
1300 else if (token->keyword == KEYWORD_variable || token->keyword == KEYWORD_const)
1302 NestingLevel *lv = nestingLevelsGetCurrent (GDScriptNestingLevels);
1303 tagEntryInfo *lvEntry = NULL;
1304 gdscriptKind kind = K_VARIABLE;
1306 if (lv)
1307 lvEntry = getEntryOfNestingLevel (lv);
1309 if (lvEntry && lvEntry->kindIndex != K_CLASS)
1310 kind = K_LOCAL_VARIABLE;
1312 if (token->keyword == KEYWORD_const)
1313 kind = K_CONST;
1315 readNext = parseVariable (token, kind, decorators, token->keyword);
1317 else if (token->keyword == KEYWORD_enum)
1319 readNext = parseEnum (token);
1321 else if (token->keyword == KEYWORD_class_name)
1323 readNext = parseClassName (token);
1325 else if (token->type == TOKEN_KEYWORD
1326 && token->keyword == KEYWORD_modifier)
1328 stringListAdd (decorators, vStringNewCopy(token->string));
1330 else if (token->type == '@' && atStatementStart &&
1331 GDScriptFields[F_ANNOTATIONS].enabled)
1333 /* collect decorators */
1334 readQualifiedName (token);
1335 if (token->type != TOKEN_IDENTIFIER
1336 && (token->keyword != KEYWORD_modifier))
1337 readNext = false;
1338 else
1340 stringListAdd (decorators, vStringNewCopy(token->string));
1341 readToken (token);
1343 vString *d = vStringNew ();
1344 readNext = skipOverPair (token, '(', ')', d, true);
1345 if (vStringLength (d) > 0)
1346 stringListAdd (decorators, d);
1347 else
1348 vStringDelete (d);
1352 /* clear collected decorators for any non-decorator tokens non-indent
1353 * token. decorator collection takes care of skipping the possible
1354 * argument list, so we should never hit here parsing a decorator */
1355 if (iterationTokenType != TOKEN_INDENT &&
1356 iterationTokenType != '@' &&
1357 iterationTokenKeyword != KEYWORD_modifier &&
1358 GDScriptFields[F_ANNOTATIONS].enabled)
1360 stringListClear (decorators);
1363 atStatementStart = (token->type == TOKEN_INDENT || token->type == ';');
1365 if (readNext)
1366 readToken (token);
1369 nestingLevelsFree (GDScriptNestingLevels);
1370 stringListDelete (decorators);
1371 deleteToken (token);
1372 Assert (NextToken == NULL);
1375 static void initialize (const langType language)
1377 Lang_gdscript = language;
1379 TokenPool = objPoolNew (16, newPoolToken, deletePoolToken, clearPoolToken, NULL);
1380 addKeywordGroup (&modifierKeywords, language);
1383 static void finalize (langType language CTAGS_ATTR_UNUSED, bool initialized)
1385 if (!initialized)
1386 return;
1388 objPoolDelete (TokenPool);
1391 extern parserDefinition* GDScriptParser (void)
1393 static const char *const extensions[] = { "gd", NULL };
1394 parserDefinition *def = parserNew ("GDScript");
1395 def->kindTable = GDScriptKinds;
1396 def->kindCount = ARRAY_SIZE (GDScriptKinds);
1397 def->extensions = extensions;
1398 def->parser = findGDScriptTags;
1399 def->initialize = initialize;
1400 def->finalize = finalize;
1401 def->keywordTable = GDScriptKeywordTable;
1402 def->keywordCount = ARRAY_SIZE (GDScriptKeywordTable);
1403 def->fieldTable = GDScriptFields;
1404 def->fieldCount = ARRAY_SIZE (GDScriptFields);
1405 def->xtagTable = GDScriptXtagTable;
1406 def->xtagCount = ARRAY_SIZE(GDScriptXtagTable);
1407 def->useCork = CORK_QUEUE;
1408 def->requestAutomaticFQTag = true;
1409 return def;