1 /*
2 *   Copyright (c) 1996-2003, Darren Hiebert
3 *
4 *   This source code is released for free distribution under the terms of the
5 *   GNU General Public License version 2 or (at your option) any later version.
6 *
7 *   This module contains functions for parsing and scanning C, C++, C#, D and Java
8 *   source files.
9 */
10 
11 /*
12 *   INCLUDE FILES
13 */
14 #include "general.h"        /* must always come first */
15 
16 #include <string.h>
17 #include <setjmp.h>
18 
19 #include "debug.h"
20 #include "mio.h"
21 #include "entry.h"
22 #include "geany_lcpp.h"
23 #include "keyword.h"
24 #include "options.h"
25 #include "parse.h"
26 #include "read.h"
27 #include "routines.h"
28 #include "xtag.h"
29 
30 /*
31 *   MACROS
32 */
33 
34 #define activeToken(st)     ((st)->token [(int) (st)->tokenIndex])
35 #define parentDecl(st)      ((st)->parent == NULL ? \
36                             DECL_NONE : (st)->parent->declaration)
37 #define isType(token,t)     (bool) ((token)->type == (t))
38 #define insideEnumBody(st)  (bool) ((st)->parent == NULL ? false : \
39 									   ((st)->parent->declaration == DECL_ENUM))
40 #define isExternCDecl(st,c) (bool) ((c) == STRING_SYMBOL  && \
41 									   ! (st)->haveQualifyingName && \
42 									   (st)->scope == SCOPE_EXTERN)
43 
44 #define isOneOf(c,s)        (bool) (strchr ((s), (c)) != NULL)
45 
46 /*
47 *   DATA DECLARATIONS
48 */
49 
50 enum { NumTokens = 12 };
51 
52 typedef enum eException
53 {
54 	ExceptionNone, ExceptionEOF, ExceptionFormattingError,
55 	ExceptionBraceFormattingError
56 } exception_t;
57 
58 /*  Used to specify type of keyword.
59  */
60 enum eKeywordId
61 {
62 	KEYWORD_ATTRIBUTE, KEYWORD_ABSTRACT, KEYWORD_ALIAS,
63 	KEYWORD_BOOLEAN, KEYWORD_BYTE, KEYWORD_BAD_STATE, KEYWORD_BAD_TRANS,
64 	KEYWORD_BIND, KEYWORD_BIND_VAR, KEYWORD_BIT, KEYWORD_BODY,
65 	KEYWORD_CASE, KEYWORD_CATCH, KEYWORD_CHAR, KEYWORD_CLASS, KEYWORD_CONST,
66 	KEYWORD_CONSTRAINT, KEYWORD_COVERAGE_BLOCK, KEYWORD_COVERAGE_DEF,
67 	KEYWORD_DEFAULT, KEYWORD_DELEGATE, KEYWORD_DELETE, KEYWORD_DO,
68 	KEYWORD_DOUBLE,
69 	KEYWORD_ELSE, KEYWORD_ENUM, KEYWORD_EXPLICIT, KEYWORD_EXTERN,
70 	KEYWORD_EXTENDS, KEYWORD_EVENT,
71 	KEYWORD_FINAL, KEYWORD_FINALLY, KEYWORD_FLOAT, KEYWORD_FOR, KEYWORD_FRIEND, KEYWORD_FUNCTION,
72 	KEYWORD_GET, KEYWORD_GOTO,
73 	KEYWORD_IF, KEYWORD_IMPLEMENTS, KEYWORD_IMPORT, KEYWORD_IN, KEYWORD_INLINE, KEYWORD_INT,
74 	KEYWORD_INOUT, KEYWORD_INPUT, KEYWORD_INTEGER, KEYWORD_INTERFACE,
75 	KEYWORD_INTERNAL,
76 	KEYWORD_LOCAL, KEYWORD_LONG,
77 	KEYWORD_M_BAD_STATE, KEYWORD_M_BAD_TRANS, KEYWORD_M_STATE, KEYWORD_M_TRANS,
78 	KEYWORD_MODULE, KEYWORD_MUTABLE,
79 	KEYWORD_NAMESPACE, KEYWORD_NEW, KEYWORD_NEWCOV, KEYWORD_NATIVE, KEYWORD_NOEXCEPT,
80 	KEYWORD_OPERATOR, KEYWORD_OUT, KEYWORD_OUTPUT, KEYWORD_OVERLOAD, KEYWORD_OVERRIDE,
81 	KEYWORD_PACKED, KEYWORD_PORT, KEYWORD_PACKAGE, KEYWORD_PRIVATE,
82 	KEYWORD_PROGRAM, KEYWORD_PROTECTED, KEYWORD_PUBLIC,
83 	KEYWORD_REF, KEYWORD_REGISTER, KEYWORD_RETURN,
84 	KEYWORD_SHADOW, KEYWORD_STATE,
85 	KEYWORD_SET, KEYWORD_SHORT, KEYWORD_SIGNAL, KEYWORD_SIGNED, KEYWORD_SIZE_T, KEYWORD_STATIC,
86 	KEYWORD_STATIC_ASSERT, KEYWORD_STRING,
87 	KEYWORD_STRUCT, KEYWORD_SWITCH, KEYWORD_SYNCHRONIZED,
88 	KEYWORD_TASK, KEYWORD_TEMPLATE, KEYWORD_THIS, KEYWORD_THROW,
89 	KEYWORD_THROWS, KEYWORD_TRANSIENT, KEYWORD_TRANS, KEYWORD_TRANSITION,
90 	KEYWORD_TRY, KEYWORD_TYPEDEF, KEYWORD_TYPENAME,
91 	KEYWORD_UINT, KEYWORD_ULONG, KEYWORD_UNION, KEYWORD_UNSIGNED, KEYWORD_USHORT,
92 	KEYWORD_USING,
93 	KEYWORD_VIRTUAL, KEYWORD_VOID, KEYWORD_VOLATILE,
94 	KEYWORD_WCHAR_T, KEYWORD_WEAK, KEYWORD_WHILE
95 };
96 typedef int keywordId; /* to allow KEYWORD_NONE */
97 
98 /*  Used to determine whether keyword is valid for the current language and
99  *  what its ID is.
100  */
101 typedef struct sKeywordDesc
102 {
103 	const char *name;
104 	keywordId id;
105 	short isValid [7]; /* indicates languages for which kw is valid */
106 } keywordDesc;
107 
108 /*  Used for reporting the type of object parsed by nextToken ().
109  */
110 typedef enum eTokenType
111 {
112 	TOKEN_NONE,          /* none */
113 	TOKEN_ARGS,          /* a parenthetical pair and its contents */
114 	TOKEN_BRACE_CLOSE,
115 	TOKEN_BRACE_OPEN,
116 	TOKEN_COMMA,         /* the comma character */
117 	TOKEN_DOUBLE_COLON,  /* double colon indicates nested-name-specifier */
118 	TOKEN_KEYWORD,
119 	TOKEN_NAME,          /* an unknown name */
120 	TOKEN_PACKAGE,       /* a Java package name */
121 	TOKEN_PAREN_NAME,    /* a single name in parentheses */
122 	TOKEN_SEMICOLON,     /* the semicolon character */
123 	TOKEN_SPEC,          /* a storage class specifier, qualifier, type, etc. */
124 	TOKEN_STAR,          /* pointer detection */
125 	TOKEN_ARRAY,         /* array detection */
126 	TOKEN_COUNT
127 } tokenType;
128 
129 /*  This describes the scoping of the current statement.
130  */
131 typedef enum eTagScope
132 {
133 	SCOPE_GLOBAL,        /* no storage class specified */
134 	SCOPE_STATIC,        /* static storage class */
135 	SCOPE_EXTERN,        /* external storage class */
136 	SCOPE_FRIEND,        /* declares access only */
137 	SCOPE_TYPEDEF,       /* scoping depends upon context */
138 	SCOPE_COUNT
139 } tagScope;
140 
141 typedef enum eDeclaration
142 {
143 	DECL_NONE,
144 	DECL_BASE,           /* base type (default) */
145 	DECL_CLASS,
146 	DECL_ENUM,
147 	DECL_EVENT,
148 	DECL_SIGNAL,
149 	DECL_FUNCTION,
150 	DECL_FUNCTION_TEMPLATE,
151 	DECL_IGNORE,         /* non-taggable "declaration" */
152 	DECL_INTERFACE,
153 	DECL_MODULE,
154 	DECL_NAMESPACE,
155 	DECL_NOMANGLE,       /* C++ name demangling block */
156 	DECL_PACKAGE,
157 	DECL_STRUCT,
158 	DECL_UNION,
159 	DECL_COUNT
160 } declType;
161 
162 typedef enum eVisibilityType
163 {
164 	ACCESS_UNDEFINED,
165 	ACCESS_PRIVATE,
166 	ACCESS_PROTECTED,
167 	ACCESS_PUBLIC,
168 	ACCESS_DEFAULT,      /* Java-specific */
169 	ACCESS_COUNT
170 } accessType;
171 
172 /*  Information about the parent class of a member (if any).
173  */
174 typedef struct sMemberInfo
175 {
176 	accessType access;           /* access of current statement */
177 	accessType accessDefault;    /* access default for current statement */
178 } memberInfo;
179 
180 typedef struct sTokenInfo
181 {
182 	tokenType     type;
183 	keywordId     keyword;
184 	vString*      name;          /* the name of the token */
185 	unsigned long lineNumber;    /* line number of tag */
186 	MIOPos        filePosition;  /* file position of line containing name */
187 } tokenInfo;
188 
189 typedef enum eImplementation
190 {
191 	IMP_DEFAULT,
192 	IMP_ABSTRACT,
193 	IMP_VIRTUAL,
194 	IMP_PURE_VIRTUAL,
195 	IMP_COUNT
196 } impType;
197 
198 /*  Describes the statement currently undergoing analysis.
199  */
200 typedef struct sStatementInfo
201 {
202 	tagScope	scope;
203 	declType	declaration;    /* specifier associated with TOKEN_SPEC */
204 	bool		gotName;        /* was a name parsed yet? */
205 	bool		haveQualifyingName;  /* do we have a name we are considering? */
206 	bool		gotParenName;   /* was a name inside parentheses parsed yet? */
207 	bool		gotArgs;        /* was a list of parameters parsed yet? */
208 	unsigned int nSemicolons;   /* how many semicolons did we see in that statement */
209 	impType		implementation; /* abstract or concrete implementation? */
210 	unsigned int tokenIndex;    /* currently active token */
211 	tokenInfo*	token [((int) NumTokens)];
212 	tokenInfo*	context;        /* accumulated scope of current statement */
213 	tokenInfo*	blockName;      /* name of current block */
214 	memberInfo	member;         /* information regarding parent class/struct */
215 	vString*	parentClasses;  /* parent classes */
216 	struct sStatementInfo *parent;  /* statement we are nested within */
217 	tokenInfo* 		firstToken; /* First token in the statement */
218 } statementInfo;
219 
220 /*  Describes the type of tag being generated.
221  */
222 typedef enum eTagType
223 {
224 	TAG_UNDEFINED,
225 	TAG_CLASS,       /* class name */
226 	TAG_ENUM,        /* enumeration name */
227 	TAG_ENUMERATOR,  /* enumerator (enumeration value) */
228 	TAG_FIELD,       /* field (Java) */
229 	TAG_FUNCTION,    /* function definition */
230 	TAG_INTERFACE,   /* interface declaration */
231 	TAG_MEMBER,      /* structure, class or interface member */
232 	TAG_METHOD,      /* method declaration */
233 	TAG_NAMESPACE,   /* namespace name */
234 	TAG_PACKAGE,     /* package name / D module name */
235 	TAG_PROTOTYPE,   /* function prototype or declaration */
236 	TAG_STRUCT,      /* structure name */
237 	TAG_TYPEDEF,     /* typedef name */
238 	TAG_UNION,       /* union name */
239 	TAG_VARIABLE,    /* variable definition */
240 	TAG_EXTERN_VAR,  /* external variable declaration */
241 	TAG_MACRO,       /* #define s */
242 	TAG_EVENT,       /* event */
243 	TAG_SIGNAL,      /* signal */
244 	TAG_LOCAL,       /* local variable definition */
245 	TAG_PROPERTY,    /* property name */
246 	TAG_COUNT        /* must be last */
247 } tagType;
248 
249 typedef struct sParenInfo
250 {
251 	bool isParamList;
252 	bool isKnrParamList;
253 	bool isNameCandidate;
254 	bool invalidContents;
255 	bool nestedArgs;
256 	unsigned int parameterCount;
257 } parenInfo;
258 
259 /*
260 *   DATA DEFINITIONS
261 */
262 
263 static jmp_buf Exception;
264 
265 static langType Lang_c;
266 static langType Lang_cpp;
267 static langType Lang_csharp;
268 static langType Lang_java;
269 static langType Lang_d;
270 static langType Lang_glsl;
271 static langType Lang_ferite;
272 static langType Lang_vala;
273 
274 /* Used to index into the CKinds table. */
275 typedef enum
276 {
277 	CK_UNDEFINED = -1,
278 	CK_CLASS, CK_DEFINE, CK_ENUMERATOR, CK_FUNCTION,
279 	CK_ENUMERATION, CK_MEMBER, CK_NAMESPACE, CK_PROTOTYPE,
280 	CK_STRUCT, CK_TYPEDEF, CK_UNION, CK_VARIABLE,
281 	CK_EXTERN_VARIABLE
282 } cKind;
283 
284 static kindDefinition CKinds [] = {
285 	{ true,  'c', "class",      "classes"},
286 	{ true,  'd', "macro",      "macro definitions"},
287 	{ true,  'e', "enumerator", "enumerators (values inside an enumeration)"},
288 	{ true,  'f', "function",   "function definitions"},
289 	{ true,  'g', "enum",       "enumeration names"},
290 	{ true,  'm', "member",     "class, struct, and union members"},
291 	{ true,  'n', "namespace",  "namespaces"},
292 	{ false, 'p', "prototype",  "function prototypes"},
293 	{ true,  's', "struct",     "structure names"},
294 	{ true,  't', "typedef",    "typedefs"},
295 	{ true,  'u', "union",      "union names"},
296 	{ true,  'v', "variable",   "variable definitions"},
297 	{ false, 'x', "externvar",  "external variable declarations"},
298 };
299 
300 /* Used to index into the DKinds table. */
301 typedef enum
302 {
303 	DK_UNDEFINED = -1,
304 	DK_CLASS, DK_ENUMERATOR, DK_FUNCTION,
305 	DK_ENUMERATION, DK_INTERFACE, DK_MEMBER, DK_NAMESPACE, DK_PROTOTYPE,
306 	DK_STRUCT, DK_TYPEDEF, DK_UNION, DK_VARIABLE,
307 	DK_EXTERN_VARIABLE
308 } dKind;
309 
310 static kindDefinition DKinds [] = {
311 	{ true,  'c', "class",      "classes"},
312 	{ true,  'e', "enumerator", "enumerators (values inside an enumeration)"},
313 	{ true,  'f', "function",   "function definitions"},
314 	{ true,  'g', "enum",       "enumeration names"},
315 	{ true,  'i', "interface",  "interfaces"},
316 	{ true,  'm', "member",     "class, struct, and union members"},
317 	{ true,  'n', "namespace",  "namespaces"},
318 	{ false, 'p', "prototype",  "function prototypes"},
319 	{ true,  's', "struct",     "structure names"},
320 	{ true,  't', "typedef",    "typedefs"},
321 	{ true,  'u', "union",      "union names"},
322 	{ true,  'v', "variable",   "variable definitions"},
323 	{ false, 'x', "externvar",  "external variable declarations"},
324 };
325 
326 /* Used to index into the JavaKinds table. */
327 typedef enum
328 {
329 	JK_UNDEFINED = -1,
330 	JK_CLASS, JK_FIELD, JK_INTERFACE, JK_METHOD,
331 	JK_PACKAGE, JK_ENUMERATOR, JK_ENUMERATION
332 } javaKind;
333 
334 static kindDefinition JavaKinds [] = {
335 	{ true,  'c', "class",         "classes"},
336 	{ true,  'f', "field",         "fields"},
337 	{ true,  'i', "interface",     "interfaces"},
338 	{ true,  'm', "method",        "methods"},
339 	{ true,  'p', "package",       "packages"},
340 	{ true,  'e', "enumConstant",  "enum constants"},
341 	{ true,  'g', "enum",          "enum types"},
342 };
343 
344 typedef enum
345 {
346 	CSK_UNDEFINED = -1,
347 	CSK_CLASS, CSK_DEFINE, CSK_ENUMERATOR, CSK_EVENT, CSK_FIELD,
348 	CSK_ENUMERATION, CSK_INTERFACE, CSK_LOCAL, CSK_METHOD,
349 	CSK_NAMESPACE, CSK_PROPERTY, CSK_STRUCT, CSK_TYPEDEF
350 } csharpKind;
351 
352 static kindDefinition CsharpKinds [] = {
353 	{ true,  'c', "class",      "classes"},
354 	{ true,  'd', "macro",      "macro definitions"},
355 	{ true,  'e', "enumerator", "enumerators (values inside an enumeration)"},
356 	{ true,  'E', "event",      "events"},
357 	{ true,  'f', "field",      "fields"},
358 	{ true,  'g', "enum",       "enumeration names"},
359 	{ true,  'i', "interface",  "interfaces"},
360 	{ false, 'l', "local",      "local variables"},
361 	{ true,  'm', "method",     "methods"},
362 	{ true,  'n', "namespace",  "namespaces"},
363 	{ true,  'p', "property",   "properties"},
364 	{ true,  's', "struct",     "structure names"},
365 	{ true,  't', "typedef",    "typedefs"},
366 };
367 
368 typedef enum {
369 	VK_UNDEFINED = -1,
370 	VK_CLASS, VK_DEFINE, VK_ENUMERATOR, VK_FIELD,
371 	VK_ENUMERATION, VK_INTERFACE, VK_LOCAL, VK_METHOD,
372 	VK_NAMESPACE, VK_PROPERTY, VK_SIGNAL, VK_STRUCT
373 } valaKind;
374 
375 static kindDefinition ValaKinds [] = {
376 	{ true,  'c', "class",      "classes"},
377 	{ true,  'd', "macro",      "macro definitions"},
378 	{ true,  'e', "enumerator", "enumerators (values inside an enumeration)"},
379 	{ true,  'f', "field",      "fields"},
380 	{ true,  'g', "enum",       "enumeration names"},
381 	{ true,  'i', "interface",  "interfaces"},
382 	{ false, 'l', "local",      "local variables"},
383 	{ true,  'm', "method",     "methods"},
384 	{ true,  'n', "namespace",  "namespaces"},
385 	{ true,  'p', "property",   "properties"},
386 	{ true,  'S', "signal",     "signals"},
387 	{ true,  's', "struct",     "structure names"},
388 };
389 
390 /* Note: some keyword aliases are added in initializeDParser, initializeValaParser */
391 static const keywordDesc KeywordTable [] = {
392 	/*                                              C++                  */
393 	/*                                       ANSI C  |  C# Java          */
394 	/*                                            |  |  |  |  Vera       */
395 	/*                                            |  |  |  |  |  Vala    */
396 	/*                                            |  |  |  |  |  |  D    */
397 	/* keyword          keyword ID                |  |  |  |  |  |  |    */
398 	{ "__attribute__",  KEYWORD_ATTRIBUTE,      { 1, 1, 1, 0, 0, 0, 1 } },
399 	{ "abstract",       KEYWORD_ABSTRACT,       { 0, 0, 1, 1, 0, 1, 1 } },
400 	{ "bad_state",      KEYWORD_BAD_STATE,      { 0, 0, 0, 0, 1, 0, 0 } },
401 	{ "bad_trans",      KEYWORD_BAD_TRANS,      { 0, 0, 0, 0, 1, 0, 0 } },
402 	{ "bind",           KEYWORD_BIND,           { 0, 0, 0, 0, 1, 0, 0 } },
403 	{ "bind_var",       KEYWORD_BIND_VAR,       { 0, 0, 0, 0, 1, 0, 0 } },
404 	{ "bit",            KEYWORD_BIT,            { 0, 0, 0, 0, 1, 0, 0 } },
405 	{ "body",           KEYWORD_BODY,           { 0, 0, 0, 0, 0, 0, 1 } },
406 	{ "boolean",        KEYWORD_BOOLEAN,        { 0, 0, 0, 1, 0, 0, 0 } },
407 	{ "byte",           KEYWORD_BYTE,           { 0, 0, 0, 1, 0, 0, 1 } },
408 	{ "case",           KEYWORD_CASE,           { 1, 1, 1, 1, 0, 1, 1 } },
409 	{ "catch",          KEYWORD_CATCH,          { 0, 1, 1, 0, 0, 1, 1 } },
410 	{ "char",           KEYWORD_CHAR,           { 1, 1, 1, 1, 0, 1, 1 } },
411 	{ "class",          KEYWORD_CLASS,          { 0, 1, 1, 1, 1, 1, 1 } },
412 	{ "const",          KEYWORD_CONST,          { 1, 1, 1, 1, 0, 1, 1 } },
413 	{ "constraint",     KEYWORD_CONSTRAINT,     { 0, 0, 0, 0, 1, 0, 0 } },
414 	{ "coverage_block", KEYWORD_COVERAGE_BLOCK, { 0, 0, 0, 0, 1, 0, 0 } },
415 	{ "coverage_def",   KEYWORD_COVERAGE_DEF,   { 0, 0, 0, 0, 1, 0, 0 } },
416 	{ "do",             KEYWORD_DO,             { 1, 1, 1, 1, 0, 1, 1 } },
417 	{ "default",        KEYWORD_DEFAULT,        { 1, 1, 1, 1, 0, 1, 1 } },
418 	{ "delegate",       KEYWORD_DELEGATE,       { 0, 0, 1, 0, 0, 1, 1 } },
419 	{ "delete",         KEYWORD_DELETE,         { 0, 1, 0, 0, 0, 1, 1 } },
420 	{ "double",         KEYWORD_DOUBLE,         { 1, 1, 1, 1, 0, 1, 1 } },
421 	{ "else",           KEYWORD_ELSE,           { 1, 1, 0, 1, 0, 1, 1 } },
422 	{ "enum",           KEYWORD_ENUM,           { 1, 1, 1, 1, 1, 1, 1 } },
423 	{ "event",          KEYWORD_EVENT,          { 0, 0, 1, 0, 1, 0, 0 } },
424 	{ "explicit",       KEYWORD_EXPLICIT,       { 0, 1, 1, 0, 0, 0, 1 } },
425 	{ "extends",        KEYWORD_EXTENDS,        { 0, 0, 0, 1, 1, 0, 0 } },
426 	{ "extern",         KEYWORD_EXTERN,         { 1, 1, 1, 0, 1, 1, 0 } },
427 	{ "extern",         KEYWORD_NAMESPACE,      { 0, 0, 0, 0, 0, 0, 1 } },	/* parse block */
428 	{ "final",          KEYWORD_FINAL,          { 0, 0, 0, 1, 0, 0, 1 } },
429 	{ "finally",        KEYWORD_FINALLY,        { 0, 0, 0, 0, 0, 1, 1 } },
430 	{ "float",          KEYWORD_FLOAT,          { 1, 1, 1, 1, 0, 1, 1 } },
431 	{ "for",            KEYWORD_FOR,            { 1, 1, 1, 1, 0, 1, 1 } },
432 	{ "friend",         KEYWORD_FRIEND,         { 0, 1, 0, 0, 0, 0, 0 } },
433 	{ "function",       KEYWORD_FUNCTION,       { 0, 0, 0, 0, 1, 0, 1 } },
434 	{ "get",            KEYWORD_GET,            { 0, 0, 0, 0, 0, 1, 0 } },
435 	{ "goto",           KEYWORD_GOTO,           { 1, 1, 1, 1, 0, 1, 1 } },
436 	{ "if",             KEYWORD_IF,             { 1, 1, 1, 1, 0, 1, 1 } },
437 	{ "implements",     KEYWORD_IMPLEMENTS,     { 0, 0, 0, 1, 0, 0, 0 } },
438 	{ "import",         KEYWORD_IMPORT,         { 0, 0, 0, 1, 0, 0, 1 } },
439 	{ "inline",         KEYWORD_INLINE,         { 0, 1, 0, 0, 0, 1, 0 } },
440 	{ "in",             KEYWORD_IN,             { 0, 0, 0, 0, 0, 0, 1 } },
441 	{ "inout",          KEYWORD_INOUT,          { 0, 0, 0, 0, 1, 0, 0 } },
442 	{ "inout",          KEYWORD_CONST,          { 0, 0, 0, 0, 0, 0, 1 } }, /* treat like const */
443 	{ "input",          KEYWORD_INPUT,          { 0, 0, 0, 0, 1, 0, 0 } },
444 	{ "int",            KEYWORD_INT,            { 1, 1, 1, 1, 0, 1, 1 } },
445 	{ "integer",        KEYWORD_INTEGER,        { 0, 0, 0, 0, 1, 0, 0 } },
446 	{ "interface",      KEYWORD_INTERFACE,      { 0, 0, 1, 1, 1, 1, 1 } },
447 	{ "internal",       KEYWORD_INTERNAL,       { 0, 0, 1, 0, 0, 0, 0 } },
448 	{ "local",          KEYWORD_LOCAL,          { 0, 0, 0, 0, 1, 0, 0 } },
449 	{ "long",           KEYWORD_LONG,           { 1, 1, 1, 1, 0, 1, 1 } },
450 	{ "m_bad_state",    KEYWORD_M_BAD_STATE,    { 0, 0, 0, 0, 1, 0, 0 } },
451 	{ "m_bad_trans",    KEYWORD_M_BAD_TRANS,    { 0, 0, 0, 0, 1, 0, 0 } },
452 	{ "m_state",        KEYWORD_M_STATE,        { 0, 0, 0, 0, 1, 0, 0 } },
453 	{ "m_trans",        KEYWORD_M_TRANS,        { 0, 0, 0, 0, 1, 0, 0 } },
454 	{ "mutable",        KEYWORD_MUTABLE,        { 0, 1, 0, 0, 0, 0, 0 } },
455 	{ "module",         KEYWORD_MODULE,         { 0, 0, 0, 0, 0, 0, 1 } },
456 	{ "namespace",      KEYWORD_NAMESPACE,      { 0, 1, 1, 0, 0, 1, 0 } },
457 	{ "native",         KEYWORD_NATIVE,         { 0, 0, 0, 1, 0, 0, 0 } },
458 	{ "new",            KEYWORD_NEW,            { 0, 1, 1, 1, 0, 1, 1 } },
459 	{ "newcov",         KEYWORD_NEWCOV,         { 0, 0, 0, 0, 1, 0, 0 } },
460 	{ "noexcept",       KEYWORD_NOEXCEPT,       { 0, 1, 0, 0, 0, 0, 0 } },
461 	{ "operator",       KEYWORD_OPERATOR,       { 0, 1, 1, 0, 0, 0, 0 } },
462 	{ "out",            KEYWORD_OUT,            { 0, 0, 0, 0, 0, 1, 1 } },
463 	{ "output",         KEYWORD_OUTPUT,         { 0, 0, 0, 0, 1, 0, 0 } },
464 	{ "overload",       KEYWORD_OVERLOAD,       { 0, 1, 0, 0, 0, 0, 0 } },
465 	{ "override",       KEYWORD_OVERRIDE,       { 0, 0, 1, 0, 0, 1, 1 } },
466 	{ "package",        KEYWORD_PACKAGE,        { 0, 0, 0, 1, 0, 0, 1 } },
467 	{ "packed",         KEYWORD_PACKED,         { 0, 0, 0, 0, 1, 0, 0 } },
468 	{ "port",           KEYWORD_PORT,           { 0, 0, 0, 0, 1, 0, 0 } },
469 	{ "private",        KEYWORD_PRIVATE,        { 0, 1, 1, 1, 0, 1, 1 } },
470 	{ "program",        KEYWORD_PROGRAM,        { 0, 0, 0, 0, 1, 0, 0 } },
471 	{ "protected",      KEYWORD_PROTECTED,      { 0, 1, 1, 1, 1, 1, 1 } },
472 	{ "public",         KEYWORD_PUBLIC,         { 0, 1, 1, 1, 1, 1, 1 } },
473 	{ "ref",            KEYWORD_REF,            { 0, 0, 0, 0, 0, 1, 1 } },
474 	{ "register",       KEYWORD_REGISTER,       { 1, 1, 0, 0, 0, 0, 0 } },
475 	{ "return",         KEYWORD_RETURN,         { 1, 1, 1, 1, 0, 1, 1 } },
476 	{ "set",            KEYWORD_SET,            { 0, 0, 0, 0, 0, 1, 0 } },
477 	{ "shadow",         KEYWORD_SHADOW,         { 0, 0, 0, 0, 1, 0, 0 } },
478 	{ "short",          KEYWORD_SHORT,          { 1, 1, 1, 1, 0, 1, 1 } },
479 	{ "signal",         KEYWORD_SIGNAL,         { 0, 0, 0, 0, 0, 1, 0 } },
480 	{ "signed",         KEYWORD_SIGNED,         { 1, 1, 0, 0, 0, 0, 0 } },
481 	{ "size_t",         KEYWORD_SIZE_T,         { 0, 0, 0, 0, 0, 1, 0 } },
482 	{ "state",          KEYWORD_STATE,          { 0, 0, 0, 0, 1, 0, 0 } },
483 	{ "static",         KEYWORD_STATIC,         { 1, 1, 1, 1, 1, 1, 1 } },
484 	{ "static_assert",  KEYWORD_STATIC_ASSERT,  { 0, 1, 0, 0, 0, 0, 0 } },
485 	{ "string",         KEYWORD_STRING,         { 0, 0, 1, 0, 1, 1, 0 } },
486 	{ "struct",         KEYWORD_STRUCT,         { 1, 1, 1, 0, 0, 1, 1 } },
487 	{ "switch",         KEYWORD_SWITCH,         { 1, 1, 1, 1, 0, 1, 1 } },
488 	{ "synchronized",   KEYWORD_SYNCHRONIZED,   { 0, 0, 0, 1, 0, 0, 1 } },
489 	{ "task",           KEYWORD_TASK,           { 0, 0, 0, 0, 1, 0, 0 } },
490 	{ "template",       KEYWORD_TEMPLATE,       { 0, 1, 0, 0, 0, 0, 0 } },
491 	{ "template",       KEYWORD_NAMESPACE,      { 0, 0, 0, 0, 0, 0, 1 } },	/* parse block */
492 	{ "this",           KEYWORD_THIS,           { 0, 0, 1, 1, 0, 1, 0 } },	/* 0 to allow D ctor tags */
493 	{ "throw",          KEYWORD_THROW,          { 0, 1, 1, 1, 0, 1, 1 } },
494 	{ "throws",         KEYWORD_THROWS,         { 0, 0, 0, 1, 0, 1, 0 } },
495 	{ "trans",          KEYWORD_TRANS,          { 0, 0, 0, 0, 1, 0, 0 } },
496 	{ "transition",     KEYWORD_TRANSITION,     { 0, 0, 0, 0, 1, 0, 0 } },
497 	{ "transient",      KEYWORD_TRANSIENT,      { 0, 0, 0, 1, 0, 0, 0 } },
498 	{ "try",            KEYWORD_TRY,            { 0, 1, 1, 0, 0, 1, 1 } },
499 	{ "typedef",        KEYWORD_TYPEDEF,        { 1, 1, 1, 0, 1, 0, 1 } },
500 	{ "typename",       KEYWORD_TYPENAME,       { 0, 1, 0, 0, 0, 0, 0 } },
501 	{ "uint",           KEYWORD_UINT,           { 0, 0, 1, 0, 0, 1, 1 } },
502 	{ "ulong",          KEYWORD_ULONG,          { 0, 0, 1, 0, 0, 1, 1 } },
503 	{ "union",          KEYWORD_UNION,          { 1, 1, 0, 0, 0, 0, 1 } },
504 	{ "unsigned",       KEYWORD_UNSIGNED,       { 1, 1, 1, 0, 0, 0, 1 } },
505 	{ "ushort",         KEYWORD_USHORT,         { 0, 0, 1, 0, 0, 1, 1 } },
506 	{ "using",          KEYWORD_USING,          { 0, 1, 1, 0, 0, 1, 0 } },
507 	{ "virtual",        KEYWORD_VIRTUAL,        { 0, 1, 1, 0, 1, 1, 0 } },
508 	{ "void",           KEYWORD_VOID,           { 1, 1, 1, 1, 1, 1, 1 } },
509 	{ "volatile",       KEYWORD_VOLATILE,       { 1, 1, 1, 1, 0, 0, 1 } },
510 	{ "wchar_t",        KEYWORD_WCHAR_T,        { 0, 1, 1, 0, 0, 0, 0 } },
511 	{ "weak",           KEYWORD_WEAK,           { 0, 0, 0, 0, 0, 1, 0 } },
512 	{ "while",          KEYWORD_WHILE,          { 1, 1, 1, 1, 0, 1, 1 } }
513 };
514 
515 
516 /*
517 *   FUNCTION PROTOTYPES
518 */
519 static void createTags (const unsigned int nestLevel, statementInfo *const parent);
520 static void copyToken (tokenInfo *const dest, const tokenInfo *const src);
521 static const char *getVarType (const statementInfo *const st,
522 							   const tokenInfo *const token);
523 
524 /*
525 *   FUNCTION DEFINITIONS
526 */
527 
528 /* Debugging functions added by Biswa */
529 #if defined(DEBUG_C) && DEBUG_C
530 static char *tokenTypeName[] = {
531 	"none", "args", "'}'", "'{'", "','", "'::'", "keyword", "name",
532 	"package", "paren-name", "';'",	"spec", "*", "[]", "count"
533 };
534 
535 static char *tagScopeNames[] = {
536 	"global", "static", "extern", "friend", "typedef", "count"};
537 
538 static char *declTypeNames[] = {
539 	"none", "base", "class", "enum", "function", "ignore", "interface",
540 	"namespace", "nomangle", "package", "struct", "union", "count"};
541 
542 static char *impTypeNames[] = {
543 	"default", "abstract", "virtual", "pure-virtual", "count"};
544 
printToken(const tokenInfo * const token)545 void printToken(const tokenInfo *const token)
546 {
547 	fprintf(stderr, "Type: %s, Keyword: %d, name: %s\n", tokenTypeName[token->type],
548 			token->keyword, vStringValue(token->name));
549 }
550 
printTagEntry(const tagEntryInfo * tag)551 void printTagEntry(const tagEntryInfo *tag)
552 {
553 	fprintf(stderr, "Tag: %s (%s) [ impl: %s, scope: %s, type: %s\n", tag->name,
554 	tag->kindName, tag->extensionFields.implementation, tag->extensionFields.scope[1],
555 	tag->extensionFields.varType);
556 }
557 
printStatement(const statementInfo * const statement)558 void printStatement(const statementInfo *const statement)
559 {
560 	int i;
561 	statementInfo *st = (statementInfo *) statement;
562 	while (NULL != st)
563 	{
564 		fprintf(stderr, "Statement Info:\n------------------------\n");
565 		fprintf(stderr, "scope: %s, decl: %s, impl: %s\n", tagScopeNames[st->scope],
566 				declTypeNames[st->declaration], impTypeNames[st->implementation]);
567 		for (i=0; i < NumTokens; ++i)
568 		{
569 			fprintf(stderr, "Token %d %s: ", i, (i == st->tokenIndex)?"(current)":"");
570 			printToken(st->token[i]);
571 		}
572 		fprintf(stderr, "Context: ");
573 		printToken(st->context);
574 		fprintf(stderr, "Block: ");
575 		printToken(st->blockName);
576 		fprintf(stderr, "Parent classes: %s\n", vStringValue(st->parentClasses));
577 		fprintf(stderr, "First token: ");
578 		printToken(st->firstToken);
579 		if (NULL != st->parent)
580 			fprintf(stderr, "Printing Parent:\n");
581 		st = st->parent;
582 	}
583 	fprintf(stderr, "-----------------------------------------------\n");
584 }
585 #endif
586 
587 /*
588 *   Token management
589 */
590 
initToken(tokenInfo * const token)591 static void initToken (tokenInfo* const token)
592 {
593 	token->type			= TOKEN_NONE;
594 	token->keyword		= KEYWORD_NONE;
595 	token->lineNumber	= getInputLineNumber ();
596 	token->filePosition	= getInputFilePosition ();
597 	vStringClear (token->name);
598 }
599 
advanceToken(statementInfo * const st)600 static void advanceToken (statementInfo* const st)
601 {
602 	if (st->tokenIndex >= (unsigned int) NumTokens - 1)
603 		st->tokenIndex = 0;
604 	else
605 		++st->tokenIndex;
606 	initToken (st->token [st->tokenIndex]);
607 }
608 
prevToken(const statementInfo * const st,unsigned int n)609 static tokenInfo *prevToken (const statementInfo *const st, unsigned int n)
610 {
611 	unsigned int tokenIndex;
612 	unsigned int num = (unsigned int) NumTokens;
613 	Assert (n < num);
614 	tokenIndex = (st->tokenIndex + num - n) % num;
615 	return st->token [tokenIndex];
616 }
617 
setToken(statementInfo * const st,const tokenType type)618 static void setToken (statementInfo *const st, const tokenType type)
619 {
620 	tokenInfo *token;
621 	token = activeToken (st);
622 	initToken (token);
623 	token->type = type;
624 }
625 
retardToken(statementInfo * const st)626 static void retardToken (statementInfo *const st)
627 {
628 	if (st->tokenIndex == 0)
629 		st->tokenIndex = (unsigned int) NumTokens - 1;
630 	else
631 		--st->tokenIndex;
632 	setToken (st, TOKEN_NONE);
633 }
634 
newToken(void)635 static tokenInfo *newToken (void)
636 {
637 	tokenInfo *const token = xMalloc (1, tokenInfo);
638 	token->name = vStringNew ();
639 	initToken (token);
640 	return token;
641 }
642 
deleteToken(tokenInfo * const token)643 static void deleteToken (tokenInfo *const token)
644 {
645 	if (token != NULL)
646 	{
647 		vStringDelete (token->name);
648 		eFree (token);
649 	}
650 }
651 
accessString(const accessType laccess)652 static const char *accessString (const accessType laccess)
653 {
654 	static const char *const names [] = {
655 		"?", "private", "protected", "public", "default"
656 	};
657 	Assert (ARRAY_SIZE (names) == ACCESS_COUNT);
658 	Assert ((int) laccess < ACCESS_COUNT);
659 	return names[(int) laccess];
660 }
661 
implementationString(const impType imp)662 static const char *implementationString (const impType imp)
663 {
664 	static const char *const names [] = {
665 		"?", "abstract", "virtual", "pure virtual"
666 	};
667 	Assert (ARRAY_SIZE (names) == IMP_COUNT);
668 	Assert ((int) imp < IMP_COUNT);
669 	return names [(int) imp];
670 }
671 
672 /*
673 *   Debugging functions
674 */
675 
676 #ifdef DEBUG
677 
678 #define boolString(c)   ((c) ? "TRUE" : "FALSE")
679 
tokenString(const tokenType type)680 static const char *tokenString (const tokenType type)
681 {
682 	static const char *const names [] = {
683 		"none", "args", "}", "{", "comma", "double colon", "keyword", "name",
684 		"package", "paren-name", "semicolon", "specifier", "*", "[]"
685 	};
686 	Assert (ARRAY_SIZE (names) == TOKEN_COUNT);
687 	Assert ((int) type < TOKEN_COUNT);
688 	return names [(int) type];
689 }
690 
scopeString(const tagScope scope)691 static const char *scopeString (const tagScope scope)
692 {
693 	static const char *const names [] = {
694 		"global", "static", "extern", "friend", "typedef"
695 	};
696 	Assert (ARRAY_SIZE (names) == SCOPE_COUNT);
697 	Assert ((int) scope < SCOPE_COUNT);
698 	return names [(int) scope];
699 }
700 
declString(const declType declaration)701 static const char *declString (const declType declaration)
702 {
703 	static const char *const names [] = {
704 		"?", "base", "class", "enum", "event", "signal", "function",
705 		"function template", "ignore", "interface", "module", "namespace",
706 		"no mangle", "package", "struct", "union",
707 	};
708 	Assert (ARRAY_SIZE (names) == DECL_COUNT);
709 	Assert ((int) declaration < DECL_COUNT);
710 	return names [(int) declaration];
711 }
712 
keywordString(const keywordId keyword)713 static const char *keywordString (const keywordId keyword)
714 {
715 	const size_t count = ARRAY_SIZE (KeywordTable);
716 	const char *name = "none";
717 	size_t i;
718 	for (i = 0  ;  i < count  ;  ++i)
719 	{
720 		const keywordDesc *p = &KeywordTable [i];
721 		if (p->id == keyword)
722 		{
723 			name = p->name;
724 			break;
725 		}
726 	}
727 	return name;
728 }
729 
pt(tokenInfo * const token)730 static void CTAGS_ATTR_UNUSED pt (tokenInfo *const token)
731 {
732 	if (isType (token, TOKEN_NAME))
733 		printf ("type: %-12s: %-13s   line: %lu\n",
734 			tokenString (token->type), vStringValue (token->name),
735 			token->lineNumber);
736 	else if (isType (token, TOKEN_KEYWORD))
737 		printf ("type: %-12s: %-13s   line: %lu\n",
738 			tokenString (token->type), keywordString (token->keyword),
739 			token->lineNumber);
740 	else
741 		printf ("type: %-12s                  line: %lu\n",
742 			tokenString (token->type), token->lineNumber);
743 }
744 
ps(statementInfo * const st)745 static void CTAGS_ATTR_UNUSED ps (statementInfo *const st)
746 {
747 	unsigned int i;
748 	printf("scope: %s   decl: %s   gotName: %s   gotParenName: %s\n",
749 		   scopeString (st->scope), declString (st->declaration),
750 		   boolString (st->gotName), boolString (st->gotParenName));
751 	printf("haveQualifyingName: %s\n", boolString (st->haveQualifyingName));
752 	printf("access: %s   default: %s\n", accessString (st->member.access),
753 		   accessString (st->member.accessDefault));
754 	printf("token  : ");
755 	pt(activeToken (st));
756 	for (i = 1  ;  i < (unsigned int) NumTokens  ;  ++i)
757 	{
758 		printf("prev %u : ", i);
759 		pt(prevToken (st, i));
760 	}
761 	printf("context: ");
762 	pt(st->context);
763 }
764 
765 #endif
766 
767 /*
768 *   Statement management
769 */
770 
isDataTypeKeyword(const tokenInfo * const token)771 static bool isDataTypeKeyword (const tokenInfo *const token)
772 {
773 	switch (token->keyword)
774 	{
775 		case KEYWORD_BOOLEAN:
776 		case KEYWORD_BYTE:
777 		case KEYWORD_CHAR:
778 		case KEYWORD_DOUBLE:
779 		case KEYWORD_FLOAT:
780 		case KEYWORD_INT:
781 		case KEYWORD_LONG:
782 		case KEYWORD_SHORT:
783 		case KEYWORD_VOID:
784 		case KEYWORD_WCHAR_T:
785 		case KEYWORD_SIZE_T:
786 			return true;
787 		default:
788 			return false;
789 	}
790 }
791 
792 #if 0
793 static bool isVariableKeyword (const tokenInfo *const token)
794 {
795 	switch (token->keyword)
796 	{
797 		case KEYWORD_CONST:
798 		case KEYWORD_EXTERN:
799 		case KEYWORD_REGISTER:
800 		case KEYWORD_STATIC:
801 		case KEYWORD_VIRTUAL:
802 		case KEYWORD_SIGNED:
803 		case KEYWORD_UNSIGNED:
804 			return true;
805 		default:
806 			return false;
807 	}
808 }
809 #endif
810 
isContextualKeyword(const tokenInfo * const token)811 static bool isContextualKeyword (const tokenInfo *const token)
812 {
813 	bool result;
814 	switch (token->keyword)
815 	{
816 		case KEYWORD_CLASS:
817 		case KEYWORD_ENUM:
818 		case KEYWORD_INTERFACE:
819 		case KEYWORD_NAMESPACE:
820 		case KEYWORD_STRUCT:
821 		case KEYWORD_UNION:
822 		{
823 			result = true;
824 			break;
825 		}
826 
827 		default:
828 		{
829 			result = false;
830 			break;
831 		}
832 	}
833 	return result;
834 }
835 
isContextualStatement(const statementInfo * const st)836 static bool isContextualStatement (const statementInfo *const st)
837 {
838 	bool result = false;
839 
840 	if (st != NULL)
841 	{
842 		if (isInputLanguage (Lang_vala))
843 		{
844 			/* All can be a contextual statement as properties can be of any type */
845 			result = true;
846 		}
847 		else
848 		{
849 			switch (st->declaration)
850 			{
851 				case DECL_CLASS:
852 				case DECL_ENUM:
853 				case DECL_INTERFACE:
854 				case DECL_NAMESPACE:
855 				case DECL_STRUCT:
856 				case DECL_UNION:
857 				{
858 					result = true;
859 					break;
860 				}
861 
862 				default:
863 				{
864 					result = false;
865 					break;
866 				}
867 			}
868 		}
869 	}
870 	return result;
871 }
872 
isMember(const statementInfo * const st)873 static bool isMember (const statementInfo *const st)
874 {
875 	bool result;
876 	if (isType (st->context, TOKEN_NAME))
877 		result = true;
878 	else
879 		result = isContextualStatement (st->parent);
880 	return result;
881 }
882 
initMemberInfo(statementInfo * const st)883 static void initMemberInfo (statementInfo *const st)
884 {
885 	accessType accessDefault = ACCESS_UNDEFINED;
886 
887 	if (st->parent != NULL) switch (st->parent->declaration)
888 	{
889 		case DECL_ENUM:
890 		case DECL_NAMESPACE:
891 		{
892 			accessDefault = ACCESS_UNDEFINED;
893 			break;
894 		}
895 		case DECL_CLASS:
896 		{
897 			if (isInputLanguage (Lang_java))
898 				accessDefault = ACCESS_DEFAULT;
899 			else
900 				accessDefault = ACCESS_PRIVATE;
901 			break;
902 		}
903 		case DECL_INTERFACE:
904 		case DECL_STRUCT:
905 		case DECL_UNION:
906 		{
907 			accessDefault = ACCESS_PUBLIC;
908 			break;
909 		}
910 		default:
911 			break;
912 	}
913 	st->member.accessDefault = accessDefault;
914 	st->member.access		 = accessDefault;
915 }
916 
reinitStatement(statementInfo * const st,const bool partial)917 static void reinitStatement (statementInfo *const st, const bool partial)
918 {
919 	unsigned int i;
920 
921 	if (! partial)
922 	{
923 		st->scope = SCOPE_GLOBAL;
924 		if (isContextualStatement (st->parent))
925 			st->declaration = DECL_BASE;
926 		else
927 			st->declaration = DECL_NONE;
928 	}
929 	st->gotParenName		= false;
930 	st->implementation		= IMP_DEFAULT;
931 	st->gotArgs				= false;
932 	st->gotName				= false;
933 	st->nSemicolons			= 0;
934 	st->haveQualifyingName	= false;
935 
936 	st->tokenIndex			= 0;
937 	for (i = 0  ;  i < (unsigned int) NumTokens  ;  ++i)
938 	{
939 		initToken (st->token [i]);
940 	}
941 
942 	initToken (st->context);
943 	initToken (st->blockName);
944 	vStringClear (st->parentClasses);
945 	cppClearSignature ();
946 
947 	/* Init member info. */
948 	if (! partial)
949 		st->member.access = st->member.accessDefault;
950 
951 	/* Init first token */
952 	if (!partial)
953 		initToken(st->firstToken);
954 }
955 
reinitStatementWithToken(statementInfo * const st,tokenInfo * token,const bool partial)956 static void reinitStatementWithToken (statementInfo *const st,
957 									  tokenInfo *token, const bool partial)
958 {
959 	tokenInfo *const save = newToken ();
960 	/* given token can be part of reinit statementInfo */
961 	copyToken (save, token);
962 	reinitStatement (st, partial);
963 	token = activeToken (st);
964 	copyToken (token, save);
965 	deleteToken (save);
966 	++st->tokenIndex;	/* this is quite safe because current tokenIndex = 0 */
967 }
968 
initStatement(statementInfo * const st,statementInfo * const parent)969 static void initStatement (statementInfo *const st, statementInfo *const parent)
970 {
971 	st->parent = parent;
972 	initMemberInfo (st);
973 	reinitStatement (st, false);
974 	if (parent)
975 	{
976 		const tokenInfo *const src = activeToken (parent);
977 		tokenInfo *const dst = activeToken (st);
978 		copyToken (dst, src);
979 		st->tokenIndex++;
980 	}
981 }
982 
983 /*
984 *   Tag generation functions
985 */
cTagKind(const tagType type)986 static cKind cTagKind (const tagType type)
987 {
988 	cKind result = CK_UNDEFINED;
989 	switch (type)
990 	{
991 		case TAG_CLASS:      result = CK_CLASS;       break;
992 		case TAG_ENUM:       result = CK_ENUMERATION; break;
993 		case TAG_ENUMERATOR: result = CK_ENUMERATOR;  break;
994 		case TAG_FUNCTION:   result = CK_FUNCTION;    break;
995 		case TAG_MEMBER:     result = CK_MEMBER;      break;
996 		case TAG_NAMESPACE:  result = CK_NAMESPACE;   break;
997 		case TAG_PROTOTYPE:  result = CK_PROTOTYPE;   break;
998 		case TAG_STRUCT:     result = CK_STRUCT;      break;
999 		case TAG_TYPEDEF:    result = CK_TYPEDEF;     break;
1000 		case TAG_UNION:      result = CK_UNION;       break;
1001 		case TAG_VARIABLE:   result = CK_VARIABLE;    break;
1002 		case TAG_EXTERN_VAR: result = CK_EXTERN_VARIABLE; break;
1003 
1004 		default: Assert ("Bad C tag type" == NULL); break;
1005 	}
1006 	return result;
1007 }
1008 
csharpTagKind(const tagType type)1009 static csharpKind csharpTagKind (const tagType type)
1010 {
1011 	csharpKind result = CSK_UNDEFINED;
1012 	switch (type)
1013 	{
1014 		case TAG_CLASS:      result = CSK_CLASS;           break;
1015 		case TAG_ENUM:       result = CSK_ENUMERATION;     break;
1016 		case TAG_ENUMERATOR: result = CSK_ENUMERATOR;      break;
1017 		case TAG_EVENT:      result = CSK_EVENT;           break;
1018 		case TAG_FIELD:      result = CSK_FIELD ;          break;
1019 		case TAG_INTERFACE:  result = CSK_INTERFACE;       break;
1020 		case TAG_LOCAL:      result = CSK_LOCAL;           break;
1021 		case TAG_METHOD:     result = CSK_METHOD;          break;
1022 		case TAG_NAMESPACE:  result = CSK_NAMESPACE;       break;
1023 		case TAG_PROPERTY:   result = CSK_PROPERTY;        break;
1024 		case TAG_STRUCT:     result = CSK_STRUCT;          break;
1025 		case TAG_TYPEDEF:    result = CSK_TYPEDEF;         break;
1026 
1027 		default: Assert ("Bad C# tag type" == NULL); break;
1028 	}
1029 	return result;
1030 }
1031 
dTagKind(const tagType type)1032 static dKind dTagKind (const tagType type)
1033 {
1034 	dKind result = DK_UNDEFINED;
1035 	switch (type)
1036 	{
1037 		case TAG_CLASS:      result = DK_CLASS;           break;
1038 		case TAG_ENUM:       result = DK_ENUMERATION;     break;
1039 		case TAG_ENUMERATOR: result = DK_ENUMERATOR;      break;
1040 		case TAG_FUNCTION:   result = DK_FUNCTION;        break;
1041 		case TAG_INTERFACE:  result = DK_INTERFACE;       break;
1042 		case TAG_MEMBER:     result = DK_MEMBER;          break;
1043 		case TAG_NAMESPACE:  result = DK_NAMESPACE;       break;
1044 		case TAG_PROTOTYPE:  result = DK_PROTOTYPE;       break;
1045 		case TAG_STRUCT:     result = DK_STRUCT;          break;
1046 		case TAG_TYPEDEF:    result = DK_TYPEDEF;         break;
1047 		case TAG_UNION:      result = DK_UNION;           break;
1048 		case TAG_VARIABLE:   result = DK_VARIABLE;        break;
1049 		case TAG_EXTERN_VAR: result = DK_EXTERN_VARIABLE; break;
1050 
1051 		default: Assert ("Bad D tag type" == NULL); break;
1052 	}
1053 	return result;
1054 }
1055 
valaTagKind(const tagType type)1056 static valaKind valaTagKind (const tagType type)
1057 {
1058 	valaKind result = VK_UNDEFINED;
1059 	switch (type)
1060 	{
1061 		case TAG_CLASS:      result = VK_CLASS;           break;
1062 		case TAG_ENUM:       result = VK_ENUMERATION;     break;
1063 		case TAG_ENUMERATOR: result = VK_ENUMERATOR;      break;
1064 		case TAG_SIGNAL:     result = VK_SIGNAL;          break;
1065 		case TAG_FIELD:      result = VK_FIELD ;          break;
1066 		case TAG_INTERFACE:  result = VK_INTERFACE;       break;
1067 		case TAG_LOCAL:      result = VK_LOCAL;           break;
1068 		case TAG_METHOD:     result = VK_METHOD;          break;
1069 		case TAG_NAMESPACE:  result = VK_NAMESPACE;       break;
1070 		case TAG_PROPERTY:   result = VK_PROPERTY;        break;
1071 		case TAG_STRUCT:     result = VK_STRUCT;          break;
1072 
1073 		default: Assert ("Bad Vala tag type" == NULL); break;
1074 	}
1075 	return result;
1076 }
1077 
javaTagKind(const tagType type)1078 static javaKind javaTagKind (const tagType type)
1079 {
1080 	javaKind result = JK_UNDEFINED;
1081 	switch (type)
1082 	{
1083 		case TAG_CLASS:      result = JK_CLASS;         break;
1084 		case TAG_FIELD:      result = JK_FIELD;         break;
1085 		case TAG_INTERFACE:  result = JK_INTERFACE;     break;
1086 		case TAG_METHOD:     result = JK_METHOD;        break;
1087 		case TAG_PACKAGE:    result = JK_PACKAGE;       break;
1088 		case TAG_ENUM:       result = JK_ENUMERATION;   break;
1089 		case TAG_ENUMERATOR: result = JK_ENUMERATOR;    break;
1090 
1091 		default: Assert ("Bad Java tag type" == NULL); break;
1092 	}
1093 	return result;
1094 }
1095 
kindIndexForType(const tagType type)1096 static int kindIndexForType (const tagType type)
1097 {
1098 	int result;
1099 	if (isInputLanguage (Lang_java))
1100 		result = javaTagKind (type);
1101 	else if (isInputLanguage (Lang_csharp))
1102 		result = csharpTagKind (type);
1103 	else if (isInputLanguage (Lang_d))
1104 		result = dTagKind (type);
1105 	else if (isInputLanguage (Lang_vala))
1106 		result = valaTagKind (type);
1107 	else
1108 		result = cTagKind (type);
1109 	return result;
1110 }
1111 
1112 /*
1113 static bool includeTag (const tagType type, const bool isFileScope)
1114 {
1115 	bool result;
1116 	if (isFileScope  &&  ! Option.include.fileScope)
1117 		result = false;
1118 	else if (isInputLanguage (Lang_java))
1119 		result = JavaKinds [javaTagKind (type)].enabled;
1120 	else
1121 		result = CKinds [cTagKind (type)].enabled;
1122 	return result;
1123 }
1124 */
1125 
declToTagType(const declType declaration)1126 static tagType declToTagType (const declType declaration)
1127 {
1128 	tagType type = TAG_UNDEFINED;
1129 
1130 	switch (declaration)
1131 	{
1132 		case DECL_CLASS:        type = TAG_CLASS;       break;
1133 		case DECL_ENUM:         type = TAG_ENUM;        break;
1134 		case DECL_FUNCTION:     type = TAG_FUNCTION;    break;
1135 		case DECL_FUNCTION_TEMPLATE: type = TAG_FUNCTION; break;
1136 		case DECL_INTERFACE:    type = TAG_INTERFACE;   break;
1137 		case DECL_NAMESPACE:    type = TAG_NAMESPACE;   break;
1138 		case DECL_STRUCT:       type = TAG_STRUCT;      break;
1139 		case DECL_UNION:        type = TAG_UNION;       break;
1140 
1141 		default: Assert ("Unexpected declaration" == NULL); break;
1142 	}
1143 	return type;
1144 }
1145 
accessField(const statementInfo * const st)1146 static const char* accessField (const statementInfo *const st)
1147 {
1148 	const char* result = NULL;
1149 
1150 	if ((isInputLanguage (Lang_cpp) || isInputLanguage (Lang_d) || isInputLanguage (Lang_ferite))  &&
1151 			st->scope == SCOPE_FRIEND)
1152 		result = "friend";
1153 	else if (st->member.access != ACCESS_UNDEFINED)
1154 		result = accessString (st->member.access);
1155 	return result;
1156 }
1157 
addOtherFields(tagEntryInfo * const tag,const tagType type,const tokenInfo * const nameToken,const statementInfo * const st,vString * const scope)1158 static void addOtherFields (tagEntryInfo* const tag, const tagType type,
1159 							const tokenInfo *const nameToken,
1160 							const statementInfo *const st, vString *const scope)
1161 {
1162 	/*  For selected tag types, append an extension flag designating the
1163 	 *  parent object in which the tag is defined.
1164 	 */
1165 	switch (type)
1166 	{
1167 		default: break;
1168 
1169 		case TAG_NAMESPACE:
1170 		case TAG_CLASS:
1171 		case TAG_ENUM:
1172 		case TAG_ENUMERATOR:
1173 		case TAG_FIELD:
1174 		case TAG_FUNCTION:
1175 		case TAG_INTERFACE:
1176 		case TAG_MEMBER:
1177 		case TAG_METHOD:
1178 		case TAG_PROTOTYPE:
1179 		case TAG_STRUCT:
1180 		case TAG_TYPEDEF:
1181 		case TAG_UNION:
1182 		{
1183 			if (vStringLength (scope) > 0  &&
1184 				(isMember (st) || st->parent->declaration == DECL_NAMESPACE))
1185 			{
1186 				if (isType (st->context, TOKEN_NAME))
1187 					tag->extensionFields.scopeKindIndex = kindIndexForType (TAG_CLASS);
1188 				else
1189 					tag->extensionFields.scopeKindIndex =
1190 						kindIndexForType (declToTagType (parentDecl (st)));
1191 				tag->extensionFields.scopeName = vStringValue (scope);
1192 			}
1193 			if ((type == TAG_CLASS  ||  type == TAG_INTERFACE  ||
1194 				 type == TAG_STRUCT) && vStringLength (st->parentClasses) > 0)
1195 			{
1196 				tag->extensionFields.inheritance =
1197 						vStringValue (st->parentClasses);
1198 			}
1199 			if (st->implementation != IMP_DEFAULT &&
1200 				(isInputLanguage (Lang_cpp) || isInputLanguage (Lang_csharp) || isInputLanguage (Lang_vala) ||
1201 				 isInputLanguage (Lang_java) || isInputLanguage (Lang_d) || isInputLanguage (Lang_ferite)))
1202 			{
1203 				tag->extensionFields.implementation =
1204 						implementationString (st->implementation);
1205 			}
1206 			if (isMember (st))
1207 			{
1208 				tag->extensionFields.access = accessField (st);
1209 			}
1210             if ((true == st->gotArgs) &&
1211 				((TAG_FUNCTION == type) || (TAG_METHOD == type) || (TAG_PROTOTYPE == type)))
1212 			{
1213 				tag->extensionFields.signature = cppGetSignature ();
1214 			}
1215 			break;
1216 		}
1217 	}
1218 
1219 	if ((TAG_FIELD == type) || (TAG_MEMBER == type) ||
1220 		(TAG_EXTERN_VAR == type) || (TAG_TYPEDEF == type) ||
1221 		(TAG_VARIABLE == type) || (TAG_METHOD == type) ||
1222 		(TAG_PROTOTYPE == type) || (TAG_FUNCTION == type))
1223 	{
1224 		if (((TOKEN_NAME == st->firstToken->type) || isDataTypeKeyword(st->firstToken))
1225 			&& (0 != strcmp(vStringValue(st->firstToken->name), tag->name)))
1226 		{
1227 			tag->extensionFields.typeRef[1] = getVarType(st, nameToken);
1228 		}
1229 	}
1230 }
1231 
getVarType(const statementInfo * const st,const tokenInfo * const nameToken)1232 static const char *getVarType (const statementInfo *const st,
1233 							   const tokenInfo *const nameToken)
1234 {
1235 	static vString *vt = NULL;
1236 	unsigned int i;
1237 	unsigned int end = st->tokenIndex;
1238 	bool seenType = false;
1239 
1240 	switch (st->declaration) {
1241 		case DECL_BASE:
1242 		case DECL_FUNCTION:
1243 		case DECL_FUNCTION_TEMPLATE:
1244 			break;
1245 		default:
1246 			return vStringValue(st->firstToken->name);
1247 	}
1248 
1249 	if (vt == NULL)
1250 		vt = vStringNew();
1251 	else
1252 		vStringClear(vt);
1253 
1254 	/* find the end of the type signature in the token list */
1255 	for (i = 0; i < st->tokenIndex; i++)
1256 	{
1257 		const tokenInfo *const t = st->token[i];
1258 
1259 		/* stop if we find the token used to generate the tag name, or
1260 		 * a name token in the middle yet not preceded by a scope separator */
1261 		if ((t == nameToken ||
1262 		     (t->type == nameToken->type &&
1263 		      t->keyword == nameToken->keyword &&
1264 		      t->lineNumber == nameToken->lineNumber &&
1265 		      strcmp(vStringValue(t->name), vStringValue(nameToken->name)) == 0)) ||
1266 		    (t->type == TOKEN_NAME && seenType &&
1267 		     (i > 0 && st->token[i - 1]->type != TOKEN_DOUBLE_COLON)))
1268 		{
1269 			break;
1270 		}
1271 		if (t->type != TOKEN_DOUBLE_COLON)
1272 			end = i + 1;
1273 		if (t->type == TOKEN_NAME)
1274 			seenType = true;
1275 		else if (t->type == TOKEN_KEYWORD && isDataTypeKeyword(t))
1276 			seenType = true;
1277 	}
1278 
1279 	/* ugly historic workaround when we can't figure out the type */
1280 	if (end < 2 && ! st->gotArgs)
1281 		return vStringValue(st->firstToken->name);
1282 
1283 	for (i = 0; i < end; i++)
1284 	{
1285 		tokenInfo *t = st->token[i];
1286 
1287 		switch (t->type)
1288 		{
1289 			case TOKEN_NAME:	/* user typename */
1290 				break;
1291 			case TOKEN_KEYWORD:
1292 				if ((t->keyword != KEYWORD_EXTERN && t->keyword != KEYWORD_STATIC) &&	/* uninteresting keywords */
1293 				    (st->gotArgs ||
1294 				     /* ignore uninteresting keywords for non-functions */
1295 				     (t->keyword != KEYWORD_PUBLIC &&
1296 				      t->keyword != KEYWORD_PRIVATE &&
1297 				      t->keyword != KEYWORD_PROTECTED &&
1298 				      t->keyword != KEYWORD_FINAL &&
1299 				      t->keyword != KEYWORD_TYPEDEF &&
1300 				      /* hack for D static conditions */
1301 				      t->keyword != KEYWORD_IF)))
1302 				{
1303 					break;
1304 				}
1305 				continue;
1306 			case TOKEN_STAR: vStringCatS(vt, " *"); continue;
1307 			case TOKEN_ARRAY: vStringCatS(vt, "[]"); continue;
1308 			case TOKEN_DOUBLE_COLON:
1309 				vStringCatS(vt, "::");
1310 				continue;
1311 			default: continue;
1312 		}
1313 		if (vStringLength(vt) > 0)
1314 			if (isalpha(vStringValue(vt)[vStringLength(vt) - 1]))
1315 				vStringPut(vt, ' ');
1316 		vStringCat(vt, t->name);
1317 	}
1318 	return vStringValue(vt);
1319 }
1320 
addContextSeparator(vString * const scope)1321 static void addContextSeparator (vString *const scope)
1322 {
1323 	if (isInputLanguage (Lang_c)  ||  isInputLanguage (Lang_cpp))
1324 		vStringCatS (scope, "::");
1325 	else if (isInputLanguage (Lang_java) || isInputLanguage (Lang_d) || isInputLanguage (Lang_ferite) ||
1326 			 isInputLanguage (Lang_csharp) || isInputLanguage (Lang_vala))
1327 		vStringCatS (scope, ".");
1328 }
1329 
findScopeHierarchy(vString * const string,const statementInfo * const st)1330 static void findScopeHierarchy (vString *const string,
1331 								const statementInfo *const st)
1332 {
1333 	const char* const anon = "<anonymous>";
1334 	bool nonAnonPresent = false;
1335 
1336 	vStringClear (string);
1337 	if (isType (st->context, TOKEN_NAME))
1338 	{
1339 		vStringCopy (string, st->context->name);
1340 		nonAnonPresent = true;
1341 	}
1342 	if (st->parent != NULL)
1343 	{
1344 		vString *temp = vStringNew ();
1345 		const statementInfo *s;
1346 
1347 		for (s = st->parent  ;  s != NULL  ;  s = s->parent)
1348 		{
1349 			if (isContextualStatement (s) ||
1350 				s->declaration == DECL_NAMESPACE)
1351 			{
1352 				vStringCopy (temp, string);
1353 				vStringClear (string);
1354 				if (isType (s->blockName, TOKEN_NAME))
1355 				{
1356 					if (isType (s->context, TOKEN_NAME) &&
1357 						vStringLength (s->context->name) > 0)
1358 					{
1359 						vStringCat (string, s->context->name);
1360 						addContextSeparator (string);
1361 					}
1362 					vStringCat (string, s->blockName->name);
1363 					nonAnonPresent = true;
1364 				}
1365 				else
1366 					vStringCopyS (string, anon);
1367 				if (vStringLength (temp) > 0)
1368 					addContextSeparator (string);
1369 				vStringCat (string, temp);
1370 			}
1371 		}
1372 		vStringDelete (temp);
1373 
1374 		if (! nonAnonPresent)
1375 			vStringClear (string);
1376 	}
1377 }
1378 
makeExtraTagEntry(const tagType type,tagEntryInfo * const e,vString * const scope)1379 static void makeExtraTagEntry (const tagType type, tagEntryInfo *const e,
1380 							   vString *const scope)
1381 {
1382 	if (isXtagEnabled(XTAG_QUALIFIED_TAGS)  &&
1383 		scope != NULL  &&  vStringLength (scope) > 0)
1384 	{
1385 		vString *const scopedName = vStringNew ();
1386 
1387 		if (type != TAG_ENUMERATOR)
1388 			vStringCopy (scopedName, scope);
1389 		else
1390 		{
1391 			/* remove last component (i.e. enumeration name) from scope */
1392 			const char* const sc = vStringValue (scope);
1393 			const char* colon = strrchr (sc, ':');
1394 			if (colon != NULL)
1395 			{
1396 				while (*colon == ':'  &&  colon > sc)
1397 					--colon;
1398 				vStringNCopy (scopedName, scope, colon + 1 - sc);
1399 			}
1400 		}
1401 		if (vStringLength (scopedName) > 0)
1402 		{
1403 			addContextSeparator (scopedName);
1404 			vStringCatS (scopedName, e->name);
1405 			e->name = vStringValue (scopedName);
1406 			makeTagEntry (e);
1407 		}
1408 		vStringDelete (scopedName);
1409 	}
1410 }
1411 
makeTag(const tokenInfo * const token,const statementInfo * const st,bool isFileScope,const tagType type)1412 static void makeTag (const tokenInfo *const token,
1413 					 const statementInfo *const st,
1414 					 bool isFileScope, const tagType type)
1415 {
1416 #ifdef DEBUG_C
1417 	printToken(token);
1418 	fprintf(stderr, "<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>\n");
1419 	printStatement(st);
1420 #endif
1421 	/*  Nothing is really of file scope when it appears in a header file.
1422 	 */
1423 	isFileScope = (bool) (isFileScope && ! isInputHeaderFile ());
1424 
1425 	if (isType (token, TOKEN_NAME)  &&  vStringLength (token->name) > 0  /* &&
1426 		includeTag (type, isFileScope) */)
1427 	{
1428 		vString *scope;
1429 		tagEntryInfo e;
1430 
1431 		/* take only functions which are introduced by "function ..." */
1432 		if (type == TAG_FUNCTION && isInputLanguage (Lang_ferite) &&
1433 			strncmp("function", st->firstToken->name->buffer, 8) != 0)
1434 		{
1435 			return;
1436 		}
1437 
1438 		initTagEntry (&e, vStringValue (token->name), kindIndexForType (type));
1439 
1440 		e.lineNumber	= token->lineNumber;
1441 		e.filePosition	= token->filePosition;
1442 		e.isFileScope = isFileScope;
1443 
1444 		scope = vStringNew ();
1445 		findScopeHierarchy (scope, st);
1446 		addOtherFields (&e, type, token, st, scope);
1447 
1448 #ifdef DEBUG_C
1449 		printTagEntry(&e);
1450 #endif
1451 		makeTagEntry (&e);
1452 		makeExtraTagEntry (type, &e, scope);
1453 		vStringDelete (scope);
1454 		if (NULL != e.extensionFields.signature)
1455 			free((char *) e.extensionFields.signature);
1456 	}
1457 }
1458 
isValidTypeSpecifier(const declType declaration)1459 static bool isValidTypeSpecifier (const declType declaration)
1460 {
1461 	bool result;
1462 	switch (declaration)
1463 	{
1464 		case DECL_BASE:
1465 		case DECL_CLASS:
1466 		case DECL_ENUM:
1467 		case DECL_STRUCT:
1468 		case DECL_UNION:
1469 			result = true;
1470 			break;
1471 
1472 		default:
1473 			result = false;
1474 			break;
1475 	}
1476 	return result;
1477 }
1478 
qualifyEnumeratorTag(const statementInfo * const st,const tokenInfo * const nameToken)1479 static void qualifyEnumeratorTag (const statementInfo *const st,
1480 								  const tokenInfo *const nameToken)
1481 {
1482 	if (isType (nameToken, TOKEN_NAME))
1483 		makeTag (nameToken, st, true, TAG_ENUMERATOR);
1484 }
1485 
qualifyFunctionTag(const statementInfo * const st,const tokenInfo * const nameToken)1486 static void qualifyFunctionTag (const statementInfo *const st,
1487 								const tokenInfo *const nameToken)
1488 {
1489 	if (isType (nameToken, TOKEN_NAME))
1490 	{
1491 		const tagType type = (isInputLanguage (Lang_java) || isInputLanguage (Lang_csharp) || isInputLanguage (Lang_vala))
1492 								? TAG_METHOD : TAG_FUNCTION;
1493 		const bool isFileScope =
1494 						(bool) (st->member.access == ACCESS_PRIVATE ||
1495 						(!isMember (st)  &&  st->scope == SCOPE_STATIC));
1496 
1497 		makeTag (nameToken, st, isFileScope, type);
1498 	}
1499 }
1500 
qualifyFunctionDeclTag(const statementInfo * const st,const tokenInfo * const nameToken)1501 static void qualifyFunctionDeclTag (const statementInfo *const st,
1502 									const tokenInfo *const nameToken)
1503 {
1504 	if (! isType (nameToken, TOKEN_NAME))
1505 		;
1506 	else if (isInputLanguage (Lang_java) || isInputLanguage (Lang_csharp) || isInputLanguage (Lang_vala))
1507 		qualifyFunctionTag (st, nameToken);
1508 	else if (st->scope == SCOPE_TYPEDEF)
1509 		makeTag (nameToken, st, true, TAG_TYPEDEF);
1510 	else if (isValidTypeSpecifier (st->declaration) &&
1511 			 ! (isInputLanguage (Lang_csharp) || isInputLanguage (Lang_vala)))
1512 		makeTag (nameToken, st, true, TAG_PROTOTYPE);
1513 }
1514 
qualifyCompoundTag(const statementInfo * const st,const tokenInfo * const nameToken)1515 static void qualifyCompoundTag (const statementInfo *const st,
1516 								const tokenInfo *const nameToken)
1517 {
1518 	if (isType (nameToken, TOKEN_NAME))
1519 	{
1520 		const tagType type = declToTagType (st->declaration);
1521 
1522 		if (type != TAG_UNDEFINED)
1523 			makeTag (nameToken, st, (bool) (! isInputLanguage (Lang_java) &&
1524 											! isInputLanguage (Lang_csharp) &&
1525 											! isInputLanguage (Lang_vala)), type);
1526     }
1527 }
1528 
qualifyBlockTag(statementInfo * const st,const tokenInfo * const nameToken)1529 static void qualifyBlockTag (statementInfo *const st,
1530 							 const tokenInfo *const nameToken)
1531 {
1532 	switch (st->declaration)
1533 	{
1534 		case DECL_CLASS:
1535 		case DECL_ENUM:
1536 		case DECL_INTERFACE:
1537 		case DECL_NAMESPACE:
1538 		case DECL_STRUCT:
1539 		case DECL_UNION:
1540 			qualifyCompoundTag (st, nameToken);
1541 			break;
1542 		default: break;
1543 	}
1544 }
1545 
qualifyVariableTag(const statementInfo * const st,const tokenInfo * const nameToken)1546 static void qualifyVariableTag (const statementInfo *const st,
1547 								const tokenInfo *const nameToken)
1548 {
1549 	/*	We have to watch that we do not interpret a declaration of the
1550 	 *	form "struct tag;" as a variable definition. In such a case, the
1551 	 *	token preceding the name will be a keyword.
1552 	 */
1553 	if (! isType (nameToken, TOKEN_NAME))
1554 		;
1555 	else if (st->declaration == DECL_IGNORE)
1556 		;
1557 	else if (st->scope == SCOPE_TYPEDEF)
1558 		makeTag (nameToken, st, true, TAG_TYPEDEF);
1559 	else if (st->declaration == DECL_PACKAGE)
1560 		makeTag (nameToken, st, false, TAG_PACKAGE);
1561 	else if (st->declaration == DECL_MODULE) /* handle modules in D as namespaces */
1562 		makeTag (nameToken, st, false, TAG_NAMESPACE);
1563 	else if (isValidTypeSpecifier (st->declaration))
1564 	{
1565 		if (isMember (st))
1566 		{
1567 			if (isInputLanguage (Lang_java) || isInputLanguage (Lang_csharp) || isInputLanguage (Lang_vala))
1568 				makeTag (nameToken, st, (bool) (st->member.access == ACCESS_PRIVATE), TAG_FIELD);
1569 			else if (st->scope == SCOPE_GLOBAL  ||  st->scope == SCOPE_STATIC)
1570 				makeTag (nameToken, st, true, TAG_MEMBER);
1571 		}
1572 		else if (isInputLanguage (Lang_java) || isInputLanguage (Lang_csharp) || isInputLanguage (Lang_vala))
1573 			;
1574 		else
1575 		{
1576 			if (st->scope == SCOPE_EXTERN  ||  ! st->haveQualifyingName)
1577 				makeTag (nameToken, st, false, TAG_EXTERN_VAR);
1578 			else
1579 				makeTag (nameToken, st, (bool) (st->scope == SCOPE_STATIC), TAG_VARIABLE);
1580 		}
1581 	}
1582 }
1583 
1584 /*
1585 *   Parsing functions
1586 */
1587 
skipToOneOf(const char * const chars)1588 static int skipToOneOf (const char *const chars)
1589 {
1590 	int c;
1591 	do
1592 		c = cppGetc ();
1593 	while (c != EOF  &&  c != '\0'  &&  strchr (chars, c) == NULL);
1594 
1595 	return c;
1596 }
1597 
1598 /*  Skip to the next non-white character.
1599  */
skipToNonWhite(void)1600 static int skipToNonWhite (void)
1601 {
1602 	int c;
1603 
1604 	do
1605 	{
1606 		c = cppGetc ();
1607 	}
1608 	while (isspace (c));
1609 
1610 	return c;
1611 }
1612 
1613 /*  Skips to the next brace in column 1. This is intended for cases where
1614  *  preprocessor constructs result in unbalanced braces.
1615  */
skipToFormattedBraceMatch(void)1616 static void skipToFormattedBraceMatch (void)
1617 {
1618 	int c, next;
1619 
1620 	c = cppGetc ();
1621 	next = cppGetc ();
1622 	while (c != EOF  &&  (c != '\n'  ||  next != '}'))
1623 	{
1624 		c = next;
1625 		next = cppGetc ();
1626 	}
1627 }
1628 
1629 /*  Skip to the matching character indicated by the pair string. If skipping
1630  *  to a matching brace and any brace is found within a different level of a
1631  *  #if conditional statement while brace formatting is in effect, we skip to
1632  *  the brace matched by its formatting. It is assumed that we have already
1633  *  read the character which starts the group (i.e. the first character of
1634  *  "pair").
1635  */
skipToMatch(const char * const pair)1636 static void skipToMatch (const char *const pair)
1637 {
1638 	const bool braceMatching = (bool) (strcmp ("{}", pair) == 0);
1639 	const bool braceFormatting = (bool) (cppIsBraceFormat () && braceMatching);
1640 	const unsigned int initialLevel = cppGetDirectiveNestLevel ();
1641 	const int begin = pair [0], end = pair [1];
1642 	const unsigned long inputLineNumber = getInputLineNumber ();
1643 	int matchLevel = 1;
1644 	int c = '\0';
1645 	if (isInputLanguage(Lang_d) && pair[0] == '<')
1646 		return; /* ignore e.g. Foo!(x < 2) */
1647 	while (matchLevel > 0  &&  (c = cppGetc ()) != EOF)
1648 	{
1649 		if (c == begin)
1650 		{
1651 			++matchLevel;
1652 			if (braceFormatting  &&  cppGetDirectiveNestLevel () != initialLevel)
1653 			{
1654 				skipToFormattedBraceMatch ();
1655 				break;
1656 			}
1657 		}
1658 		else if (c == end)
1659 		{
1660 			--matchLevel;
1661 			if (braceFormatting  &&  cppGetDirectiveNestLevel () != initialLevel)
1662 			{
1663 				skipToFormattedBraceMatch ();
1664 				break;
1665 			}
1666 		}
1667 		/* early out if matching "<>" and we encounter a ";" or "{" to mitigate
1668 		 * match problems with C++ generics containing a static expression like
1669 		 *     foo<X<Y> bar;
1670 		 * normally neither ";" nor "{" could appear inside "<>" anyway. */
1671 		else if (isInputLanguage (Lang_cpp) && begin == '<' &&
1672 		         (c == ';' || c == '{'))
1673 		{
1674 			cppUngetc (c);
1675 			break;
1676 		}
1677 	}
1678 	if (c == EOF)
1679 	{
1680 		verbose ("%s: failed to find match for '%c' at line %lu\n",
1681 				getInputFileName (), begin, inputLineNumber);
1682 		if (braceMatching)
1683 			longjmp (Exception, (int) ExceptionBraceFormattingError);
1684 		else
1685 			longjmp (Exception, (int) ExceptionFormattingError);
1686 	}
1687 }
1688 
skipParens(void)1689 static void skipParens (void)
1690 {
1691 	const int c = skipToNonWhite ();
1692 
1693 	if (c == '(')
1694 		skipToMatch ("()");
1695 	else
1696 		cppUngetc (c);
1697 }
1698 
skipBraces(void)1699 static void skipBraces (void)
1700 {
1701 	const int c = skipToNonWhite ();
1702 
1703 	if (c == '{')
1704 		skipToMatch ("{}");
1705 	else
1706 		cppUngetc (c);
1707 }
1708 
analyzeKeyword(const char * const name)1709 static keywordId analyzeKeyword (const char *const name)
1710 {
1711 	const keywordId id = (keywordId) lookupKeyword (name, getInputLanguage ());
1712 
1713 	/* ignore D @attributes and Java @annotations(...), but show them in function signatures */
1714 	if ((isInputLanguage(Lang_d) || isInputLanguage(Lang_java)) && id == KEYWORD_NONE && name[0] == '@')
1715 	{
1716 		skipParens(); /* if annotation has parameters, skip them */
1717 		return KEYWORD_CONST;
1718 	}
1719 	return id;
1720 }
1721 
analyzeIdentifier(tokenInfo * const token)1722 static void analyzeIdentifier (tokenInfo *const token)
1723 {
1724 	char *const name = vStringValue (token->name);
1725 	const char *replacement = NULL;
1726 	bool parensToo = false;
1727 
1728 	if (isInputLanguage (Lang_java)  ||
1729 		! cppIsIgnoreToken (name, &parensToo, &replacement))
1730 	{
1731 		if (replacement != NULL)
1732 			token->keyword = analyzeKeyword (replacement);
1733 		else
1734 			token->keyword = analyzeKeyword (vStringValue (token->name));
1735 
1736 		if (token->keyword == KEYWORD_NONE)
1737 			token->type = TOKEN_NAME;
1738 		else
1739 			token->type = TOKEN_KEYWORD;
1740 	}
1741 	else
1742 	{
1743 		initToken (token);
1744 		if (parensToo)
1745 		{
1746 			int c = skipToNonWhite ();
1747 
1748 			if (c == '(')
1749 				skipToMatch ("()");
1750 		}
1751 	}
1752 }
1753 
readIdentifier(tokenInfo * const token,const int firstChar)1754 static void readIdentifier (tokenInfo *const token, const int firstChar)
1755 {
1756 	vString *const name = token->name;
1757 	int c = firstChar;
1758 
1759 	initToken (token);
1760 
1761 	/* Bug #1585745 (CTags): strangely, C++ destructors allow whitespace between
1762 	* the ~ and the class name. */
1763 	if (isInputLanguage (Lang_cpp) && firstChar == '~')
1764 	{
1765 		vStringPut (name, c);
1766 		c = skipToNonWhite ();
1767 	}
1768 
1769 	do
1770 	{
1771 		vStringPut (name, c);
1772 		c = cppGetc ();
1773 	} while (cppIsident (c) || (isInputLanguage (Lang_vala) && '.' == c));
1774 	cppUngetc (c);        /* unget non-identifier character */
1775 
1776 	/* Vala supports '?' at end of a type (with or without whitespace before) for nullable types */
1777 	if (isInputLanguage (Lang_vala))
1778 	{
1779 		c = skipToNonWhite ();
1780 		if ('?' == c)
1781 			vStringPut (name, c);
1782 		else
1783 			cppUngetc (c);
1784 	}
1785 
1786 	analyzeIdentifier (token);
1787 }
1788 
readPackageName(tokenInfo * const token,const int firstChar)1789 static void readPackageName (tokenInfo *const token, const int firstChar)
1790 {
1791 	vString *const name = token->name;
1792 	int c = firstChar;
1793 
1794 	initToken (token);
1795 
1796 	while (cppIsident (c)  ||  c == '.')
1797 	{
1798 		vStringPut (name, c);
1799 		c = cppGetc ();
1800 	}
1801 	cppUngetc (c);        /* unget non-package character */
1802 }
1803 
readPackageOrNamespace(statementInfo * const st,const declType declaration)1804 static void readPackageOrNamespace (statementInfo *const st, const declType declaration)
1805 {
1806 	st->declaration = declaration;
1807 
1808 	if (declaration == DECL_NAMESPACE && !(isInputLanguage (Lang_csharp) || isInputLanguage (Lang_vala)))
1809 	{
1810 		/* In C++ a namespace is specified one level at a time. */
1811 		return;
1812 	}
1813 	else
1814 	{
1815 		/* In C#, a namespace can also be specified like a Java package name. */
1816 		tokenInfo *const token = activeToken (st);
1817 		Assert (isType (token, TOKEN_KEYWORD));
1818 		readPackageName (token, skipToNonWhite ());
1819 		token->type = TOKEN_NAME;
1820 		st->gotName = true;
1821 		st->haveQualifyingName = true;
1822 	}
1823 }
1824 
readPackage(statementInfo * const st)1825 static void readPackage (statementInfo *const st)
1826 {
1827 	tokenInfo *const token = activeToken (st);
1828 	Assert (isType (token, TOKEN_KEYWORD));
1829 	readPackageName (token, skipToNonWhite ());
1830 	token->type = TOKEN_NAME;
1831 	if (isInputLanguage (Lang_d))
1832 		st->declaration = DECL_MODULE;
1833 	else
1834 		st->declaration = DECL_PACKAGE;
1835 	st->gotName = true;
1836 	st->haveQualifyingName = true;
1837 }
1838 
processName(statementInfo * const st)1839 static void processName (statementInfo *const st)
1840 {
1841 	Assert (isType (activeToken (st), TOKEN_NAME));
1842 	if (st->gotName  &&  st->declaration == DECL_NONE)
1843 		st->declaration = DECL_BASE;
1844 	st->gotName = true;
1845 	st->haveQualifyingName = true;
1846 }
1847 
readOperator(statementInfo * const st)1848 static void readOperator (statementInfo *const st)
1849 {
1850 	const char *const acceptable = "+-*/%^&|~!=<>,[]";
1851 	const tokenInfo* const prev = prevToken (st,1);
1852 	tokenInfo *const token = activeToken (st);
1853 	vString *const name = token->name;
1854 	int c = skipToNonWhite ();
1855 
1856 	/*  When we arrive here, we have the keyword "operator" in 'name'.
1857 	 */
1858 	if (isType (prev, TOKEN_KEYWORD) && (prev->keyword == KEYWORD_ENUM ||
1859 		prev->keyword == KEYWORD_STRUCT || prev->keyword == KEYWORD_UNION))
1860 			; /* ignore "operator" keyword if preceded by these keywords */
1861 	else if (c == '(')
1862 	{
1863 		/*  Verify whether this is a valid function call (i.e. "()") operator.
1864 		 */
1865 		if (cppGetc () == ')')
1866 		{
1867 			vStringPut (name, ' ');  /* always separate operator from keyword */
1868 			c = skipToNonWhite ();
1869 			if (c == '(')
1870 				vStringCatS (name, "()");
1871 		}
1872 		else
1873 		{
1874 			skipToMatch ("()");
1875 			c = cppGetc ();
1876 		}
1877 	}
1878 	else if (cppIsident1 (c))
1879 	{
1880 		/*  Handle "new" and "delete" operators, and conversion functions
1881 		 *  (per 13.3.1.1.2 [2] of the C++ spec).
1882 		 */
1883 		bool whiteSpace = true;  /* default causes insertion of space */
1884 		do
1885 		{
1886 			if (isspace (c))
1887 				whiteSpace = true;
1888 			else
1889 			{
1890 				if (whiteSpace)
1891 				{
1892 					vStringPut (name, ' ');
1893 					whiteSpace = false;
1894 				}
1895 				vStringPut (name, c);
1896 			}
1897 			c = cppGetc ();
1898 		} while (! isOneOf (c, "(;")  &&  c != EOF);
1899 	}
1900 	else if (isOneOf (c, acceptable))
1901 	{
1902 		vStringPut (name, ' ');  /* always separate operator from keyword */
1903 		do
1904 		{
1905 			vStringPut (name, c);
1906 			c = cppGetc ();
1907 		} while (isOneOf (c, acceptable));
1908 	}
1909 
1910 	cppUngetc (c);
1911 
1912 	token->type	= TOKEN_NAME;
1913 	token->keyword = KEYWORD_NONE;
1914 	processName (st);
1915 }
1916 
copyToken(tokenInfo * const dest,const tokenInfo * const src)1917 static void copyToken (tokenInfo *const dest, const tokenInfo *const src)
1918 {
1919 	dest->type         = src->type;
1920 	dest->keyword      = src->keyword;
1921 	dest->filePosition = src->filePosition;
1922 	dest->lineNumber   = src->lineNumber;
1923 	vStringCopy (dest->name, src->name);
1924 }
1925 
setAccess(statementInfo * const st,const accessType laccess)1926 static void setAccess (statementInfo *const st, const accessType laccess)
1927 {
1928 	if (isMember (st))
1929 	{
1930 		if (isInputLanguage (Lang_cpp) || isInputLanguage (Lang_d) || isInputLanguage (Lang_ferite))
1931 		{
1932 			int c = skipToNonWhite ();
1933 
1934 			if (c == ':')
1935 				reinitStatementWithToken (st, prevToken (st, 1), false);
1936 			else
1937 				cppUngetc (c);
1938 
1939 			st->member.accessDefault = laccess;
1940 		}
1941 		st->member.access = laccess;
1942 	}
1943 }
1944 
discardTypeList(tokenInfo * const token)1945 static void discardTypeList (tokenInfo *const token)
1946 {
1947 	int c = skipToNonWhite ();
1948 	while (cppIsident1 (c))
1949 	{
1950 		readIdentifier (token, c);
1951 		c = skipToNonWhite ();
1952 		if (c == '.'  ||  c == ',')
1953 			c = skipToNonWhite ();
1954 	}
1955 	cppUngetc (c);
1956 }
1957 
addParentClass(statementInfo * const st,tokenInfo * const token)1958 static void addParentClass (statementInfo *const st, tokenInfo *const token)
1959 {
1960 	if (vStringLength (token->name) > 0  &&
1961 		vStringLength (st->parentClasses) > 0)
1962 	{
1963 		vStringPut (st->parentClasses, ',');
1964 	}
1965 	vStringCat (st->parentClasses, token->name);
1966 }
1967 
readParents(statementInfo * const st,const int qualifier)1968 static void readParents (statementInfo *const st, const int qualifier)
1969 {
1970 	tokenInfo *const token = newToken ();
1971 	tokenInfo *const parent = newToken ();
1972 	int c;
1973 
1974 	do
1975 	{
1976 		c = skipToNonWhite ();
1977 		if (cppIsident1 (c))
1978 		{
1979 			readIdentifier (token, c);
1980 			if (isType (token, TOKEN_NAME))
1981 				vStringCat (parent->name, token->name);
1982 			else
1983 			{
1984 				addParentClass (st, parent);
1985 				initToken (parent);
1986 			}
1987 		}
1988 		else if (c == qualifier)
1989 			vStringPut (parent->name, c);
1990 		else if (c == '<')
1991 			skipToMatch ("<>");
1992 		else if (isType (token, TOKEN_NAME))
1993 		{
1994 			addParentClass (st, parent);
1995 			initToken (parent);
1996 		}
1997 	} while (c != '{'  &&  c != EOF);
1998 	cppUngetc (c);
1999 	deleteToken (parent);
2000 	deleteToken (token);
2001 }
2002 
checkIsClassEnum(statementInfo * const st,const declType decl)2003 static void checkIsClassEnum (statementInfo *const st, const declType decl)
2004 {
2005 	if (! isInputLanguage (Lang_cpp) || st->declaration != DECL_ENUM)
2006 		st->declaration = decl;
2007 }
2008 
processToken(tokenInfo * const token,statementInfo * const st)2009 static void processToken (tokenInfo *const token, statementInfo *const st)
2010 {
2011 	switch (token->keyword)		/* is it a reserved word? */
2012 	{
2013 		default: break;
2014 
2015 		case KEYWORD_NONE:      processName (st);                       break;
2016 		case KEYWORD_ABSTRACT:  st->implementation = IMP_ABSTRACT;      break;
2017 		case KEYWORD_ATTRIBUTE: skipParens (); initToken (token);       break;
2018 		case KEYWORD_CATCH:     skipParens (); skipBraces ();           break;
2019 		case KEYWORD_CHAR:      st->declaration = DECL_BASE;            break;
2020 		case KEYWORD_CLASS:     checkIsClassEnum (st, DECL_CLASS);      break;
2021 		case KEYWORD_CONST:     st->declaration = DECL_BASE;            break;
2022 		case KEYWORD_DOUBLE:    st->declaration = DECL_BASE;            break;
2023 		case KEYWORD_ENUM:      st->declaration = DECL_ENUM;            break;
2024 		case KEYWORD_EXTENDS:   readParents (st, '.');
2025 		                        setToken (st, TOKEN_NONE);              break;
2026 		case KEYWORD_FLOAT:     st->declaration = DECL_BASE;            break;
2027 		case KEYWORD_FRIEND:    st->scope       = SCOPE_FRIEND;         break;
2028 		case KEYWORD_IMPLEMENTS:readParents (st, '.');
2029 		                        setToken (st, TOKEN_NONE);              break;
2030 		case KEYWORD_IMPORT:    st->declaration = DECL_IGNORE;          break;
2031 		case KEYWORD_INT:       st->declaration = DECL_BASE;            break;
2032 		case KEYWORD_BOOLEAN:   st->declaration = DECL_BASE;            break;
2033 		case KEYWORD_WCHAR_T:   st->declaration = DECL_BASE;            break;
2034 		case KEYWORD_SIZE_T:    st->declaration = DECL_BASE;            break;
2035 		case KEYWORD_INTERFACE: st->declaration = DECL_INTERFACE;       break;
2036 		case KEYWORD_LONG:      st->declaration = DECL_BASE;            break;
2037 		case KEYWORD_OPERATOR:  readOperator (st);                      break;
2038 		case KEYWORD_MODULE:    readPackage (st);                       break;
2039 		case KEYWORD_PRIVATE:   setAccess (st, ACCESS_PRIVATE);         break;
2040 		case KEYWORD_PROTECTED: setAccess (st, ACCESS_PROTECTED);       break;
2041 		case KEYWORD_PUBLIC:    setAccess (st, ACCESS_PUBLIC);          break;
2042 		case KEYWORD_SHORT:     st->declaration = DECL_BASE;            break;
2043 		case KEYWORD_SIGNED:    st->declaration = DECL_BASE;            break;
2044 		case KEYWORD_STRUCT:    checkIsClassEnum (st, DECL_STRUCT);     break;
2045 		case KEYWORD_STATIC_ASSERT: skipParens ();                      break;
2046 		case KEYWORD_THROWS:    discardTypeList (token);                break;
2047 		case KEYWORD_TYPEDEF:   st->scope	= SCOPE_TYPEDEF;            break;
2048 		case KEYWORD_UNION:     st->declaration = DECL_UNION;           break;
2049 		case KEYWORD_UNSIGNED:  st->declaration = DECL_BASE;            break;
2050 		case KEYWORD_USING:     st->declaration = DECL_IGNORE;          break;
2051 		case KEYWORD_VOID:      st->declaration = DECL_BASE;            break;
2052 		case KEYWORD_VOLATILE:  st->declaration = DECL_BASE;            break;
2053 		case KEYWORD_VIRTUAL:   st->implementation = IMP_VIRTUAL;       break;
2054 
2055 		case KEYWORD_NAMESPACE: readPackageOrNamespace (st, DECL_NAMESPACE); break;
2056 		case KEYWORD_PACKAGE:   readPackageOrNamespace (st, DECL_PACKAGE);   break;
2057 		case KEYWORD_EVENT:
2058 		{
2059 			if (isInputLanguage (Lang_csharp))
2060 				st->declaration = DECL_EVENT;
2061 			break;
2062 		}
2063 		case KEYWORD_SIGNAL:
2064 		{
2065 			if (isInputLanguage (Lang_vala))
2066 				st->declaration = DECL_SIGNAL;
2067 			break;
2068 		}
2069 		case KEYWORD_EXTERN:
2070 		{
2071 			if (! isInputLanguage (Lang_csharp) || !st->gotName)
2072 			{
2073 				/*reinitStatement (st, false);*/
2074 				st->scope = SCOPE_EXTERN;
2075 				st->declaration = DECL_BASE;
2076 			}
2077 			break;
2078 		}
2079 		case KEYWORD_STATIC:
2080 		{
2081 			if (! isInputLanguage (Lang_java) && ! isInputLanguage (Lang_csharp) && ! isInputLanguage (Lang_vala))
2082 			{
2083 				/*reinitStatement (st, false);*/
2084 				st->scope = SCOPE_STATIC;
2085 				st->declaration = DECL_BASE;
2086 			}
2087 			break;
2088 		}
2089 		case KEYWORD_IF:
2090 			if (isInputLanguage (Lang_d))
2091 			{	/* static if (is(typeof(__traits(getMember, a, name)) == function)) */
2092 				int c = skipToNonWhite ();
2093 				if (c == '(')
2094 					skipToMatch ("()");
2095 			}
2096 			break;
2097 	}
2098 }
2099 
2100 /*
2101 *   Parenthesis handling functions
2102 */
2103 
restartStatement(statementInfo * const st)2104 static void restartStatement (statementInfo *const st)
2105 {
2106 	tokenInfo *const save = newToken ();
2107 	tokenInfo *token = activeToken (st);
2108 
2109 	copyToken (save, token);
2110 	DebugStatement ( if (debug (DEBUG_PARSE)) printf ("<ES>");)
2111 	reinitStatement (st, false);
2112 	token = activeToken (st);
2113 	copyToken (token, save);
2114 	deleteToken (save);
2115 	processToken (token, st);
2116 }
2117 
2118 /*  Skips over a mem-initializer-list of a ctor-initializer, defined as:
2119  *
2120  *  mem-initializer-list:
2121  *    mem-initializer, mem-initializer-list
2122  *
2123  *  mem-initializer:
2124  *    [::] [nested-name-spec] class-name (...)
2125  *    identifier
2126  */
skipMemIntializerList(tokenInfo * const token)2127 static void skipMemIntializerList (tokenInfo *const token)
2128 {
2129 	int c;
2130 
2131 	do
2132 	{
2133 		c = skipToNonWhite ();
2134 		while (cppIsident1 (c)  ||  c == ':')
2135 		{
2136 			if (c != ':')
2137 				readIdentifier (token, c);
2138 			c = skipToNonWhite ();
2139 		}
2140 		if (c == '<')
2141 		{
2142 			skipToMatch ("<>");
2143 			c = skipToNonWhite ();
2144 		}
2145 		if (c == '(')
2146 		{
2147 			skipToMatch ("()");
2148 			c = skipToNonWhite ();
2149 		}
2150 	} while (c == ',');
2151 	cppUngetc (c);
2152 }
2153 
skipMacro(statementInfo * const st)2154 static void skipMacro (statementInfo *const st)
2155 {
2156 	tokenInfo *const prev2 = prevToken (st, 2);
2157 
2158 	if (isType (prev2, TOKEN_NAME))
2159 		retardToken (st);
2160 	skipToMatch ("()");
2161 }
2162 
isDPostArgumentToken(tokenInfo * const token)2163 static bool isDPostArgumentToken(tokenInfo *const token)
2164 {
2165 	switch (token->keyword)
2166 	{
2167 		/* Note: some other keywords e.g. immutable are parsed as
2168 		 * KEYWORD_CONST - see initializeDParser */
2169 		case KEYWORD_CONST:
2170 		/* template constraint */
2171 		case KEYWORD_IF:
2172 		/* contracts */
2173 		case KEYWORD_IN:
2174 		case KEYWORD_OUT:
2175 		case KEYWORD_BODY:
2176 			return true;
2177 		default:
2178 			break;
2179 	}
2180 	/* @attributes */
2181 	if (vStringValue(token->name)[0] == '@')
2182 		return true;
2183 	return false;
2184 }
2185 
2186 /*  Skips over characters following the parameter list. This will be either
2187  *  non-ANSI style function declarations or C++ stuff. Our choices:
2188  *
2189  *  C (K&R):
2190  *    int func ();
2191  *    int func (one, two) int one; float two; {...}
2192  *  C (ANSI):
2193  *    int func (int one, float two);
2194  *    int func (int one, float two) {...}
2195  *  C++:
2196  *    int foo (...) [const|volatile] [throw (...)];
2197  *    int foo (...) [const|volatile] [throw (...)] [ctor-initializer] {...}
2198  *    int foo (...) [const|volatile] [throw (...)] try [ctor-initializer] {...}
2199  *        catch (...) {...}
2200  */
skipPostArgumentStuff(statementInfo * const st,parenInfo * const info)2201 static bool skipPostArgumentStuff (
2202 		statementInfo *const st, parenInfo *const info)
2203 {
2204 	tokenInfo *const token = activeToken (st);
2205 	unsigned int parameters = info->parameterCount;
2206 	unsigned int elementCount = 0;
2207 	bool restart = false;
2208 	bool end = false;
2209 	int c = skipToNonWhite ();
2210 
2211 	do
2212 	{
2213 		switch (c)
2214 		{
2215 		case ')':                               break;
2216 		case ':': skipMemIntializerList (token);break;  /* ctor-initializer */
2217 		case '[': skipToMatch ("[]");           break;
2218 		case '=': cppUngetc (c); end = true;    break;
2219 		case '{': cppUngetc (c); end = true;    break;
2220 		case '}': cppUngetc (c); end = true;    break;
2221 
2222 			case '(':
2223 			{
2224 				if (elementCount > 0)
2225 					++elementCount;
2226 				skipToMatch ("()");
2227 				break;
2228 			}
2229 
2230 			case ';':
2231 			{
2232 				if (parameters == 0  ||  elementCount < 2)
2233 				{
2234 					cppUngetc (c);
2235 					end = true;
2236 				}
2237 				else if (--parameters == 0)
2238 					end = true;
2239 				break;
2240 			}
2241 
2242 			default:
2243 			{
2244 				if (cppIsident1 (c))
2245 				{
2246 					readIdentifier (token, c);
2247 					if (isInputLanguage(Lang_d) && isDPostArgumentToken(token))
2248 						token->keyword = KEYWORD_CONST;
2249 
2250 					switch (token->keyword)
2251 					{
2252 					case KEYWORD_ATTRIBUTE:	skipParens ();	break;
2253 					case KEYWORD_THROW:	skipParens ();		break;
2254 					case KEYWORD_CONST:						break;
2255 					case KEYWORD_NOEXCEPT:					break;
2256 					case KEYWORD_TRY:						break;
2257 					case KEYWORD_VOLATILE:					break;
2258 
2259 					case KEYWORD_CATCH:			case KEYWORD_CLASS:
2260 					case KEYWORD_EXPLICIT:		case KEYWORD_EXTERN:
2261 					case KEYWORD_FRIEND:		case KEYWORD_INLINE:
2262 					case KEYWORD_MUTABLE:		case KEYWORD_NAMESPACE:
2263 					case KEYWORD_NEW:			case KEYWORD_OPERATOR:
2264 					case KEYWORD_OVERLOAD:		case KEYWORD_PRIVATE:
2265 					case KEYWORD_PROTECTED:		case KEYWORD_PUBLIC:
2266 					case KEYWORD_STATIC:		case KEYWORD_TEMPLATE:
2267 					case KEYWORD_TYPEDEF:		case KEYWORD_TYPENAME:
2268 					case KEYWORD_USING:			case KEYWORD_VIRTUAL:
2269 						/*  Never allowed within parameter declarations.
2270 						 */
2271 						restart = true;
2272 						end = true;
2273 						break;
2274 
2275 					default:
2276 						/* "override" and "final" are only keywords in the declaration of a virtual
2277 						 * member function, so need to be handled specially, not as keywords */
2278 						if (isInputLanguage(Lang_cpp) && isType (token, TOKEN_NAME) &&
2279 							(strcmp ("override", vStringValue (token->name)) == 0 ||
2280 							 strcmp ("final", vStringValue (token->name)) == 0))
2281 							;
2282 						else if (isType (token, TOKEN_NONE))
2283 							;
2284 						else if (info->isKnrParamList  &&  info->parameterCount > 0)
2285 							++elementCount;
2286 						else
2287 						{
2288 							/*  If we encounter any other identifier immediately
2289 							 *  following an empty parameter list, this is almost
2290 							 *  certainly one of those Microsoft macro "thingies"
2291 							 *  that the automatic source code generation sticks
2292 							 *  in. Terminate the current statement.
2293 							 */
2294 							restart = true;
2295 							end = true;
2296 						}
2297 						break;
2298 					}
2299 				}
2300 			}
2301 		}
2302 		if (! end)
2303 		{
2304 			c = skipToNonWhite ();
2305 			if (c == EOF)
2306 				end = true;
2307 		}
2308 	} while (! end);
2309 
2310 	if (restart)
2311 		restartStatement (st);
2312 	else
2313 		setToken (st, TOKEN_NONE);
2314 
2315 	return (bool) (c != EOF);
2316 }
2317 
skipJavaThrows(statementInfo * const st)2318 static void skipJavaThrows (statementInfo *const st)
2319 {
2320 	tokenInfo *const token = activeToken (st);
2321 	int c = skipToNonWhite ();
2322 
2323 	if (cppIsident1 (c))
2324 	{
2325 		readIdentifier (token, c);
2326 		if (token->keyword == KEYWORD_THROWS)
2327 		{
2328 			do
2329 			{
2330 				c = skipToNonWhite ();
2331 				if (cppIsident1 (c))
2332 				{
2333 					readIdentifier (token, c);
2334 					c = skipToNonWhite ();
2335 				}
2336 			} while (c == '.'  ||  c == ',');
2337 		}
2338 	}
2339 	cppUngetc (c);
2340 	setToken (st, TOKEN_NONE);
2341 }
2342 
skipValaPostParens(statementInfo * const st)2343 static void skipValaPostParens (statementInfo *const st)
2344 {
2345 	tokenInfo *const token = activeToken (st);
2346 	int c = skipToNonWhite ();
2347 
2348 	while (cppIsident1 (c))
2349 	{
2350 		readIdentifier (token, c);
2351 		if (token->keyword == KEYWORD_ATTRIBUTE)
2352 		{
2353 			/* parse contracts */
2354 			skipParens ();
2355 			c = skipToNonWhite ();
2356 		}
2357 		else if (token->keyword == KEYWORD_THROWS)
2358 		{
2359 			do
2360 			{
2361 				c = skipToNonWhite ();
2362 				if (cppIsident1 (c))
2363 				{
2364 					readIdentifier (token, c);
2365 					c = skipToNonWhite ();
2366 				}
2367 			} while (c == '.'  ||  c == ',');
2368 		}
2369 		else
2370 			break;
2371 	}
2372 	cppUngetc (c);
2373 	setToken (st, TOKEN_NONE);
2374 }
2375 
analyzePostParens(statementInfo * const st,parenInfo * const info)2376 static void analyzePostParens (statementInfo *const st, parenInfo *const info)
2377 {
2378 	const unsigned long inputLineNumber = getInputLineNumber ();
2379 	int c = skipToNonWhite ();
2380 
2381 	cppUngetc (c);
2382 	if (isOneOf (c, "{;,="))
2383 		;
2384 	else if (isInputLanguage (Lang_java))
2385 		skipJavaThrows (st);
2386 	else if (isInputLanguage (Lang_vala))
2387 		skipValaPostParens(st);
2388 	else
2389 	{
2390 		if (! skipPostArgumentStuff (st, info))
2391 		{
2392 			verbose (
2393 				"%s: confusing argument declarations beginning at line %lu\n",
2394 				getInputFileName (), inputLineNumber);
2395 			longjmp (Exception, (int) ExceptionFormattingError);
2396 		}
2397 	}
2398 }
2399 
parseParens(statementInfo * const st,parenInfo * const info)2400 static int parseParens (statementInfo *const st, parenInfo *const info)
2401 {
2402 	tokenInfo *const token = activeToken (st);
2403 	unsigned int identifierCount = 0;
2404 	unsigned int depth = 1;
2405 	bool firstChar = true;
2406 	int nextChar = '\0';
2407 
2408 	cppStartCollectingSignature ();
2409 
2410 	info->parameterCount = 1;
2411 	do
2412 	{
2413 		int c = skipToNonWhite ();
2414 
2415 		switch (c)
2416 		{
2417 			case '&':
2418 			case '*':
2419 			{
2420 				/* DEBUG_PRINT("parseParens, po++\n"); */
2421 				info->isKnrParamList = false;
2422 				if (identifierCount == 0)
2423 					info->isParamList = false;
2424 				initToken (token);
2425 				break;
2426 			}
2427 			case ':':
2428 			{
2429 				info->isKnrParamList = false;
2430 				break;
2431 			}
2432 			case '.':
2433 			{
2434 				info->isNameCandidate = false;
2435 				info->isKnrParamList = false;
2436 				break;
2437 			}
2438 			case ',':
2439 			{
2440 				info->isNameCandidate = false;
2441 				if (info->isKnrParamList)
2442 				{
2443 					++info->parameterCount;
2444 					identifierCount = 0;
2445 				}
2446 				break;
2447 			}
2448 			case '=':
2449 			{
2450 				info->isKnrParamList = false;
2451 				info->isNameCandidate = false;
2452 				if (firstChar)
2453 				{
2454 					info->isParamList = false;
2455 					skipMacro (st);
2456 					depth = 0;
2457 				}
2458 				break;
2459 			}
2460 			case '[':
2461 			{
2462 				info->isKnrParamList = false;
2463 				skipToMatch ("[]");
2464 				break;
2465 			}
2466 			case '<':
2467 			{
2468 				info->isKnrParamList = false;
2469 				skipToMatch ("<>");
2470 				break;
2471 			}
2472 			case ')':
2473 			{
2474 				if (firstChar)
2475 					info->parameterCount = 0;
2476 				--depth;
2477 				break;
2478 			}
2479 			case '(':
2480 			{
2481 				info->isKnrParamList = false;
2482 				if (firstChar)
2483 				{
2484 					info->isNameCandidate = false;
2485 					cppUngetc (c);
2486 					skipMacro (st);
2487 					depth = 0;
2488 				}
2489 				else if (isType (token, TOKEN_PAREN_NAME))
2490 				{
2491 					c = skipToNonWhite ();
2492 					if (c == '*')        /* check for function pointer */
2493 					{
2494 						skipToMatch ("()");
2495 						c = skipToNonWhite ();
2496 						if (c == '(')
2497 							skipToMatch ("()");
2498 					}
2499 					else
2500 					{
2501 						cppUngetc (c);
2502 						cppUngetc ('(');
2503 						info->nestedArgs = true;
2504 					}
2505 				}
2506 				else
2507 					++depth;
2508 				break;
2509 			}
2510 
2511 			default:
2512 			{
2513 				if (cppIsident1 (c))
2514 				{
2515 					if (++identifierCount > 1)
2516 						info->isKnrParamList = false;
2517 					readIdentifier (token, c);
2518 					if (isType (token, TOKEN_NAME)  &&  info->isNameCandidate)
2519 						token->type = TOKEN_PAREN_NAME;
2520 					else if (isType (token, TOKEN_KEYWORD))
2521 					{
2522 						info->isKnrParamList = false;
2523 						info->isNameCandidate = false;
2524 					}
2525 				}
2526 				else if (isInputLanguage(Lang_d) && c == '!')
2527 				{ /* D template instantiation */
2528 					info->isNameCandidate = false;
2529 					info->isKnrParamList = false;
2530 				}
2531 				else
2532 				{
2533 					info->isParamList     = false;
2534 					info->isKnrParamList  = false;
2535 					info->isNameCandidate = false;
2536 					info->invalidContents = true;
2537 				}
2538 				break;
2539 			}
2540 		}
2541 		firstChar = false;
2542 	} while (! info->nestedArgs  &&  depth > 0  &&
2543 			 (info->isKnrParamList  ||  info->isNameCandidate));
2544 
2545 	if (! info->nestedArgs) while (depth > 0)
2546 	{
2547 		skipToMatch ("()");
2548 		--depth;
2549 	}
2550 
2551 	cppStopCollectingSignature ();
2552 
2553 	if (! info->isNameCandidate)
2554 		initToken (token);
2555 
2556 	return nextChar;
2557 }
2558 
initParenInfo(parenInfo * const info)2559 static void initParenInfo (parenInfo *const info)
2560 {
2561 	info->isParamList			= true;
2562 	info->isKnrParamList		= true;
2563 	info->isNameCandidate		= true;
2564 	info->invalidContents		= false;
2565 	info->nestedArgs			= false;
2566 	info->parameterCount		= 0;
2567 }
2568 
analyzeParens(statementInfo * const st)2569 static void analyzeParens (statementInfo *const st)
2570 {
2571 	tokenInfo *const prev = prevToken (st, 1);
2572 
2573 	if (! isType (prev, TOKEN_NONE))    /* in case of ignored enclosing macros */
2574 	{
2575 		tokenInfo *const token = activeToken (st);
2576 		parenInfo info;
2577 		int c;
2578 
2579 		initParenInfo (&info);
2580 		parseParens (st, &info);
2581 		c = skipToNonWhite ();
2582 		cppUngetc (c);
2583 		if (info.invalidContents)
2584 		{
2585 			reinitStatement (st, false);
2586 		}
2587 		else if (info.isNameCandidate  &&  isType (token, TOKEN_PAREN_NAME)  &&
2588 				 ! st->gotParenName  &&
2589 				 (! info.isParamList || ! st->haveQualifyingName  ||
2590 				  c == '('  ||
2591 				  (c == '='  &&  st->implementation != IMP_VIRTUAL) ||
2592 				  (st->declaration == DECL_NONE  &&  isOneOf (c, ",;"))))
2593 		{
2594 			token->type = TOKEN_NAME;
2595 			processName (st);
2596 			st->gotParenName = true;
2597 			if (isInputLanguage(Lang_d) && c == '(' && isType (prev, TOKEN_NAME))
2598 			{
2599 				st->declaration = DECL_FUNCTION_TEMPLATE;
2600 				copyToken (st->blockName, prev);
2601 			}
2602 		}
2603 		else if (! st->gotArgs  &&  info.isParamList)
2604 		{
2605 			st->gotArgs = true;
2606 			setToken (st, TOKEN_ARGS);
2607 			advanceToken (st);
2608 			analyzePostParens (st, &info);
2609 		}
2610 		else
2611 			setToken (st, TOKEN_NONE);
2612 	}
2613 }
2614 
2615 /*
2616 *   Token parsing functions
2617 */
2618 
addContext(statementInfo * const st,const tokenInfo * const token)2619 static void addContext (statementInfo *const st, const tokenInfo* const token)
2620 {
2621 	if (isType (token, TOKEN_NAME))
2622 	{
2623 		if (vStringLength (st->context->name) > 0)
2624 		{
2625 			if (isInputLanguage (Lang_c)  ||  isInputLanguage (Lang_cpp))
2626 				vStringCatS (st->context->name, "::");
2627 			else if (isInputLanguage (Lang_java) ||
2628 					 isInputLanguage (Lang_d) || isInputLanguage (Lang_ferite) ||
2629 					 isInputLanguage (Lang_csharp) || isInputLanguage (Lang_vala))
2630 				vStringCatS (st->context->name, ".");
2631 		}
2632 		vStringCat (st->context->name, token->name);
2633 		st->context->type = TOKEN_NAME;
2634 	}
2635 }
2636 
inheritingDeclaration(declType decl)2637 static bool inheritingDeclaration (declType decl)
2638 {
2639 	/* enum base types */
2640 	if (decl == DECL_ENUM)
2641 	{
2642 		return (bool) (isInputLanguage (Lang_cpp) || isInputLanguage (Lang_csharp) ||
2643 			isInputLanguage (Lang_d));
2644 	}
2645 	return (bool) (
2646 		decl == DECL_CLASS ||
2647 		decl == DECL_STRUCT ||
2648 		decl == DECL_INTERFACE);
2649 }
2650 
processColon(statementInfo * const st)2651 static void processColon (statementInfo *const st)
2652 {
2653 	int c = cppGetc ();
2654 	const bool doubleColon = (bool) (c == ':');
2655 
2656 	if (doubleColon)
2657 	{
2658 		setToken (st, TOKEN_DOUBLE_COLON);
2659 		st->haveQualifyingName = false;
2660 	}
2661 	else
2662 	{
2663 		cppUngetc (c);
2664 		if ((isInputLanguage (Lang_cpp) || isInputLanguage (Lang_csharp) || isInputLanguage (Lang_d) ||
2665 			isInputLanguage (Lang_vala)) &&
2666 			inheritingDeclaration (st->declaration))
2667 		{
2668 			readParents (st, ':');
2669 		}
2670 		else if (parentDecl (st) == DECL_STRUCT || parentDecl (st) == DECL_CLASS)
2671 		{
2672 			c = skipToOneOf (",;");
2673 			if (c == ',')
2674 				setToken (st, TOKEN_COMMA);
2675 			else if (c == ';')
2676 				setToken (st, TOKEN_SEMICOLON);
2677 		}
2678 		else
2679 		{
2680 			const tokenInfo *const prev  = prevToken (st, 1);
2681 			const tokenInfo *const prev2 = prevToken (st, 2);
2682 			if (prev->keyword == KEYWORD_DEFAULT ||
2683 				prev2->keyword == KEYWORD_CASE ||
2684 				st->parent != NULL)
2685 			{
2686 				reinitStatement (st, false);
2687 			}
2688 		}
2689 	}
2690 }
2691 
2692 /*  Skips over any initializing value which may follow an '=' character in a
2693  *  variable definition.
2694  */
skipInitializer(statementInfo * const st)2695 static int skipInitializer (statementInfo *const st)
2696 {
2697 	bool done = false;
2698 	int c;
2699 
2700 	while (! done)
2701 	{
2702 		c = skipToNonWhite ();
2703 
2704 		if (c == EOF)
2705 			longjmp (Exception, (int) ExceptionFormattingError);
2706 		else switch (c)
2707 		{
2708 			case ',':
2709 			case ';': done = true; break;
2710 
2711 			case '0':
2712 				if (st->implementation == IMP_VIRTUAL)
2713 					st->implementation = IMP_PURE_VIRTUAL;
2714 				break;
2715 
2716 			case '[': skipToMatch ("[]"); break;
2717 			case '(': skipToMatch ("()"); break;
2718 			case '{': skipToMatch ("{}"); break;
2719 
2720 			case '}':
2721 				if (insideEnumBody (st))
2722 					done = true;
2723 				else if (! cppIsBraceFormat ())
2724 				{
2725 					verbose ("%s: unexpected closing brace at line %lu\n",
2726 							getInputFileName (), getInputLineNumber ());
2727 					longjmp (Exception, (int) ExceptionBraceFormattingError);
2728 				}
2729 				break;
2730 
2731 			default: break;
2732 		}
2733 	}
2734 	return c;
2735 }
2736 
processInitializer(statementInfo * const st)2737 static void processInitializer (statementInfo *const st)
2738 {
2739 	const bool inEnumBody = insideEnumBody (st);
2740 	const int c = skipInitializer (st);
2741 
2742 	if (c == ';')
2743 		setToken (st, TOKEN_SEMICOLON);
2744 	else if (c == ',')
2745 		setToken (st, TOKEN_COMMA);
2746 	else if (c == '}'  &&  inEnumBody)
2747 	{
2748 		cppUngetc (c);
2749 		setToken (st, TOKEN_COMMA);
2750 	}
2751 	if (st->scope == SCOPE_EXTERN)
2752 		st->scope = SCOPE_GLOBAL;
2753 }
2754 
parseIdentifier(statementInfo * const st,const int c)2755 static void parseIdentifier (statementInfo *const st, const int c)
2756 {
2757 	tokenInfo *const token = activeToken (st);
2758 
2759 	readIdentifier (token, c);
2760 	if (! isType (token, TOKEN_NONE))
2761 		processToken (token, st);
2762 }
2763 
parseGeneralToken(statementInfo * const st,const int c)2764 static void parseGeneralToken (statementInfo *const st, const int c)
2765 {
2766 	const tokenInfo *const prev = prevToken (st, 1);
2767 
2768 	if (cppIsident1(c))
2769 	{
2770 		parseIdentifier (st, c);
2771 		if (isType (st->context, TOKEN_NAME) &&
2772 			isType (activeToken (st), TOKEN_NAME) && isType (prev, TOKEN_NAME))
2773 		{
2774 			initToken (st->context);
2775 		}
2776 	}
2777 	else if (isExternCDecl (st, c))
2778 	{
2779 		st->declaration = DECL_NOMANGLE;
2780 		st->scope = SCOPE_GLOBAL;
2781 	}
2782 }
2783 
2784 /*  Reads characters from the pre-processor and assembles tokens, setting
2785  *  the current statement state.
2786  */
nextToken(statementInfo * const st)2787 static void nextToken (statementInfo *const st)
2788 {
2789 	int c;
2790 	tokenInfo *token = activeToken (st);
2791 	do
2792 	{
2793 		c = skipToNonWhite();
2794 		switch (c)
2795 		{
2796 			case EOF: longjmp (Exception, (int) ExceptionEOF);  break;
2797 			case '(': analyzeParens (st); token = activeToken (st); break;
2798 			case '*': setToken (st, TOKEN_STAR);                break;
2799 			case ',': setToken (st, TOKEN_COMMA);               break;
2800 			case ':': processColon (st);                        break;
2801 			case ';': setToken (st, TOKEN_SEMICOLON);           break;
2802 			case '<': skipToMatch ("<>");                       break;
2803 			case '=': processInitializer (st);                  break;
2804 			case '[':
2805 				/* Hack for Vala: [..] can be a function attribute.
2806 				 * Seems not to have bad side effects, but have to test it more. */
2807 				if (!isInputLanguage (Lang_vala))
2808 					setToken (st, TOKEN_ARRAY);
2809 				skipToMatch ("[]");
2810 				break;
2811 			case '{': setToken (st, TOKEN_BRACE_OPEN);          break;
2812 			case '}': setToken (st, TOKEN_BRACE_CLOSE);         break;
2813 			default:  parseGeneralToken (st, c);                break;
2814 		}
2815 	} while (isType (token, TOKEN_NONE));
2816 
2817 	if (isType (token, TOKEN_SEMICOLON) && st->parent)
2818 		st->parent->nSemicolons ++;
2819 
2820 	/* We want to know about non-keyword variable types */
2821 	if (TOKEN_NONE == st->firstToken->type)
2822 	{
2823 		if ((TOKEN_NAME == token->type) || isDataTypeKeyword(token))
2824 			copyToken(st->firstToken, token);
2825 	}
2826 }
2827 
2828 /*
2829 *   Scanning support functions
2830 */
2831 static unsigned int contextual_fake_count = 0;
2832 static statementInfo *CurrentStatement = NULL;
2833 
newStatement(statementInfo * const parent)2834 static statementInfo *newStatement (statementInfo *const parent)
2835 {
2836 	statementInfo *const st = xMalloc (1, statementInfo);
2837 	unsigned int i;
2838 
2839 	for (i = 0  ;  i < (unsigned int) NumTokens  ;  ++i)
2840 		st->token [i] = newToken ();
2841 
2842 	st->context = newToken ();
2843 	st->blockName = newToken ();
2844 	st->parentClasses = vStringNew ();
2845 	st->firstToken = newToken();
2846 
2847 	initStatement (st, parent);
2848 	CurrentStatement = st;
2849 
2850 	return st;
2851 }
2852 
deleteStatement(void)2853 static void deleteStatement (void)
2854 {
2855 	statementInfo *const st = CurrentStatement;
2856 	statementInfo *const parent = st->parent;
2857 	unsigned int i;
2858 
2859 	for (i = 0  ;  i < (unsigned int) NumTokens  ;  ++i)
2860 	{
2861 		deleteToken (st->token [i]);       st->token [i] = NULL;
2862 	}
2863 	deleteToken (st->blockName);           st->blockName = NULL;
2864 	deleteToken (st->context);             st->context = NULL;
2865 	vStringDelete (st->parentClasses);     st->parentClasses = NULL;
2866 	deleteToken(st->firstToken);
2867 	eFree (st);
2868 	CurrentStatement = parent;
2869 }
2870 
deleteAllStatements(void)2871 static void deleteAllStatements (void)
2872 {
2873 	while (CurrentStatement != NULL)
2874 		deleteStatement ();
2875 }
2876 
isStatementEnd(const statementInfo * const st)2877 static bool isStatementEnd (const statementInfo *const st)
2878 {
2879 	const tokenInfo *const token = activeToken (st);
2880 	bool isEnd;
2881 
2882 	if (isType (token, TOKEN_SEMICOLON))
2883 		isEnd = true;
2884 	else if (isType (token, TOKEN_BRACE_CLOSE))
2885 		/* Java, D, C#, Vala do not require semicolons to end a block. Neither do
2886 		 * C++ namespaces. All other blocks require a semicolon to terminate them.
2887 		 */
2888 		isEnd = (bool) (isInputLanguage (Lang_java) || isInputLanguage (Lang_d) ||
2889 						isInputLanguage (Lang_csharp) || isInputLanguage (Lang_vala) ||
2890 						! isContextualStatement (st));
2891 	else
2892 		isEnd = false;
2893 
2894 	return isEnd;
2895 }
2896 
checkStatementEnd(statementInfo * const st)2897 static void checkStatementEnd (statementInfo *const st)
2898 {
2899 	const tokenInfo *const token = activeToken (st);
2900 	bool comma = isType (token, TOKEN_COMMA);
2901 
2902 	if (comma || isStatementEnd (st))
2903 	{
2904 		reinitStatementWithToken (st, activeToken (st), comma);
2905 
2906 		DebugStatement ( if (debug (DEBUG_PARSE)) printf ("<ES>"); )
2907 		cppEndStatement ();
2908 	}
2909 	else
2910 	{
2911 		cppBeginStatement ();
2912 		advanceToken (st);
2913 	}
2914 }
2915 
nest(statementInfo * const st,const unsigned int nestLevel)2916 static void nest (statementInfo *const st, const unsigned int nestLevel)
2917 {
2918 	switch (st->declaration)
2919 	{
2920 		case DECL_CLASS:
2921 		case DECL_ENUM:
2922 		case DECL_INTERFACE:
2923 		case DECL_NAMESPACE:
2924 		case DECL_NOMANGLE:
2925 		case DECL_STRUCT:
2926 		case DECL_UNION:
2927 			createTags (nestLevel, st);
2928 			break;
2929 		default:
2930 			skipToMatch ("{}");
2931 			break;
2932 	}
2933 	advanceToken (st);
2934 	setToken (st, TOKEN_BRACE_CLOSE);
2935 }
2936 
tagCheck(statementInfo * const st)2937 static void tagCheck (statementInfo *const st)
2938 {
2939 	const tokenInfo *const token = activeToken (st);
2940 	const tokenInfo *const prev  = prevToken (st, 1);
2941 	const tokenInfo *const prev2 = prevToken (st, 2);
2942 
2943 	switch (token->type)
2944 	{
2945 		case TOKEN_NAME:
2946 		{
2947 			if (insideEnumBody (st) &&
2948 				/* Java enumerations can contain members after a semicolon */
2949 				(! isInputLanguage(Lang_java) || st->parent->nSemicolons < 1))
2950 				qualifyEnumeratorTag (st, token);
2951 			break;
2952 		}
2953 #if 0
2954 		case TOKEN_PACKAGE:
2955 		{
2956 			if (st->haveQualifyingName)
2957 				makeTag (token, st, false, TAG_PACKAGE);
2958 			break;
2959 		}
2960 #endif
2961 		case TOKEN_BRACE_OPEN:
2962 		{
2963 			if (isType (prev, TOKEN_ARGS))
2964 			{
2965 				if (st->declaration == DECL_FUNCTION_TEMPLATE)
2966 					qualifyFunctionTag (st, st->blockName);
2967 				else if (st->haveQualifyingName)
2968 				{
2969 					if (isType (prev2, TOKEN_NAME))
2970 						copyToken (st->blockName, prev2);
2971 					/* D structure templates */
2972 					if (isInputLanguage (Lang_d) &&
2973 						(st->declaration == DECL_CLASS || st->declaration == DECL_STRUCT ||
2974 						st->declaration == DECL_INTERFACE || st->declaration == DECL_NAMESPACE))
2975 						qualifyBlockTag (st, prev2);
2976 					else
2977 					{
2978 						st->declaration = DECL_FUNCTION;
2979 						qualifyFunctionTag (st, prev2);
2980 					}
2981 				}
2982 			}
2983 			else if (isContextualStatement (st))
2984 			{
2985 				tokenInfo *name_token = (tokenInfo *)prev;
2986 				bool free_name_token = false;
2987 
2988 				/* C++ 11 allows class <name> final { ... } */
2989 				if (isInputLanguage (Lang_cpp) && isType (prev, TOKEN_NAME) &&
2990 					strcmp("final", vStringValue(prev->name)) == 0 &&
2991 					isType(prev2, TOKEN_NAME))
2992 				{
2993 					name_token = (tokenInfo *)prev2;
2994 					copyToken (st->blockName, name_token);
2995 				}
2996 				else if (isType (name_token, TOKEN_NAME))
2997 				{
2998 					if (!isInputLanguage (Lang_vala))
2999 						copyToken (st->blockName, name_token);
3000 					else
3001 					{
3002 						switch (st->declaration)
3003 						{
3004 							case DECL_CLASS:
3005 							case DECL_ENUM:
3006 							case DECL_INTERFACE:
3007 							case DECL_NAMESPACE:
3008 							case DECL_STRUCT:
3009 								copyToken (st->blockName, name_token);
3010 								break;
3011 
3012 							/* anything else can be a property */
3013 							default:
3014 								/* makeTag (prev, st, false, TAG_PROPERTY); */
3015 								/* FIXME: temporary hack to get properties shown */
3016 								makeTag (prev, st, false, TAG_FIELD);
3017 								break;
3018 						}
3019 					}
3020 				}
3021 				else if (isInputLanguage (Lang_csharp))
3022 					makeTag (prev, st, false, TAG_PROPERTY);
3023 				else
3024 				{
3025 					tokenInfo *contextual_token = (tokenInfo *)prev;
3026 					if(isContextualKeyword (contextual_token))
3027 					{
3028 						char buffer[64];
3029 
3030 						name_token = newToken ();
3031 						free_name_token = true;
3032 						copyToken (name_token, contextual_token);
3033 
3034 						sprintf(buffer, "anon_%s_%d", name_token->name->buffer, contextual_fake_count++);
3035 						vStringClear(name_token->name);
3036 						vStringCatS(name_token->name, buffer);
3037 
3038 						name_token->type = TOKEN_NAME;
3039 						name_token->keyword	= KEYWORD_NONE;
3040 
3041 						advanceToken (st);
3042 						contextual_token = activeToken (st);
3043 						copyToken (contextual_token, token);
3044 						copyToken ((tokenInfo *const)token, name_token);
3045 						copyToken (st->blockName, name_token);
3046 						copyToken (st->firstToken, name_token);
3047 					}
3048 				}
3049 				qualifyBlockTag (st, name_token);
3050 				if (free_name_token)
3051 					deleteToken (name_token);
3052 			}
3053 			break;
3054 		}
3055 		case TOKEN_ARRAY:
3056 		case TOKEN_SEMICOLON:
3057 		case TOKEN_COMMA:
3058 		{
3059 			if (insideEnumBody (st) &&
3060 				/* Java enumerations can contain members after a semicolon */
3061 				(! isInputLanguage (Lang_java) || st->parent->nSemicolons < 2))
3062 				;
3063 			else if (isType (prev, TOKEN_NAME))
3064 			{
3065 				if (isContextualKeyword (prev2))
3066 					makeTag (prev, st, true, TAG_EXTERN_VAR);
3067 				else
3068 					qualifyVariableTag (st, prev);
3069 			}
3070 			else if (isType (prev, TOKEN_ARGS)  &&  isType (prev2, TOKEN_NAME))
3071 			{
3072 				qualifyFunctionDeclTag (st, prev2);
3073 			}
3074 			break;
3075 		}
3076 		default:
3077 			break;
3078 	}
3079 }
3080 
3081 /*  Parses the current file and decides whether to write out and tags that
3082  *  are discovered.
3083  */
createTags(const unsigned int nestLevel,statementInfo * const parent)3084 static void createTags (const unsigned int nestLevel,
3085 						statementInfo *const parent)
3086 {
3087 	statementInfo *const st = newStatement (parent);
3088 
3089 	DebugStatement ( if (nestLevel > 0) debugParseNest (true, nestLevel); )
3090 	while (true)
3091 	{
3092 		tokenInfo *token;
3093 
3094 		nextToken (st);
3095 		token = activeToken (st);
3096 		if (isType (token, TOKEN_BRACE_CLOSE))
3097 		{
3098 			if (nestLevel > 0)
3099 				break;
3100 			else
3101 			{
3102 				verbose ("%s: unexpected closing brace at line %lu\n",
3103 						getInputFileName (), getInputLineNumber ());
3104 				longjmp (Exception, (int) ExceptionBraceFormattingError);
3105 			}
3106 		}
3107 		else if (isType (token, TOKEN_DOUBLE_COLON))
3108 		{
3109 			addContext (st, prevToken (st, 1));
3110 			advanceToken (st);
3111 		}
3112 		else
3113 		{
3114 			tagCheck (st);/* this can add new token */
3115 			if (isType (activeToken (st), TOKEN_BRACE_OPEN))
3116 				nest (st, nestLevel + 1);
3117 			checkStatementEnd (st);
3118 		}
3119 	}
3120 	deleteStatement ();
3121 	DebugStatement ( if (nestLevel > 0) debugParseNest (false, nestLevel - 1); )
3122 }
3123 
findCTags(const unsigned int passCount)3124 static rescanReason findCTags (const unsigned int passCount)
3125 {
3126 	exception_t exception;
3127 	rescanReason rescan = RESCAN_NONE;
3128 
3129 	contextual_fake_count = 0;
3130 
3131 	Assert (passCount < 3);
3132 
3133 	cppInit ((bool) (passCount > 1), isInputLanguage (Lang_csharp), isInputLanguage(Lang_cpp),
3134 		CK_DEFINE);
3135 
3136 	exception = (exception_t) setjmp (Exception);
3137 	rescan = RESCAN_NONE;
3138 	if (exception == ExceptionNone)
3139 	{
3140 		createTags (0, NULL);
3141 	}
3142 	else
3143 	{
3144 		deleteAllStatements ();
3145 		if (exception == ExceptionBraceFormattingError  &&  passCount == 1)
3146 		{
3147 			rescan = RESCAN_FAILED;
3148 			verbose ("%s: retrying file with fallback brace matching algorithm\n",
3149 					getInputFileName ());
3150 		}
3151 	}
3152 	cppTerminate ();
3153 	return rescan;
3154 }
3155 
buildKeywordHash(const langType language,unsigned int idx)3156 static void buildKeywordHash (const langType language, unsigned int idx)
3157 {
3158 	const size_t count = ARRAY_SIZE (KeywordTable);
3159 	size_t i;
3160 	for (i = 0  ;  i < count  ;  ++i)
3161 	{
3162 		const keywordDesc* const p = &KeywordTable [i];
3163 		if (p->isValid [idx])
3164 			addKeyword (p->name, language, (int) p->id);
3165 	}
3166 }
3167 
initializeCParser(const langType language)3168 static void initializeCParser (const langType language)
3169 {
3170 	Lang_c = language;
3171 	buildKeywordHash (language, 0);
3172 }
3173 
initializeCppParser(const langType language)3174 static void initializeCppParser (const langType language)
3175 {
3176 	Lang_cpp = language;
3177 	buildKeywordHash (language, 1);
3178 }
3179 
initializeJavaParser(const langType language)3180 static void initializeJavaParser (const langType language)
3181 {
3182 	Lang_java = language;
3183 	buildKeywordHash (language, 3);
3184 }
3185 
initializeDParser(const langType language)3186 static void initializeDParser (const langType language)
3187 {
3188 	/* treat these like const - some are for parsing like const(Type), some are just
3189 	 * function attributes */
3190 	const char *const_aliases[] = {"immutable", "nothrow", "pure", "shared", NULL};
3191 	const char **s;
3192 
3193 	Lang_d = language;
3194 	buildKeywordHash (language, 6);
3195 
3196 	for (s = const_aliases; *s != NULL; s++)
3197 	{
3198 		addKeyword (*s, language, KEYWORD_CONST);
3199 	}
3200 	/* other keyword aliases */
3201 	addKeyword ("alias", language, KEYWORD_TYPEDEF);
3202 	/* skip 'static assert(...)' like 'static if (...)' */
3203 	addKeyword ("assert", language, KEYWORD_IF);
3204 	addKeyword ("unittest", language, KEYWORD_BODY);	/* ignore */
3205 	addKeyword ("version", language, KEYWORD_NAMESPACE);	/* parse block */
3206 }
3207 
initializeGLSLParser(const langType language)3208 static void initializeGLSLParser (const langType language)
3209 {
3210 	Lang_glsl = language;
3211 	buildKeywordHash (language, 0); /* C keywords */
3212 }
3213 
initializeFeriteParser(const langType language)3214 static void initializeFeriteParser (const langType language)
3215 {
3216 	Lang_ferite = language;
3217 	buildKeywordHash (language, 1);	/* C++ keywords */
3218 }
3219 
initializeCsharpParser(const langType language)3220 static void initializeCsharpParser (const langType language)
3221 {
3222 	Lang_csharp = language;
3223 	buildKeywordHash (language, 2);
3224 }
3225 
initializeValaParser(const langType language)3226 static void initializeValaParser (const langType language)
3227 {
3228 	Lang_vala = language;
3229 	buildKeywordHash (language, 5);
3230 
3231 	/* keyword aliases */
3232 	addKeyword ("ensures", language, KEYWORD_ATTRIBUTE);	/* ignore */
3233 	addKeyword ("errordomain", language, KEYWORD_ENUM); /* looks like enum */
3234 	addKeyword ("requires", language, KEYWORD_ATTRIBUTE);	/* ignore */
3235 }
3236 
CParser(void)3237 extern parserDefinition* CParser (void)
3238 {
3239 	static const char *const extensions [] = { "c", "pc", "sc", NULL };
3240 	parserDefinition* def = parserNew ("C");
3241 	def->kindTable  = CKinds;
3242 	def->kindCount  = ARRAY_SIZE (CKinds);
3243 	def->extensions = extensions;
3244 	def->parser2    = findCTags;
3245 	def->initialize = initializeCParser;
3246 	return def;
3247 }
3248 
CppParser(void)3249 extern parserDefinition* CppParser (void)
3250 {
3251 	static const char *const extensions [] = {
3252 		"c++", "cc", "cp", "cpp", "cxx", "h", "h++", "hh", "hp", "hpp", "hxx",
3253 		"i",
3254 #ifndef CASE_INSENSITIVE_FILENAMES
3255 		"C", "H",
3256 #endif
3257 		NULL
3258 	};
3259 	parserDefinition* def = parserNew ("C++");
3260 	def->kindTable  = CKinds;
3261 	def->kindCount  = ARRAY_SIZE (CKinds);
3262 	def->extensions = extensions;
3263 	def->parser2    = findCTags;
3264 	def->initialize = initializeCppParser;
3265 	return def;
3266 }
3267 
JavaParser(void)3268 extern parserDefinition* JavaParser (void)
3269 {
3270 	static const char *const extensions [] = { "java", NULL };
3271 	parserDefinition* def = parserNew ("Java");
3272 	def->kindTable  = JavaKinds;
3273 	def->kindCount  = ARRAY_SIZE (JavaKinds);
3274 	def->extensions = extensions;
3275 	def->parser2    = findCTags;
3276 	def->initialize = initializeJavaParser;
3277 	return def;
3278 }
3279 
DParser(void)3280 extern parserDefinition* DParser (void)
3281 {
3282 	static const char *const extensions [] = { "d", "di", NULL };
3283 	parserDefinition* def = parserNew ("D");
3284 	def->kindTable  = DKinds;
3285 	def->kindCount  = ARRAY_SIZE (DKinds);
3286 	def->extensions = extensions;
3287 	def->parser2    = findCTags;
3288 	def->initialize = initializeDParser;
3289 	return def;
3290 }
3291 
GLSLParser(void)3292 extern parserDefinition* GLSLParser (void)
3293 {
3294 	static const char *const extensions [] = { "glsl", "frag", "vert", NULL };
3295 	parserDefinition* def = parserNew ("GLSL");
3296 	def->kindTable  = CKinds;
3297 	def->kindCount  = ARRAY_SIZE (CKinds);
3298 	def->extensions = extensions;
3299 	def->parser2    = findCTags;
3300 	def->initialize = initializeGLSLParser;
3301 	return def;
3302 }
3303 
FeriteParser(void)3304 extern parserDefinition* FeriteParser (void)
3305 {
3306 	static const char *const extensions [] = { "fe", NULL };
3307 	parserDefinition* def = parserNew ("Ferite");
3308 	def->kindTable  = CKinds;
3309 	def->kindCount  = ARRAY_SIZE (CKinds);
3310 	def->extensions = extensions;
3311 	def->parser2    = findCTags;
3312 	def->initialize = initializeFeriteParser;
3313 	return def;
3314 }
3315 
CsharpParser(void)3316 extern parserDefinition* CsharpParser (void)
3317 {
3318 	static const char *const extensions [] = { "cs", NULL };
3319 	parserDefinition* def = parserNew ("C#");
3320 	def->kindTable  = CsharpKinds;
3321 	def->kindCount  = ARRAY_SIZE (CsharpKinds);
3322 	def->extensions = extensions;
3323 	def->parser2    = findCTags;
3324 	def->initialize = initializeCsharpParser;
3325 	return def;
3326 }
3327 
ValaParser(void)3328 extern parserDefinition* ValaParser (void)
3329 {
3330 	static const char *const extensions [] = { "vala", NULL };
3331 	parserDefinition* def = parserNew ("Vala");
3332 	def->kindTable  = ValaKinds;
3333 	def->kindCount  = ARRAY_SIZE (ValaKinds);
3334 	def->extensions = extensions;
3335 	def->parser2    = findCTags;
3336 	def->initialize = initializeValaParser;
3337 	return def;
3338 }
3339