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 */
24 #include "nestlevel.h"
35 #define isIdentifierChar(c) \
36 (isalnum (c) || (c) == '_' || (c) >= 0x80)
37 #define newToken() (objPoolGet (TokenPool))
38 #define deleteToken(t) (objPoolPut (TokenPool, (t)))
54 typedef int keywordId
; /* to allow KEYWORD_NONE */
63 static const char *const GDScriptAccesses
[COUNT_ACCESS
] = {
74 static fieldDefinition GDScriptFields
[COUNT_FIELD
] = {
75 { .name
= "annotations",
76 .description
= "annotations on functions and variables",
94 GDSCRIPT_CLASS_EXTENDED
,
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"},
118 static xtagDefinition GDScriptXtagTable
[] = {
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 static const struct keywordGroup modifierKeywords
= {
143 .value
= KEYWORD_modifier
,
144 .addingUnlessExisting
= false,
147 "remote", "remotesync",
148 "master", "mastersycn",
149 "puppet", "puppetsync",
154 typedef enum eTokenType
{
155 /* 0..255 are the byte's value */
163 TOKEN_ARROW
, /* -> */
172 unsigned long lineNumber
;
176 struct gdscriptNestingLevelUserData
{
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 */
202 return ACCESS_PROTECTED
;
205 static void initGDScriptEntry (tagEntryInfo
*const e
, const tokenInfo
*const token
,
206 const gdscriptKind kind
)
212 initTagEntry (e
, vStringValue (token
->string
), kind
);
213 updateTagLine (e
, token
->lineNumber
, token
->filePosition
);
215 nl
= nestingLevelsGetCurrent (GDScriptNestingLevels
);
218 tagEntryInfo
*nlEntry
= getEntryOfNestingLevel (nl
);
220 e
->extensionFields
.scopeIndex
= nl
->corkIndex
;
222 /* nlEntry can be NULL if a kind was disabled. But what can we do
223 * here? Even disabled kinds should count for the hierarchy I
224 * guess -- as it'd otherwise be wrong -- but with cork we're
225 * fucked up as there's nothing to look up. Damn. */
227 parentKind
= nlEntry
->kindIndex
;
230 access
= accessFromIdentifier (token
->string
, parentKind
);
231 e
->extensionFields
.access
= GDScriptAccesses
[access
];
232 /* FIXME: should we really set isFileScope in addition to access? */
233 if (access
== ACCESS_PRIVATE
)
234 e
->isFileScope
= true;
237 static int makeClassTag (const tokenInfo
*const token
,
238 const vString
*const inheritance
)
240 if (GDScriptKinds
[K_CLASS
].enabled
)
244 initGDScriptEntry (&e
, token
, K_CLASS
);
246 e
.extensionFields
.inheritance
= inheritance
? vStringValue (inheritance
) : "";
248 return makeTagEntry (&e
);
254 static vString
*makeDecoratorString (const stringList
*const strlist
)
256 vString
*vstr
= vStringNew ();
258 for (unsigned int i
= 0; i
< stringListCount (strlist
); i
++)
260 vString
*elt
= stringListItem (strlist
, i
);
261 if (i
!= 0 && (vStringValue (elt
) > 0
262 && vStringValue (elt
)[0] != '('))
263 vStringPut (vstr
, ',');
264 vStringCat (vstr
, elt
);
269 static int makeFunctionTag (const tokenInfo
*const token
,
271 const vString
*const arglist
,
272 const stringList
*const decorators
)
274 if (GDScriptKinds
[kind
].enabled
)
277 vString
*vstr
= NULL
;
280 initGDScriptEntry (&e
, token
, kind
);
283 e
.extensionFields
.signature
= vStringValue (arglist
);
284 if (decorators
&& stringListCount (decorators
) > 0)
286 vstr
= makeDecoratorString (decorators
);
287 attachParserField (&e
, GDScriptFields
[F_ANNOTATIONS
].ftype
,
288 vStringValue (vstr
));
291 r
= makeTagEntry (&e
);
292 vStringDelete (vstr
); /* NULL is ignored. */
300 static int makeSimpleGDScriptTag (const tokenInfo
*const token
, gdscriptKind
const kind
)
302 if (GDScriptKinds
[kind
].enabled
)
306 initGDScriptEntry (&e
, token
, kind
);
307 return makeTagEntry (&e
);
313 static int makeSimpleGDScriptRefTag (const tokenInfo
*const token
,
314 gdscriptKind
const kind
,
315 int roleIndex
, xtagType xtag
)
317 if (isXtagEnabled (XTAG_REFERENCE_TAGS
))
321 initRefTagEntry (&e
, vStringValue (token
->string
),
323 updateTagLine (&e
, token
->lineNumber
, token
->filePosition
);
325 if (xtag
!= XTAG_UNKNOWN
)
326 markTagExtraBit (&e
, xtag
);
328 return makeTagEntry (&e
);
334 static void *newPoolToken (void *createArg CTAGS_ATTR_UNUSED
)
336 tokenInfo
*token
= xMalloc (1, tokenInfo
);
337 token
->string
= vStringNew ();
341 static void deletePoolToken (void *data
)
343 tokenInfo
*token
= data
;
344 vStringDelete (token
->string
);
348 static void clearPoolToken (void *data
)
350 tokenInfo
*token
= data
;
352 token
->type
= TOKEN_UNDEFINED
;
353 token
->keyword
= KEYWORD_NONE
;
355 token
->lineNumber
= getInputLineNumber ();
356 token
->filePosition
= getInputFilePosition ();
357 vStringClear (token
->string
);
360 static void copyToken (tokenInfo
*const dest
, const tokenInfo
*const src
)
362 dest
->lineNumber
= src
->lineNumber
;
363 dest
->filePosition
= src
->filePosition
;
364 dest
->type
= src
->type
;
365 dest
->keyword
= src
->keyword
;
366 dest
->indent
= src
->indent
;
367 vStringCopy (dest
->string
, src
->string
);
370 /* Skip a single or double quoted string. */
371 static void readString (vString
*const string
, const int delimiter
)
376 while ((c
= getcFromInputFile ()) != EOF
)
380 vStringPut (string
, c
);
385 else if (c
== delimiter
|| c
== '\n' || c
== '\r')
388 ungetcToInputFile (c
);
392 vStringPut (string
, c
);
396 /* Skip a single or double triple quoted string. */
397 static void readTripleString (vString
*const string
, const int delimiter
)
402 while ((c
= getcFromInputFile ()) != EOF
)
404 if (c
== delimiter
&& ! escaped
)
412 vStringPut (string
, delimiter
);
413 if (c
!= '\\' || escaped
)
414 vStringPut (string
, c
);
425 static void readIdentifier (vString
*const string
, const int firstChar
)
430 vStringPut (string
, c
);
431 c
= getcFromInputFile ();
433 while (isIdentifierChar (c
));
434 ungetcToInputFile (c
);
437 static void ungetToken (tokenInfo
*const token
)
439 Assert (NextToken
== NULL
);
440 NextToken
= newToken ();
441 copyToken (NextToken
, token
);
444 static void readTokenFull (tokenInfo
*const token
, bool inclWhitespaces
)
449 /* if we've got a token held back, emit it */
452 copyToken (token
, NextToken
);
453 deleteToken (NextToken
);
458 token
->type
= TOKEN_UNDEFINED
;
459 token
->keyword
= KEYWORD_NONE
;
460 vStringClear (token
->string
);
467 c
= getcFromInputFile ();
470 while (c
== ' ' || c
== '\t' || c
== '\f');
472 token
->lineNumber
= getInputLineNumber ();
473 token
->filePosition
= getInputFilePosition ();
475 if (inclWhitespaces
&& n
> 1 && c
!= '\r' && c
!= '\n')
477 ungetcToInputFile (c
);
478 vStringPut (token
->string
, ' ');
479 token
->type
= TOKEN_WHITESPACE
;
486 token
->type
= TOKEN_EOF
;
492 int d
= getcFromInputFile ();
493 token
->type
= TOKEN_STRING
;
494 vStringPut (token
->string
, c
);
497 ungetcToInputFile (d
);
498 readString (token
->string
, c
);
500 else if ((d
= getcFromInputFile ()) == c
)
501 readTripleString (token
->string
, c
);
502 else /* empty string */
503 ungetcToInputFile (d
);
504 vStringPut (token
->string
, c
);
505 token
->lineNumber
= getInputLineNumber ();
506 token
->filePosition
= getInputFilePosition ();
512 int d
= getcFromInputFile ();
513 vStringPut (token
->string
, c
);
516 vStringPut (token
->string
, d
);
517 token
->type
= TOKEN_OPERATOR
;
521 ungetcToInputFile (d
);
529 int d
= getcFromInputFile ();
532 vStringPut (token
->string
, c
);
533 vStringPut (token
->string
, d
);
534 token
->type
= TOKEN_ARROW
;
537 ungetcToInputFile (d
);
547 int d
= getcFromInputFile ();
548 vStringPut (token
->string
, c
);
551 ungetcToInputFile (d
);
556 vStringPut (token
->string
, d
);
557 token
->type
= TOKEN_OPERATOR
;
562 /* eats newline to implement line continuation */
565 int d
= getcFromInputFile ();
567 d
= getcFromInputFile ();
569 ungetcToInputFile (d
);
573 case '#': /* comment */
574 case '\r': /* newlines for indent */
583 c
= getcFromInputFile ();
584 while (c
!= EOF
&& c
!= '\r' && c
!= '\n');
588 int d
= getcFromInputFile ();
590 ungetcToInputFile (d
);
593 while ((c
= getcFromInputFile ()) == ' ' || c
== '\t' || c
== '\f')
596 indent
+= 8 - (indent
% 8);
597 else if (c
== '\f') /* yeah, it's weird */
602 } /* skip completely empty lines, so retry */
603 while (c
== '\r' || c
== '\n' || c
== '#');
604 ungetcToInputFile (c
);
605 if (TokenContinuationDepth
> 0)
609 vStringPut (token
->string
, ' ');
610 token
->type
= TOKEN_WHITESPACE
;
617 token
->type
= TOKEN_INDENT
;
618 token
->indent
= indent
;
624 if (! isIdentifierChar (c
))
626 vStringPut (token
->string
, c
);
631 /* FIXME: handle U, B, R and F string prefixes? */
632 readIdentifier (token
->string
, c
);
633 token
->keyword
= lookupKeyword (vStringValue (token
->string
), Lang_gdscript
);
634 if (token
->keyword
== KEYWORD_NONE
)
635 token
->type
= TOKEN_IDENTIFIER
;
637 token
->type
= TOKEN_KEYWORD
;
642 // handle implicit continuation lines not to emit INDENT inside brackets
643 if (token
->type
== '(' ||
644 token
->type
== '{' ||
647 TokenContinuationDepth
++;
649 else if (TokenContinuationDepth
> 0 &&
650 (token
->type
== ')' ||
651 token
->type
== '}' ||
654 TokenContinuationDepth
--;
658 static void readToken (tokenInfo
*const token
)
660 readTokenFull (token
, false);
663 /*================================= parsing =================================*/
666 static void reprCat (vString
*const repr
, const tokenInfo
*const token
)
668 if (token
->type
!= TOKEN_INDENT
&&
669 token
->type
!= TOKEN_WHITESPACE
)
671 vStringCat (repr
, token
->string
);
673 else if (vStringLength (repr
) > 0 && vStringLast (repr
) != ' ')
675 vStringPut (repr
, ' ');
679 static bool skipOverPair (tokenInfo
*const token
, int tOpen
, int tClose
,
680 vString
*const repr
, bool reprOuterPair
)
682 if (token
->type
== tOpen
)
686 if (repr
&& reprOuterPair
)
687 reprCat (repr
, token
);
690 readTokenFull (token
, true);
691 if (repr
&& (reprOuterPair
|| token
->type
!= tClose
|| depth
> 1))
693 reprCat (repr
, token
);
695 if (token
->type
== tOpen
)
697 else if (token
->type
== tClose
)
700 while (token
->type
!= TOKEN_EOF
&& depth
> 0);
703 return token
->type
== tClose
;
706 static void readQualifiedName (tokenInfo
*const nameToken
)
708 readToken (nameToken
);
710 if (nameToken
->type
== TOKEN_IDENTIFIER
||
711 nameToken
->type
== '.')
713 vString
*qualifiedName
= vStringNew ();
714 tokenInfo
*token
= newToken ();
716 while (nameToken
->type
== TOKEN_IDENTIFIER
||
717 nameToken
->type
== '.')
719 vStringCat (qualifiedName
, nameToken
->string
);
720 copyToken (token
, nameToken
);
722 readToken (nameToken
);
724 /* put the last, non-matching, token back */
725 ungetToken (nameToken
);
727 copyToken (nameToken
, token
);
728 nameToken
->type
= TOKEN_IDENTIFIER
;
729 vStringCopy (nameToken
->string
, qualifiedName
);
732 vStringDelete (qualifiedName
);
736 static vString
*parseParamTypeAnnotation (tokenInfo
*const token
,
740 if (token
->type
!= ':')
746 reprCat (arglist
, token
);
748 vString
*t
= vStringNew ();
751 readTokenFull (token
, true);
752 if (token
->type
== TOKEN_WHITESPACE
)
754 reprCat (arglist
, token
);
757 else if (token
->type
== TOKEN_EOF
)
760 if (token
->type
== '(' ||
761 token
->type
== '[' ||
764 else if (token
->type
== ')' ||
765 token
->type
== ']' ||
770 || (depth
== 0 && (token
->type
== '='
771 || token
->type
== ',')))
776 reprCat (arglist
, token
);
783 static vString
*parseReturnTypeAnnotation (tokenInfo
*const token
)
786 if (token
->type
!= TOKEN_ARROW
)
792 vString
*t
= vStringNew ();
796 if (token
->type
== TOKEN_EOF
)
799 if (token
->type
== '(' ||
800 token
->type
== '[' ||
803 else if (token
->type
== ')' ||
804 token
->type
== ']' ||
807 if (depth
== 0 && token
->type
== ':')
819 static bool parseClassOrDef (tokenInfo
*const token
,
820 const stringList
*const decorators
,
823 vString
*arglist
= NULL
;
824 tokenInfo
*name
= NULL
;
825 tokenInfo
*parameterTokens
[16] = { NULL
};
826 vString
*parameterTypes
[ARRAY_SIZE (parameterTokens
)] = { NULL
};
827 unsigned int parameterCount
= 0;
832 if (token
->type
!= TOKEN_IDENTIFIER
)
836 copyToken (name
, token
);
839 /* collect parameters or inheritance */
840 if (token
->type
== '(')
842 int prevTokenType
= token
->type
;
845 arglist
= vStringNew ();
847 reprCat (arglist
, token
);
851 if (token
->type
!= TOKEN_WHITESPACE
&&
854 prevTokenType
= token
->type
;
857 readTokenFull (token
, true);
858 if (kind
!= K_CLASS
|| token
->type
!= ')' || depth
> 1)
859 reprCat (arglist
, token
);
861 if (token
->type
== '(' ||
862 token
->type
== '[' ||
865 else if (token
->type
== ')' ||
866 token
->type
== ']' ||
869 else if (kind
!= K_CLASS
&& depth
== 1 &&
870 token
->type
== TOKEN_IDENTIFIER
&&
871 (prevTokenType
== '(' || prevTokenType
== ',') &&
872 parameterCount
< ARRAY_SIZE (parameterTokens
) &&
873 GDScriptKinds
[K_PARAMETER
].enabled
)
875 tokenInfo
*parameterName
= newToken ();
877 copyToken (parameterName
, token
);
878 parameterTokens
[parameterCount
] = parameterName
;
879 parameterTypes
[parameterCount
++] = parseParamTypeAnnotation (token
, arglist
);
882 while (token
->type
!= TOKEN_EOF
&& depth
> 0);
884 else if (token
->type
== TOKEN_KEYWORD
&& token
->keyword
== KEYWORD_extends
)
887 if (token
->type
== TOKEN_IDENTIFIER
)
889 makeSimpleGDScriptRefTag (token
, K_CLASS
, GDSCRIPT_CLASS_EXTENDED
, XTAG_UNKNOWN
);
890 arglist
= vStringNewCopy (token
->string
);
893 else if (kind
== K_SIGNAL
)
895 /* signal can be defined with no parameter list. */
900 corkIndex
= makeClassTag (name
, arglist
);
902 corkIndex
= makeFunctionTag (name
, kind
, arglist
, decorators
);
904 lv
= nestingLevelsPush (GDScriptNestingLevels
, corkIndex
);
905 GDS_NL (lv
)->indentation
= token
->indent
;
908 vStringDelete (arglist
);
910 if (parameterCount
> 0)
914 for (i
= 0; i
< parameterCount
; i
++)
916 int paramCorkIndex
= makeSimpleGDScriptTag (parameterTokens
[i
], K_PARAMETER
);
917 deleteToken (parameterTokens
[i
]);
918 tagEntryInfo
*e
= getEntryInCorkQueue (paramCorkIndex
);
919 if (e
&& parameterTypes
[i
])
921 e
->extensionFields
.typeRef
[0] = eStrdup ("typename");
922 e
->extensionFields
.typeRef
[1] = vStringDeleteUnwrap (parameterTypes
[i
]);
923 parameterTypes
[i
] = NULL
;
925 vStringDelete (parameterTypes
[i
]); /* NULL is acceptable. */
932 && (e
= getEntryInCorkQueue (corkIndex
))
933 && (t
= parseReturnTypeAnnotation (token
)))
935 e
->extensionFields
.typeRef
[0] = eStrdup ("typename");
936 e
->extensionFields
.typeRef
[1] = vStringDeleteUnwrap (t
);
939 if (kind
== K_SIGNAL
)
940 nestingLevelsPop (GDScriptNestingLevels
);
945 static bool parseEnum (tokenInfo
*const token
)
951 if (token
->type
== '{')
953 tokenInfo
*name
= newToken ();
954 copyToken (name
, token
);
955 vStringClear (name
->string
);
956 anonGenerate (name
->string
, "anon_enum_", K_ENUM
);
957 name
->type
= TOKEN_IDENTIFIER
;
958 corkIndex
= makeSimpleGDScriptTag (name
, K_ENUM
);
960 tagEntryInfo
*e
= getEntryInCorkQueue (corkIndex
);
962 markTagExtraBit (e
, XTAG_ANONYMOUS
);
964 else if (token
->type
== TOKEN_IDENTIFIER
)
966 corkIndex
= makeSimpleGDScriptTag (token
, K_ENUM
);
972 if (token
->type
!= '{')
976 nestingLevelsPush (GDScriptNestingLevels
, corkIndex
);
978 while (token
->type
!= '}' && token
->type
!= TOKEN_EOF
)
980 if (token
->type
== TOKEN_IDENTIFIER
)
981 makeSimpleGDScriptTag (token
, K_ENUMERATOR
);
982 else if (token
->type
== '=')
984 /* Skip the right value. */
987 while (token
->type
!= ','
988 && token
->type
!= '}'
989 && token
->type
!= TOKEN_EOF
);
990 if (token
->type
!= ',')
998 if ((e
= getEntryInCorkQueue (corkIndex
))
999 && (t
= parseReturnTypeAnnotation (token
)))
1001 e
->extensionFields
.typeRef
[0] = eStrdup ("typename");
1002 e
->extensionFields
.typeRef
[1] = vStringDeleteUnwrap (t
);
1005 nestingLevelsPop (GDScriptNestingLevels
);
1009 static bool parseClassName (tokenInfo
*const token
)
1012 if (token
->type
== TOKEN_IDENTIFIER
)
1014 /* A class name is explicitly given with "class_name" keyword.
1015 * Let's overwrite the anonymous tag for the class
1017 NestingLevel
*nl
= nestingLevelsGetNthFromRoot (GDScriptNestingLevels
, 0);
1018 tagEntryInfo
*klass
= nl
? getEntryInCorkQueue (nl
->corkIndex
): NULL
;
1021 char *name
= vStringStrdup (token
->string
);
1022 initTagEntry (&e
, klass
? "UNUSED": name
, K_CLASS
);
1026 eFree ((void *)klass
->name
);
1029 unmarkTagExtraBit (klass
, XTAG_ANONYMOUS
);
1030 unmarkTagExtraBit (klass
, GDScriptXtagTable
[X_IMPLICIT_CLASS
].xtype
);
1032 /* Adjust the position. */
1033 setTagPositionFromTag (klass
, &e
);
1036 /* Extract B in class_name C extends B */
1038 if (token
->type
== TOKEN_KEYWORD
1039 && token
->keyword
== KEYWORD_extends
)
1042 if (token
->type
== TOKEN_IDENTIFIER
)
1044 makeSimpleGDScriptRefTag (token
, K_CLASS
,
1045 GDSCRIPT_CLASS_EXTENDED
,
1049 if (klass
->extensionFields
.inheritance
)
1050 eFree ((void *)klass
->extensionFields
.inheritance
);
1051 klass
->extensionFields
.inheritance
= vStringStrdup (token
->string
);
1054 e
.extensionFields
.inheritance
= vStringValue (token
->string
);
1065 while (token
->type
!= TOKEN_EOF
&&
1066 token
->type
!= ';' &&
1067 token
->type
!= TOKEN_INDENT
)
1073 static bool parseExtends (tokenInfo
*const token
)
1075 if (token
->keyword
== KEYWORD_extends
)
1077 readQualifiedName (token
);
1078 if (token
->type
== TOKEN_IDENTIFIER
)
1080 makeSimpleGDScriptRefTag (token
, K_CLASS
, GDSCRIPT_CLASS_EXTENDED
, XTAG_UNKNOWN
);
1081 NestingLevel
*nl
= nestingLevelsGetCurrent (GDScriptNestingLevels
);
1084 tagEntryInfo
*klass
= getEntryInCorkQueue (nl
->corkIndex
);
1087 if (klass
->extensionFields
.inheritance
)
1088 eFree ((void *)klass
->extensionFields
.inheritance
);
1089 klass
->extensionFields
.inheritance
= vStringStrdup (token
->string
);
1098 /* this only handles the most common cases, but an annotation can be any
1099 * expression in theory.
1100 * this function assumes there must be an annotation, and doesn't do any check
1101 * on the token on which it is called: the caller should do that part. */
1102 static bool skipVariableTypeAnnotation (tokenInfo
*const token
, vString
*const repr
)
1104 bool readNext
= true;
1107 switch (token
->type
)
1109 case '[': readNext
= skipOverPair (token
, '[', ']', repr
, true); break;
1110 case '(': readNext
= skipOverPair (token
, '(', ')', repr
, true); break;
1111 case '{': readNext
= skipOverPair (token
, '{', '}', repr
, true); break;
1112 default: reprCat (repr
, token
);
1116 /* skip subscripts and calls */
1117 while (token
->type
== '[' || token
->type
== '(' || token
->type
== '.' || token
->type
== '|')
1119 switch (token
->type
)
1121 case '[': readNext
= skipOverPair (token
, '[', ']', repr
, true); break;
1122 case '(': readNext
= skipOverPair (token
, '(', ')', repr
, true); break;
1124 reprCat (repr
, token
);
1125 skipVariableTypeAnnotation (token
, repr
);
1129 reprCat (repr
, token
);
1131 readNext
= token
->type
== TOKEN_IDENTIFIER
;
1133 reprCat (repr
, token
);
1135 default: readNext
= false; break;
1144 static bool parseVariable (tokenInfo
*const token
, const gdscriptKind kind
,
1145 const stringList
*const decorators
,
1149 vString
*type
= vStringNew ();
1150 tokenInfo
*name
= newToken ();
1151 copyToken (name
, token
);
1156 // Variable declarations with dotted names are illegal
1157 if (token
->type
== '.')
1160 /* (parse and) skip annotations. we need not to be too permissive because we
1161 * aren't yet sure we're actually parsing a variable. */
1162 if (token
->type
== ':' && skipVariableTypeAnnotation (token
, type
))
1165 int index
= makeSimpleGDScriptTag (name
, kind
);
1167 tagEntryInfo
*e
= getEntryInCorkQueue (index
);
1169 if (e
&& decorators
&& stringListCount (decorators
) > 0)
1171 vString
*vstr
= makeDecoratorString (decorators
);
1172 attachParserField (e
, GDScriptFields
[F_ANNOTATIONS
].ftype
,
1173 vStringValue (vstr
));
1174 vStringDelete (vstr
);
1177 vString
*vtype
= vStringNew ();
1178 char * stype
= vStringValue (type
);
1179 if (strcmp (stype
, "=") && strcmp (stype
, ""))
1181 vStringCatS (vtype
, stype
);
1183 vStringDelete (type
);
1185 if (e
&& vStringLength (vtype
) > 0) /// TODO: Fix types away
1187 e
->extensionFields
.typeRef
[0] = eStrdup ("typename");
1188 e
->extensionFields
.typeRef
[1] = vStringDeleteUnwrap (vtype
);
1192 vStringDelete (vtype
);
1196 while ((TokenContinuationDepth
> 0 || token
->type
!= ',') &&
1197 token
->type
!= TOKEN_EOF
&&
1198 token
->type
!= ';' &&
1199 token
->type
!= TOKEN_INDENT
)
1208 /* pops any level >= to indent */
1209 static void setIndent (tokenInfo
*const token
)
1211 NestingLevel
*lv
= nestingLevelsGetCurrent (GDScriptNestingLevels
);
1213 while (lv
&& GDS_NL (lv
)->indentation
>= token
->indent
)
1215 setTagEndLineToCorkEntry (lv
->corkIndex
, token
->lineNumber
);
1217 nestingLevelsPop (GDScriptNestingLevels
);
1218 lv
= nestingLevelsGetCurrent (GDScriptNestingLevels
);
1222 static int prepareUnnamedClass (struct NestingLevels
*nls
)
1225 /* Ugly: we need a "position" on the input stream for making a tag.
1226 * At the begining of parsing, the position is undefined.
1227 * By reading a byte, the position is defined.
1229 int c
= getcFromInputFile ();
1232 ungetcToInputFile (c
);
1235 vString
* tmp_class
= anonGenerateNew ("anon_class_", K_CLASS
);
1236 int corkIndex
= makeSimpleTag (tmp_class
, K_CLASS
);
1237 vStringDelete (tmp_class
);
1239 tagEntryInfo
*e
= getEntryInCorkQueue (corkIndex
);
1241 markTagExtraBit (e
, XTAG_ANONYMOUS
);
1243 /* This virtual scope should not be poped. */
1244 NestingLevel
*lv
= nestingLevelsPush (nls
, corkIndex
);
1245 GDS_NL (lv
)->indentation
= -1;
1250 static void findGDScriptTags (void)
1252 tokenInfo
*const token
= newToken ();
1253 stringList
*decorators
= stringListNew ();
1254 bool atStatementStart
= true;
1256 TokenContinuationDepth
= 0;
1258 GDScriptNestingLevels
= nestingLevelsNew (sizeof (struct gdscriptNestingLevelUserData
));
1260 if (isXtagEnabled (GDScriptXtagTable
[X_IMPLICIT_CLASS
].xtype
))
1262 int index
= prepareUnnamedClass (GDScriptNestingLevels
);
1263 tagEntryInfo
*e
= getEntryInCorkQueue (index
);
1265 markTagExtraBit (e
, GDScriptXtagTable
[X_IMPLICIT_CLASS
].xtype
);
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
)
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;
1288 AssertNotReached ();
1290 readNext
= parseClassOrDef (token
, decorators
, kind
);
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
;
1307 lvEntry
= getEntryOfNestingLevel (lv
);
1309 if (lvEntry
&& lvEntry
->kindIndex
!= K_CLASS
)
1310 kind
= K_LOCAL_VARIABLE
;
1312 if (token
->keyword
== KEYWORD_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
))
1340 stringListAdd (decorators
, vStringNewCopy (token
->string
));
1343 vString
*d
= vStringNew ();
1344 readNext
= skipOverPair (token
, '(', ')', d
, true);
1345 if (vStringLength (d
) > 0)
1346 stringListAdd (decorators
, 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
== ';');
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
)
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;