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