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