xref: /Universal-ctags/parsers/gdscript.c (revision 385c6b897bec246fce1c7cafb19f34be98156eae)
1ebbfee1eSDavid Yu Yang /*
2ebbfee1eSDavid Yu Yang *   Copyright (c) 2000-2003, Darren Hiebert
3ebbfee1eSDavid Yu Yang *   Copyright (c) 2014-2016, Colomban Wendling <ban@herbesfolles.org>
4ebbfee1eSDavid Yu Yang *	Copyright (c) 2021, David Yang <davidyang6us@gmail.com>
5ebbfee1eSDavid Yu Yang *
6ebbfee1eSDavid Yu Yang *   This source code is released for free distribution under the terms of the
7ebbfee1eSDavid Yu Yang *   GNU General Public License version 2 or (at your option) any later version.
8ebbfee1eSDavid Yu Yang *
9ebbfee1eSDavid Yu Yang *   This module contains functions for generating tags for GDScript language
10ebbfee1eSDavid Yu Yang *   files. This module is derived from the Python module.
11ebbfee1eSDavid Yu Yang *
12ebbfee1eSDavid Yu Yang *	GDScript language reference:
13ebbfee1eSDavid Yu Yang *	https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/gdscript_basics.html
14ebbfee1eSDavid Yu Yang *	https://docs.godotengine.org/en/stable/development/file_formats/gdscript_grammar.html#doc-gdscript-grammar
15ebbfee1eSDavid Yu Yang *	https://godotengine.org/article/gdscript-progress-report-new-gdscript-now-merged
16ebbfee1eSDavid Yu Yang *
17ebbfee1eSDavid Yu Yang */
18ebbfee1eSDavid Yu Yang 
19ebbfee1eSDavid Yu Yang #include "general.h"  /* must always come first */
20ebbfee1eSDavid Yu Yang 
21ebbfee1eSDavid Yu Yang #include <string.h>
22ebbfee1eSDavid Yu Yang 
23ebbfee1eSDavid Yu Yang #include "entry.h"
24ebbfee1eSDavid Yu Yang #include "nestlevel.h"
25ebbfee1eSDavid Yu Yang #include "read.h"
26ebbfee1eSDavid Yu Yang #include "parse.h"
27ebbfee1eSDavid Yu Yang #include "vstring.h"
28ebbfee1eSDavid Yu Yang #include "keyword.h"
29ebbfee1eSDavid Yu Yang #include "routines.h"
30ebbfee1eSDavid Yu Yang #include "debug.h"
31ebbfee1eSDavid Yu Yang #include "xtag.h"
32ebbfee1eSDavid Yu Yang #include "objpool.h"
33ebbfee1eSDavid Yu Yang #include "strlist.h"
34ebbfee1eSDavid Yu Yang 
35ebbfee1eSDavid Yu Yang #define isIdentifierChar(c) \
36ebbfee1eSDavid Yu Yang 	(isalnum (c) || (c) == '_' || (c) >= 0x80)
37ebbfee1eSDavid Yu Yang #define newToken() (objPoolGet (TokenPool))
38ebbfee1eSDavid Yu Yang #define deleteToken(t) (objPoolPut (TokenPool, (t)))
39ebbfee1eSDavid Yu Yang 
40ebbfee1eSDavid Yu Yang enum {
41ebbfee1eSDavid Yu Yang 	KEYWORD_class,
42ebbfee1eSDavid Yu Yang 	KEYWORD_func,
43ebbfee1eSDavid Yu Yang 	KEYWORD_extends,
44ebbfee1eSDavid Yu Yang 	KEYWORD_pass,
45ebbfee1eSDavid Yu Yang 	KEYWORD_return,
46ebbfee1eSDavid Yu Yang 	KEYWORD_lambda,
47ebbfee1eSDavid Yu Yang 	KEYWORD_variable,
48ebbfee1eSDavid Yu Yang 	KEYWORD_const,
49ebbfee1eSDavid Yu Yang 	KEYWORD_enum,
50ebbfee1eSDavid Yu Yang 	KEYWORD_class_name,
51ebbfee1eSDavid Yu Yang 	KEYWORD_signal,
52ebbfee1eSDavid Yu Yang 	KEYWORD_modifier,
53ebbfee1eSDavid Yu Yang };
54ebbfee1eSDavid Yu Yang typedef int keywordId; /* to allow KEYWORD_NONE */
55ebbfee1eSDavid Yu Yang 
56ebbfee1eSDavid Yu Yang typedef enum {
57ebbfee1eSDavid Yu Yang 	ACCESS_PRIVATE,
58ebbfee1eSDavid Yu Yang 	ACCESS_PROTECTED,
59ebbfee1eSDavid Yu Yang 	ACCESS_PUBLIC,
60ebbfee1eSDavid Yu Yang 	COUNT_ACCESS
61ebbfee1eSDavid Yu Yang } accessType;
62ebbfee1eSDavid Yu Yang 
63ebbfee1eSDavid Yu Yang static const char *const GDScriptAccesses[COUNT_ACCESS] = {
64ebbfee1eSDavid Yu Yang 	"private",
65ebbfee1eSDavid Yu Yang 	"protected",
66ebbfee1eSDavid Yu Yang 	"public"
67ebbfee1eSDavid Yu Yang };
68ebbfee1eSDavid Yu Yang 
69ebbfee1eSDavid Yu Yang typedef enum {
70ebbfee1eSDavid Yu Yang 	F_ANNOTATIONS,
71ebbfee1eSDavid Yu Yang 	COUNT_FIELD
72ebbfee1eSDavid Yu Yang } gdscriptField;
73ebbfee1eSDavid Yu Yang 
74ebbfee1eSDavid Yu Yang static fieldDefinition GDScriptFields[COUNT_FIELD] = {
75ebbfee1eSDavid Yu Yang 	{ .name = "annotations",
76ebbfee1eSDavid Yu Yang 	  .description = "annotations on functions and variables",
77ebbfee1eSDavid Yu Yang 	  .enabled = true },
78ebbfee1eSDavid Yu Yang };
79ebbfee1eSDavid Yu Yang 
80ebbfee1eSDavid Yu Yang typedef enum {
81ebbfee1eSDavid Yu Yang 	K_CLASS,
82ebbfee1eSDavid Yu Yang 	K_METHOD,
83ebbfee1eSDavid Yu Yang 	K_VARIABLE,
84ebbfee1eSDavid Yu Yang 	K_CONST,
85ebbfee1eSDavid Yu Yang 	K_ENUM,
86ebbfee1eSDavid Yu Yang 	K_ENUMERATOR,
87ebbfee1eSDavid Yu Yang 	K_PARAMETER,
88ebbfee1eSDavid Yu Yang 	K_LOCAL_VARIABLE,
89ebbfee1eSDavid Yu Yang 	K_SIGNAL,
90ebbfee1eSDavid Yu Yang 	COUNT_KIND
91ebbfee1eSDavid Yu Yang } gdscriptKind;
92ebbfee1eSDavid Yu Yang 
93ebbfee1eSDavid Yu Yang typedef enum {
94ebbfee1eSDavid Yu Yang 	GDSCRIPT_CLASS_EXTENDED,
95ebbfee1eSDavid Yu Yang } gdscriptClassRole;
96ebbfee1eSDavid Yu Yang 
97ebbfee1eSDavid Yu Yang static roleDefinition GDScriptClassRoles [] = {
98ebbfee1eSDavid Yu Yang 	{ true, "extended",   "used as a base class for extending" },
99ebbfee1eSDavid Yu Yang };
100ebbfee1eSDavid Yu Yang 
101ebbfee1eSDavid Yu Yang static kindDefinition GDScriptKinds[COUNT_KIND] = {
102ebbfee1eSDavid Yu Yang 	{true, 'c', "class",	"classes",
103ebbfee1eSDavid Yu Yang 	 .referenceOnly = false, ATTACH_ROLES(GDScriptClassRoles)},
104ebbfee1eSDavid Yu Yang 	{true, 'm', "method",	"methods"},
105ebbfee1eSDavid Yu Yang 	{true, 'v', "variable",	"variables"},
106ebbfee1eSDavid Yu Yang 	{true, 'C', "const", "constants"},
107ebbfee1eSDavid Yu Yang 	{true, 'g', "enum",	"enumeration names"},
108ebbfee1eSDavid Yu Yang 	{true, 'e', "enumerator",	"enumerated values"},
109ebbfee1eSDavid Yu Yang 	{false,'z', "parameter",	"function parameters"},
110ebbfee1eSDavid Yu Yang 	{false,'l', "local",	"local variables"},
111ebbfee1eSDavid Yu Yang 	{true, 's', "signal",   "signals"},
112ebbfee1eSDavid Yu Yang };
113ebbfee1eSDavid Yu Yang 
114ebbfee1eSDavid Yu Yang typedef enum {
115ebbfee1eSDavid Yu Yang 	X_IMPLICIT_CLASS,
116ebbfee1eSDavid Yu Yang } gdscriptXtag;
117ebbfee1eSDavid Yu Yang 
118ebbfee1eSDavid Yu Yang static xtagDefinition GDScriptXtagTable [] = {
119ebbfee1eSDavid Yu Yang 	{
120ebbfee1eSDavid Yu Yang 		.enabled     = false,
121ebbfee1eSDavid Yu Yang 		.name        = "implicitClass",
122ebbfee1eSDavid Yu Yang 		.description = "Include tag for the implicitly defined unnamed class",
123ebbfee1eSDavid Yu Yang 	},
124ebbfee1eSDavid Yu Yang };
125ebbfee1eSDavid Yu Yang 
126ebbfee1eSDavid Yu Yang static const keywordTable GDScriptKeywordTable[] = {
127ebbfee1eSDavid Yu Yang 	/* keyword			keyword ID */
128ebbfee1eSDavid Yu Yang 	{ "class",			KEYWORD_class			},
129ebbfee1eSDavid Yu Yang 	{ "func",			KEYWORD_func			},
130ebbfee1eSDavid Yu Yang 	{ "extends",		KEYWORD_extends			},
131ebbfee1eSDavid Yu Yang 	{ "lambda",			KEYWORD_lambda			}, // Future GDScript lambda currently uses func, may change
132ebbfee1eSDavid Yu Yang 	{ "pass",			KEYWORD_pass			},
133ebbfee1eSDavid Yu Yang 	{ "return",			KEYWORD_return			},
134ebbfee1eSDavid Yu Yang 	{ "var",			KEYWORD_variable		},
135ebbfee1eSDavid Yu Yang 	{ "const",			KEYWORD_const			},
136ebbfee1eSDavid Yu Yang 	{ "enum",			KEYWORD_enum			},
137ebbfee1eSDavid Yu Yang 	{ "class_name",		KEYWORD_class_name		},
138ebbfee1eSDavid Yu Yang 	{ "signal",			KEYWORD_signal			},
139ebbfee1eSDavid Yu Yang 
140ebbfee1eSDavid Yu Yang };
141ebbfee1eSDavid Yu Yang 
142ebbfee1eSDavid Yu Yang const static struct keywordGroup modifierKeywords = {
143ebbfee1eSDavid Yu Yang 	.value = KEYWORD_modifier,
144ebbfee1eSDavid Yu Yang 	.addingUnlessExisting = false,
145ebbfee1eSDavid Yu Yang 	.keywords = {
146ebbfee1eSDavid Yu Yang 		"static",
147ebbfee1eSDavid Yu Yang 		"remote", "remotesync",
148ebbfee1eSDavid Yu Yang 		"master", "mastersycn",
149ebbfee1eSDavid Yu Yang 		"puppet", "puppetsync",
150ebbfee1eSDavid Yu Yang 		NULL,
151ebbfee1eSDavid Yu Yang 	},
152ebbfee1eSDavid Yu Yang };
153ebbfee1eSDavid Yu Yang 
154ebbfee1eSDavid Yu Yang typedef enum eTokenType {
155ebbfee1eSDavid Yu Yang 	/* 0..255 are the byte's value */
156ebbfee1eSDavid Yu Yang 	TOKEN_EOF = 256,
157ebbfee1eSDavid Yu Yang 	TOKEN_UNDEFINED,
158ebbfee1eSDavid Yu Yang 	TOKEN_INDENT,
159ebbfee1eSDavid Yu Yang 	TOKEN_KEYWORD,
160ebbfee1eSDavid Yu Yang 	TOKEN_OPERATOR,
161ebbfee1eSDavid Yu Yang 	TOKEN_IDENTIFIER,
162ebbfee1eSDavid Yu Yang 	TOKEN_STRING,
163ebbfee1eSDavid Yu Yang 	TOKEN_ARROW,				/* -> */
164ebbfee1eSDavid Yu Yang 	TOKEN_WHITESPACE,
165ebbfee1eSDavid Yu Yang } tokenType;
166ebbfee1eSDavid Yu Yang 
167ebbfee1eSDavid Yu Yang typedef struct {
168ebbfee1eSDavid Yu Yang 	int				type;
169ebbfee1eSDavid Yu Yang 	keywordId		keyword;
170ebbfee1eSDavid Yu Yang 	vString *		string;
171ebbfee1eSDavid Yu Yang 	int				indent;
172ebbfee1eSDavid Yu Yang 	unsigned long	lineNumber;
173ebbfee1eSDavid Yu Yang 	MIOPos			filePosition;
174ebbfee1eSDavid Yu Yang } tokenInfo;
175ebbfee1eSDavid Yu Yang 
176ebbfee1eSDavid Yu Yang struct gdscriptNestingLevelUserData {
177ebbfee1eSDavid Yu Yang 	int indentation;
178ebbfee1eSDavid Yu Yang };
179ebbfee1eSDavid Yu Yang #define GDS_NL(nl) ((struct gdscriptNestingLevelUserData *) nestingLevelGetUserData (nl))
180ebbfee1eSDavid Yu Yang 
181ebbfee1eSDavid Yu Yang static langType Lang_gdscript;
182ebbfee1eSDavid Yu Yang static unsigned int TokenContinuationDepth = 0;
183ebbfee1eSDavid Yu Yang static tokenInfo *NextToken = NULL;
184ebbfee1eSDavid Yu Yang static NestingLevels *GDScriptNestingLevels = NULL;
185ebbfee1eSDavid Yu Yang static objPool *TokenPool = NULL;
186ebbfee1eSDavid Yu Yang 
187ebbfee1eSDavid Yu Yang 
188ebbfee1eSDavid Yu Yang // Always reports single-underscores as protected
accessFromIdentifier(const vString * const ident,int parentKind)189ebbfee1eSDavid Yu Yang static accessType accessFromIdentifier (const vString *const ident, int parentKind)
190ebbfee1eSDavid Yu Yang {
191ebbfee1eSDavid Yu Yang 	const char *const p = vStringValue (ident);
192ebbfee1eSDavid Yu Yang 	const size_t len = vStringLength (ident);
193ebbfee1eSDavid Yu Yang 
194ebbfee1eSDavid Yu Yang 	/* inside a function/method, private */
195ebbfee1eSDavid Yu Yang 	if (parentKind != -1 && parentKind != K_CLASS)
196ebbfee1eSDavid Yu Yang 		return ACCESS_PRIVATE;
197ebbfee1eSDavid Yu Yang 	/* not starting with "_", public */
198ebbfee1eSDavid Yu Yang 	else if (len < 1 || p[0] != '_')
199ebbfee1eSDavid Yu Yang 		return ACCESS_PUBLIC;
200ebbfee1eSDavid Yu Yang 	/* "_...": suggested as non-public, but easily accessible */
201ebbfee1eSDavid Yu Yang 	else
202ebbfee1eSDavid Yu Yang 		return ACCESS_PROTECTED;
203ebbfee1eSDavid Yu Yang }
204ebbfee1eSDavid Yu Yang 
initGDScriptEntry(tagEntryInfo * const e,const tokenInfo * const token,const gdscriptKind kind)205ebbfee1eSDavid Yu Yang static void initGDScriptEntry (tagEntryInfo *const e, const tokenInfo *const token,
206ebbfee1eSDavid Yu Yang 							 const gdscriptKind kind)
207ebbfee1eSDavid Yu Yang {
208ebbfee1eSDavid Yu Yang 	accessType access;
209ebbfee1eSDavid Yu Yang 	int parentKind = -1;
210ebbfee1eSDavid Yu Yang 	NestingLevel *nl;
211ebbfee1eSDavid Yu Yang 
212ebbfee1eSDavid Yu Yang 	initTagEntry (e, vStringValue (token->string), kind);
213ebbfee1eSDavid Yu Yang 
214ebbfee1eSDavid Yu Yang 	e->lineNumber	= token->lineNumber;
215ebbfee1eSDavid Yu Yang 	e->filePosition	= token->filePosition;
216ebbfee1eSDavid Yu Yang 
217ebbfee1eSDavid Yu Yang 	nl = nestingLevelsGetCurrent (GDScriptNestingLevels);
218ebbfee1eSDavid Yu Yang 	if (nl)
219ebbfee1eSDavid Yu Yang 	{
220ebbfee1eSDavid Yu Yang 		tagEntryInfo *nlEntry = getEntryOfNestingLevel (nl);
221ebbfee1eSDavid Yu Yang 
222ebbfee1eSDavid Yu Yang 		e->extensionFields.scopeIndex = nl->corkIndex;
223ebbfee1eSDavid Yu Yang 
224ebbfee1eSDavid Yu Yang 		/* nlEntry can be NULL if a kind was disabled.  But what can we do
225ebbfee1eSDavid Yu Yang 		 * here?  Even disabled kinds should count for the hierarchy I
226ebbfee1eSDavid Yu Yang 		 * guess -- as it'd otherwise be wrong -- but with cork we're
227ebbfee1eSDavid Yu Yang 		 * fucked up as there's nothing to look up.  Damn. */
228ebbfee1eSDavid Yu Yang 		if (nlEntry)
229ebbfee1eSDavid Yu Yang 			parentKind = nlEntry->kindIndex;
230ebbfee1eSDavid Yu Yang 	}
231ebbfee1eSDavid Yu Yang 
232ebbfee1eSDavid Yu Yang 	access = accessFromIdentifier (token->string, parentKind);
233ebbfee1eSDavid Yu Yang 	e->extensionFields.access = GDScriptAccesses[access];
234ebbfee1eSDavid Yu Yang 	/* FIXME: should we really set isFileScope in addition to access? */
235ebbfee1eSDavid Yu Yang 	if (access == ACCESS_PRIVATE)
236ebbfee1eSDavid Yu Yang 		e->isFileScope = true;
237ebbfee1eSDavid Yu Yang }
238ebbfee1eSDavid Yu Yang 
makeClassTag(const tokenInfo * const token,const vString * const inheritance)239ebbfee1eSDavid Yu Yang static int makeClassTag (const tokenInfo *const token,
240ebbfee1eSDavid Yu Yang 						 const vString *const inheritance)
241ebbfee1eSDavid Yu Yang {
242ebbfee1eSDavid Yu Yang 	if (GDScriptKinds[K_CLASS].enabled)
243ebbfee1eSDavid Yu Yang 	{
244ebbfee1eSDavid Yu Yang 		tagEntryInfo e;
245ebbfee1eSDavid Yu Yang 
246ebbfee1eSDavid Yu Yang 		initGDScriptEntry (&e, token, K_CLASS);
247ebbfee1eSDavid Yu Yang 
248ebbfee1eSDavid Yu Yang 		e.extensionFields.inheritance = inheritance ? vStringValue (inheritance) : "";
249ebbfee1eSDavid Yu Yang 
250ebbfee1eSDavid Yu Yang 		return makeTagEntry (&e);
251ebbfee1eSDavid Yu Yang 	}
252ebbfee1eSDavid Yu Yang 
253ebbfee1eSDavid Yu Yang 	return CORK_NIL;
254ebbfee1eSDavid Yu Yang }
255ebbfee1eSDavid Yu Yang 
makeDecoratorString(const stringList * const strlist)256ebbfee1eSDavid Yu Yang static vString *makeDecoratorString (const stringList *const strlist)
257ebbfee1eSDavid Yu Yang {
258ebbfee1eSDavid Yu Yang 	vString *vstr = vStringNew ();
259ebbfee1eSDavid Yu Yang 
260ebbfee1eSDavid Yu Yang 	for (unsigned int i = 0; i < stringListCount (strlist); i++)
261ebbfee1eSDavid Yu Yang 	{
262ebbfee1eSDavid Yu Yang 		vString *elt = stringListItem (strlist, i);
263ebbfee1eSDavid Yu Yang 		if (i != 0 && (vStringValue (elt) > 0
264ebbfee1eSDavid Yu Yang 					   && vStringValue (elt)[0] != '('))
265ebbfee1eSDavid Yu Yang 			vStringPut (vstr, ',');
266ebbfee1eSDavid Yu Yang 		vStringCat (vstr, elt);
267ebbfee1eSDavid Yu Yang 	}
268ebbfee1eSDavid Yu Yang 	return vstr;
269ebbfee1eSDavid Yu Yang }
270ebbfee1eSDavid Yu Yang 
makeFunctionTag(const tokenInfo * const token,int kind,const vString * const arglist,const stringList * const decorators)271ebbfee1eSDavid Yu Yang static int makeFunctionTag (const tokenInfo *const token,
272ebbfee1eSDavid Yu Yang 							int kind,
273ebbfee1eSDavid Yu Yang 							const vString *const arglist,
274ebbfee1eSDavid Yu Yang 							const stringList *const decorators)
275ebbfee1eSDavid Yu Yang {
276ebbfee1eSDavid Yu Yang 	if (GDScriptKinds[kind].enabled)
277ebbfee1eSDavid Yu Yang 	{
278ebbfee1eSDavid Yu Yang 		tagEntryInfo e;
279ebbfee1eSDavid Yu Yang 		vString *vstr = NULL;
280ebbfee1eSDavid Yu Yang 		int r;
281ebbfee1eSDavid Yu Yang 
282ebbfee1eSDavid Yu Yang 		initGDScriptEntry (&e, token, kind);
283ebbfee1eSDavid Yu Yang 
284ebbfee1eSDavid Yu Yang 		if (arglist)
285ebbfee1eSDavid Yu Yang 			e.extensionFields.signature = vStringValue (arglist);
286ebbfee1eSDavid Yu Yang 		if (decorators && stringListCount (decorators) > 0)
287ebbfee1eSDavid Yu Yang 		{
288ebbfee1eSDavid Yu Yang 			vstr = makeDecoratorString (decorators);
289ebbfee1eSDavid Yu Yang 			attachParserField (&e, false, GDScriptFields[F_ANNOTATIONS].ftype,
290ebbfee1eSDavid Yu Yang 							   vStringValue (vstr));
291ebbfee1eSDavid Yu Yang 		}
292ebbfee1eSDavid Yu Yang 
293ebbfee1eSDavid Yu Yang 		r = makeTagEntry (&e);
294ebbfee1eSDavid Yu Yang 		vStringDelete (vstr);	/* NULL is ignored. */
295ebbfee1eSDavid Yu Yang 
296ebbfee1eSDavid Yu Yang 		return r;
297ebbfee1eSDavid Yu Yang 	}
298ebbfee1eSDavid Yu Yang 
299ebbfee1eSDavid Yu Yang 	return CORK_NIL;
300ebbfee1eSDavid Yu Yang }
301ebbfee1eSDavid Yu Yang 
makeSimpleGDScriptTag(const tokenInfo * const token,gdscriptKind const kind)302ebbfee1eSDavid Yu Yang static int makeSimpleGDScriptTag (const tokenInfo *const token, gdscriptKind const kind)
303ebbfee1eSDavid Yu Yang {
304ebbfee1eSDavid Yu Yang 	if (GDScriptKinds[kind].enabled)
305ebbfee1eSDavid Yu Yang 	{
306ebbfee1eSDavid Yu Yang 		tagEntryInfo e;
307ebbfee1eSDavid Yu Yang 
308ebbfee1eSDavid Yu Yang 		initGDScriptEntry (&e, token, kind);
309ebbfee1eSDavid Yu Yang 		return makeTagEntry (&e);
310ebbfee1eSDavid Yu Yang 	}
311ebbfee1eSDavid Yu Yang 
312ebbfee1eSDavid Yu Yang 	return CORK_NIL;
313ebbfee1eSDavid Yu Yang }
314ebbfee1eSDavid Yu Yang 
makeSimpleGDScriptRefTag(const tokenInfo * const token,gdscriptKind const kind,int roleIndex,xtagType xtag)315ebbfee1eSDavid Yu Yang static int makeSimpleGDScriptRefTag (const tokenInfo *const token,
316ebbfee1eSDavid Yu Yang 									 gdscriptKind const kind,
317ebbfee1eSDavid Yu Yang 									 int roleIndex, xtagType xtag)
318ebbfee1eSDavid Yu Yang {
319ebbfee1eSDavid Yu Yang 	if (isXtagEnabled (XTAG_REFERENCE_TAGS))
320ebbfee1eSDavid Yu Yang 	{
321ebbfee1eSDavid Yu Yang 		tagEntryInfo e;
322ebbfee1eSDavid Yu Yang 
323ebbfee1eSDavid Yu Yang 		initRefTagEntry (&e, vStringValue (token->string),
324ebbfee1eSDavid Yu Yang 						 kind, roleIndex);
325ebbfee1eSDavid Yu Yang 
326ebbfee1eSDavid Yu Yang 		e.lineNumber	= token->lineNumber;
327ebbfee1eSDavid Yu Yang 		e.filePosition	= token->filePosition;
328ebbfee1eSDavid Yu Yang 
329ebbfee1eSDavid Yu Yang 		if (xtag != XTAG_UNKNOWN)
330ebbfee1eSDavid Yu Yang 			markTagExtraBit (&e, xtag);
331ebbfee1eSDavid Yu Yang 
332ebbfee1eSDavid Yu Yang 		return makeTagEntry (&e);
333ebbfee1eSDavid Yu Yang 	}
334ebbfee1eSDavid Yu Yang 
335ebbfee1eSDavid Yu Yang 	return CORK_NIL;
336ebbfee1eSDavid Yu Yang }
337ebbfee1eSDavid Yu Yang 
newPoolToken(void * createArg CTAGS_ATTR_UNUSED)338ebbfee1eSDavid Yu Yang static void *newPoolToken (void *createArg CTAGS_ATTR_UNUSED)
339ebbfee1eSDavid Yu Yang {
340ebbfee1eSDavid Yu Yang 	tokenInfo *token = xMalloc (1, tokenInfo);
341ebbfee1eSDavid Yu Yang 	token->string = vStringNew ();
342ebbfee1eSDavid Yu Yang 	return token;
343ebbfee1eSDavid Yu Yang }
344ebbfee1eSDavid Yu Yang 
deletePoolToken(void * data)345ebbfee1eSDavid Yu Yang static void deletePoolToken (void *data)
346ebbfee1eSDavid Yu Yang {
347ebbfee1eSDavid Yu Yang 	tokenInfo *token = data;
348ebbfee1eSDavid Yu Yang 	vStringDelete (token->string);
349ebbfee1eSDavid Yu Yang 	eFree (token);
350ebbfee1eSDavid Yu Yang }
351ebbfee1eSDavid Yu Yang 
clearPoolToken(void * data)352ebbfee1eSDavid Yu Yang static void clearPoolToken (void *data)
353ebbfee1eSDavid Yu Yang {
354ebbfee1eSDavid Yu Yang 	tokenInfo *token = data;
355ebbfee1eSDavid Yu Yang 
356ebbfee1eSDavid Yu Yang 	token->type			= TOKEN_UNDEFINED;
357ebbfee1eSDavid Yu Yang 	token->keyword		= KEYWORD_NONE;
358ebbfee1eSDavid Yu Yang 	token->indent		= 0;
359ebbfee1eSDavid Yu Yang 	token->lineNumber   = getInputLineNumber ();
360ebbfee1eSDavid Yu Yang 	token->filePosition = getInputFilePosition ();
361ebbfee1eSDavid Yu Yang 	vStringClear (token->string);
362ebbfee1eSDavid Yu Yang }
363ebbfee1eSDavid Yu Yang 
copyToken(tokenInfo * const dest,const tokenInfo * const src)364ebbfee1eSDavid Yu Yang static void copyToken (tokenInfo *const dest, const tokenInfo *const src)
365ebbfee1eSDavid Yu Yang {
366ebbfee1eSDavid Yu Yang 	dest->lineNumber = src->lineNumber;
367ebbfee1eSDavid Yu Yang 	dest->filePosition = src->filePosition;
368ebbfee1eSDavid Yu Yang 	dest->type = src->type;
369ebbfee1eSDavid Yu Yang 	dest->keyword = src->keyword;
370ebbfee1eSDavid Yu Yang 	dest->indent = src->indent;
371ebbfee1eSDavid Yu Yang 	vStringCopy(dest->string, src->string);
372ebbfee1eSDavid Yu Yang }
373ebbfee1eSDavid Yu Yang 
374ebbfee1eSDavid Yu Yang /* Skip a single or double quoted string. */
readString(vString * const string,const int delimiter)375ebbfee1eSDavid Yu Yang static void readString (vString *const string, const int delimiter)
376ebbfee1eSDavid Yu Yang {
377ebbfee1eSDavid Yu Yang 	int escaped = 0;
378ebbfee1eSDavid Yu Yang 	int c;
379ebbfee1eSDavid Yu Yang 
380ebbfee1eSDavid Yu Yang 	while ((c = getcFromInputFile ()) != EOF)
381ebbfee1eSDavid Yu Yang 	{
382ebbfee1eSDavid Yu Yang 		if (escaped)
383ebbfee1eSDavid Yu Yang 		{
384ebbfee1eSDavid Yu Yang 			vStringPut (string, c);
385ebbfee1eSDavid Yu Yang 			escaped--;
386ebbfee1eSDavid Yu Yang 		}
387ebbfee1eSDavid Yu Yang 		else if (c == '\\')
388ebbfee1eSDavid Yu Yang 			escaped++;
389ebbfee1eSDavid Yu Yang 		else if (c == delimiter || c == '\n' || c == '\r')
390ebbfee1eSDavid Yu Yang 		{
391ebbfee1eSDavid Yu Yang 			if (c != delimiter)
392ebbfee1eSDavid Yu Yang 				ungetcToInputFile (c);
393ebbfee1eSDavid Yu Yang 			break;
394ebbfee1eSDavid Yu Yang 		}
395ebbfee1eSDavid Yu Yang 		else
396ebbfee1eSDavid Yu Yang 			vStringPut (string, c);
397ebbfee1eSDavid Yu Yang 	}
398ebbfee1eSDavid Yu Yang }
399ebbfee1eSDavid Yu Yang 
400ebbfee1eSDavid Yu Yang /* Skip a single or double triple quoted string. */
readTripleString(vString * const string,const int delimiter)401ebbfee1eSDavid Yu Yang static void readTripleString (vString *const string, const int delimiter)
402ebbfee1eSDavid Yu Yang {
403ebbfee1eSDavid Yu Yang 	int c;
404ebbfee1eSDavid Yu Yang 	int escaped = 0;
405ebbfee1eSDavid Yu Yang 	int n = 0;
406ebbfee1eSDavid Yu Yang 	while ((c = getcFromInputFile ()) != EOF)
407ebbfee1eSDavid Yu Yang 	{
408ebbfee1eSDavid Yu Yang 		if (c == delimiter && ! escaped)
409ebbfee1eSDavid Yu Yang 		{
410ebbfee1eSDavid Yu Yang 			if (++n >= 3)
411ebbfee1eSDavid Yu Yang 				break;
412ebbfee1eSDavid Yu Yang 		}
413ebbfee1eSDavid Yu Yang 		else
414ebbfee1eSDavid Yu Yang 		{
415ebbfee1eSDavid Yu Yang 			for (; n > 0; n--)
416ebbfee1eSDavid Yu Yang 				vStringPut (string, delimiter);
417ebbfee1eSDavid Yu Yang 			if (c != '\\' || escaped)
418ebbfee1eSDavid Yu Yang 				vStringPut (string, c);
419ebbfee1eSDavid Yu Yang 			n = 0;
420ebbfee1eSDavid Yu Yang 		}
421ebbfee1eSDavid Yu Yang 
422ebbfee1eSDavid Yu Yang 		if (escaped)
423ebbfee1eSDavid Yu Yang 			escaped--;
424ebbfee1eSDavid Yu Yang 		else if (c == '\\')
425ebbfee1eSDavid Yu Yang 			escaped++;
426ebbfee1eSDavid Yu Yang 	}
427ebbfee1eSDavid Yu Yang }
428ebbfee1eSDavid Yu Yang 
readIdentifier(vString * const string,const int firstChar)429ebbfee1eSDavid Yu Yang static void readIdentifier (vString *const string, const int firstChar)
430ebbfee1eSDavid Yu Yang {
431ebbfee1eSDavid Yu Yang 	int c = firstChar;
432ebbfee1eSDavid Yu Yang 	do
433ebbfee1eSDavid Yu Yang 	{
434ebbfee1eSDavid Yu Yang 		vStringPut (string, c);
435ebbfee1eSDavid Yu Yang 		c = getcFromInputFile ();
436ebbfee1eSDavid Yu Yang 	}
437ebbfee1eSDavid Yu Yang 	while (isIdentifierChar (c));
438ebbfee1eSDavid Yu Yang 	ungetcToInputFile (c);
439ebbfee1eSDavid Yu Yang }
440ebbfee1eSDavid Yu Yang 
ungetToken(tokenInfo * const token)441ebbfee1eSDavid Yu Yang static void ungetToken (tokenInfo *const token)
442ebbfee1eSDavid Yu Yang {
443ebbfee1eSDavid Yu Yang 	Assert (NextToken == NULL);
444ebbfee1eSDavid Yu Yang 	NextToken = newToken ();
445ebbfee1eSDavid Yu Yang 	copyToken (NextToken, token);
446ebbfee1eSDavid Yu Yang }
447ebbfee1eSDavid Yu Yang 
readTokenFull(tokenInfo * const token,bool inclWhitespaces)448ebbfee1eSDavid Yu Yang static void readTokenFull (tokenInfo *const token, bool inclWhitespaces)
449ebbfee1eSDavid Yu Yang {
450ebbfee1eSDavid Yu Yang 	int c;
451ebbfee1eSDavid Yu Yang 	int n;
452ebbfee1eSDavid Yu Yang 
453ebbfee1eSDavid Yu Yang 	/* if we've got a token held back, emit it */
454ebbfee1eSDavid Yu Yang 	if (NextToken)
455ebbfee1eSDavid Yu Yang 	{
456ebbfee1eSDavid Yu Yang 		copyToken (token, NextToken);
457ebbfee1eSDavid Yu Yang 		deleteToken (NextToken);
458ebbfee1eSDavid Yu Yang 		NextToken = NULL;
459ebbfee1eSDavid Yu Yang 		return;
460ebbfee1eSDavid Yu Yang 	}
461ebbfee1eSDavid Yu Yang 
462ebbfee1eSDavid Yu Yang 	token->type		= TOKEN_UNDEFINED;
463ebbfee1eSDavid Yu Yang 	token->keyword	= KEYWORD_NONE;
464ebbfee1eSDavid Yu Yang 	vStringClear (token->string);
465ebbfee1eSDavid Yu Yang 
466ebbfee1eSDavid Yu Yang getNextChar:
467ebbfee1eSDavid Yu Yang 
468ebbfee1eSDavid Yu Yang 	n = 0;
469ebbfee1eSDavid Yu Yang 	do
470ebbfee1eSDavid Yu Yang 	{
471ebbfee1eSDavid Yu Yang 		c = getcFromInputFile ();
472ebbfee1eSDavid Yu Yang 		n++;
473ebbfee1eSDavid Yu Yang 	}
474ebbfee1eSDavid Yu Yang 	while (c == ' ' || c == '\t' || c == '\f');
475ebbfee1eSDavid Yu Yang 
476ebbfee1eSDavid Yu Yang 	token->lineNumber   = getInputLineNumber ();
477ebbfee1eSDavid Yu Yang 	token->filePosition = getInputFilePosition ();
478ebbfee1eSDavid Yu Yang 
479ebbfee1eSDavid Yu Yang 	if (inclWhitespaces && n > 1 && c != '\r' && c != '\n')
480ebbfee1eSDavid Yu Yang 	{
481ebbfee1eSDavid Yu Yang 		ungetcToInputFile (c);
482ebbfee1eSDavid Yu Yang 		vStringPut (token->string, ' ');
483ebbfee1eSDavid Yu Yang 		token->type = TOKEN_WHITESPACE;
484ebbfee1eSDavid Yu Yang 		return;
485ebbfee1eSDavid Yu Yang 	}
486ebbfee1eSDavid Yu Yang 
487ebbfee1eSDavid Yu Yang 	switch (c)
488ebbfee1eSDavid Yu Yang 	{
489ebbfee1eSDavid Yu Yang 		case EOF:
490ebbfee1eSDavid Yu Yang 			token->type = TOKEN_EOF;
491ebbfee1eSDavid Yu Yang 			break;
492ebbfee1eSDavid Yu Yang 
493ebbfee1eSDavid Yu Yang 		case '\'':
494ebbfee1eSDavid Yu Yang 		case '"':
495ebbfee1eSDavid Yu Yang 		{
496ebbfee1eSDavid Yu Yang 			int d = getcFromInputFile ();
497ebbfee1eSDavid Yu Yang 			token->type = TOKEN_STRING;
498ebbfee1eSDavid Yu Yang 			vStringPut (token->string, c);
499ebbfee1eSDavid Yu Yang 			if (d != c)
500ebbfee1eSDavid Yu Yang 			{
501ebbfee1eSDavid Yu Yang 				ungetcToInputFile (d);
502ebbfee1eSDavid Yu Yang 				readString (token->string, c);
503ebbfee1eSDavid Yu Yang 			}
504ebbfee1eSDavid Yu Yang 			else if ((d = getcFromInputFile ()) == c)
505ebbfee1eSDavid Yu Yang 				readTripleString (token->string, c);
506ebbfee1eSDavid Yu Yang 			else /* empty string */
507ebbfee1eSDavid Yu Yang 				ungetcToInputFile (d);
508ebbfee1eSDavid Yu Yang 			vStringPut (token->string, c);
509ebbfee1eSDavid Yu Yang 			token->lineNumber = getInputLineNumber ();
510ebbfee1eSDavid Yu Yang 			token->filePosition = getInputFilePosition ();
511ebbfee1eSDavid Yu Yang 			break;
512ebbfee1eSDavid Yu Yang 		}
513ebbfee1eSDavid Yu Yang 
514ebbfee1eSDavid Yu Yang 		case '=':
515ebbfee1eSDavid Yu Yang 		{
516ebbfee1eSDavid Yu Yang 			int d = getcFromInputFile ();
517ebbfee1eSDavid Yu Yang 			vStringPut (token->string, c);
518ebbfee1eSDavid Yu Yang 			if (d == c)
519ebbfee1eSDavid Yu Yang 			{
520ebbfee1eSDavid Yu Yang 				vStringPut (token->string, d);
521ebbfee1eSDavid Yu Yang 				token->type = TOKEN_OPERATOR;
522ebbfee1eSDavid Yu Yang 			}
523ebbfee1eSDavid Yu Yang 			else
524ebbfee1eSDavid Yu Yang 			{
525ebbfee1eSDavid Yu Yang 				ungetcToInputFile (d);
526ebbfee1eSDavid Yu Yang 				token->type = c;
527ebbfee1eSDavid Yu Yang 			}
528ebbfee1eSDavid Yu Yang 			break;
529ebbfee1eSDavid Yu Yang 		}
530ebbfee1eSDavid Yu Yang 
531ebbfee1eSDavid Yu Yang 		case '-':
532ebbfee1eSDavid Yu Yang 		{
533ebbfee1eSDavid Yu Yang 			int d = getcFromInputFile ();
534ebbfee1eSDavid Yu Yang 			if (d == '>')
535ebbfee1eSDavid Yu Yang 			{
536ebbfee1eSDavid Yu Yang 				vStringPut (token->string, c);
537ebbfee1eSDavid Yu Yang 				vStringPut (token->string, d);
538ebbfee1eSDavid Yu Yang 				token->type = TOKEN_ARROW;
539ebbfee1eSDavid Yu Yang 				break;
540ebbfee1eSDavid Yu Yang 			}
541ebbfee1eSDavid Yu Yang 			ungetcToInputFile (d);
542ebbfee1eSDavid Yu Yang 			/* fall through */
543ebbfee1eSDavid Yu Yang 		}
544ebbfee1eSDavid Yu Yang 		case '+':
545ebbfee1eSDavid Yu Yang 		case '*':
546ebbfee1eSDavid Yu Yang 		case '%':
547ebbfee1eSDavid Yu Yang 		case '<':
548ebbfee1eSDavid Yu Yang 		case '>':
549ebbfee1eSDavid Yu Yang 		case '/':
550ebbfee1eSDavid Yu Yang 		{
551ebbfee1eSDavid Yu Yang 			int d = getcFromInputFile ();
552ebbfee1eSDavid Yu Yang 			vStringPut (token->string, c);
553ebbfee1eSDavid Yu Yang 			if (d != '=')
554ebbfee1eSDavid Yu Yang 			{
555ebbfee1eSDavid Yu Yang 				ungetcToInputFile (d);
556ebbfee1eSDavid Yu Yang 				token->type = c;
557ebbfee1eSDavid Yu Yang 			}
558ebbfee1eSDavid Yu Yang 			else
559ebbfee1eSDavid Yu Yang 			{
560ebbfee1eSDavid Yu Yang 				vStringPut (token->string, d);
561ebbfee1eSDavid Yu Yang 				token->type = TOKEN_OPERATOR;
562ebbfee1eSDavid Yu Yang 			}
563ebbfee1eSDavid Yu Yang 			break;
564ebbfee1eSDavid Yu Yang 		}
565ebbfee1eSDavid Yu Yang 
566ebbfee1eSDavid Yu Yang 		/* eats newline to implement line continuation  */
567ebbfee1eSDavid Yu Yang 		case '\\':
568ebbfee1eSDavid Yu Yang 		{
569ebbfee1eSDavid Yu Yang 			int d = getcFromInputFile ();
570ebbfee1eSDavid Yu Yang 			if (d == '\r')
571ebbfee1eSDavid Yu Yang 				d = getcFromInputFile ();
572ebbfee1eSDavid Yu Yang 			if (d != '\n')
573ebbfee1eSDavid Yu Yang 				ungetcToInputFile (d);
574ebbfee1eSDavid Yu Yang 			goto getNextChar;
575ebbfee1eSDavid Yu Yang 		}
576ebbfee1eSDavid Yu Yang 
577ebbfee1eSDavid Yu Yang 		case '#': /* comment */
578ebbfee1eSDavid Yu Yang 		case '\r': /* newlines for indent */
579ebbfee1eSDavid Yu Yang 		case '\n':
580ebbfee1eSDavid Yu Yang 		{
581ebbfee1eSDavid Yu Yang 			int indent = 0;
582ebbfee1eSDavid Yu Yang 			do
583ebbfee1eSDavid Yu Yang 			{
584ebbfee1eSDavid Yu Yang 				if (c == '#')
585ebbfee1eSDavid Yu Yang 				{
586ebbfee1eSDavid Yu Yang 					do
587ebbfee1eSDavid Yu Yang 						c = getcFromInputFile ();
588ebbfee1eSDavid Yu Yang 					while (c != EOF && c != '\r' && c != '\n');
589ebbfee1eSDavid Yu Yang 				}
590ebbfee1eSDavid Yu Yang 				if (c == '\r')
591ebbfee1eSDavid Yu Yang 				{
592ebbfee1eSDavid Yu Yang 					int d = getcFromInputFile ();
593ebbfee1eSDavid Yu Yang 					if (d != '\n')
594ebbfee1eSDavid Yu Yang 						ungetcToInputFile (d);
595ebbfee1eSDavid Yu Yang 				}
596ebbfee1eSDavid Yu Yang 				indent = 0;
597ebbfee1eSDavid Yu Yang 				while ((c = getcFromInputFile ()) == ' ' || c == '\t' || c == '\f')
598ebbfee1eSDavid Yu Yang 				{
599ebbfee1eSDavid Yu Yang 					if (c == '\t')
600ebbfee1eSDavid Yu Yang 						indent += 8 - (indent % 8);
601ebbfee1eSDavid Yu Yang 					else if (c == '\f') /* yeah, it's weird */
602ebbfee1eSDavid Yu Yang 						indent = 0;
603ebbfee1eSDavid Yu Yang 					else
604ebbfee1eSDavid Yu Yang 						indent++;
605ebbfee1eSDavid Yu Yang 				}
606ebbfee1eSDavid Yu Yang 			} /* skip completely empty lines, so retry */
607ebbfee1eSDavid Yu Yang 			while (c == '\r' || c == '\n' || c == '#');
608ebbfee1eSDavid Yu Yang 			ungetcToInputFile (c);
609ebbfee1eSDavid Yu Yang 			if (TokenContinuationDepth > 0)
610ebbfee1eSDavid Yu Yang 			{
611ebbfee1eSDavid Yu Yang 				if (inclWhitespaces)
612ebbfee1eSDavid Yu Yang 				{
613ebbfee1eSDavid Yu Yang 					vStringPut (token->string, ' ');
614ebbfee1eSDavid Yu Yang 					token->type = TOKEN_WHITESPACE;
615ebbfee1eSDavid Yu Yang 				}
616ebbfee1eSDavid Yu Yang 				else
617ebbfee1eSDavid Yu Yang 					goto getNextChar;
618ebbfee1eSDavid Yu Yang 			}
619ebbfee1eSDavid Yu Yang 			else
620ebbfee1eSDavid Yu Yang 			{
621ebbfee1eSDavid Yu Yang 				token->type = TOKEN_INDENT;
622ebbfee1eSDavid Yu Yang 				token->indent = indent;
623ebbfee1eSDavid Yu Yang 			}
624ebbfee1eSDavid Yu Yang 			break;
625ebbfee1eSDavid Yu Yang 		}
626ebbfee1eSDavid Yu Yang 
627ebbfee1eSDavid Yu Yang 		default:
628ebbfee1eSDavid Yu Yang 			if (! isIdentifierChar (c))
629ebbfee1eSDavid Yu Yang 			{
630ebbfee1eSDavid Yu Yang 				vStringPut (token->string, c);
631ebbfee1eSDavid Yu Yang 				token->type = c;
632ebbfee1eSDavid Yu Yang 			}
633ebbfee1eSDavid Yu Yang 			else
634ebbfee1eSDavid Yu Yang 			{
635ebbfee1eSDavid Yu Yang 				/* FIXME: handle U, B, R and F string prefixes? */
636ebbfee1eSDavid Yu Yang 				readIdentifier (token->string, c);
637ebbfee1eSDavid Yu Yang 				token->keyword = lookupKeyword (vStringValue (token->string), Lang_gdscript);
638ebbfee1eSDavid Yu Yang 				if (token->keyword == KEYWORD_NONE)
639ebbfee1eSDavid Yu Yang 					token->type = TOKEN_IDENTIFIER;
640ebbfee1eSDavid Yu Yang 				else
641ebbfee1eSDavid Yu Yang 					token->type = TOKEN_KEYWORD;
642ebbfee1eSDavid Yu Yang 			}
643ebbfee1eSDavid Yu Yang 			break;
644ebbfee1eSDavid Yu Yang 	}
645ebbfee1eSDavid Yu Yang 
646ebbfee1eSDavid Yu Yang 	// handle implicit continuation lines not to emit INDENT inside brackets
647ebbfee1eSDavid Yu Yang 	if (token->type == '(' ||
648ebbfee1eSDavid Yu Yang 		token->type == '{' ||
649ebbfee1eSDavid Yu Yang 		token->type == '[')
650ebbfee1eSDavid Yu Yang 	{
651ebbfee1eSDavid Yu Yang 		TokenContinuationDepth ++;
652ebbfee1eSDavid Yu Yang 	}
653ebbfee1eSDavid Yu Yang 	else if (TokenContinuationDepth > 0 &&
654ebbfee1eSDavid Yu Yang 			 (token->type == ')' ||
655ebbfee1eSDavid Yu Yang 			  token->type == '}' ||
656ebbfee1eSDavid Yu Yang 			  token->type == ']'))
657ebbfee1eSDavid Yu Yang 	{
658ebbfee1eSDavid Yu Yang 		TokenContinuationDepth --;
659ebbfee1eSDavid Yu Yang 	}
660ebbfee1eSDavid Yu Yang }
661ebbfee1eSDavid Yu Yang 
readToken(tokenInfo * const token)662ebbfee1eSDavid Yu Yang static void readToken (tokenInfo *const token)
663ebbfee1eSDavid Yu Yang {
664ebbfee1eSDavid Yu Yang 	readTokenFull (token, false);
665ebbfee1eSDavid Yu Yang }
666ebbfee1eSDavid Yu Yang 
667ebbfee1eSDavid Yu Yang /*================================= parsing =================================*/
668ebbfee1eSDavid Yu Yang 
669ebbfee1eSDavid Yu Yang 
reprCat(vString * const repr,const tokenInfo * const token)670ebbfee1eSDavid Yu Yang static void reprCat (vString *const repr, const tokenInfo *const token)
671ebbfee1eSDavid Yu Yang {
672ebbfee1eSDavid Yu Yang 	if (token->type != TOKEN_INDENT &&
673ebbfee1eSDavid Yu Yang 		token->type != TOKEN_WHITESPACE)
674ebbfee1eSDavid Yu Yang 	{
675ebbfee1eSDavid Yu Yang 		vStringCat (repr, token->string);
676ebbfee1eSDavid Yu Yang 	}
677ebbfee1eSDavid Yu Yang 	else if (vStringLength (repr) > 0 && vStringLast (repr) != ' ')
678ebbfee1eSDavid Yu Yang 	{
679ebbfee1eSDavid Yu Yang 		vStringPut (repr, ' ');
680ebbfee1eSDavid Yu Yang 	}
681ebbfee1eSDavid Yu Yang }
682ebbfee1eSDavid Yu Yang 
skipOverPair(tokenInfo * const token,int tOpen,int tClose,vString * const repr,bool reprOuterPair)683ebbfee1eSDavid Yu Yang static bool skipOverPair (tokenInfo *const token, int tOpen, int tClose,
684ebbfee1eSDavid Yu Yang 						  vString *const repr, bool reprOuterPair)
685ebbfee1eSDavid Yu Yang {
686ebbfee1eSDavid Yu Yang 	if (token->type == tOpen)
687ebbfee1eSDavid Yu Yang 	{
688ebbfee1eSDavid Yu Yang 		int depth = 1;
689ebbfee1eSDavid Yu Yang 
690ebbfee1eSDavid Yu Yang 		if (repr && reprOuterPair)
691ebbfee1eSDavid Yu Yang 			reprCat (repr, token);
692ebbfee1eSDavid Yu Yang 		do
693ebbfee1eSDavid Yu Yang 		{
694ebbfee1eSDavid Yu Yang 			readTokenFull (token, true);
695ebbfee1eSDavid Yu Yang 			if (repr && (reprOuterPair || token->type != tClose || depth > 1))
696ebbfee1eSDavid Yu Yang 			{
697ebbfee1eSDavid Yu Yang 				reprCat (repr, token);
698ebbfee1eSDavid Yu Yang 			}
699ebbfee1eSDavid Yu Yang 			if (token->type == tOpen)
700ebbfee1eSDavid Yu Yang 				depth ++;
701ebbfee1eSDavid Yu Yang 			else if (token->type == tClose)
702ebbfee1eSDavid Yu Yang 				depth --;
703ebbfee1eSDavid Yu Yang 		}
704ebbfee1eSDavid Yu Yang 		while (token->type != TOKEN_EOF && depth > 0);
705ebbfee1eSDavid Yu Yang 	}
706ebbfee1eSDavid Yu Yang 
707ebbfee1eSDavid Yu Yang 	return token->type == tClose;
708ebbfee1eSDavid Yu Yang }
709ebbfee1eSDavid Yu Yang 
readQualifiedName(tokenInfo * const nameToken)710ebbfee1eSDavid Yu Yang static void readQualifiedName (tokenInfo *const nameToken)
711ebbfee1eSDavid Yu Yang {
712ebbfee1eSDavid Yu Yang 	readToken (nameToken);
713ebbfee1eSDavid Yu Yang 
714ebbfee1eSDavid Yu Yang 	if (nameToken->type == TOKEN_IDENTIFIER ||
715ebbfee1eSDavid Yu Yang 		nameToken->type == '.')
716ebbfee1eSDavid Yu Yang 	{
717ebbfee1eSDavid Yu Yang 		vString *qualifiedName = vStringNew ();
718ebbfee1eSDavid Yu Yang 		tokenInfo *token = newToken ();
719ebbfee1eSDavid Yu Yang 
720ebbfee1eSDavid Yu Yang 		while (nameToken->type == TOKEN_IDENTIFIER ||
721ebbfee1eSDavid Yu Yang 			   nameToken->type == '.')
722ebbfee1eSDavid Yu Yang 		{
723ebbfee1eSDavid Yu Yang 			vStringCat (qualifiedName, nameToken->string);
724ebbfee1eSDavid Yu Yang 			copyToken (token, nameToken);
725ebbfee1eSDavid Yu Yang 
726ebbfee1eSDavid Yu Yang 			readToken (nameToken);
727ebbfee1eSDavid Yu Yang 		}
728ebbfee1eSDavid Yu Yang 		/* put the last, non-matching, token back */
729ebbfee1eSDavid Yu Yang 		ungetToken (nameToken);
730ebbfee1eSDavid Yu Yang 
731ebbfee1eSDavid Yu Yang 		copyToken (nameToken, token);
732ebbfee1eSDavid Yu Yang 		nameToken->type = TOKEN_IDENTIFIER;
733ebbfee1eSDavid Yu Yang 		vStringCopy (nameToken->string, qualifiedName);
734ebbfee1eSDavid Yu Yang 
735ebbfee1eSDavid Yu Yang 		deleteToken (token);
736ebbfee1eSDavid Yu Yang 		vStringDelete (qualifiedName);
737ebbfee1eSDavid Yu Yang 	}
738ebbfee1eSDavid Yu Yang }
739ebbfee1eSDavid Yu Yang 
parseParamTypeAnnotation(tokenInfo * const token,vString * arglist)740ebbfee1eSDavid Yu Yang static vString *parseParamTypeAnnotation (tokenInfo *const token,
741ebbfee1eSDavid Yu Yang 										  vString *arglist)
742ebbfee1eSDavid Yu Yang {
743ebbfee1eSDavid Yu Yang 	readToken (token);
744ebbfee1eSDavid Yu Yang 	if (token->type != ':')
745ebbfee1eSDavid Yu Yang 	{
746ebbfee1eSDavid Yu Yang 		ungetToken (token);
747ebbfee1eSDavid Yu Yang 		return NULL;
748ebbfee1eSDavid Yu Yang 	}
749ebbfee1eSDavid Yu Yang 
750ebbfee1eSDavid Yu Yang 	reprCat (arglist, token);
751ebbfee1eSDavid Yu Yang 	int depth = 0;
752ebbfee1eSDavid Yu Yang 	vString *t = vStringNew ();
753ebbfee1eSDavid Yu Yang 	while (true)
754ebbfee1eSDavid Yu Yang 	{
755ebbfee1eSDavid Yu Yang 		readTokenFull (token, true);
756ebbfee1eSDavid Yu Yang 		if (token->type == TOKEN_WHITESPACE)
757ebbfee1eSDavid Yu Yang 		{
758ebbfee1eSDavid Yu Yang 			reprCat (arglist, token);
759ebbfee1eSDavid Yu Yang 			continue;
760ebbfee1eSDavid Yu Yang 		}
761ebbfee1eSDavid Yu Yang 		else if (token->type == TOKEN_EOF)
762ebbfee1eSDavid Yu Yang 			break;
763ebbfee1eSDavid Yu Yang 
764ebbfee1eSDavid Yu Yang 		if (token->type == '(' ||
765ebbfee1eSDavid Yu Yang 			token->type == '[' ||
766ebbfee1eSDavid Yu Yang 			token->type == '{')
767ebbfee1eSDavid Yu Yang 			depth ++;
768ebbfee1eSDavid Yu Yang 		else if (token->type == ')' ||
769ebbfee1eSDavid Yu Yang 				 token->type == ']' ||
770ebbfee1eSDavid Yu Yang 				 token->type == '}')
771ebbfee1eSDavid Yu Yang 			depth --;
772ebbfee1eSDavid Yu Yang 
773ebbfee1eSDavid Yu Yang 		if (depth < 0
774ebbfee1eSDavid Yu Yang 			|| (depth == 0 && (token->type == '='
775ebbfee1eSDavid Yu Yang 							   || token->type == ',')))
776ebbfee1eSDavid Yu Yang 		{
777ebbfee1eSDavid Yu Yang 			ungetToken (token);
778ebbfee1eSDavid Yu Yang 			return t;
779ebbfee1eSDavid Yu Yang 		}
780ebbfee1eSDavid Yu Yang 		reprCat (arglist, token);
781ebbfee1eSDavid Yu Yang 		reprCat (t, token);
782ebbfee1eSDavid Yu Yang 	}
783ebbfee1eSDavid Yu Yang 	vStringDelete (t);
784ebbfee1eSDavid Yu Yang 	return NULL;
785ebbfee1eSDavid Yu Yang }
786ebbfee1eSDavid Yu Yang 
parseReturnTypeAnnotation(tokenInfo * const token)787ebbfee1eSDavid Yu Yang static vString *parseReturnTypeAnnotation (tokenInfo *const token)
788ebbfee1eSDavid Yu Yang {
789ebbfee1eSDavid Yu Yang 	readToken (token);
790ebbfee1eSDavid Yu Yang 	if (token->type != TOKEN_ARROW)
791ebbfee1eSDavid Yu Yang 	{
792ebbfee1eSDavid Yu Yang 		return NULL;
793ebbfee1eSDavid Yu Yang 	}
794ebbfee1eSDavid Yu Yang 
795ebbfee1eSDavid Yu Yang 	int depth = 0;
796ebbfee1eSDavid Yu Yang 	vString *t = vStringNew ();
797ebbfee1eSDavid Yu Yang 	while (true)
798ebbfee1eSDavid Yu Yang 	{
799ebbfee1eSDavid Yu Yang 		readToken (token);
800ebbfee1eSDavid Yu Yang 		if (token->type == TOKEN_EOF)
801ebbfee1eSDavid Yu Yang 			break;
802ebbfee1eSDavid Yu Yang 
803ebbfee1eSDavid Yu Yang 		if (token->type == '(' ||
804ebbfee1eSDavid Yu Yang 			token->type == '[' ||
805ebbfee1eSDavid Yu Yang 			token->type == '{')
806ebbfee1eSDavid Yu Yang 			depth ++;
807ebbfee1eSDavid Yu Yang 		else if (token->type == ')' ||
808ebbfee1eSDavid Yu Yang 				 token->type == ']' ||
809ebbfee1eSDavid Yu Yang 				 token->type == '}')
810ebbfee1eSDavid Yu Yang 			depth --;
811ebbfee1eSDavid Yu Yang 		if (depth == 0 && token->type == ':')
812ebbfee1eSDavid Yu Yang 		{
813ebbfee1eSDavid Yu Yang 			ungetToken (token);
814ebbfee1eSDavid Yu Yang 			return t;
815ebbfee1eSDavid Yu Yang 		}
816ebbfee1eSDavid Yu Yang 		else
817ebbfee1eSDavid Yu Yang 			reprCat (t, token);
818ebbfee1eSDavid Yu Yang 	}
819ebbfee1eSDavid Yu Yang 	vStringDelete (t);
820ebbfee1eSDavid Yu Yang 	return NULL;
821ebbfee1eSDavid Yu Yang }
822ebbfee1eSDavid Yu Yang 
parseClassOrDef(tokenInfo * const token,const stringList * const decorators,gdscriptKind kind)823ebbfee1eSDavid Yu Yang static bool parseClassOrDef (tokenInfo *const token,
824ebbfee1eSDavid Yu Yang 							 const stringList *const decorators,
825*385c6b89SMasatake YAMATO 							 gdscriptKind kind)
826ebbfee1eSDavid Yu Yang {
827ebbfee1eSDavid Yu Yang 	vString *arglist = NULL;
828ebbfee1eSDavid Yu Yang 	tokenInfo *name = NULL;
829ebbfee1eSDavid Yu Yang 	tokenInfo *parameterTokens[16] = { NULL };
830ebbfee1eSDavid Yu Yang 	vString   *parameterTypes [ARRAY_SIZE(parameterTokens)] = { NULL };
831ebbfee1eSDavid Yu Yang 	unsigned int parameterCount = 0;
832ebbfee1eSDavid Yu Yang 	NestingLevel *lv;
833ebbfee1eSDavid Yu Yang 	int corkIndex;
834ebbfee1eSDavid Yu Yang 
835ebbfee1eSDavid Yu Yang 	readToken (token);
836ebbfee1eSDavid Yu Yang 	if (token->type != TOKEN_IDENTIFIER)
837ebbfee1eSDavid Yu Yang 		return false;
838ebbfee1eSDavid Yu Yang 
839ebbfee1eSDavid Yu Yang 	name = newToken ();
840ebbfee1eSDavid Yu Yang 	copyToken (name, token);
841ebbfee1eSDavid Yu Yang 
842ebbfee1eSDavid Yu Yang 	readToken (token);
843ebbfee1eSDavid Yu Yang 	/* collect parameters or inheritance */
844ebbfee1eSDavid Yu Yang 	if (token->type == '(')
845ebbfee1eSDavid Yu Yang 	{
846ebbfee1eSDavid Yu Yang 		int prevTokenType = token->type;
847ebbfee1eSDavid Yu Yang 		int depth = 1;
848ebbfee1eSDavid Yu Yang 
849ebbfee1eSDavid Yu Yang 		arglist = vStringNew ();
850ebbfee1eSDavid Yu Yang 		if (kind != K_CLASS)
851ebbfee1eSDavid Yu Yang 			reprCat (arglist, token);
852ebbfee1eSDavid Yu Yang 
853ebbfee1eSDavid Yu Yang 		do
854ebbfee1eSDavid Yu Yang 		{
855ebbfee1eSDavid Yu Yang 			if (token->type != TOKEN_WHITESPACE &&
856ebbfee1eSDavid Yu Yang 				token->type != '*')
857ebbfee1eSDavid Yu Yang 			{
858ebbfee1eSDavid Yu Yang 				prevTokenType = token->type;
859ebbfee1eSDavid Yu Yang 			}
860ebbfee1eSDavid Yu Yang 
861ebbfee1eSDavid Yu Yang 			readTokenFull (token, true);
862ebbfee1eSDavid Yu Yang 			if (kind != K_CLASS || token->type != ')' || depth > 1)
863ebbfee1eSDavid Yu Yang 				reprCat (arglist, token);
864ebbfee1eSDavid Yu Yang 
865ebbfee1eSDavid Yu Yang 			if (token->type == '(' ||
866ebbfee1eSDavid Yu Yang 				token->type == '[' ||
867ebbfee1eSDavid Yu Yang 				token->type == '{')
868ebbfee1eSDavid Yu Yang 				depth ++;
869ebbfee1eSDavid Yu Yang 			else if (token->type == ')' ||
870ebbfee1eSDavid Yu Yang 					 token->type == ']' ||
871ebbfee1eSDavid Yu Yang 					 token->type == '}')
872ebbfee1eSDavid Yu Yang 				depth --;
873ebbfee1eSDavid Yu Yang 			else if (kind != K_CLASS && depth == 1 &&
874ebbfee1eSDavid Yu Yang 					 token->type == TOKEN_IDENTIFIER &&
875ebbfee1eSDavid Yu Yang 					 (prevTokenType == '(' || prevTokenType == ',') &&
876ebbfee1eSDavid Yu Yang 					 parameterCount < ARRAY_SIZE (parameterTokens) &&
877ebbfee1eSDavid Yu Yang 					 GDScriptKinds[K_PARAMETER].enabled)
878ebbfee1eSDavid Yu Yang 			{
879ebbfee1eSDavid Yu Yang 				tokenInfo *parameterName = newToken ();
880ebbfee1eSDavid Yu Yang 
881ebbfee1eSDavid Yu Yang 				copyToken (parameterName, token);
882ebbfee1eSDavid Yu Yang 				parameterTokens[parameterCount] = parameterName;
883ebbfee1eSDavid Yu Yang 				parameterTypes [parameterCount++] = parseParamTypeAnnotation (token, arglist);
884ebbfee1eSDavid Yu Yang 			}
885ebbfee1eSDavid Yu Yang 		}
886ebbfee1eSDavid Yu Yang 		while (token->type != TOKEN_EOF && depth > 0);
887ebbfee1eSDavid Yu Yang 	}
888ebbfee1eSDavid Yu Yang 	else if (token->type == TOKEN_KEYWORD && token->keyword == KEYWORD_extends)
889ebbfee1eSDavid Yu Yang 	{
890ebbfee1eSDavid Yu Yang 		readToken (token);
891ebbfee1eSDavid Yu Yang 		if (token->type == TOKEN_IDENTIFIER)
892ebbfee1eSDavid Yu Yang 		{
893ebbfee1eSDavid Yu Yang 			makeSimpleGDScriptRefTag (token, K_CLASS, GDSCRIPT_CLASS_EXTENDED, XTAG_UNKNOWN);
894ebbfee1eSDavid Yu Yang 			arglist = vStringNewCopy (token->string);
895ebbfee1eSDavid Yu Yang 		}
896ebbfee1eSDavid Yu Yang 	}
897ebbfee1eSDavid Yu Yang 	else if (kind == K_SIGNAL)
898ebbfee1eSDavid Yu Yang 	{
899ebbfee1eSDavid Yu Yang 		/* signal can be defined with no parameter list. */
900ebbfee1eSDavid Yu Yang 		ungetToken (token);
901ebbfee1eSDavid Yu Yang 	}
902ebbfee1eSDavid Yu Yang 
903ebbfee1eSDavid Yu Yang 	if (kind == K_CLASS)
904ebbfee1eSDavid Yu Yang 		corkIndex = makeClassTag (name, arglist);
905ebbfee1eSDavid Yu Yang 	else
906ebbfee1eSDavid Yu Yang 		corkIndex = makeFunctionTag (name, kind, arglist, decorators);
907ebbfee1eSDavid Yu Yang 
908ebbfee1eSDavid Yu Yang 	lv = nestingLevelsPush (GDScriptNestingLevels, corkIndex);
909ebbfee1eSDavid Yu Yang 	GDS_NL (lv)->indentation = token->indent;
910ebbfee1eSDavid Yu Yang 
911ebbfee1eSDavid Yu Yang 	deleteToken (name);
912ebbfee1eSDavid Yu Yang 	vStringDelete (arglist);
913ebbfee1eSDavid Yu Yang 
914ebbfee1eSDavid Yu Yang 	if (parameterCount > 0)
915ebbfee1eSDavid Yu Yang 	{
916ebbfee1eSDavid Yu Yang 		unsigned int i;
917ebbfee1eSDavid Yu Yang 
918ebbfee1eSDavid Yu Yang 		for (i = 0; i < parameterCount; i++)
919ebbfee1eSDavid Yu Yang 		{
920ebbfee1eSDavid Yu Yang 			int paramCorkIndex = makeSimpleGDScriptTag (parameterTokens[i], K_PARAMETER);
921ebbfee1eSDavid Yu Yang 			deleteToken (parameterTokens[i]);
922ebbfee1eSDavid Yu Yang 			tagEntryInfo *e = getEntryInCorkQueue (paramCorkIndex);
923ebbfee1eSDavid Yu Yang 			if (e && parameterTypes[i])
924ebbfee1eSDavid Yu Yang 			{
925ebbfee1eSDavid Yu Yang 				e->extensionFields.typeRef [0] = eStrdup ("typename");
926ebbfee1eSDavid Yu Yang 				e->extensionFields.typeRef [1] = vStringDeleteUnwrap (parameterTypes[i]);
927ebbfee1eSDavid Yu Yang 				parameterTypes[i] = NULL;
928ebbfee1eSDavid Yu Yang 			}
929ebbfee1eSDavid Yu Yang 			vStringDelete (parameterTypes[i]); /* NULL is acceptable. */
930ebbfee1eSDavid Yu Yang 		}
931ebbfee1eSDavid Yu Yang 	}
932ebbfee1eSDavid Yu Yang 
933ebbfee1eSDavid Yu Yang 	tagEntryInfo *e;
934ebbfee1eSDavid Yu Yang 	vString *t;
935ebbfee1eSDavid Yu Yang 	if (kind != K_CLASS
936ebbfee1eSDavid Yu Yang 		&& (e = getEntryInCorkQueue (corkIndex))
937ebbfee1eSDavid Yu Yang 		&& (t = parseReturnTypeAnnotation (token)))
938ebbfee1eSDavid Yu Yang 	{
939ebbfee1eSDavid Yu Yang 		e->extensionFields.typeRef [0] = eStrdup ("typename");
940ebbfee1eSDavid Yu Yang 		e->extensionFields.typeRef [1] = vStringDeleteUnwrap (t);
941ebbfee1eSDavid Yu Yang 	}
942ebbfee1eSDavid Yu Yang 
943ebbfee1eSDavid Yu Yang 	if (kind == K_SIGNAL)
944ebbfee1eSDavid Yu Yang 		nestingLevelsPop (GDScriptNestingLevels);
945ebbfee1eSDavid Yu Yang 
946ebbfee1eSDavid Yu Yang 	return true;
947ebbfee1eSDavid Yu Yang }
948ebbfee1eSDavid Yu Yang 
parseEnum(tokenInfo * const token)949ebbfee1eSDavid Yu Yang static bool parseEnum (tokenInfo *const token)
950ebbfee1eSDavid Yu Yang {
951ebbfee1eSDavid Yu Yang 	int corkIndex;
952ebbfee1eSDavid Yu Yang 
953ebbfee1eSDavid Yu Yang 	readToken (token);
954ebbfee1eSDavid Yu Yang 
955ebbfee1eSDavid Yu Yang 	if (token->type == '{')
956ebbfee1eSDavid Yu Yang 	{
957ebbfee1eSDavid Yu Yang 		tokenInfo *name = newToken ();
958ebbfee1eSDavid Yu Yang 		copyToken (name, token);
959ebbfee1eSDavid Yu Yang 		vStringClear (name->string);
960ebbfee1eSDavid Yu Yang 		anonGenerate (name->string, "anon_enum_", K_ENUM);
961ebbfee1eSDavid Yu Yang 		name->type = TOKEN_IDENTIFIER;
962ebbfee1eSDavid Yu Yang 		corkIndex = makeSimpleGDScriptTag (name, K_ENUM);
963ebbfee1eSDavid Yu Yang 		deleteToken (name);
964ebbfee1eSDavid Yu Yang 		tagEntryInfo *e = getEntryInCorkQueue (corkIndex);
965ebbfee1eSDavid Yu Yang 		if (e)
966ebbfee1eSDavid Yu Yang 			markTagExtraBit (e, XTAG_ANONYMOUS);
967ebbfee1eSDavid Yu Yang 	}
968ebbfee1eSDavid Yu Yang 	else if (token->type == TOKEN_IDENTIFIER)
969ebbfee1eSDavid Yu Yang 	{
970ebbfee1eSDavid Yu Yang 		corkIndex = makeSimpleGDScriptTag(token, K_ENUM);
971ebbfee1eSDavid Yu Yang 		readToken (token);
972ebbfee1eSDavid Yu Yang 	}
973ebbfee1eSDavid Yu Yang 	else
974ebbfee1eSDavid Yu Yang 		return false;
975ebbfee1eSDavid Yu Yang 
976ebbfee1eSDavid Yu Yang 	if (token->type != '{')
977ebbfee1eSDavid Yu Yang 		return false;
978ebbfee1eSDavid Yu Yang 
979ebbfee1eSDavid Yu Yang 	readToken (token);
980ebbfee1eSDavid Yu Yang 	nestingLevelsPush (GDScriptNestingLevels, corkIndex);
981ebbfee1eSDavid Yu Yang 
982ebbfee1eSDavid Yu Yang 	while (token->type != '}' && token->type != TOKEN_EOF)
983ebbfee1eSDavid Yu Yang 	{
984ebbfee1eSDavid Yu Yang 		if (token->type == TOKEN_IDENTIFIER)
985ebbfee1eSDavid Yu Yang 			makeSimpleGDScriptTag(token, K_ENUMERATOR);
986ebbfee1eSDavid Yu Yang 		else if (token->type == '=')
987ebbfee1eSDavid Yu Yang 		{
988ebbfee1eSDavid Yu Yang 			/* Skip the right value. */
989ebbfee1eSDavid Yu Yang 			do
990ebbfee1eSDavid Yu Yang 				readToken (token);
991ebbfee1eSDavid Yu Yang 			while (token->type != ','
992ebbfee1eSDavid Yu Yang 				   && token->type != '}'
993ebbfee1eSDavid Yu Yang 				   && token->type != TOKEN_EOF);
994ebbfee1eSDavid Yu Yang 			if (token->type != ',')
995ebbfee1eSDavid Yu Yang 				continue;
996ebbfee1eSDavid Yu Yang 		}
997ebbfee1eSDavid Yu Yang 		readToken (token);
998ebbfee1eSDavid Yu Yang 	}
999ebbfee1eSDavid Yu Yang 
1000ebbfee1eSDavid Yu Yang 	tagEntryInfo *e;
1001ebbfee1eSDavid Yu Yang 	vString *t;
1002ebbfee1eSDavid Yu Yang 	if ((e = getEntryInCorkQueue (corkIndex))
1003ebbfee1eSDavid Yu Yang 		&& (t = parseReturnTypeAnnotation (token)))
1004ebbfee1eSDavid Yu Yang 	{
1005ebbfee1eSDavid Yu Yang 		e->extensionFields.typeRef [0] = eStrdup ("typename");
1006ebbfee1eSDavid Yu Yang 		e->extensionFields.typeRef [1] = vStringDeleteUnwrap (t);
1007ebbfee1eSDavid Yu Yang 	}
1008ebbfee1eSDavid Yu Yang 
1009ebbfee1eSDavid Yu Yang 	nestingLevelsPop (GDScriptNestingLevels);
1010ebbfee1eSDavid Yu Yang 	return true;
1011ebbfee1eSDavid Yu Yang }
1012ebbfee1eSDavid Yu Yang 
parseClassName(tokenInfo * const token)1013ebbfee1eSDavid Yu Yang static bool parseClassName (tokenInfo *const token)
1014ebbfee1eSDavid Yu Yang {
1015ebbfee1eSDavid Yu Yang 	readToken (token);
1016ebbfee1eSDavid Yu Yang 	if (token->type == TOKEN_IDENTIFIER)
1017ebbfee1eSDavid Yu Yang 	{
1018ebbfee1eSDavid Yu Yang 		/* A class name is explicitly given with "class_name" keyword.
1019ebbfee1eSDavid Yu Yang 		 * Let's overwrite the anonymous tag for the class
1020ebbfee1eSDavid Yu Yang 		 */
1021ebbfee1eSDavid Yu Yang 		NestingLevel *nl = nestingLevelsGetNthFromRoot (GDScriptNestingLevels, 0);
1022ebbfee1eSDavid Yu Yang 		tagEntryInfo *klass = nl? getEntryInCorkQueue (nl->corkIndex): NULL;
1023ebbfee1eSDavid Yu Yang 
1024ebbfee1eSDavid Yu Yang 		tagEntryInfo e;
1025ebbfee1eSDavid Yu Yang 		char *name = vStringStrdup (token->string);
1026ebbfee1eSDavid Yu Yang 		initTagEntry (&e, klass? "UNUSED": name, K_CLASS);
1027ebbfee1eSDavid Yu Yang 
1028ebbfee1eSDavid Yu Yang 		if (klass)
1029ebbfee1eSDavid Yu Yang 		{
1030ebbfee1eSDavid Yu Yang 			eFree ((void *)klass->name);
1031ebbfee1eSDavid Yu Yang 			klass->name = name;
1032ebbfee1eSDavid Yu Yang 			name = NULL;
1033ebbfee1eSDavid Yu Yang 			unmarkTagExtraBit(klass, XTAG_ANONYMOUS);
1034ebbfee1eSDavid Yu Yang 
1035ebbfee1eSDavid Yu Yang 			/* Adjust the position. */
1036ebbfee1eSDavid Yu Yang 			setTagPositionFromTag (klass, &e);
1037ebbfee1eSDavid Yu Yang 		}
1038ebbfee1eSDavid Yu Yang 
1039ebbfee1eSDavid Yu Yang 		/* Extract B in class_name C extends B */
1040ebbfee1eSDavid Yu Yang 		readToken (token);
1041ebbfee1eSDavid Yu Yang 		if (token->type == TOKEN_KEYWORD
1042ebbfee1eSDavid Yu Yang 			&& token->keyword == KEYWORD_extends)
1043ebbfee1eSDavid Yu Yang 		{
1044ebbfee1eSDavid Yu Yang 			readToken (token);
1045ebbfee1eSDavid Yu Yang 			if (token->type == TOKEN_IDENTIFIER)
1046ebbfee1eSDavid Yu Yang 			{
1047ebbfee1eSDavid Yu Yang 				makeSimpleGDScriptRefTag (token, K_CLASS,
1048ebbfee1eSDavid Yu Yang 										  GDSCRIPT_CLASS_EXTENDED,
1049ebbfee1eSDavid Yu Yang 										  XTAG_UNKNOWN);
1050ebbfee1eSDavid Yu Yang 				if (klass)
1051ebbfee1eSDavid Yu Yang 				{
1052ebbfee1eSDavid Yu Yang 					if (klass->extensionFields.inheritance)
1053ebbfee1eSDavid Yu Yang 						eFree ((void *)klass->extensionFields.inheritance);
1054ebbfee1eSDavid Yu Yang 					klass->extensionFields.inheritance = vStringStrdup (token->string);
1055ebbfee1eSDavid Yu Yang 				}
1056ebbfee1eSDavid Yu Yang 				else
1057ebbfee1eSDavid Yu Yang 					e.extensionFields.inheritance = vStringValue(token->string);
1058ebbfee1eSDavid Yu Yang 			}
1059ebbfee1eSDavid Yu Yang 		}
1060ebbfee1eSDavid Yu Yang 
1061ebbfee1eSDavid Yu Yang 		if (!klass)
1062ebbfee1eSDavid Yu Yang 			makeTagEntry (&e);
1063ebbfee1eSDavid Yu Yang 
1064ebbfee1eSDavid Yu Yang 		if (name)
1065ebbfee1eSDavid Yu Yang 			eFree (name);
1066ebbfee1eSDavid Yu Yang 	}
1067ebbfee1eSDavid Yu Yang 
1068ebbfee1eSDavid Yu Yang 	while (token->type != TOKEN_EOF &&
1069ebbfee1eSDavid Yu Yang 		   token->type != ';' &&
1070ebbfee1eSDavid Yu Yang 		   token->type != TOKEN_INDENT)
1071ebbfee1eSDavid Yu Yang 		readToken (token);
1072ebbfee1eSDavid Yu Yang 
1073ebbfee1eSDavid Yu Yang 	return false;
1074ebbfee1eSDavid Yu Yang }
1075ebbfee1eSDavid Yu Yang 
parseExtends(tokenInfo * const token)1076ebbfee1eSDavid Yu Yang static bool parseExtends (tokenInfo *const token)
1077ebbfee1eSDavid Yu Yang {
1078ebbfee1eSDavid Yu Yang 	if (token->keyword == KEYWORD_extends)
1079ebbfee1eSDavid Yu Yang 	{
1080ebbfee1eSDavid Yu Yang 		readQualifiedName (token);
1081ebbfee1eSDavid Yu Yang 		if (token->type == TOKEN_IDENTIFIER)
1082ebbfee1eSDavid Yu Yang 		{
1083ebbfee1eSDavid Yu Yang 			makeSimpleGDScriptRefTag (token, K_CLASS, GDSCRIPT_CLASS_EXTENDED, XTAG_UNKNOWN);
1084ebbfee1eSDavid Yu Yang 			NestingLevel *nl = nestingLevelsGetCurrent (GDScriptNestingLevels);
1085ebbfee1eSDavid Yu Yang 			if (nl)
1086ebbfee1eSDavid Yu Yang 			{
1087ebbfee1eSDavid Yu Yang 				tagEntryInfo *klass = getEntryInCorkQueue (nl->corkIndex);
1088ebbfee1eSDavid Yu Yang 				if (klass)
1089ebbfee1eSDavid Yu Yang 				{
1090ebbfee1eSDavid Yu Yang 					if (klass->extensionFields.inheritance)
1091ebbfee1eSDavid Yu Yang 						eFree ((void *)klass->extensionFields.inheritance);
1092ebbfee1eSDavid Yu Yang 					klass->extensionFields.inheritance = vStringStrdup(token->string);
1093ebbfee1eSDavid Yu Yang 				}
1094ebbfee1eSDavid Yu Yang 			}
1095ebbfee1eSDavid Yu Yang 		}
1096ebbfee1eSDavid Yu Yang 	}
1097ebbfee1eSDavid Yu Yang 	readToken (token);
1098ebbfee1eSDavid Yu Yang 	return false;
1099ebbfee1eSDavid Yu Yang }
1100ebbfee1eSDavid Yu Yang 
1101ebbfee1eSDavid Yu Yang /* this only handles the most common cases, but an annotation can be any
1102ebbfee1eSDavid Yu Yang  * expression in theory.
1103ebbfee1eSDavid Yu Yang  * this function assumes there must be an annotation, and doesn't do any check
1104ebbfee1eSDavid Yu Yang  * on the token on which it is called: the caller should do that part. */
skipVariableTypeAnnotation(tokenInfo * const token,vString * const repr)1105ebbfee1eSDavid Yu Yang static bool skipVariableTypeAnnotation (tokenInfo *const token, vString *const repr)
1106ebbfee1eSDavid Yu Yang {
1107ebbfee1eSDavid Yu Yang 	bool readNext = true;
1108ebbfee1eSDavid Yu Yang 
1109ebbfee1eSDavid Yu Yang 	readToken (token);
1110ebbfee1eSDavid Yu Yang 	switch (token->type)
1111ebbfee1eSDavid Yu Yang 	{
1112ebbfee1eSDavid Yu Yang 		case '[': readNext = skipOverPair (token, '[', ']', repr, true); break;
1113ebbfee1eSDavid Yu Yang 		case '(': readNext = skipOverPair (token, '(', ')', repr, true); break;
1114ebbfee1eSDavid Yu Yang 		case '{': readNext = skipOverPair (token, '{', '}', repr, true); break;
1115ebbfee1eSDavid Yu Yang 		default: reprCat (repr, token);
1116ebbfee1eSDavid Yu Yang 	}
1117ebbfee1eSDavid Yu Yang 	if (readNext)
1118ebbfee1eSDavid Yu Yang 		readToken (token);
1119ebbfee1eSDavid Yu Yang 	/* skip subscripts and calls */
1120ebbfee1eSDavid Yu Yang 	while (token->type == '[' || token->type == '(' || token->type == '.' || token->type == '|')
1121ebbfee1eSDavid Yu Yang 	{
1122ebbfee1eSDavid Yu Yang 		switch (token->type)
1123ebbfee1eSDavid Yu Yang 		{
1124ebbfee1eSDavid Yu Yang 			case '[': readNext = skipOverPair (token, '[', ']', repr, true); break;
1125ebbfee1eSDavid Yu Yang 			case '(': readNext = skipOverPair (token, '(', ')', repr, true); break;
1126ebbfee1eSDavid Yu Yang 			case '|':
1127ebbfee1eSDavid Yu Yang 				reprCat (repr, token);
1128ebbfee1eSDavid Yu Yang 				skipVariableTypeAnnotation (token, repr);
1129ebbfee1eSDavid Yu Yang 				readNext = false;
1130ebbfee1eSDavid Yu Yang 				break;
1131ebbfee1eSDavid Yu Yang 			case '.':
1132ebbfee1eSDavid Yu Yang 				reprCat (repr, token);
1133ebbfee1eSDavid Yu Yang 				readToken (token);
1134ebbfee1eSDavid Yu Yang 				readNext = token->type == TOKEN_IDENTIFIER;
1135ebbfee1eSDavid Yu Yang 				if (readNext)
1136ebbfee1eSDavid Yu Yang 					reprCat (repr, token);
1137ebbfee1eSDavid Yu Yang 				break;
1138ebbfee1eSDavid Yu Yang 			default:  readNext = false; break;
1139ebbfee1eSDavid Yu Yang 		}
1140ebbfee1eSDavid Yu Yang 		if (readNext)
1141ebbfee1eSDavid Yu Yang 			readToken (token);
1142ebbfee1eSDavid Yu Yang 	}
1143ebbfee1eSDavid Yu Yang 
1144ebbfee1eSDavid Yu Yang 	return false;
1145ebbfee1eSDavid Yu Yang }
1146ebbfee1eSDavid Yu Yang 
parseVariable(tokenInfo * const token,const gdscriptKind kind,const stringList * const decorators,const int keyword)1147ebbfee1eSDavid Yu Yang static bool parseVariable (tokenInfo *const token, const gdscriptKind kind,
1148ebbfee1eSDavid Yu Yang 							const stringList *const decorators,
1149ebbfee1eSDavid Yu Yang 							const int keyword)
1150ebbfee1eSDavid Yu Yang {
1151ebbfee1eSDavid Yu Yang 	readToken(token);
1152ebbfee1eSDavid Yu Yang 	vString *type = vStringNew();
1153ebbfee1eSDavid Yu Yang 	tokenInfo *name = newToken ();
1154ebbfee1eSDavid Yu Yang 	copyToken (name, token);
1155ebbfee1eSDavid Yu Yang 	if (!name)
1156ebbfee1eSDavid Yu Yang 		return false;
1157ebbfee1eSDavid Yu Yang 
1158ebbfee1eSDavid Yu Yang 	readToken (token);
1159ebbfee1eSDavid Yu Yang 	// Variable declarations with dotted names are illegal
1160ebbfee1eSDavid Yu Yang 	if (token->type == '.')
1161ebbfee1eSDavid Yu Yang 		return false;
1162ebbfee1eSDavid Yu Yang 
1163ebbfee1eSDavid Yu Yang 	/* (parse and) skip annotations.  we need not to be too permissive because we
1164ebbfee1eSDavid Yu Yang 	 * aren't yet sure we're actually parsing a variable. */
1165ebbfee1eSDavid Yu Yang 	if (token->type == ':' && skipVariableTypeAnnotation (token, type))
1166ebbfee1eSDavid Yu Yang 		readToken (token);
1167ebbfee1eSDavid Yu Yang 
1168ebbfee1eSDavid Yu Yang 	int index = makeSimpleGDScriptTag (name, kind);
1169ebbfee1eSDavid Yu Yang 	deleteToken(name);
1170ebbfee1eSDavid Yu Yang 	tagEntryInfo *e = getEntryInCorkQueue (index);
1171ebbfee1eSDavid Yu Yang 
1172ebbfee1eSDavid Yu Yang 	if (e && decorators && stringListCount (decorators) > 0)
1173ebbfee1eSDavid Yu Yang 	{
1174ebbfee1eSDavid Yu Yang 		vString *vstr = makeDecoratorString (decorators);
1175ebbfee1eSDavid Yu Yang 		attachParserField (e, true, GDScriptFields[F_ANNOTATIONS].ftype,
1176ebbfee1eSDavid Yu Yang 						   vStringValue (vstr));
1177ebbfee1eSDavid Yu Yang 		vStringDelete (vstr);
1178ebbfee1eSDavid Yu Yang 	}
1179ebbfee1eSDavid Yu Yang 
1180ebbfee1eSDavid Yu Yang 	vString *vtype = vStringNew();
1181ebbfee1eSDavid Yu Yang 	char * stype = vStringValue (type);
1182ebbfee1eSDavid Yu Yang 	if (strcmp(stype, "=") && strcmp(stype, ""))
1183ebbfee1eSDavid Yu Yang 	{
1184ebbfee1eSDavid Yu Yang 			vStringCatS(vtype, stype);
1185ebbfee1eSDavid Yu Yang 	}
1186ebbfee1eSDavid Yu Yang 	vStringDelete(type);
1187ebbfee1eSDavid Yu Yang 
1188ebbfee1eSDavid Yu Yang 	if (e && vStringLength(vtype) > 0) /// TODO: Fix types away
1189ebbfee1eSDavid Yu Yang 	{
1190ebbfee1eSDavid Yu Yang 		e->extensionFields.typeRef [0] = eStrdup ("typename");
1191ebbfee1eSDavid Yu Yang 		e->extensionFields.typeRef [1] = vStringDeleteUnwrap (vtype);
1192ebbfee1eSDavid Yu Yang 	}
1193ebbfee1eSDavid Yu Yang 	else
1194ebbfee1eSDavid Yu Yang 	{
1195ebbfee1eSDavid Yu Yang 		vStringDelete(vtype);
1196ebbfee1eSDavid Yu Yang 	}
1197ebbfee1eSDavid Yu Yang 
1198ebbfee1eSDavid Yu Yang 
1199ebbfee1eSDavid Yu Yang 	while ((TokenContinuationDepth > 0 || token->type != ',') &&
1200ebbfee1eSDavid Yu Yang 		   token->type != TOKEN_EOF &&
1201ebbfee1eSDavid Yu Yang 		   token->type != ';' &&
1202ebbfee1eSDavid Yu Yang 		   token->type != TOKEN_INDENT)
1203ebbfee1eSDavid Yu Yang 	{
1204ebbfee1eSDavid Yu Yang 		readToken (token);
1205ebbfee1eSDavid Yu Yang 	}
1206ebbfee1eSDavid Yu Yang 
1207ebbfee1eSDavid Yu Yang 
1208ebbfee1eSDavid Yu Yang 	return false;
1209ebbfee1eSDavid Yu Yang }
1210ebbfee1eSDavid Yu Yang 
1211ebbfee1eSDavid Yu Yang /* pops any level >= to indent */
setIndent(tokenInfo * const token)1212ebbfee1eSDavid Yu Yang static void setIndent (tokenInfo *const token)
1213ebbfee1eSDavid Yu Yang {
1214ebbfee1eSDavid Yu Yang 	NestingLevel *lv = nestingLevelsGetCurrent (GDScriptNestingLevels);
1215ebbfee1eSDavid Yu Yang 
1216ebbfee1eSDavid Yu Yang 	while (lv && GDS_NL (lv)->indentation >= token->indent)
1217ebbfee1eSDavid Yu Yang 	{
1218ebbfee1eSDavid Yu Yang 		tagEntryInfo *e = getEntryInCorkQueue (lv->corkIndex);
1219ebbfee1eSDavid Yu Yang 		if (e)
1220ebbfee1eSDavid Yu Yang 			e->extensionFields.endLine = token->lineNumber;
1221ebbfee1eSDavid Yu Yang 
1222ebbfee1eSDavid Yu Yang 		nestingLevelsPop (GDScriptNestingLevels);
1223ebbfee1eSDavid Yu Yang 		lv = nestingLevelsGetCurrent (GDScriptNestingLevels);
1224ebbfee1eSDavid Yu Yang 	}
1225ebbfee1eSDavid Yu Yang }
1226ebbfee1eSDavid Yu Yang 
prepareUnnamedClass(struct NestingLevels * nls)1227ebbfee1eSDavid Yu Yang static int prepareUnnamedClass (struct NestingLevels *nls)
1228ebbfee1eSDavid Yu Yang {
1229ebbfee1eSDavid Yu Yang 	{
1230ebbfee1eSDavid Yu Yang 		/* Ugly: we need a "position" on the input stream for making a tag.
1231ebbfee1eSDavid Yu Yang 		 * At the begining of parsing, the position is undefined.
1232ebbfee1eSDavid Yu Yang 		 * By reading a byte, the position is defined.
1233ebbfee1eSDavid Yu Yang 		 */
1234ebbfee1eSDavid Yu Yang 		int c = getcFromInputFile ();
1235ebbfee1eSDavid Yu Yang 		if (c == EOF)
1236ebbfee1eSDavid Yu Yang 			return CORK_NIL;
1237ebbfee1eSDavid Yu Yang 		ungetcToInputFile (c);
1238ebbfee1eSDavid Yu Yang 	}
1239ebbfee1eSDavid Yu Yang 
1240ebbfee1eSDavid Yu Yang 	vString * tmp_class = anonGenerateNew ("anon_class_", K_CLASS);
1241ebbfee1eSDavid Yu Yang 	int corkIndex = makeSimpleTag (tmp_class, K_CLASS);
1242ebbfee1eSDavid Yu Yang 	vStringDelete (tmp_class);
1243ebbfee1eSDavid Yu Yang 
1244ebbfee1eSDavid Yu Yang 	tagEntryInfo *e = getEntryInCorkQueue (corkIndex);
1245ebbfee1eSDavid Yu Yang 	if (e)
1246ebbfee1eSDavid Yu Yang 		markTagExtraBit (e, XTAG_ANONYMOUS);
1247ebbfee1eSDavid Yu Yang 
1248ebbfee1eSDavid Yu Yang 	/* This virtual scope should not be poped. */
1249ebbfee1eSDavid Yu Yang 	NestingLevel *lv = nestingLevelsPush (nls, corkIndex);
1250ebbfee1eSDavid Yu Yang 	GDS_NL (lv)->indentation = -1;
1251ebbfee1eSDavid Yu Yang 
1252ebbfee1eSDavid Yu Yang 	return corkIndex;
1253ebbfee1eSDavid Yu Yang }
1254ebbfee1eSDavid Yu Yang 
findGDScriptTags(void)1255ebbfee1eSDavid Yu Yang static void findGDScriptTags (void)
1256ebbfee1eSDavid Yu Yang {
1257ebbfee1eSDavid Yu Yang 	tokenInfo *const token = newToken ();
1258ebbfee1eSDavid Yu Yang 	stringList *decorators = stringListNew();
1259ebbfee1eSDavid Yu Yang 	bool atStatementStart = true;
1260ebbfee1eSDavid Yu Yang 
1261ebbfee1eSDavid Yu Yang 	TokenContinuationDepth = 0;
1262ebbfee1eSDavid Yu Yang 	NextToken = NULL;
1263ebbfee1eSDavid Yu Yang 	GDScriptNestingLevels = nestingLevelsNew (sizeof (struct gdscriptNestingLevelUserData));
1264ebbfee1eSDavid Yu Yang 
1265ebbfee1eSDavid Yu Yang 	if (isXtagEnabled (GDScriptXtagTable[X_IMPLICIT_CLASS].xtype))
1266ebbfee1eSDavid Yu Yang 		prepareUnnamedClass (GDScriptNestingLevels);
1267ebbfee1eSDavid Yu Yang 
1268ebbfee1eSDavid Yu Yang 	readToken (token);
1269ebbfee1eSDavid Yu Yang 	while (token->type != TOKEN_EOF)
1270ebbfee1eSDavid Yu Yang 	{
1271ebbfee1eSDavid Yu Yang 		tokenType iterationTokenType = token->type;
1272ebbfee1eSDavid Yu Yang 		int iterationTokenKeyword = token->keyword;
1273ebbfee1eSDavid Yu Yang 		bool readNext = true;
1274ebbfee1eSDavid Yu Yang 
1275ebbfee1eSDavid Yu Yang 		if (token->type == TOKEN_INDENT)
1276ebbfee1eSDavid Yu Yang 			setIndent (token);
1277ebbfee1eSDavid Yu Yang 		else if (token->keyword == KEYWORD_class ||
1278ebbfee1eSDavid Yu Yang 				 token->keyword == KEYWORD_func  ||
1279ebbfee1eSDavid Yu Yang 				 token->keyword == KEYWORD_signal)
1280ebbfee1eSDavid Yu Yang 		{
1281ebbfee1eSDavid Yu Yang 			gdscriptKind kind = K_METHOD;
1282ebbfee1eSDavid Yu Yang 			switch (token->keyword)
1283ebbfee1eSDavid Yu Yang 			{
1284ebbfee1eSDavid Yu Yang 			case KEYWORD_class:  kind = K_CLASS;  break;
1285ebbfee1eSDavid Yu Yang 			case KEYWORD_func:   kind = K_METHOD; break;
1286ebbfee1eSDavid Yu Yang 			case KEYWORD_signal: kind = K_SIGNAL; break;
1287ebbfee1eSDavid Yu Yang 			default:
1288ebbfee1eSDavid Yu Yang 				AssertNotReached();
1289ebbfee1eSDavid Yu Yang 			}
1290*385c6b89SMasatake YAMATO 			readNext = parseClassOrDef (token, decorators, kind);
1291ebbfee1eSDavid Yu Yang 		}
1292ebbfee1eSDavid Yu Yang 		else if (token->keyword == KEYWORD_extends)
1293ebbfee1eSDavid Yu Yang 		{
1294ebbfee1eSDavid Yu Yang 			readNext = parseExtends (token);
1295ebbfee1eSDavid Yu Yang 		}
1296ebbfee1eSDavid Yu Yang 		else if (token->type == '(')
1297ebbfee1eSDavid Yu Yang 		{ /* skip parentheses to avoid finding stuff inside them */
1298ebbfee1eSDavid Yu Yang 			readNext = skipOverPair (token, '(', ')', NULL, false);
1299ebbfee1eSDavid Yu Yang 		}
1300ebbfee1eSDavid Yu Yang 		else if (token->keyword == KEYWORD_variable || token->keyword == KEYWORD_const)
1301ebbfee1eSDavid Yu Yang 		{
1302ebbfee1eSDavid Yu Yang 			NestingLevel *lv = nestingLevelsGetCurrent (GDScriptNestingLevels);
1303ebbfee1eSDavid Yu Yang 			tagEntryInfo *lvEntry = NULL;
1304ebbfee1eSDavid Yu Yang 			gdscriptKind kind = K_VARIABLE;
1305ebbfee1eSDavid Yu Yang 
1306ebbfee1eSDavid Yu Yang 			if (lv)
1307ebbfee1eSDavid Yu Yang 				lvEntry = getEntryOfNestingLevel (lv);
1308ebbfee1eSDavid Yu Yang 
1309ebbfee1eSDavid Yu Yang 			if (lvEntry && lvEntry->kindIndex != K_CLASS)
1310ebbfee1eSDavid Yu Yang 				kind = K_LOCAL_VARIABLE;
1311ebbfee1eSDavid Yu Yang 
1312ebbfee1eSDavid Yu Yang 			if (token->keyword == KEYWORD_const)
1313ebbfee1eSDavid Yu Yang 				kind = K_CONST;
1314ebbfee1eSDavid Yu Yang 
1315ebbfee1eSDavid Yu Yang 			readNext = parseVariable (token, kind, decorators, token->keyword);
1316ebbfee1eSDavid Yu Yang 		}
1317ebbfee1eSDavid Yu Yang 		else if (token->keyword == KEYWORD_enum)
1318ebbfee1eSDavid Yu Yang 		{
1319ebbfee1eSDavid Yu Yang 			readNext = parseEnum (token);
1320ebbfee1eSDavid Yu Yang 		}
1321ebbfee1eSDavid Yu Yang 		else if (token->keyword == KEYWORD_class_name)
1322ebbfee1eSDavid Yu Yang 		{
1323ebbfee1eSDavid Yu Yang 			readNext = parseClassName (token);
1324ebbfee1eSDavid Yu Yang 		}
1325ebbfee1eSDavid Yu Yang 		else if (token->type == TOKEN_KEYWORD
1326ebbfee1eSDavid Yu Yang 				 && token->keyword == KEYWORD_modifier)
1327ebbfee1eSDavid Yu Yang 		{
1328ebbfee1eSDavid Yu Yang 			stringListAdd (decorators, vStringNewCopy(token->string));
1329ebbfee1eSDavid Yu Yang 		}
1330ebbfee1eSDavid Yu Yang 		else if (token->type == '@' && atStatementStart &&
1331ebbfee1eSDavid Yu Yang 				 GDScriptFields[F_ANNOTATIONS].enabled)
1332ebbfee1eSDavid Yu Yang 		{
1333ebbfee1eSDavid Yu Yang 			/* collect decorators */
1334ebbfee1eSDavid Yu Yang 			readQualifiedName (token);
1335ebbfee1eSDavid Yu Yang 			if (token->type != TOKEN_IDENTIFIER
1336ebbfee1eSDavid Yu Yang 				&& (token->keyword != KEYWORD_modifier))
1337ebbfee1eSDavid Yu Yang 				readNext = false;
1338ebbfee1eSDavid Yu Yang 			else
1339ebbfee1eSDavid Yu Yang 			{
1340ebbfee1eSDavid Yu Yang 				stringListAdd (decorators, vStringNewCopy(token->string));
1341ebbfee1eSDavid Yu Yang 				readToken (token);
1342ebbfee1eSDavid Yu Yang 
1343ebbfee1eSDavid Yu Yang 				vString *d = vStringNew ();
1344ebbfee1eSDavid Yu Yang 				readNext = skipOverPair (token, '(', ')', d, true);
1345ebbfee1eSDavid Yu Yang 				if (vStringLength (d) > 0)
1346ebbfee1eSDavid Yu Yang 					stringListAdd (decorators, d);
1347ebbfee1eSDavid Yu Yang 				else
1348ebbfee1eSDavid Yu Yang 					vStringDelete (d);
1349ebbfee1eSDavid Yu Yang 			}
1350ebbfee1eSDavid Yu Yang 		}
1351ebbfee1eSDavid Yu Yang 
1352ebbfee1eSDavid Yu Yang 		/* clear collected decorators for any non-decorator tokens non-indent
1353ebbfee1eSDavid Yu Yang 		 * token.  decorator collection takes care of skipping the possible
1354ebbfee1eSDavid Yu Yang 		 * argument list, so we should never hit here parsing a decorator */
1355ebbfee1eSDavid Yu Yang 		if (iterationTokenType != TOKEN_INDENT &&
1356ebbfee1eSDavid Yu Yang 			iterationTokenType != '@' &&
1357ebbfee1eSDavid Yu Yang 			iterationTokenKeyword != KEYWORD_modifier &&
1358ebbfee1eSDavid Yu Yang 			GDScriptFields[F_ANNOTATIONS].enabled)
1359ebbfee1eSDavid Yu Yang 		{
1360ebbfee1eSDavid Yu Yang 			stringListClear (decorators);
1361ebbfee1eSDavid Yu Yang 		}
1362ebbfee1eSDavid Yu Yang 
1363ebbfee1eSDavid Yu Yang 		atStatementStart = (token->type == TOKEN_INDENT || token->type == ';');
1364ebbfee1eSDavid Yu Yang 
1365ebbfee1eSDavid Yu Yang 		if (readNext)
1366ebbfee1eSDavid Yu Yang 			readToken (token);
1367ebbfee1eSDavid Yu Yang 	}
1368ebbfee1eSDavid Yu Yang 
1369ebbfee1eSDavid Yu Yang 	nestingLevelsFree (GDScriptNestingLevels);
1370ebbfee1eSDavid Yu Yang 	stringListDelete (decorators);
1371ebbfee1eSDavid Yu Yang 	deleteToken (token);
1372ebbfee1eSDavid Yu Yang 	Assert (NextToken == NULL);
1373ebbfee1eSDavid Yu Yang }
1374ebbfee1eSDavid Yu Yang 
initialize(const langType language)1375ebbfee1eSDavid Yu Yang static void initialize (const langType language)
1376ebbfee1eSDavid Yu Yang {
1377ebbfee1eSDavid Yu Yang 	Lang_gdscript = language;
1378ebbfee1eSDavid Yu Yang 
1379ebbfee1eSDavid Yu Yang 	TokenPool = objPoolNew (16, newPoolToken, deletePoolToken, clearPoolToken, NULL);
1380ebbfee1eSDavid Yu Yang 	addKeywordGroup (&modifierKeywords, language);
1381ebbfee1eSDavid Yu Yang }
1382ebbfee1eSDavid Yu Yang 
finalize(langType language CTAGS_ATTR_UNUSED,bool initialized)1383ebbfee1eSDavid Yu Yang static void finalize (langType language CTAGS_ATTR_UNUSED, bool initialized)
1384ebbfee1eSDavid Yu Yang {
1385ebbfee1eSDavid Yu Yang 	if (!initialized)
1386ebbfee1eSDavid Yu Yang 		return;
1387ebbfee1eSDavid Yu Yang 
1388ebbfee1eSDavid Yu Yang 	objPoolDelete (TokenPool);
1389ebbfee1eSDavid Yu Yang }
1390ebbfee1eSDavid Yu Yang 
GDScriptParser(void)1391ebbfee1eSDavid Yu Yang extern parserDefinition* GDScriptParser (void)
1392ebbfee1eSDavid Yu Yang {
1393ebbfee1eSDavid Yu Yang 	static const char *const extensions[] = { "gd", NULL };
1394ebbfee1eSDavid Yu Yang 	parserDefinition *def = parserNew ("GDScript");
1395ebbfee1eSDavid Yu Yang 	def->kindTable = GDScriptKinds;
1396ebbfee1eSDavid Yu Yang 	def->kindCount = ARRAY_SIZE (GDScriptKinds);
1397ebbfee1eSDavid Yu Yang 	def->extensions = extensions;
1398ebbfee1eSDavid Yu Yang 	def->parser = findGDScriptTags;
1399ebbfee1eSDavid Yu Yang 	def->initialize = initialize;
1400ebbfee1eSDavid Yu Yang 	def->finalize = finalize;
1401ebbfee1eSDavid Yu Yang 	def->keywordTable = GDScriptKeywordTable;
1402ebbfee1eSDavid Yu Yang 	def->keywordCount = ARRAY_SIZE (GDScriptKeywordTable);
1403ebbfee1eSDavid Yu Yang 	def->fieldTable = GDScriptFields;
1404ebbfee1eSDavid Yu Yang 	def->fieldCount = ARRAY_SIZE (GDScriptFields);
1405ebbfee1eSDavid Yu Yang 	def->xtagTable     = GDScriptXtagTable;
1406ebbfee1eSDavid Yu Yang 	def->xtagCount     = ARRAY_SIZE(GDScriptXtagTable);
1407ebbfee1eSDavid Yu Yang 	def->useCork = CORK_QUEUE;
1408ebbfee1eSDavid Yu Yang 	def->requestAutomaticFQTag = true;
1409ebbfee1eSDavid Yu Yang 	return def;
1410ebbfee1eSDavid Yu Yang }
1411