xref: /Universal-ctags/parsers/gdscript.c (revision 385c6b897bec246fce1c7cafb19f34be98156eae)
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>
5 *
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.
8 *
9 *   This module contains functions for generating tags for GDScript language
10 *   files. This module is derived from the Python module.
11 *
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
16 *
17 */
18 
19 #include "general.h"  /* must always come first */
20 
21 #include <string.h>
22 
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"
34 
35 #define isIdentifierChar(c) \
36 	(isalnum (c) || (c) == '_' || (c) >= 0x80)
37 #define newToken() (objPoolGet (TokenPool))
38 #define deleteToken(t) (objPoolPut (TokenPool, (t)))
39 
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,
53 };
54 typedef int keywordId; /* to allow KEYWORD_NONE */
55 
56 typedef enum {
57 	ACCESS_PRIVATE,
58 	ACCESS_PROTECTED,
59 	ACCESS_PUBLIC,
60 	COUNT_ACCESS
61 } accessType;
62 
63 static const char *const GDScriptAccesses[COUNT_ACCESS] = {
64 	"private",
65 	"protected",
66 	"public"
67 };
68 
69 typedef enum {
70 	F_ANNOTATIONS,
71 	COUNT_FIELD
72 } gdscriptField;
73 
74 static fieldDefinition GDScriptFields[COUNT_FIELD] = {
75 	{ .name = "annotations",
76 	  .description = "annotations on functions and variables",
77 	  .enabled = true },
78 };
79 
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;
92 
93 typedef enum {
94 	GDSCRIPT_CLASS_EXTENDED,
95 } gdscriptClassRole;
96 
97 static roleDefinition GDScriptClassRoles [] = {
98 	{ true, "extended",   "used as a base class for extending" },
99 };
100 
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"},
112 };
113 
114 typedef enum {
115 	X_IMPLICIT_CLASS,
116 } gdscriptXtag;
117 
118 static xtagDefinition GDScriptXtagTable [] = {
119 	{
120 		.enabled     = false,
121 		.name        = "implicitClass",
122 		.description = "Include tag for the implicitly defined unnamed class",
123 	},
124 };
125 
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			},
139 
140 };
141 
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,
151 	},
152 };
153 
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;
166 
167 typedef struct {
168 	int				type;
169 	keywordId		keyword;
170 	vString *		string;
171 	int				indent;
172 	unsigned long	lineNumber;
173 	MIOPos			filePosition;
174 } tokenInfo;
175 
176 struct gdscriptNestingLevelUserData {
177 	int indentation;
178 };
179 #define GDS_NL(nl) ((struct gdscriptNestingLevelUserData *) nestingLevelGetUserData (nl))
180 
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;
186 
187 
188 // Always reports single-underscores as protected
accessFromIdentifier(const vString * const ident,int parentKind)189 static accessType accessFromIdentifier (const vString *const ident, int parentKind)
190 {
191 	const char *const p = vStringValue (ident);
192 	const size_t len = vStringLength (ident);
193 
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;
203 }
204 
initGDScriptEntry(tagEntryInfo * const e,const tokenInfo * const token,const gdscriptKind kind)205 static void initGDScriptEntry (tagEntryInfo *const e, const tokenInfo *const token,
206 							 const gdscriptKind kind)
207 {
208 	accessType access;
209 	int parentKind = -1;
210 	NestingLevel *nl;
211 
212 	initTagEntry (e, vStringValue (token->string), kind);
213 
214 	e->lineNumber	= token->lineNumber;
215 	e->filePosition	= token->filePosition;
216 
217 	nl = nestingLevelsGetCurrent (GDScriptNestingLevels);
218 	if (nl)
219 	{
220 		tagEntryInfo *nlEntry = getEntryOfNestingLevel (nl);
221 
222 		e->extensionFields.scopeIndex = nl->corkIndex;
223 
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;
230 	}
231 
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;
237 }
238 
makeClassTag(const tokenInfo * const token,const vString * const inheritance)239 static int makeClassTag (const tokenInfo *const token,
240 						 const vString *const inheritance)
241 {
242 	if (GDScriptKinds[K_CLASS].enabled)
243 	{
244 		tagEntryInfo e;
245 
246 		initGDScriptEntry (&e, token, K_CLASS);
247 
248 		e.extensionFields.inheritance = inheritance ? vStringValue (inheritance) : "";
249 
250 		return makeTagEntry (&e);
251 	}
252 
253 	return CORK_NIL;
254 }
255 
makeDecoratorString(const stringList * const strlist)256 static vString *makeDecoratorString (const stringList *const strlist)
257 {
258 	vString *vstr = vStringNew ();
259 
260 	for (unsigned int i = 0; i < stringListCount (strlist); i++)
261 	{
262 		vString *elt = stringListItem (strlist, i);
263 		if (i != 0 && (vStringValue (elt) > 0
264 					   && vStringValue (elt)[0] != '('))
265 			vStringPut (vstr, ',');
266 		vStringCat (vstr, elt);
267 	}
268 	return vstr;
269 }
270 
makeFunctionTag(const tokenInfo * const token,int kind,const vString * const arglist,const stringList * const decorators)271 static int makeFunctionTag (const tokenInfo *const token,
272 							int kind,
273 							const vString *const arglist,
274 							const stringList *const decorators)
275 {
276 	if (GDScriptKinds[kind].enabled)
277 	{
278 		tagEntryInfo e;
279 		vString *vstr = NULL;
280 		int r;
281 
282 		initGDScriptEntry (&e, token, kind);
283 
284 		if (arglist)
285 			e.extensionFields.signature = vStringValue (arglist);
286 		if (decorators && stringListCount (decorators) > 0)
287 		{
288 			vstr = makeDecoratorString (decorators);
289 			attachParserField (&e, false, GDScriptFields[F_ANNOTATIONS].ftype,
290 							   vStringValue (vstr));
291 		}
292 
293 		r = makeTagEntry (&e);
294 		vStringDelete (vstr);	/* NULL is ignored. */
295 
296 		return r;
297 	}
298 
299 	return CORK_NIL;
300 }
301 
makeSimpleGDScriptTag(const tokenInfo * const token,gdscriptKind const kind)302 static int makeSimpleGDScriptTag (const tokenInfo *const token, gdscriptKind const kind)
303 {
304 	if (GDScriptKinds[kind].enabled)
305 	{
306 		tagEntryInfo e;
307 
308 		initGDScriptEntry (&e, token, kind);
309 		return makeTagEntry (&e);
310 	}
311 
312 	return CORK_NIL;
313 }
314 
makeSimpleGDScriptRefTag(const tokenInfo * const token,gdscriptKind const kind,int roleIndex,xtagType xtag)315 static int makeSimpleGDScriptRefTag (const tokenInfo *const token,
316 									 gdscriptKind const kind,
317 									 int roleIndex, xtagType xtag)
318 {
319 	if (isXtagEnabled (XTAG_REFERENCE_TAGS))
320 	{
321 		tagEntryInfo e;
322 
323 		initRefTagEntry (&e, vStringValue (token->string),
324 						 kind, roleIndex);
325 
326 		e.lineNumber	= token->lineNumber;
327 		e.filePosition	= token->filePosition;
328 
329 		if (xtag != XTAG_UNKNOWN)
330 			markTagExtraBit (&e, xtag);
331 
332 		return makeTagEntry (&e);
333 	}
334 
335 	return CORK_NIL;
336 }
337 
newPoolToken(void * createArg CTAGS_ATTR_UNUSED)338 static void *newPoolToken (void *createArg CTAGS_ATTR_UNUSED)
339 {
340 	tokenInfo *token = xMalloc (1, tokenInfo);
341 	token->string = vStringNew ();
342 	return token;
343 }
344 
deletePoolToken(void * data)345 static void deletePoolToken (void *data)
346 {
347 	tokenInfo *token = data;
348 	vStringDelete (token->string);
349 	eFree (token);
350 }
351 
clearPoolToken(void * data)352 static void clearPoolToken (void *data)
353 {
354 	tokenInfo *token = data;
355 
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);
362 }
363 
copyToken(tokenInfo * const dest,const tokenInfo * const src)364 static void copyToken (tokenInfo *const dest, const tokenInfo *const src)
365 {
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);
372 }
373 
374 /* Skip a single or double quoted string. */
readString(vString * const string,const int delimiter)375 static void readString (vString *const string, const int delimiter)
376 {
377 	int escaped = 0;
378 	int c;
379 
380 	while ((c = getcFromInputFile ()) != EOF)
381 	{
382 		if (escaped)
383 		{
384 			vStringPut (string, c);
385 			escaped--;
386 		}
387 		else if (c == '\\')
388 			escaped++;
389 		else if (c == delimiter || c == '\n' || c == '\r')
390 		{
391 			if (c != delimiter)
392 				ungetcToInputFile (c);
393 			break;
394 		}
395 		else
396 			vStringPut (string, c);
397 	}
398 }
399 
400 /* Skip a single or double triple quoted string. */
readTripleString(vString * const string,const int delimiter)401 static void readTripleString (vString *const string, const int delimiter)
402 {
403 	int c;
404 	int escaped = 0;
405 	int n = 0;
406 	while ((c = getcFromInputFile ()) != EOF)
407 	{
408 		if (c == delimiter && ! escaped)
409 		{
410 			if (++n >= 3)
411 				break;
412 		}
413 		else
414 		{
415 			for (; n > 0; n--)
416 				vStringPut (string, delimiter);
417 			if (c != '\\' || escaped)
418 				vStringPut (string, c);
419 			n = 0;
420 		}
421 
422 		if (escaped)
423 			escaped--;
424 		else if (c == '\\')
425 			escaped++;
426 	}
427 }
428 
readIdentifier(vString * const string,const int firstChar)429 static void readIdentifier (vString *const string, const int firstChar)
430 {
431 	int c = firstChar;
432 	do
433 	{
434 		vStringPut (string, c);
435 		c = getcFromInputFile ();
436 	}
437 	while (isIdentifierChar (c));
438 	ungetcToInputFile (c);
439 }
440 
ungetToken(tokenInfo * const token)441 static void ungetToken (tokenInfo *const token)
442 {
443 	Assert (NextToken == NULL);
444 	NextToken = newToken ();
445 	copyToken (NextToken, token);
446 }
447 
readTokenFull(tokenInfo * const token,bool inclWhitespaces)448 static void readTokenFull (tokenInfo *const token, bool inclWhitespaces)
449 {
450 	int c;
451 	int n;
452 
453 	/* if we've got a token held back, emit it */
454 	if (NextToken)
455 	{
456 		copyToken (token, NextToken);
457 		deleteToken (NextToken);
458 		NextToken = NULL;
459 		return;
460 	}
461 
462 	token->type		= TOKEN_UNDEFINED;
463 	token->keyword	= KEYWORD_NONE;
464 	vStringClear (token->string);
465 
466 getNextChar:
467 
468 	n = 0;
469 	do
470 	{
471 		c = getcFromInputFile ();
472 		n++;
473 	}
474 	while (c == ' ' || c == '\t' || c == '\f');
475 
476 	token->lineNumber   = getInputLineNumber ();
477 	token->filePosition = getInputFilePosition ();
478 
479 	if (inclWhitespaces && n > 1 && c != '\r' && c != '\n')
480 	{
481 		ungetcToInputFile (c);
482 		vStringPut (token->string, ' ');
483 		token->type = TOKEN_WHITESPACE;
484 		return;
485 	}
486 
487 	switch (c)
488 	{
489 		case EOF:
490 			token->type = TOKEN_EOF;
491 			break;
492 
493 		case '\'':
494 		case '"':
495 		{
496 			int d = getcFromInputFile ();
497 			token->type = TOKEN_STRING;
498 			vStringPut (token->string, c);
499 			if (d != c)
500 			{
501 				ungetcToInputFile (d);
502 				readString (token->string, c);
503 			}
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;
512 		}
513 
514 		case '=':
515 		{
516 			int d = getcFromInputFile ();
517 			vStringPut (token->string, c);
518 			if (d == c)
519 			{
520 				vStringPut (token->string, d);
521 				token->type = TOKEN_OPERATOR;
522 			}
523 			else
524 			{
525 				ungetcToInputFile (d);
526 				token->type = c;
527 			}
528 			break;
529 		}
530 
531 		case '-':
532 		{
533 			int d = getcFromInputFile ();
534 			if (d == '>')
535 			{
536 				vStringPut (token->string, c);
537 				vStringPut (token->string, d);
538 				token->type = TOKEN_ARROW;
539 				break;
540 			}
541 			ungetcToInputFile (d);
542 			/* fall through */
543 		}
544 		case '+':
545 		case '*':
546 		case '%':
547 		case '<':
548 		case '>':
549 		case '/':
550 		{
551 			int d = getcFromInputFile ();
552 			vStringPut (token->string, c);
553 			if (d != '=')
554 			{
555 				ungetcToInputFile (d);
556 				token->type = c;
557 			}
558 			else
559 			{
560 				vStringPut (token->string, d);
561 				token->type = TOKEN_OPERATOR;
562 			}
563 			break;
564 		}
565 
566 		/* eats newline to implement line continuation  */
567 		case '\\':
568 		{
569 			int d = getcFromInputFile ();
570 			if (d == '\r')
571 				d = getcFromInputFile ();
572 			if (d != '\n')
573 				ungetcToInputFile (d);
574 			goto getNextChar;
575 		}
576 
577 		case '#': /* comment */
578 		case '\r': /* newlines for indent */
579 		case '\n':
580 		{
581 			int indent = 0;
582 			do
583 			{
584 				if (c == '#')
585 				{
586 					do
587 						c = getcFromInputFile ();
588 					while (c != EOF && c != '\r' && c != '\n');
589 				}
590 				if (c == '\r')
591 				{
592 					int d = getcFromInputFile ();
593 					if (d != '\n')
594 						ungetcToInputFile (d);
595 				}
596 				indent = 0;
597 				while ((c = getcFromInputFile ()) == ' ' || c == '\t' || c == '\f')
598 				{
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++;
605 				}
606 			} /* skip completely empty lines, so retry */
607 			while (c == '\r' || c == '\n' || c == '#');
608 			ungetcToInputFile (c);
609 			if (TokenContinuationDepth > 0)
610 			{
611 				if (inclWhitespaces)
612 				{
613 					vStringPut (token->string, ' ');
614 					token->type = TOKEN_WHITESPACE;
615 				}
616 				else
617 					goto getNextChar;
618 			}
619 			else
620 			{
621 				token->type = TOKEN_INDENT;
622 				token->indent = indent;
623 			}
624 			break;
625 		}
626 
627 		default:
628 			if (! isIdentifierChar (c))
629 			{
630 				vStringPut (token->string, c);
631 				token->type = c;
632 			}
633 			else
634 			{
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;
642 			}
643 			break;
644 	}
645 
646 	// handle implicit continuation lines not to emit INDENT inside brackets
647 	if (token->type == '(' ||
648 		token->type == '{' ||
649 		token->type == '[')
650 	{
651 		TokenContinuationDepth ++;
652 	}
653 	else if (TokenContinuationDepth > 0 &&
654 			 (token->type == ')' ||
655 			  token->type == '}' ||
656 			  token->type == ']'))
657 	{
658 		TokenContinuationDepth --;
659 	}
660 }
661 
readToken(tokenInfo * const token)662 static void readToken (tokenInfo *const token)
663 {
664 	readTokenFull (token, false);
665 }
666 
667 /*================================= parsing =================================*/
668 
669 
reprCat(vString * const repr,const tokenInfo * const token)670 static void reprCat (vString *const repr, const tokenInfo *const token)
671 {
672 	if (token->type != TOKEN_INDENT &&
673 		token->type != TOKEN_WHITESPACE)
674 	{
675 		vStringCat (repr, token->string);
676 	}
677 	else if (vStringLength (repr) > 0 && vStringLast (repr) != ' ')
678 	{
679 		vStringPut (repr, ' ');
680 	}
681 }
682 
skipOverPair(tokenInfo * const token,int tOpen,int tClose,vString * const repr,bool reprOuterPair)683 static bool skipOverPair (tokenInfo *const token, int tOpen, int tClose,
684 						  vString *const repr, bool reprOuterPair)
685 {
686 	if (token->type == tOpen)
687 	{
688 		int depth = 1;
689 
690 		if (repr && reprOuterPair)
691 			reprCat (repr, token);
692 		do
693 		{
694 			readTokenFull (token, true);
695 			if (repr && (reprOuterPair || token->type != tClose || depth > 1))
696 			{
697 				reprCat (repr, token);
698 			}
699 			if (token->type == tOpen)
700 				depth ++;
701 			else if (token->type == tClose)
702 				depth --;
703 		}
704 		while (token->type != TOKEN_EOF && depth > 0);
705 	}
706 
707 	return token->type == tClose;
708 }
709 
readQualifiedName(tokenInfo * const nameToken)710 static void readQualifiedName (tokenInfo *const nameToken)
711 {
712 	readToken (nameToken);
713 
714 	if (nameToken->type == TOKEN_IDENTIFIER ||
715 		nameToken->type == '.')
716 	{
717 		vString *qualifiedName = vStringNew ();
718 		tokenInfo *token = newToken ();
719 
720 		while (nameToken->type == TOKEN_IDENTIFIER ||
721 			   nameToken->type == '.')
722 		{
723 			vStringCat (qualifiedName, nameToken->string);
724 			copyToken (token, nameToken);
725 
726 			readToken (nameToken);
727 		}
728 		/* put the last, non-matching, token back */
729 		ungetToken (nameToken);
730 
731 		copyToken (nameToken, token);
732 		nameToken->type = TOKEN_IDENTIFIER;
733 		vStringCopy (nameToken->string, qualifiedName);
734 
735 		deleteToken (token);
736 		vStringDelete (qualifiedName);
737 	}
738 }
739 
parseParamTypeAnnotation(tokenInfo * const token,vString * arglist)740 static vString *parseParamTypeAnnotation (tokenInfo *const token,
741 										  vString *arglist)
742 {
743 	readToken (token);
744 	if (token->type != ':')
745 	{
746 		ungetToken (token);
747 		return NULL;
748 	}
749 
750 	reprCat (arglist, token);
751 	int depth = 0;
752 	vString *t = vStringNew ();
753 	while (true)
754 	{
755 		readTokenFull (token, true);
756 		if (token->type == TOKEN_WHITESPACE)
757 		{
758 			reprCat (arglist, token);
759 			continue;
760 		}
761 		else if (token->type == TOKEN_EOF)
762 			break;
763 
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 --;
772 
773 		if (depth < 0
774 			|| (depth == 0 && (token->type == '='
775 							   || token->type == ',')))
776 		{
777 			ungetToken (token);
778 			return t;
779 		}
780 		reprCat (arglist, token);
781 		reprCat (t, token);
782 	}
783 	vStringDelete (t);
784 	return NULL;
785 }
786 
parseReturnTypeAnnotation(tokenInfo * const token)787 static vString *parseReturnTypeAnnotation (tokenInfo *const token)
788 {
789 	readToken (token);
790 	if (token->type != TOKEN_ARROW)
791 	{
792 		return NULL;
793 	}
794 
795 	int depth = 0;
796 	vString *t = vStringNew ();
797 	while (true)
798 	{
799 		readToken (token);
800 		if (token->type == TOKEN_EOF)
801 			break;
802 
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 == ':')
812 		{
813 			ungetToken (token);
814 			return t;
815 		}
816 		else
817 			reprCat (t, token);
818 	}
819 	vStringDelete (t);
820 	return NULL;
821 }
822 
parseClassOrDef(tokenInfo * const token,const stringList * const decorators,gdscriptKind kind)823 static bool parseClassOrDef (tokenInfo *const token,
824 							 const stringList *const decorators,
825 							 gdscriptKind kind)
826 {
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;
834 
835 	readToken (token);
836 	if (token->type != TOKEN_IDENTIFIER)
837 		return false;
838 
839 	name = newToken ();
840 	copyToken (name, token);
841 
842 	readToken (token);
843 	/* collect parameters or inheritance */
844 	if (token->type == '(')
845 	{
846 		int prevTokenType = token->type;
847 		int depth = 1;
848 
849 		arglist = vStringNew ();
850 		if (kind != K_CLASS)
851 			reprCat (arglist, token);
852 
853 		do
854 		{
855 			if (token->type != TOKEN_WHITESPACE &&
856 				token->type != '*')
857 			{
858 				prevTokenType = token->type;
859 			}
860 
861 			readTokenFull (token, true);
862 			if (kind != K_CLASS || token->type != ')' || depth > 1)
863 				reprCat (arglist, token);
864 
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)
878 			{
879 				tokenInfo *parameterName = newToken ();
880 
881 				copyToken (parameterName, token);
882 				parameterTokens[parameterCount] = parameterName;
883 				parameterTypes [parameterCount++] = parseParamTypeAnnotation (token, arglist);
884 			}
885 		}
886 		while (token->type != TOKEN_EOF && depth > 0);
887 	}
888 	else if (token->type == TOKEN_KEYWORD && token->keyword == KEYWORD_extends)
889 	{
890 		readToken (token);
891 		if (token->type == TOKEN_IDENTIFIER)
892 		{
893 			makeSimpleGDScriptRefTag (token, K_CLASS, GDSCRIPT_CLASS_EXTENDED, XTAG_UNKNOWN);
894 			arglist = vStringNewCopy (token->string);
895 		}
896 	}
897 	else if (kind == K_SIGNAL)
898 	{
899 		/* signal can be defined with no parameter list. */
900 		ungetToken (token);
901 	}
902 
903 	if (kind == K_CLASS)
904 		corkIndex = makeClassTag (name, arglist);
905 	else
906 		corkIndex = makeFunctionTag (name, kind, arglist, decorators);
907 
908 	lv = nestingLevelsPush (GDScriptNestingLevels, corkIndex);
909 	GDS_NL (lv)->indentation = token->indent;
910 
911 	deleteToken (name);
912 	vStringDelete (arglist);
913 
914 	if (parameterCount > 0)
915 	{
916 		unsigned int i;
917 
918 		for (i = 0; i < parameterCount; i++)
919 		{
920 			int paramCorkIndex = makeSimpleGDScriptTag (parameterTokens[i], K_PARAMETER);
921 			deleteToken (parameterTokens[i]);
922 			tagEntryInfo *e = getEntryInCorkQueue (paramCorkIndex);
923 			if (e && parameterTypes[i])
924 			{
925 				e->extensionFields.typeRef [0] = eStrdup ("typename");
926 				e->extensionFields.typeRef [1] = vStringDeleteUnwrap (parameterTypes[i]);
927 				parameterTypes[i] = NULL;
928 			}
929 			vStringDelete (parameterTypes[i]); /* NULL is acceptable. */
930 		}
931 	}
932 
933 	tagEntryInfo *e;
934 	vString *t;
935 	if (kind != K_CLASS
936 		&& (e = getEntryInCorkQueue (corkIndex))
937 		&& (t = parseReturnTypeAnnotation (token)))
938 	{
939 		e->extensionFields.typeRef [0] = eStrdup ("typename");
940 		e->extensionFields.typeRef [1] = vStringDeleteUnwrap (t);
941 	}
942 
943 	if (kind == K_SIGNAL)
944 		nestingLevelsPop (GDScriptNestingLevels);
945 
946 	return true;
947 }
948 
parseEnum(tokenInfo * const token)949 static bool parseEnum (tokenInfo *const token)
950 {
951 	int corkIndex;
952 
953 	readToken (token);
954 
955 	if (token->type == '{')
956 	{
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);
967 	}
968 	else if (token->type == TOKEN_IDENTIFIER)
969 	{
970 		corkIndex = makeSimpleGDScriptTag(token, K_ENUM);
971 		readToken (token);
972 	}
973 	else
974 		return false;
975 
976 	if (token->type != '{')
977 		return false;
978 
979 	readToken (token);
980 	nestingLevelsPush (GDScriptNestingLevels, corkIndex);
981 
982 	while (token->type != '}' && token->type != TOKEN_EOF)
983 	{
984 		if (token->type == TOKEN_IDENTIFIER)
985 			makeSimpleGDScriptTag(token, K_ENUMERATOR);
986 		else if (token->type == '=')
987 		{
988 			/* Skip the right value. */
989 			do
990 				readToken (token);
991 			while (token->type != ','
992 				   && token->type != '}'
993 				   && token->type != TOKEN_EOF);
994 			if (token->type != ',')
995 				continue;
996 		}
997 		readToken (token);
998 	}
999 
1000 	tagEntryInfo *e;
1001 	vString *t;
1002 	if ((e = getEntryInCorkQueue (corkIndex))
1003 		&& (t = parseReturnTypeAnnotation (token)))
1004 	{
1005 		e->extensionFields.typeRef [0] = eStrdup ("typename");
1006 		e->extensionFields.typeRef [1] = vStringDeleteUnwrap (t);
1007 	}
1008 
1009 	nestingLevelsPop (GDScriptNestingLevels);
1010 	return true;
1011 }
1012 
parseClassName(tokenInfo * const token)1013 static bool parseClassName (tokenInfo *const token)
1014 {
1015 	readToken (token);
1016 	if (token->type == TOKEN_IDENTIFIER)
1017 	{
1018 		/* A class name is explicitly given with "class_name" keyword.
1019 		 * Let's overwrite the anonymous tag for the class
1020 		 */
1021 		NestingLevel *nl = nestingLevelsGetNthFromRoot (GDScriptNestingLevels, 0);
1022 		tagEntryInfo *klass = nl? getEntryInCorkQueue (nl->corkIndex): NULL;
1023 
1024 		tagEntryInfo e;
1025 		char *name = vStringStrdup (token->string);
1026 		initTagEntry (&e, klass? "UNUSED": name, K_CLASS);
1027 
1028 		if (klass)
1029 		{
1030 			eFree ((void *)klass->name);
1031 			klass->name = name;
1032 			name = NULL;
1033 			unmarkTagExtraBit(klass, XTAG_ANONYMOUS);
1034 
1035 			/* Adjust the position. */
1036 			setTagPositionFromTag (klass, &e);
1037 		}
1038 
1039 		/* Extract B in class_name C extends B */
1040 		readToken (token);
1041 		if (token->type == TOKEN_KEYWORD
1042 			&& token->keyword == KEYWORD_extends)
1043 		{
1044 			readToken (token);
1045 			if (token->type == TOKEN_IDENTIFIER)
1046 			{
1047 				makeSimpleGDScriptRefTag (token, K_CLASS,
1048 										  GDSCRIPT_CLASS_EXTENDED,
1049 										  XTAG_UNKNOWN);
1050 				if (klass)
1051 				{
1052 					if (klass->extensionFields.inheritance)
1053 						eFree ((void *)klass->extensionFields.inheritance);
1054 					klass->extensionFields.inheritance = vStringStrdup (token->string);
1055 				}
1056 				else
1057 					e.extensionFields.inheritance = vStringValue(token->string);
1058 			}
1059 		}
1060 
1061 		if (!klass)
1062 			makeTagEntry (&e);
1063 
1064 		if (name)
1065 			eFree (name);
1066 	}
1067 
1068 	while (token->type != TOKEN_EOF &&
1069 		   token->type != ';' &&
1070 		   token->type != TOKEN_INDENT)
1071 		readToken (token);
1072 
1073 	return false;
1074 }
1075 
parseExtends(tokenInfo * const token)1076 static bool parseExtends (tokenInfo *const token)
1077 {
1078 	if (token->keyword == KEYWORD_extends)
1079 	{
1080 		readQualifiedName (token);
1081 		if (token->type == TOKEN_IDENTIFIER)
1082 		{
1083 			makeSimpleGDScriptRefTag (token, K_CLASS, GDSCRIPT_CLASS_EXTENDED, XTAG_UNKNOWN);
1084 			NestingLevel *nl = nestingLevelsGetCurrent (GDScriptNestingLevels);
1085 			if (nl)
1086 			{
1087 				tagEntryInfo *klass = getEntryInCorkQueue (nl->corkIndex);
1088 				if (klass)
1089 				{
1090 					if (klass->extensionFields.inheritance)
1091 						eFree ((void *)klass->extensionFields.inheritance);
1092 					klass->extensionFields.inheritance = vStringStrdup(token->string);
1093 				}
1094 			}
1095 		}
1096 	}
1097 	readToken (token);
1098 	return false;
1099 }
1100 
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. */
skipVariableTypeAnnotation(tokenInfo * const token,vString * const repr)1105 static bool skipVariableTypeAnnotation (tokenInfo *const token, vString *const repr)
1106 {
1107 	bool readNext = true;
1108 
1109 	readToken (token);
1110 	switch (token->type)
1111 	{
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);
1116 	}
1117 	if (readNext)
1118 		readToken (token);
1119 	/* skip subscripts and calls */
1120 	while (token->type == '[' || token->type == '(' || token->type == '.' || token->type == '|')
1121 	{
1122 		switch (token->type)
1123 		{
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;
1139 		}
1140 		if (readNext)
1141 			readToken (token);
1142 	}
1143 
1144 	return false;
1145 }
1146 
parseVariable(tokenInfo * const token,const gdscriptKind kind,const stringList * const decorators,const int keyword)1147 static bool parseVariable (tokenInfo *const token, const gdscriptKind kind,
1148 							const stringList *const decorators,
1149 							const int keyword)
1150 {
1151 	readToken(token);
1152 	vString *type = vStringNew();
1153 	tokenInfo *name = newToken ();
1154 	copyToken (name, token);
1155 	if (!name)
1156 		return false;
1157 
1158 	readToken (token);
1159 	// Variable declarations with dotted names are illegal
1160 	if (token->type == '.')
1161 		return false;
1162 
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);
1167 
1168 	int index = makeSimpleGDScriptTag (name, kind);
1169 	deleteToken(name);
1170 	tagEntryInfo *e = getEntryInCorkQueue (index);
1171 
1172 	if (e && decorators && stringListCount (decorators) > 0)
1173 	{
1174 		vString *vstr = makeDecoratorString (decorators);
1175 		attachParserField (e, true, GDScriptFields[F_ANNOTATIONS].ftype,
1176 						   vStringValue (vstr));
1177 		vStringDelete (vstr);
1178 	}
1179 
1180 	vString *vtype = vStringNew();
1181 	char * stype = vStringValue (type);
1182 	if (strcmp(stype, "=") && strcmp(stype, ""))
1183 	{
1184 			vStringCatS(vtype, stype);
1185 	}
1186 	vStringDelete(type);
1187 
1188 	if (e && vStringLength(vtype) > 0) /// TODO: Fix types away
1189 	{
1190 		e->extensionFields.typeRef [0] = eStrdup ("typename");
1191 		e->extensionFields.typeRef [1] = vStringDeleteUnwrap (vtype);
1192 	}
1193 	else
1194 	{
1195 		vStringDelete(vtype);
1196 	}
1197 
1198 
1199 	while ((TokenContinuationDepth > 0 || token->type != ',') &&
1200 		   token->type != TOKEN_EOF &&
1201 		   token->type != ';' &&
1202 		   token->type != TOKEN_INDENT)
1203 	{
1204 		readToken (token);
1205 	}
1206 
1207 
1208 	return false;
1209 }
1210 
1211 /* pops any level >= to indent */
setIndent(tokenInfo * const token)1212 static void setIndent (tokenInfo *const token)
1213 {
1214 	NestingLevel *lv = nestingLevelsGetCurrent (GDScriptNestingLevels);
1215 
1216 	while (lv && GDS_NL (lv)->indentation >= token->indent)
1217 	{
1218 		tagEntryInfo *e = getEntryInCorkQueue (lv->corkIndex);
1219 		if (e)
1220 			e->extensionFields.endLine = token->lineNumber;
1221 
1222 		nestingLevelsPop (GDScriptNestingLevels);
1223 		lv = nestingLevelsGetCurrent (GDScriptNestingLevels);
1224 	}
1225 }
1226 
prepareUnnamedClass(struct NestingLevels * nls)1227 static int prepareUnnamedClass (struct NestingLevels *nls)
1228 {
1229 	{
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.
1233 		 */
1234 		int c = getcFromInputFile ();
1235 		if (c == EOF)
1236 			return CORK_NIL;
1237 		ungetcToInputFile (c);
1238 	}
1239 
1240 	vString * tmp_class = anonGenerateNew ("anon_class_", K_CLASS);
1241 	int corkIndex = makeSimpleTag (tmp_class, K_CLASS);
1242 	vStringDelete (tmp_class);
1243 
1244 	tagEntryInfo *e = getEntryInCorkQueue (corkIndex);
1245 	if (e)
1246 		markTagExtraBit (e, XTAG_ANONYMOUS);
1247 
1248 	/* This virtual scope should not be poped. */
1249 	NestingLevel *lv = nestingLevelsPush (nls, corkIndex);
1250 	GDS_NL (lv)->indentation = -1;
1251 
1252 	return corkIndex;
1253 }
1254 
findGDScriptTags(void)1255 static void findGDScriptTags (void)
1256 {
1257 	tokenInfo *const token = newToken ();
1258 	stringList *decorators = stringListNew();
1259 	bool atStatementStart = true;
1260 
1261 	TokenContinuationDepth = 0;
1262 	NextToken = NULL;
1263 	GDScriptNestingLevels = nestingLevelsNew (sizeof (struct gdscriptNestingLevelUserData));
1264 
1265 	if (isXtagEnabled (GDScriptXtagTable[X_IMPLICIT_CLASS].xtype))
1266 		prepareUnnamedClass (GDScriptNestingLevels);
1267 
1268 	readToken (token);
1269 	while (token->type != TOKEN_EOF)
1270 	{
1271 		tokenType iterationTokenType = token->type;
1272 		int iterationTokenKeyword = token->keyword;
1273 		bool readNext = true;
1274 
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)
1280 		{
1281 			gdscriptKind kind = K_METHOD;
1282 			switch (token->keyword)
1283 			{
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();
1289 			}
1290 			readNext = parseClassOrDef (token, decorators, kind);
1291 		}
1292 		else if (token->keyword == KEYWORD_extends)
1293 		{
1294 			readNext = parseExtends (token);
1295 		}
1296 		else if (token->type == '(')
1297 		{ /* skip parentheses to avoid finding stuff inside them */
1298 			readNext = skipOverPair (token, '(', ')', NULL, false);
1299 		}
1300 		else if (token->keyword == KEYWORD_variable || token->keyword == KEYWORD_const)
1301 		{
1302 			NestingLevel *lv = nestingLevelsGetCurrent (GDScriptNestingLevels);
1303 			tagEntryInfo *lvEntry = NULL;
1304 			gdscriptKind kind = K_VARIABLE;
1305 
1306 			if (lv)
1307 				lvEntry = getEntryOfNestingLevel (lv);
1308 
1309 			if (lvEntry && lvEntry->kindIndex != K_CLASS)
1310 				kind = K_LOCAL_VARIABLE;
1311 
1312 			if (token->keyword == KEYWORD_const)
1313 				kind = K_CONST;
1314 
1315 			readNext = parseVariable (token, kind, decorators, token->keyword);
1316 		}
1317 		else if (token->keyword == KEYWORD_enum)
1318 		{
1319 			readNext = parseEnum (token);
1320 		}
1321 		else if (token->keyword == KEYWORD_class_name)
1322 		{
1323 			readNext = parseClassName (token);
1324 		}
1325 		else if (token->type == TOKEN_KEYWORD
1326 				 && token->keyword == KEYWORD_modifier)
1327 		{
1328 			stringListAdd (decorators, vStringNewCopy(token->string));
1329 		}
1330 		else if (token->type == '@' && atStatementStart &&
1331 				 GDScriptFields[F_ANNOTATIONS].enabled)
1332 		{
1333 			/* collect decorators */
1334 			readQualifiedName (token);
1335 			if (token->type != TOKEN_IDENTIFIER
1336 				&& (token->keyword != KEYWORD_modifier))
1337 				readNext = false;
1338 			else
1339 			{
1340 				stringListAdd (decorators, vStringNewCopy(token->string));
1341 				readToken (token);
1342 
1343 				vString *d = vStringNew ();
1344 				readNext = skipOverPair (token, '(', ')', d, true);
1345 				if (vStringLength (d) > 0)
1346 					stringListAdd (decorators, d);
1347 				else
1348 					vStringDelete (d);
1349 			}
1350 		}
1351 
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)
1359 		{
1360 			stringListClear (decorators);
1361 		}
1362 
1363 		atStatementStart = (token->type == TOKEN_INDENT || token->type == ';');
1364 
1365 		if (readNext)
1366 			readToken (token);
1367 	}
1368 
1369 	nestingLevelsFree (GDScriptNestingLevels);
1370 	stringListDelete (decorators);
1371 	deleteToken (token);
1372 	Assert (NextToken == NULL);
1373 }
1374 
initialize(const langType language)1375 static void initialize (const langType language)
1376 {
1377 	Lang_gdscript = language;
1378 
1379 	TokenPool = objPoolNew (16, newPoolToken, deletePoolToken, clearPoolToken, NULL);
1380 	addKeywordGroup (&modifierKeywords, language);
1381 }
1382 
finalize(langType language CTAGS_ATTR_UNUSED,bool initialized)1383 static void finalize (langType language CTAGS_ATTR_UNUSED, bool initialized)
1384 {
1385 	if (!initialized)
1386 		return;
1387 
1388 	objPoolDelete (TokenPool);
1389 }
1390 
GDScriptParser(void)1391 extern parserDefinition* GDScriptParser (void)
1392 {
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;
1410 }
1411