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 const static 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
);
214 e
->lineNumber
= token
->lineNumber
;
215 e
->filePosition
= token
->filePosition
;
217 nl
= nestingLevelsGetCurrent (GDScriptNestingLevels
);
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. */
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
)
246 initGDScriptEntry (&e
, token
, K_CLASS
);
248 e
.extensionFields
.inheritance
= inheritance
? vStringValue (inheritance
) : "";
250 return makeTagEntry (&e
);
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
);
271 static int makeFunctionTag (const tokenInfo
*const token
,
273 const vString
*const arglist
,
274 const stringList
*const decorators
)
276 if (GDScriptKinds
[kind
].enabled
)
279 vString
*vstr
= NULL
;
282 initGDScriptEntry (&e
, token
, kind
);
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. */
302 static int makeSimpleGDScriptTag (const tokenInfo
*const token
, gdscriptKind
const kind
)
304 if (GDScriptKinds
[kind
].enabled
)
308 initGDScriptEntry (&e
, token
, kind
);
309 return makeTagEntry (&e
);
315 static int makeSimpleGDScriptRefTag (const tokenInfo
*const token
,
316 gdscriptKind
const kind
,
317 int roleIndex
, xtagType xtag
)
319 if (isXtagEnabled (XTAG_REFERENCE_TAGS
))
323 initRefTagEntry (&e
, vStringValue (token
->string
),
326 e
.lineNumber
= token
->lineNumber
;
327 e
.filePosition
= token
->filePosition
;
329 if (xtag
!= XTAG_UNKNOWN
)
330 markTagExtraBit (&e
, xtag
);
332 return makeTagEntry (&e
);
338 static void *newPoolToken (void *createArg CTAGS_ATTR_UNUSED
)
340 tokenInfo
*token
= xMalloc (1, tokenInfo
);
341 token
->string
= vStringNew ();
345 static void deletePoolToken (void *data
)
347 tokenInfo
*token
= data
;
348 vStringDelete (token
->string
);
352 static void clearPoolToken (void *data
)
354 tokenInfo
*token
= data
;
356 token
->type
= TOKEN_UNDEFINED
;
357 token
->keyword
= KEYWORD_NONE
;
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
)
380 while ((c
= getcFromInputFile ()) != EOF
)
384 vStringPut (string
, c
);
389 else if (c
== delimiter
|| c
== '\n' || c
== '\r')
392 ungetcToInputFile (c
);
396 vStringPut (string
, c
);
400 /* Skip a single or double triple quoted string. */
401 static void readTripleString (vString
*const string
, const int delimiter
)
406 while ((c
= getcFromInputFile ()) != EOF
)
408 if (c
== delimiter
&& ! escaped
)
416 vStringPut (string
, delimiter
);
417 if (c
!= '\\' || escaped
)
418 vStringPut (string
, c
);
429 static void readIdentifier (vString
*const string
, const int 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
)
453 /* if we've got a token held back, emit it */
456 copyToken (token
, NextToken
);
457 deleteToken (NextToken
);
462 token
->type
= TOKEN_UNDEFINED
;
463 token
->keyword
= KEYWORD_NONE
;
464 vStringClear (token
->string
);
471 c
= getcFromInputFile ();
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
;
490 token
->type
= TOKEN_EOF
;
496 int d
= getcFromInputFile ();
497 token
->type
= TOKEN_STRING
;
498 vStringPut (token
->string
, 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 ();
516 int d
= getcFromInputFile ();
517 vStringPut (token
->string
, c
);
520 vStringPut (token
->string
, d
);
521 token
->type
= TOKEN_OPERATOR
;
525 ungetcToInputFile (d
);
533 int d
= getcFromInputFile ();
536 vStringPut (token
->string
, c
);
537 vStringPut (token
->string
, d
);
538 token
->type
= TOKEN_ARROW
;
541 ungetcToInputFile (d
);
551 int d
= getcFromInputFile ();
552 vStringPut (token
->string
, c
);
555 ungetcToInputFile (d
);
560 vStringPut (token
->string
, d
);
561 token
->type
= TOKEN_OPERATOR
;
566 /* eats newline to implement line continuation */
569 int d
= getcFromInputFile ();
571 d
= getcFromInputFile ();
573 ungetcToInputFile (d
);
577 case '#': /* comment */
578 case '\r': /* newlines for indent */
587 c
= getcFromInputFile ();
588 while (c
!= EOF
&& c
!= '\r' && c
!= '\n');
592 int d
= getcFromInputFile ();
594 ungetcToInputFile (d
);
597 while ((c
= getcFromInputFile ()) == ' ' || c
== '\t' || c
== '\f')
600 indent
+= 8 - (indent
% 8);
601 else if (c
== '\f') /* yeah, it's weird */
606 } /* skip completely empty lines, so retry */
607 while (c
== '\r' || c
== '\n' || c
== '#');
608 ungetcToInputFile (c
);
609 if (TokenContinuationDepth
> 0)
613 vStringPut (token
->string
, ' ');
614 token
->type
= TOKEN_WHITESPACE
;
621 token
->type
= TOKEN_INDENT
;
622 token
->indent
= indent
;
628 if (! isIdentifierChar (c
))
630 vStringPut (token
->string
, c
);
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
;
641 token
->type
= TOKEN_KEYWORD
;
646 // handle implicit continuation lines not to emit INDENT inside brackets
647 if (token
->type
== '(' ||
648 token
->type
== '{' ||
651 TokenContinuationDepth
++;
653 else if (TokenContinuationDepth
> 0 &&
654 (token
->type
== ')' ||
655 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
)
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
)
701 else if (token
->type
== tClose
)
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
);
736 vStringDelete (qualifiedName
);
740 static vString
*parseParamTypeAnnotation (tokenInfo
*const token
,
744 if (token
->type
!= ':')
750 reprCat (arglist
, token
);
752 vString
*t
= vStringNew ();
755 readTokenFull (token
, true);
756 if (token
->type
== TOKEN_WHITESPACE
)
758 reprCat (arglist
, token
);
761 else if (token
->type
== TOKEN_EOF
)
764 if (token
->type
== '(' ||
765 token
->type
== '[' ||
768 else if (token
->type
== ')' ||
769 token
->type
== ']' ||
774 || (depth
== 0 && (token
->type
== '='
775 || token
->type
== ',')))
780 reprCat (arglist
, token
);
787 static vString
*parseReturnTypeAnnotation (tokenInfo
*const token
)
790 if (token
->type
!= TOKEN_ARROW
)
796 vString
*t
= vStringNew ();
800 if (token
->type
== TOKEN_EOF
)
803 if (token
->type
== '(' ||
804 token
->type
== '[' ||
807 else if (token
->type
== ')' ||
808 token
->type
== ']' ||
811 if (depth
== 0 && token
->type
== ':')
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;
836 if (token
->type
!= TOKEN_IDENTIFIER
)
840 copyToken (name
, token
);
843 /* collect parameters or inheritance */
844 if (token
->type
== '(')
846 int prevTokenType
= token
->type
;
849 arglist
= vStringNew ();
851 reprCat (arglist
, token
);
855 if (token
->type
!= TOKEN_WHITESPACE
&&
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
== '[' ||
869 else if (token
->type
== ')' ||
870 token
->type
== ']' ||
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
)
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. */
904 corkIndex
= makeClassTag (name
, arglist
);
906 corkIndex
= makeFunctionTag (name
, kind
, arglist
, decorators
);
908 lv
= nestingLevelsPush (GDScriptNestingLevels
, corkIndex
);
909 GDS_NL (lv
)->indentation
= token
->indent
;
912 vStringDelete (arglist
);
914 if (parameterCount
> 0)
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. */
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
);
949 static bool parseEnum (tokenInfo
*const 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
);
964 tagEntryInfo
*e
= getEntryInCorkQueue (corkIndex
);
966 markTagExtraBit (e
, XTAG_ANONYMOUS
);
968 else if (token
->type
== TOKEN_IDENTIFIER
)
970 corkIndex
= makeSimpleGDScriptTag(token
, K_ENUM
);
976 if (token
->type
!= '{')
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. */
991 while (token
->type
!= ','
992 && token
->type
!= '}'
993 && token
->type
!= TOKEN_EOF
);
994 if (token
->type
!= ',')
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
);
1013 static bool parseClassName (tokenInfo
*const 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
;
1025 char *name
= vStringStrdup (token
->string
);
1026 initTagEntry (&e
, klass
? "UNUSED": name
, K_CLASS
);
1030 eFree ((void *)klass
->name
);
1033 unmarkTagExtraBit(klass
, XTAG_ANONYMOUS
);
1035 /* Adjust the position. */
1036 setTagPositionFromTag (klass
, &e
);
1039 /* Extract B in class_name C extends B */
1041 if (token
->type
== TOKEN_KEYWORD
1042 && token
->keyword
== KEYWORD_extends
)
1045 if (token
->type
== TOKEN_IDENTIFIER
)
1047 makeSimpleGDScriptRefTag (token
, K_CLASS
,
1048 GDSCRIPT_CLASS_EXTENDED
,
1052 if (klass
->extensionFields
.inheritance
)
1053 eFree ((void *)klass
->extensionFields
.inheritance
);
1054 klass
->extensionFields
.inheritance
= vStringStrdup (token
->string
);
1057 e
.extensionFields
.inheritance
= vStringValue(token
->string
);
1068 while (token
->type
!= TOKEN_EOF
&&
1069 token
->type
!= ';' &&
1070 token
->type
!= TOKEN_INDENT
)
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
);
1087 tagEntryInfo
*klass
= getEntryInCorkQueue (nl
->corkIndex
);
1090 if (klass
->extensionFields
.inheritance
)
1091 eFree ((void *)klass
->extensionFields
.inheritance
);
1092 klass
->extensionFields
.inheritance
= vStringStrdup(token
->string
);
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;
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
);
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;
1127 reprCat (repr
, token
);
1128 skipVariableTypeAnnotation (token
, repr
);
1132 reprCat (repr
, token
);
1134 readNext
= token
->type
== TOKEN_IDENTIFIER
;
1136 reprCat (repr
, token
);
1138 default: readNext
= false; break;
1147 static bool parseVariable (tokenInfo
*const token
, const gdscriptKind kind
,
1148 const stringList
*const decorators
,
1152 vString
*type
= vStringNew();
1153 tokenInfo
*name
= newToken ();
1154 copyToken (name
, token
);
1159 // Variable declarations with dotted names are illegal
1160 if (token
->type
== '.')
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
))
1168 int index
= makeSimpleGDScriptTag (name
, kind
);
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
);
1195 vStringDelete(vtype
);
1199 while ((TokenContinuationDepth
> 0 || token
->type
!= ',') &&
1200 token
->type
!= TOKEN_EOF
&&
1201 token
->type
!= ';' &&
1202 token
->type
!= TOKEN_INDENT
)
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
);
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 ();
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
);
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;
1255 static void findGDScriptTags (void)
1257 tokenInfo
*const token
= newToken ();
1258 stringList
*decorators
= stringListNew();
1259 bool atStatementStart
= true;
1261 TokenContinuationDepth
= 0;
1263 GDScriptNestingLevels
= nestingLevelsNew (sizeof (struct gdscriptNestingLevelUserData
));
1265 if (isXtagEnabled (GDScriptXtagTable
[X_IMPLICIT_CLASS
].xtype
))
1266 prepareUnnamedClass (GDScriptNestingLevels
);
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;
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
;
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;