2 * $Id: c.c 689 2008-12-13 21:17:36Z elliotth $
4 * Copyright (c) 1996-2003, Darren Hiebert
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains functions for parsing and scanning C, C++ and Java
16 #include "general.h" /* must always come first */
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) (boolean) ((token)->type == (t))
38 #define insideEnumBody(st) ((st)->parent == NULL ? FALSE : \
39 (boolean) ((st)->parent->declaration == DECL_ENUM))
40 #define isExternCDecl(st,c) (boolean) ((c) == STRING_SYMBOL && \
41 ! (st)->haveQualifyingName && (st)->scope == SCOPE_EXTERN)
43 #define isOneOf(c,s) (boolean) (strchr ((s), (c)) != NULL)
45 #define isHighChar(c) ((c) != EOF && (unsigned char)(c) >= 0xc0)
51 enum { NumTokens = 3 };
53 typedef enum eException {
54 ExceptionNone, ExceptionEOF, ExceptionFormattingError,
55 ExceptionBraceFormattingError
58 /* Used to specify type of keyword.
60 typedef enum eKeywordId {
62 KEYWORD_ATTRIBUTE, KEYWORD_ABSTRACT,
63 KEYWORD_BOOLEAN, KEYWORD_BYTE, KEYWORD_BAD_STATE, KEYWORD_BAD_TRANS,
64 KEYWORD_BIND, KEYWORD_BIND_VAR, KEYWORD_BIT,
65 KEYWORD_CASE, KEYWORD_CATCH, KEYWORD_CHAR, KEYWORD_CLASS, KEYWORD_CONST,
66 KEYWORD_CONSTRAINT, KEYWORD_COVERAGE_BLOCK, KEYWORD_COVERAGE_DEF,
67 KEYWORD_DEFAULT, KEYWORD_DELEGATE, KEYWORD_DELETE, KEYWORD_DO,
69 KEYWORD_ELSE, KEYWORD_ENUM, KEYWORD_EXPLICIT, KEYWORD_EXTERN,
70 KEYWORD_EXTENDS, KEYWORD_EVENT,
71 KEYWORD_FINAL, KEYWORD_FLOAT, KEYWORD_FOR, KEYWORD_FOREACH,
72 KEYWORD_FRIEND, KEYWORD_FUNCTION,
74 KEYWORD_IF, KEYWORD_IMPLEMENTS, KEYWORD_IMPORT, KEYWORD_INLINE, KEYWORD_INT,
75 KEYWORD_INOUT, KEYWORD_INPUT, KEYWORD_INTEGER, KEYWORD_INTERFACE,
77 KEYWORD_LOCAL, KEYWORD_LONG,
78 KEYWORD_M_BAD_STATE, KEYWORD_M_BAD_TRANS, KEYWORD_M_STATE, KEYWORD_M_TRANS,
80 KEYWORD_NAMESPACE, KEYWORD_NEW, KEYWORD_NEWCOV, KEYWORD_NATIVE,
81 KEYWORD_OPERATOR, KEYWORD_OUTPUT, KEYWORD_OVERLOAD, KEYWORD_OVERRIDE,
82 KEYWORD_PACKED, KEYWORD_PORT, KEYWORD_PACKAGE, KEYWORD_PRIVATE,
83 KEYWORD_PROGRAM, KEYWORD_PROTECTED, KEYWORD_PUBLIC,
84 KEYWORD_REGISTER, KEYWORD_RETURN,
85 KEYWORD_SHADOW, KEYWORD_STATE,
86 KEYWORD_SHORT, KEYWORD_SIGNED, KEYWORD_STATIC, KEYWORD_STRING,
87 KEYWORD_STRUCT, KEYWORD_SWITCH, KEYWORD_SYNCHRONIZED,
88 KEYWORD_TASK, KEYWORD_TEMPLATE, KEYWORD_THIS, KEYWORD_THROW,
89 KEYWORD_THROWS, KEYWORD_TRANSIENT, KEYWORD_TRANS, KEYWORD_TRANSITION,
90 KEYWORD_TRY, KEYWORD_TYPEDEF, KEYWORD_TYPENAME,
91 KEYWORD_UINT, KEYWORD_ULONG, KEYWORD_UNION, KEYWORD_UNSIGNED, KEYWORD_USHORT,
93 KEYWORD_VIRTUAL, KEYWORD_VOID, KEYWORD_VOLATILE,
94 KEYWORD_WCHAR_T, KEYWORD_WHILE
97 /* Used to determine whether keyword is valid for the current language and
100 typedef struct sKeywordDesc {
103 short isValid [5]; /* indicates languages for which kw is valid */
106 /* Used for reporting the type of object parsed by nextToken ().
108 typedef enum eTokenType {
109 TOKEN_NONE, /* none */
110 TOKEN_ARGS, /* a parenthetical pair and its contents */
113 TOKEN_COLON, /* the colon character */
114 TOKEN_COMMA, /* the comma character */
115 TOKEN_DOUBLE_COLON, /* double colon indicates nested-name-specifier */
117 TOKEN_NAME, /* an unknown name */
118 TOKEN_PACKAGE, /* a Java package name */
119 TOKEN_PAREN_NAME, /* a single name in parentheses */
120 TOKEN_SEMICOLON, /* the semicolon character */
121 TOKEN_SPEC, /* a storage class specifier, qualifier, type, etc. */
125 /* This describes the scoping of the current statement.
127 typedef enum eTagScope {
128 SCOPE_GLOBAL, /* no storage class specified */
129 SCOPE_STATIC, /* static storage class */
130 SCOPE_EXTERN, /* external storage class */
131 SCOPE_FRIEND, /* declares access only */
132 SCOPE_TYPEDEF, /* scoping depends upon context */
136 typedef enum eDeclaration {
138 DECL_BASE, /* base type (default) */
143 DECL_IGNORE, /* non-taggable "declaration" */
146 DECL_NOMANGLE, /* C++ name demangling block */
148 DECL_PROGRAM, /* Vera program */
150 DECL_TASK, /* Vera task */
155 typedef enum eVisibilityType {
161 ACCESS_DEFAULT, /* Java-specific */
165 /* Information about the parent class of a member (if any).
167 typedef struct sMemberInfo {
168 accessType access; /* access of current statement */
169 accessType accessDefault; /* access default for current statement */
172 typedef struct sTokenInfo {
175 vString* name; /* the name of the token */
176 unsigned long lineNumber; /* line number of tag */
177 fpos_t filePosition; /* file position of line containing name */
180 typedef enum eImplementation {
188 /* Describes the statement currently undergoing analysis.
190 typedef struct sStatementInfo {
192 declType declaration; /* specifier associated with TOKEN_SPEC */
193 boolean gotName; /* was a name parsed yet? */
194 boolean haveQualifyingName; /* do we have a name we are considering? */
195 boolean gotParenName; /* was a name inside parentheses parsed yet? */
196 boolean gotArgs; /* was a list of parameters parsed yet? */
197 boolean isPointer; /* is 'name' a pointer? */
198 boolean inFunction; /* are we inside of a function? */
199 boolean assignment; /* have we handled an '='? */
200 boolean notVariable; /* has a variable declaration been disqualified ? */
201 impType implementation; /* abstract or concrete implementation? */
202 unsigned int tokenIndex; /* currently active token */
203 tokenInfo* token [(int) NumTokens];
204 tokenInfo* context; /* accumulated scope of current statement */
205 tokenInfo* blockName; /* name of current block */
206 memberInfo member; /* information regarding parent class/struct */
207 vString* parentClasses; /* parent classes */
208 struct sStatementInfo *parent; /* statement we are nested within */
211 /* Describes the type of tag being generated.
213 typedef enum eTagType {
215 TAG_CLASS, /* class name */
216 TAG_ENUM, /* enumeration name */
217 TAG_ENUMERATOR, /* enumerator (enumeration value) */
218 TAG_EVENT, /* event */
219 TAG_FIELD, /* field (Java) */
220 TAG_FUNCTION, /* function definition */
221 TAG_INTERFACE, /* interface declaration */
222 TAG_LOCAL, /* local variable definition */
223 TAG_MEMBER, /* structure, class or interface member */
224 TAG_METHOD, /* method declaration */
225 TAG_NAMESPACE, /* namespace name */
226 TAG_PACKAGE, /* package name */
227 TAG_PROGRAM, /* program name */
228 TAG_PROPERTY, /* property name */
229 TAG_PROTOTYPE, /* function prototype or declaration */
230 TAG_STRUCT, /* structure name */
231 TAG_TASK, /* task name */
232 TAG_TYPEDEF, /* typedef name */
233 TAG_UNION, /* union name */
234 TAG_VARIABLE, /* variable definition */
235 TAG_EXTERN_VAR, /* external variable declaration */
236 TAG_COUNT /* must be last */
239 typedef struct sParenInfo {
242 boolean isKnrParamList;
243 boolean isNameCandidate;
244 boolean invalidContents;
246 unsigned int parameterCount;
253 static jmp_buf Exception;
255 static langType Lang_c;
256 static langType Lang_cpp;
257 static langType Lang_csharp;
258 static langType Lang_java;
259 static langType Lang_vera;
260 static vString *Signature;
261 static boolean CollectingSignature;
263 /* Number used to uniquely identify anonymous structs and unions. */
264 static int AnonymousID = 0;
266 /* Used to index into the CKinds table. */
269 CK_CLASS, CK_DEFINE, CK_ENUMERATOR, CK_FUNCTION,
270 CK_ENUMERATION, CK_LOCAL, CK_MEMBER, CK_NAMESPACE, CK_PROTOTYPE,
271 CK_STRUCT, CK_TYPEDEF, CK_UNION, CK_VARIABLE,
275 static kindOption CKinds [] = {
276 { TRUE, 'c', "class", "classes"},
277 { TRUE, 'd', "macro", "macro definitions"},
278 { TRUE, 'e', "enumerator", "enumerators (values inside an enumeration)"},
279 { TRUE, 'f', "function", "function definitions"},
280 { TRUE, 'g', "enum", "enumeration names"},
281 { FALSE, 'l', "local", "local variables"},
282 { TRUE, 'm', "member", "class, struct, and union members"},
283 { TRUE, 'n', "namespace", "namespaces"},
284 { FALSE, 'p', "prototype", "function prototypes"},
285 { TRUE, 's', "struct", "structure names"},
286 { TRUE, 't', "typedef", "typedefs"},
287 { TRUE, 'u', "union", "union names"},
288 { TRUE, 'v', "variable", "variable definitions"},
289 { FALSE, 'x', "externvar", "external and forward variable declarations"},
294 CSK_CLASS, CSK_DEFINE, CSK_ENUMERATOR, CSK_EVENT, CSK_FIELD,
295 CSK_ENUMERATION, CSK_INTERFACE, CSK_LOCAL, CSK_METHOD,
296 CSK_NAMESPACE, CSK_PROPERTY, CSK_STRUCT, CSK_TYPEDEF
299 static kindOption CsharpKinds [] = {
300 { TRUE, 'c', "class", "classes"},
301 { TRUE, 'd', "macro", "macro definitions"},
302 { TRUE, 'e', "enumerator", "enumerators (values inside an enumeration)"},
303 { TRUE, 'E', "event", "events"},
304 { TRUE, 'f', "field", "fields"},
305 { TRUE, 'g', "enum", "enumeration names"},
306 { TRUE, 'i', "interface", "interfaces"},
307 { FALSE, 'l', "local", "local variables"},
308 { TRUE, 'm', "method", "methods"},
309 { TRUE, 'n', "namespace", "namespaces"},
310 { TRUE, 'p', "property", "properties"},
311 { TRUE, 's', "struct", "structure names"},
312 { TRUE, 't', "typedef", "typedefs"},
315 /* Used to index into the JavaKinds table. */
318 JK_CLASS, JK_ENUM_CONSTANT, JK_FIELD, JK_ENUM, JK_INTERFACE,
319 JK_LOCAL, JK_METHOD, JK_PACKAGE, JK_ACCESS, JK_CLASS_PREFIX
322 static kindOption JavaKinds [] = {
323 { TRUE, 'c', "class", "classes"},
324 { TRUE, 'e', "enum constant", "enum constants"},
325 { TRUE, 'f', "field", "fields"},
326 { TRUE, 'g', "enum", "enum types"},
327 { TRUE, 'i', "interface", "interfaces"},
328 { FALSE, 'l', "local", "local variables"},
329 { TRUE, 'm', "method", "methods"},
330 { TRUE, 'p', "package", "packages"},
333 /* Used to index into the VeraKinds table. */
336 VK_CLASS, VK_DEFINE, VK_ENUMERATOR, VK_FUNCTION,
337 VK_ENUMERATION, VK_LOCAL, VK_MEMBER, VK_PROGRAM, VK_PROTOTYPE,
338 VK_TASK, VK_TYPEDEF, VK_VARIABLE,
342 static kindOption VeraKinds [] = {
343 { TRUE, 'c', "class", "classes"},
344 { TRUE, 'd', "macro", "macro definitions"},
345 { TRUE, 'e', "enumerator", "enumerators (values inside an enumeration)"},
346 { TRUE, 'f', "function", "function definitions"},
347 { TRUE, 'g', "enum", "enumeration names"},
348 { FALSE, 'l', "local", "local variables"},
349 { TRUE, 'm', "member", "class, struct, and union members"},
350 { TRUE, 'p', "program", "programs"},
351 { FALSE, 'P', "prototype", "function prototypes"},
352 { TRUE, 't', "task", "tasks"},
353 { TRUE, 'T', "typedef", "typedefs"},
354 { TRUE, 'v', "variable", "variable definitions"},
355 { FALSE, 'x', "externvar", "external variable declarations"}
358 static const keywordDesc KeywordTable [] = {
360 /* ANSI C | C# Java */
362 /* keyword keyword ID | | | | | */
363 { "__attribute__", KEYWORD_ATTRIBUTE, { 1, 1, 1, 0, 0 } },
364 { "abstract", KEYWORD_ABSTRACT, { 0, 0, 1, 1, 0 } },
365 { "bad_state", KEYWORD_BAD_STATE, { 0, 0, 0, 0, 1 } },
366 { "bad_trans", KEYWORD_BAD_TRANS, { 0, 0, 0, 0, 1 } },
367 { "bind", KEYWORD_BIND, { 0, 0, 0, 0, 1 } },
368 { "bind_var", KEYWORD_BIND_VAR, { 0, 0, 0, 0, 1 } },
369 { "bit", KEYWORD_BIT, { 0, 0, 0, 0, 1 } },
370 { "boolean", KEYWORD_BOOLEAN, { 0, 0, 0, 1, 0 } },
371 { "byte", KEYWORD_BYTE, { 0, 0, 0, 1, 0 } },
372 { "case", KEYWORD_CASE, { 1, 1, 1, 1, 0 } },
373 { "catch", KEYWORD_CATCH, { 0, 1, 1, 0, 0 } },
374 { "char", KEYWORD_CHAR, { 1, 1, 1, 1, 0 } },
375 { "class", KEYWORD_CLASS, { 0, 1, 1, 1, 1 } },
376 { "const", KEYWORD_CONST, { 1, 1, 1, 1, 0 } },
377 { "constraint", KEYWORD_CONSTRAINT, { 0, 0, 0, 0, 1 } },
378 { "coverage_block", KEYWORD_COVERAGE_BLOCK, { 0, 0, 0, 0, 1 } },
379 { "coverage_def", KEYWORD_COVERAGE_DEF, { 0, 0, 0, 0, 1 } },
380 { "do", KEYWORD_DO, { 1, 1, 1, 1, 0 } },
381 { "default", KEYWORD_DEFAULT, { 1, 1, 1, 1, 0 } },
382 { "delegate", KEYWORD_DELEGATE, { 0, 0, 1, 0, 0 } },
383 { "delete", KEYWORD_DELETE, { 0, 1, 0, 0, 0 } },
384 { "double", KEYWORD_DOUBLE, { 1, 1, 1, 1, 0 } },
385 { "else", KEYWORD_ELSE, { 1, 1, 1, 1, 0 } },
386 { "enum", KEYWORD_ENUM, { 1, 1, 1, 1, 1 } },
387 { "event", KEYWORD_EVENT, { 0, 0, 1, 0, 1 } },
388 { "explicit", KEYWORD_EXPLICIT, { 0, 1, 1, 0, 0 } },
389 { "extends", KEYWORD_EXTENDS, { 0, 0, 0, 1, 1 } },
390 { "extern", KEYWORD_EXTERN, { 1, 1, 1, 0, 1 } },
391 { "final", KEYWORD_FINAL, { 0, 0, 0, 1, 0 } },
392 { "float", KEYWORD_FLOAT, { 1, 1, 1, 1, 0 } },
393 { "for", KEYWORD_FOR, { 1, 1, 1, 1, 0 } },
394 { "foreach", KEYWORD_FOREACH, { 0, 0, 1, 0, 0 } },
395 { "friend", KEYWORD_FRIEND, { 0, 1, 0, 0, 0 } },
396 { "function", KEYWORD_FUNCTION, { 0, 0, 0, 0, 1 } },
397 { "goto", KEYWORD_GOTO, { 1, 1, 1, 1, 0 } },
398 { "if", KEYWORD_IF, { 1, 1, 1, 1, 0 } },
399 { "implements", KEYWORD_IMPLEMENTS, { 0, 0, 0, 1, 0 } },
400 { "import", KEYWORD_IMPORT, { 0, 0, 0, 1, 0 } },
401 { "inline", KEYWORD_INLINE, { 0, 1, 0, 0, 0 } },
402 { "inout", KEYWORD_INOUT, { 0, 0, 0, 0, 1 } },
403 { "input", KEYWORD_INPUT, { 0, 0, 0, 0, 1 } },
404 { "int", KEYWORD_INT, { 1, 1, 1, 1, 0 } },
405 { "integer", KEYWORD_INTEGER, { 0, 0, 0, 0, 1 } },
406 { "interface", KEYWORD_INTERFACE, { 0, 0, 1, 1, 1 } },
407 { "internal", KEYWORD_INTERNAL, { 0, 0, 1, 0, 0 } },
408 { "local", KEYWORD_LOCAL, { 0, 0, 0, 0, 1 } },
409 { "long", KEYWORD_LONG, { 1, 1, 1, 1, 0 } },
410 { "m_bad_state", KEYWORD_M_BAD_STATE, { 0, 0, 0, 0, 1 } },
411 { "m_bad_trans", KEYWORD_M_BAD_TRANS, { 0, 0, 0, 0, 1 } },
412 { "m_state", KEYWORD_M_STATE, { 0, 0, 0, 0, 1 } },
413 { "m_trans", KEYWORD_M_TRANS, { 0, 0, 0, 0, 1 } },
414 { "mutable", KEYWORD_MUTABLE, { 0, 1, 0, 0, 0 } },
415 { "namespace", KEYWORD_NAMESPACE, { 0, 1, 1, 0, 0 } },
416 { "native", KEYWORD_NATIVE, { 0, 0, 0, 1, 0 } },
417 { "new", KEYWORD_NEW, { 0, 1, 1, 1, 0 } },
418 { "newcov", KEYWORD_NEWCOV, { 0, 0, 0, 0, 1 } },
419 { "operator", KEYWORD_OPERATOR, { 0, 1, 1, 0, 0 } },
420 { "output", KEYWORD_OUTPUT, { 0, 0, 0, 0, 1 } },
421 { "overload", KEYWORD_OVERLOAD, { 0, 1, 0, 0, 0 } },
422 { "override", KEYWORD_OVERRIDE, { 0, 0, 1, 0, 0 } },
423 { "package", KEYWORD_PACKAGE, { 0, 0, 0, 1, 0 } },
424 { "packed", KEYWORD_PACKED, { 0, 0, 0, 0, 1 } },
425 { "port", KEYWORD_PORT, { 0, 0, 0, 0, 1 } },
426 { "private", KEYWORD_PRIVATE, { 0, 1, 1, 1, 0 } },
427 { "program", KEYWORD_PROGRAM, { 0, 0, 0, 0, 1 } },
428 { "protected", KEYWORD_PROTECTED, { 0, 1, 1, 1, 1 } },
429 { "public", KEYWORD_PUBLIC, { 0, 1, 1, 1, 1 } },
430 { "register", KEYWORD_REGISTER, { 1, 1, 0, 0, 0 } },
431 { "return", KEYWORD_RETURN, { 1, 1, 1, 1, 0 } },
432 { "shadow", KEYWORD_SHADOW, { 0, 0, 0, 0, 1 } },
433 { "short", KEYWORD_SHORT, { 1, 1, 1, 1, 0 } },
434 { "signed", KEYWORD_SIGNED, { 1, 1, 0, 0, 0 } },
435 { "state", KEYWORD_STATE, { 0, 0, 0, 0, 1 } },
436 { "static", KEYWORD_STATIC, { 1, 1, 1, 1, 1 } },
437 { "string", KEYWORD_STRING, { 0, 0, 1, 0, 1 } },
438 { "struct", KEYWORD_STRUCT, { 1, 1, 1, 0, 0 } },
439 { "switch", KEYWORD_SWITCH, { 1, 1, 1, 1, 0 } },
440 { "synchronized", KEYWORD_SYNCHRONIZED, { 0, 0, 0, 1, 0 } },
441 { "task", KEYWORD_TASK, { 0, 0, 0, 0, 1 } },
442 { "template", KEYWORD_TEMPLATE, { 0, 1, 0, 0, 0 } },
443 { "this", KEYWORD_THIS, { 0, 1, 1, 1, 0 } },
444 { "throw", KEYWORD_THROW, { 0, 1, 1, 1, 0 } },
445 { "throws", KEYWORD_THROWS, { 0, 0, 0, 1, 0 } },
446 { "trans", KEYWORD_TRANS, { 0, 0, 0, 0, 1 } },
447 { "transition", KEYWORD_TRANSITION, { 0, 0, 0, 0, 1 } },
448 { "transient", KEYWORD_TRANSIENT, { 0, 0, 0, 1, 0 } },
449 { "try", KEYWORD_TRY, { 0, 1, 1, 0, 0 } },
450 { "typedef", KEYWORD_TYPEDEF, { 1, 1, 1, 0, 1 } },
451 { "typename", KEYWORD_TYPENAME, { 0, 1, 0, 0, 0 } },
452 { "uint", KEYWORD_UINT, { 0, 0, 1, 0, 0 } },
453 { "ulong", KEYWORD_ULONG, { 0, 0, 1, 0, 0 } },
454 { "union", KEYWORD_UNION, { 1, 1, 0, 0, 0 } },
455 { "unsigned", KEYWORD_UNSIGNED, { 1, 1, 1, 0, 0 } },
456 { "ushort", KEYWORD_USHORT, { 0, 0, 1, 0, 0 } },
457 { "using", KEYWORD_USING, { 0, 1, 1, 0, 0 } },
458 { "virtual", KEYWORD_VIRTUAL, { 0, 1, 1, 0, 1 } },
459 { "void", KEYWORD_VOID, { 1, 1, 1, 1, 1 } },
460 { "volatile", KEYWORD_VOLATILE, { 1, 1, 1, 1, 0 } },
461 { "wchar_t", KEYWORD_WCHAR_T, { 1, 1, 1, 0, 0 } },
462 { "while", KEYWORD_WHILE, { 1, 1, 1, 1, 0 } }
466 * FUNCTION PROTOTYPES
468 static void createTags (const unsigned int nestLevel, statementInfo *const parent);
471 * FUNCTION DEFINITIONS
474 extern boolean includingDefineTags (void)
476 return CKinds [CK_DEFINE].enabled;
483 static void initToken (tokenInfo* const token)
485 token->type = TOKEN_NONE;
486 token->keyword = KEYWORD_NONE;
487 token->lineNumber = getSourceLineNumber ();
488 token->filePosition = getInputFilePosition ();
489 vStringClear (token->name);
492 static void advanceToken (statementInfo* const st)
494 if (st->tokenIndex >= (unsigned int) NumTokens - 1)
498 initToken (st->token [st->tokenIndex]);
501 static tokenInfo *prevToken (const statementInfo *const st, unsigned int n)
503 unsigned int tokenIndex;
504 unsigned int num = (unsigned int) NumTokens;
506 tokenIndex = (st->tokenIndex + num - n) % num;
507 return st->token [tokenIndex];
510 static void setToken (statementInfo *const st, const tokenType type)
513 token = activeToken (st);
518 static void retardToken (statementInfo *const st)
520 if (st->tokenIndex == 0)
521 st->tokenIndex = (unsigned int) NumTokens - 1;
524 setToken (st, TOKEN_NONE);
527 static tokenInfo *newToken (void)
529 tokenInfo *const token = xMalloc (1, tokenInfo);
530 token->name = vStringNew ();
535 static void deleteToken (tokenInfo *const token)
539 vStringDelete (token->name);
544 static const char *accessString (const accessType access)
546 static const char *const names [] = {
547 "?", "local", "private", "protected", "public", "default"
549 Assert (sizeof (names) / sizeof (names [0]) == ACCESS_COUNT);
550 Assert ((int) access < ACCESS_COUNT);
551 return names [(int) access];
554 static const char *implementationString (const impType imp)
556 static const char *const names [] ={
557 "?", "abstract", "virtual", "pure virtual"
559 Assert (sizeof (names) / sizeof (names [0]) == IMP_COUNT);
560 Assert ((int) imp < IMP_COUNT);
561 return names [(int) imp];
565 * Debugging functions
570 #define boolString(c) ((c) ? "TRUE" : "FALSE")
572 static const char *tokenString (const tokenType type)
574 static const char *const names [] = {
575 "none", "args", "}", "{", "colon", "comma", "double colon", "keyword",
576 "name", "package", "paren-name", "semicolon", "specifier"
578 Assert (sizeof (names) / sizeof (names [0]) == TOKEN_COUNT);
579 Assert ((int) type < TOKEN_COUNT);
580 return names [(int) type];
583 static const char *scopeString (const tagScope scope)
585 static const char *const names [] = {
586 "global", "static", "extern", "friend", "typedef"
588 Assert (sizeof (names) / sizeof (names [0]) == SCOPE_COUNT);
589 Assert ((int) scope < SCOPE_COUNT);
590 return names [(int) scope];
593 static const char *declString (const declType declaration)
595 static const char *const names [] = {
596 "?", "base", "class", "enum", "event", "function", "ignore",
597 "interface", "namespace", "no mangle", "package", "program",
598 "struct", "task", "union",
600 Assert (sizeof (names) / sizeof (names [0]) == DECL_COUNT);
601 Assert ((int) declaration < DECL_COUNT);
602 return names [(int) declaration];
605 static const char *keywordString (const keywordId keyword)
607 const size_t count = sizeof (KeywordTable) / sizeof (KeywordTable [0]);
608 const char *name = "none";
610 for (i = 0 ; i < count ; ++i)
612 const keywordDesc *p = &KeywordTable [i];
613 if (p->id == keyword)
622 static void __ctags_unused__ pt (tokenInfo *const token)
624 if (isType (token, TOKEN_NAME))
625 printf ("type: %-12s: %-13s line: %lu\n",
626 tokenString (token->type), vStringValue (token->name),
628 else if (isType (token, TOKEN_KEYWORD))
629 printf ("type: %-12s: %-13s line: %lu\n",
630 tokenString (token->type), keywordString (token->keyword),
633 printf ("type: %-12s line: %lu\n",
634 tokenString (token->type), token->lineNumber);
637 static void __ctags_unused__ ps (statementInfo *const st)
640 printf ("scope: %s decl: %s gotName: %s gotParenName: %s\n",
641 scopeString (st->scope), declString (st->declaration),
642 boolString (st->gotName), boolString (st->gotParenName));
643 printf ("haveQualifyingName: %s\n", boolString (st->haveQualifyingName));
644 printf ("access: %s default: %s\n", accessString (st->member.access),
645 accessString (st->member.accessDefault));
647 pt (activeToken (st));
648 for (i = 1 ; i < (unsigned int) NumTokens ; ++i)
650 printf ("prev %u : ", i);
651 pt (prevToken (st, i));
653 printf ("context: ");
660 * Statement management
663 static boolean isContextualKeyword (const tokenInfo *const token)
666 switch (token->keyword)
670 case KEYWORD_INTERFACE:
671 case KEYWORD_NAMESPACE:
677 default: result = FALSE; break;
682 static boolean isContextualStatement (const statementInfo *const st)
684 boolean result = FALSE;
685 if (st != NULL) switch (st->declaration)
696 default: result = FALSE; break;
701 static boolean isMember (const statementInfo *const st)
704 if (isType (st->context, TOKEN_NAME))
708 (st->parent != NULL && isContextualStatement (st->parent));
712 static void initMemberInfo (statementInfo *const st)
714 accessType accessDefault = ACCESS_UNDEFINED;
716 if (st->parent != NULL) switch (st->parent->declaration)
719 accessDefault = (isLanguage (Lang_java) ? ACCESS_PUBLIC : ACCESS_UNDEFINED);
722 accessDefault = ACCESS_UNDEFINED;
726 if (isLanguage (Lang_java))
727 accessDefault = ACCESS_DEFAULT;
729 accessDefault = ACCESS_PRIVATE;
735 accessDefault = ACCESS_PUBLIC;
740 st->member.accessDefault = accessDefault;
741 st->member.access = accessDefault;
744 static void reinitStatement (statementInfo *const st, const boolean partial)
750 st->scope = SCOPE_GLOBAL;
751 if (isContextualStatement (st->parent))
752 st->declaration = DECL_BASE;
754 st->declaration = DECL_NONE;
756 st->gotParenName = FALSE;
757 st->isPointer = FALSE;
758 st->inFunction = FALSE;
759 st->assignment = FALSE;
760 st->notVariable = FALSE;
761 st->implementation = IMP_DEFAULT;
764 st->haveQualifyingName = FALSE;
767 if (st->parent != NULL)
768 st->inFunction = st->parent->inFunction;
770 for (i = 0 ; i < (unsigned int) NumTokens ; ++i)
771 initToken (st->token [i]);
773 initToken (st->context);
775 /* Keep the block name, so that a variable following after a comma will
776 * still have the structure name.
779 initToken (st->blockName);
781 vStringClear (st->parentClasses);
786 st->member.access = st->member.accessDefault;
789 static void initStatement (statementInfo *const st, statementInfo *const parent)
793 reinitStatement (st, FALSE);
797 * Tag generation functions
799 static cKind cTagKind (const tagType type)
801 cKind result = CK_UNDEFINED;
804 case TAG_CLASS: result = CK_CLASS; break;
805 case TAG_ENUM: result = CK_ENUMERATION; break;
806 case TAG_ENUMERATOR: result = CK_ENUMERATOR; break;
807 case TAG_FUNCTION: result = CK_FUNCTION; break;
808 case TAG_LOCAL: result = CK_LOCAL; break;
809 case TAG_MEMBER: result = CK_MEMBER; break;
810 case TAG_NAMESPACE: result = CK_NAMESPACE; break;
811 case TAG_PROTOTYPE: result = CK_PROTOTYPE; break;
812 case TAG_STRUCT: result = CK_STRUCT; break;
813 case TAG_TYPEDEF: result = CK_TYPEDEF; break;
814 case TAG_UNION: result = CK_UNION; break;
815 case TAG_VARIABLE: result = CK_VARIABLE; break;
816 case TAG_EXTERN_VAR: result = CK_EXTERN_VARIABLE; break;
818 default: Assert ("Bad C tag type" == NULL); break;
823 static csharpKind csharpTagKind (const tagType type)
825 csharpKind result = CSK_UNDEFINED;
828 case TAG_CLASS: result = CSK_CLASS; break;
829 case TAG_ENUM: result = CSK_ENUMERATION; break;
830 case TAG_ENUMERATOR: result = CSK_ENUMERATOR; break;
831 case TAG_EVENT: result = CSK_EVENT; break;
832 case TAG_FIELD: result = CSK_FIELD ; break;
833 case TAG_INTERFACE: result = CSK_INTERFACE; break;
834 case TAG_LOCAL: result = CSK_LOCAL; break;
835 case TAG_METHOD: result = CSK_METHOD; break;
836 case TAG_NAMESPACE: result = CSK_NAMESPACE; break;
837 case TAG_PROPERTY: result = CSK_PROPERTY; break;
838 case TAG_STRUCT: result = CSK_STRUCT; break;
839 case TAG_TYPEDEF: result = CSK_TYPEDEF; break;
841 default: Assert ("Bad C# tag type" == NULL); break;
846 static javaKind javaTagKind (const tagType type)
848 javaKind result = JK_UNDEFINED;
851 case TAG_CLASS: result = JK_CLASS; break;
852 case TAG_ENUM: result = JK_ENUM; break;
853 case TAG_ENUMERATOR: result = JK_ENUM_CONSTANT; break;
854 case TAG_FIELD: result = JK_FIELD; break;
855 case TAG_INTERFACE: result = JK_INTERFACE; break;
856 case TAG_LOCAL: result = JK_LOCAL; break;
857 case TAG_METHOD: result = JK_METHOD; break;
858 case TAG_PACKAGE: result = JK_PACKAGE; break;
860 default: Assert ("Bad Java tag type" == NULL); break;
865 static veraKind veraTagKind (const tagType type) {
866 veraKind result = VK_UNDEFINED;
869 case TAG_CLASS: result = VK_CLASS; break;
870 case TAG_ENUM: result = VK_ENUMERATION; break;
871 case TAG_ENUMERATOR: result = VK_ENUMERATOR; break;
872 case TAG_FUNCTION: result = VK_FUNCTION; break;
873 case TAG_LOCAL: result = VK_LOCAL; break;
874 case TAG_MEMBER: result = VK_MEMBER; break;
875 case TAG_PROGRAM: result = VK_PROGRAM; break;
876 case TAG_PROTOTYPE: result = VK_PROTOTYPE; break;
877 case TAG_TASK: result = VK_TASK; break;
878 case TAG_TYPEDEF: result = VK_TYPEDEF; break;
879 case TAG_VARIABLE: result = VK_VARIABLE; break;
880 case TAG_EXTERN_VAR: result = VK_EXTERN_VARIABLE; break;
882 default: Assert ("Bad Vera tag type" == NULL); break;
887 static const char *tagName (const tagType type)
890 if (isLanguage (Lang_csharp))
891 result = CsharpKinds [csharpTagKind (type)].name;
892 else if (isLanguage (Lang_java))
893 result = JavaKinds [javaTagKind (type)].name;
894 else if (isLanguage (Lang_vera))
895 result = VeraKinds [veraTagKind (type)].name;
897 result = CKinds [cTagKind (type)].name;
901 static int tagLetter (const tagType type)
904 if (isLanguage (Lang_csharp))
905 result = CsharpKinds [csharpTagKind (type)].letter;
906 else if (isLanguage (Lang_java))
907 result = JavaKinds [javaTagKind (type)].letter;
908 else if (isLanguage (Lang_vera))
909 result = VeraKinds [veraTagKind (type)].letter;
911 result = CKinds [cTagKind (type)].letter;
915 static boolean includeTag (const tagType type, const boolean isFileScope)
918 if (isFileScope && ! Option.include.fileScope)
920 else if (isLanguage (Lang_csharp))
921 result = CsharpKinds [csharpTagKind (type)].enabled;
922 else if (isLanguage (Lang_java))
923 result = JavaKinds [javaTagKind (type)].enabled;
924 else if (isLanguage (Lang_vera))
925 result = VeraKinds [veraTagKind (type)].enabled;
927 result = CKinds [cTagKind (type)].enabled;
931 static tagType declToTagType (const declType declaration)
933 tagType type = TAG_UNDEFINED;
937 case DECL_CLASS: type = TAG_CLASS; break;
938 case DECL_ENUM: type = TAG_ENUM; break;
939 case DECL_EVENT: type = TAG_EVENT; break;
940 case DECL_FUNCTION: type = TAG_FUNCTION; break;
941 case DECL_INTERFACE: type = TAG_INTERFACE; break;
942 case DECL_NAMESPACE: type = TAG_NAMESPACE; break;
943 case DECL_PROGRAM: type = TAG_PROGRAM; break;
944 case DECL_TASK: type = TAG_TASK; break;
945 case DECL_STRUCT: type = TAG_STRUCT; break;
946 case DECL_UNION: type = TAG_UNION; break;
948 default: Assert ("Unexpected declaration" == NULL); break;
953 static const char* accessField (const statementInfo *const st)
955 const char* result = NULL;
956 if (isLanguage (Lang_cpp) && st->scope == SCOPE_FRIEND)
958 else if (st->member.access != ACCESS_UNDEFINED)
959 result = accessString (st->member.access);
963 static void addContextSeparator (vString *const scope)
965 if (isLanguage (Lang_c) || isLanguage (Lang_cpp))
966 vStringCatS (scope, "::");
967 else if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
968 vStringCatS (scope, ".");
971 static void addOtherFields (tagEntryInfo* const tag, const tagType type,
972 const statementInfo *const st,
973 vString *const scope, vString *const typeRef)
975 /* For selected tag types, append an extension flag designating the
976 * parent object in which the tag is defined.
985 if (vStringLength (Signature) > 0)
986 tag->extensionFields.signature = vStringValue (Signature);
1000 if (vStringLength (scope) > 0 &&
1001 (isMember (st) || st->parent->declaration == DECL_NAMESPACE))
1003 if (isType (st->context, TOKEN_NAME))
1004 tag->extensionFields.scope [0] = tagName (TAG_CLASS);
1006 tag->extensionFields.scope [0] =
1007 tagName (declToTagType (parentDecl (st)));
1008 tag->extensionFields.scope [1] = vStringValue (scope);
1010 if ((type == TAG_CLASS || type == TAG_INTERFACE ||
1011 type == TAG_STRUCT) && vStringLength (st->parentClasses) > 0)
1014 tag->extensionFields.inheritance =
1015 vStringValue (st->parentClasses);
1017 if (st->implementation != IMP_DEFAULT &&
1018 (isLanguage (Lang_cpp) || isLanguage (Lang_csharp) ||
1019 isLanguage (Lang_java)))
1021 tag->extensionFields.implementation =
1022 implementationString (st->implementation);
1026 tag->extensionFields.access = accessField (st);
1031 /* Add typename info, type of the tag and name of struct/union/etc. */
1032 if ((type == TAG_TYPEDEF || type == TAG_VARIABLE || type == TAG_MEMBER)
1033 && isContextualStatement(st))
1037 tag->extensionFields.typeRef [0] =
1038 tagName (declToTagType (st->declaration));
1039 p = vStringValue (st->blockName->name);
1041 /* If there was no {} block get the name from the token before the
1042 * name (current token is ';' or ',', previous token is the name).
1044 if (p == NULL || *p == '\0')
1046 tokenInfo *const prev2 = prevToken (st, 2);
1047 if (isType (prev2, TOKEN_NAME))
1048 p = vStringValue (prev2->name);
1051 /* Prepend the scope name if there is one. */
1052 if (vStringLength (scope) > 0)
1054 vStringCopy(typeRef, scope);
1055 addContextSeparator (typeRef);
1056 vStringCatS(typeRef, p);
1057 p = vStringValue (typeRef);
1059 tag->extensionFields.typeRef [1] = p;
1063 static void findScopeHierarchy (vString *const string,
1064 const statementInfo *const st)
1066 vStringClear (string);
1067 if (isType (st->context, TOKEN_NAME))
1068 vStringCopy (string, st->context->name);
1069 if (st->parent != NULL)
1071 vString *temp = vStringNew ();
1072 const statementInfo *s;
1073 for (s = st->parent ; s != NULL ; s = s->parent)
1075 if (isContextualStatement (s) ||
1076 s->declaration == DECL_NAMESPACE ||
1077 s->declaration == DECL_PROGRAM)
1079 vStringCopy (temp, string);
1080 vStringClear (string);
1081 Assert (isType (s->blockName, TOKEN_NAME));
1082 if (isType (s->context, TOKEN_NAME) &&
1083 vStringLength (s->context->name) > 0)
1085 vStringCat (string, s->context->name);
1086 addContextSeparator (string);
1088 vStringCat (string, s->blockName->name);
1089 if (vStringLength (temp) > 0)
1090 addContextSeparator (string);
1091 vStringCat (string, temp);
1094 vStringDelete (temp);
1098 static void makeExtraTagEntry (const tagType type, tagEntryInfo *const e,
1099 vString *const scope)
1101 if (Option.include.qualifiedTags &&
1102 scope != NULL && vStringLength (scope) > 0)
1104 vString *const scopedName = vStringNew ();
1106 if (type != TAG_ENUMERATOR)
1107 vStringCopy (scopedName, scope);
1110 /* remove last component (i.e. enumeration name) from scope */
1111 const char* const sc = vStringValue (scope);
1112 const char* colon = strrchr (sc, ':');
1115 while (*colon == ':' && colon > sc)
1117 vStringNCopy (scopedName, scope, colon + 1 - sc);
1120 if (vStringLength (scopedName) > 0)
1122 addContextSeparator (scopedName);
1123 vStringCatS (scopedName, e->name);
1124 e->name = vStringValue (scopedName);
1127 vStringDelete (scopedName);
1131 static void makeTag (const tokenInfo *const token,
1132 const statementInfo *const st,
1133 boolean isFileScope, const tagType type)
1135 /* Nothing is really of file scope when it appears in a header file.
1137 isFileScope = (boolean) (isFileScope && ! isHeaderFile ());
1139 if (isType (token, TOKEN_NAME) && vStringLength (token->name) > 0 &&
1140 includeTag (type, isFileScope))
1142 vString *scope = vStringNew ();
1143 /* Use "typeRef" to store the typename from addOtherFields() until
1144 * it's used in makeTagEntry().
1146 vString *typeRef = vStringNew ();
1149 initTagEntry (&e, vStringValue (token->name));
1151 e.lineNumber = token->lineNumber;
1152 e.filePosition = token->filePosition;
1153 e.isFileScope = isFileScope;
1154 e.kindName = tagName (type);
1155 e.kind = tagLetter (type);
1157 findScopeHierarchy (scope, st);
1158 addOtherFields (&e, type, st, scope, typeRef);
1161 makeExtraTagEntry (type, &e, scope);
1162 vStringDelete (scope);
1163 vStringDelete (typeRef);
1167 static boolean isValidTypeSpecifier (const declType declaration)
1170 switch (declaration)
1188 static void qualifyEnumeratorTag (const statementInfo *const st,
1189 const tokenInfo *const nameToken)
1191 if (isType (nameToken, TOKEN_NAME))
1192 makeTag (nameToken, st, TRUE, TAG_ENUMERATOR);
1195 static void qualifyFunctionTag (const statementInfo *const st,
1196 const tokenInfo *const nameToken)
1198 if (isType (nameToken, TOKEN_NAME))
1201 const boolean isFileScope =
1202 (boolean) (st->member.access == ACCESS_PRIVATE ||
1203 (!isMember (st) && st->scope == SCOPE_STATIC));
1204 if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
1206 else if (isLanguage (Lang_vera) && st->declaration == DECL_TASK)
1209 type = TAG_FUNCTION;
1210 makeTag (nameToken, st, isFileScope, type);
1214 static void qualifyFunctionDeclTag (const statementInfo *const st,
1215 const tokenInfo *const nameToken)
1217 if (! isType (nameToken, TOKEN_NAME))
1219 else if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
1220 qualifyFunctionTag (st, nameToken);
1221 else if (st->scope == SCOPE_TYPEDEF)
1222 makeTag (nameToken, st, TRUE, TAG_TYPEDEF);
1223 else if (isValidTypeSpecifier (st->declaration) && ! isLanguage (Lang_csharp))
1224 makeTag (nameToken, st, TRUE, TAG_PROTOTYPE);
1227 static void qualifyCompoundTag (const statementInfo *const st,
1228 const tokenInfo *const nameToken)
1230 if (isType (nameToken, TOKEN_NAME))
1232 const tagType type = declToTagType (st->declaration);
1233 const boolean fileScoped = (boolean)
1234 (!(isLanguage (Lang_java) ||
1235 isLanguage (Lang_csharp) ||
1236 isLanguage (Lang_vera)));
1238 if (type != TAG_UNDEFINED)
1239 makeTag (nameToken, st, fileScoped, type);
1243 static void qualifyBlockTag (statementInfo *const st,
1244 const tokenInfo *const nameToken)
1246 switch (st->declaration)
1250 case DECL_INTERFACE:
1251 case DECL_NAMESPACE:
1255 qualifyCompoundTag (st, nameToken);
1261 static void qualifyVariableTag (const statementInfo *const st,
1262 const tokenInfo *const nameToken)
1264 /* We have to watch that we do not interpret a declaration of the
1265 * form "struct tag;" as a variable definition. In such a case, the
1266 * token preceding the name will be a keyword.
1268 if (! isType (nameToken, TOKEN_NAME))
1270 else if (st->scope == SCOPE_TYPEDEF)
1271 makeTag (nameToken, st, TRUE, TAG_TYPEDEF);
1272 else if (st->declaration == DECL_EVENT)
1273 makeTag (nameToken, st, (boolean) (st->member.access == ACCESS_PRIVATE),
1275 else if (st->declaration == DECL_PACKAGE)
1276 makeTag (nameToken, st, FALSE, TAG_PACKAGE);
1277 else if (isValidTypeSpecifier (st->declaration))
1279 if (st->notVariable)
1281 else if (isMember (st))
1283 if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
1284 makeTag (nameToken, st,
1285 (boolean) (st->member.access == ACCESS_PRIVATE), TAG_FIELD);
1286 else if (st->scope == SCOPE_GLOBAL || st->scope == SCOPE_STATIC)
1287 makeTag (nameToken, st, TRUE, TAG_MEMBER);
1291 if (st->scope == SCOPE_EXTERN || ! st->haveQualifyingName)
1292 makeTag (nameToken, st, FALSE, TAG_EXTERN_VAR);
1293 else if (st->inFunction)
1294 makeTag (nameToken, st, (boolean) (st->scope == SCOPE_STATIC),
1297 makeTag (nameToken, st, (boolean) (st->scope == SCOPE_STATIC),
1307 static int skipToOneOf (const char *const chars)
1312 while (c != EOF && c != '\0' && strchr (chars, c) == NULL);
1316 /* Skip to the next non-white character.
1318 static int skipToNonWhite (void)
1320 boolean found = FALSE;
1326 while (isspace (c));
1336 if (CollectingSignature && found)
1337 vStringPut (Signature, ' ');
1343 /* Skips to the next brace in column 1. This is intended for cases where
1344 * preprocessor constructs result in unbalanced braces.
1346 static void skipToFormattedBraceMatch (void)
1352 while (c != EOF && (c != '\n' || next != '}'))
1359 /* Skip to the matching character indicated by the pair string. If skipping
1360 * to a matching brace and any brace is found within a different level of a
1361 * #if conditional statement while brace formatting is in effect, we skip to
1362 * the brace matched by its formatting. It is assumed that we have already
1363 * read the character which starts the group (i.e. the first character of
1366 static void skipToMatch (const char *const pair)
1368 const boolean braceMatching = (boolean) (strcmp ("{}", pair) == 0);
1369 const boolean braceFormatting = (boolean) (isBraceFormat () && braceMatching);
1370 const unsigned int initialLevel = getDirectiveNestLevel ();
1371 const int begin = pair [0], end = pair [1];
1372 const unsigned long inputLineNumber = getInputLineNumber ();
1376 while (matchLevel > 0 && (c = skipToNonWhite ()) != EOF)
1378 if (CollectingSignature)
1379 vStringPut (Signature, c);
1383 if (braceFormatting && getDirectiveNestLevel () != initialLevel)
1385 skipToFormattedBraceMatch ();
1392 if (braceFormatting && getDirectiveNestLevel () != initialLevel)
1394 skipToFormattedBraceMatch ();
1401 verbose ("%s: failed to find match for '%c' at line %lu\n",
1402 getInputFileName (), begin, inputLineNumber);
1404 longjmp (Exception, (int) ExceptionBraceFormattingError);
1406 longjmp (Exception, (int) ExceptionFormattingError);
1410 static void skipParens (void)
1412 const int c = skipToNonWhite ();
1420 static void skipBraces (void)
1422 const int c = skipToNonWhite ();
1430 static keywordId analyzeKeyword (const char *const name)
1432 const keywordId id = (keywordId) lookupKeyword (name, getSourceLanguage ());
1436 static void analyzeIdentifier (tokenInfo *const token)
1438 char *const name = vStringValue (token->name);
1439 const char *replacement = NULL;
1440 boolean parensToo = FALSE;
1442 if (isLanguage (Lang_java) ||
1443 ! isIgnoreToken (name, &parensToo, &replacement))
1445 if (replacement != NULL)
1446 token->keyword = analyzeKeyword (replacement);
1448 token->keyword = analyzeKeyword (vStringValue (token->name));
1450 if (token->keyword == KEYWORD_NONE)
1451 token->type = TOKEN_NAME;
1453 token->type = TOKEN_KEYWORD;
1460 int c = skipToNonWhite ();
1468 static void readIdentifier (tokenInfo *const token, const int firstChar)
1470 vString *const name = token->name;
1472 boolean first = TRUE;
1476 /* Bug #1585745: strangely, C++ destructors allow whitespace between
1477 * the ~ and the class name. */
1478 if (isLanguage (Lang_cpp) && firstChar == '~')
1480 vStringPut (name, c);
1481 c = skipToNonWhite ();
1486 vStringPut (name, c);
1487 if (CollectingSignature)
1490 vStringPut (Signature, c);
1494 } while (isident (c) || ((isLanguage (Lang_java) || isLanguage (Lang_csharp)) && (isHighChar (c) || c == '.')));
1495 vStringTerminate (name);
1496 cppUngetc (c); /* unget non-identifier character */
1498 analyzeIdentifier (token);
1501 static void readPackageName (tokenInfo *const token, const int firstChar)
1503 vString *const name = token->name;
1508 while (isident (c) || c == '.')
1510 vStringPut (name, c);
1513 vStringTerminate (name);
1514 cppUngetc (c); /* unget non-package character */
1517 static void readPackageOrNamespace (statementInfo *const st, const declType declaration)
1519 st->declaration = declaration;
1521 if (declaration == DECL_NAMESPACE && !isLanguage (Lang_csharp))
1523 /* In C++ a namespace is specified one level at a time. */
1528 /* In C#, a namespace can also be specified like a Java package name. */
1529 tokenInfo *const token = activeToken (st);
1530 Assert (isType (token, TOKEN_KEYWORD));
1531 readPackageName (token, skipToNonWhite ());
1532 token->type = TOKEN_NAME;
1534 st->haveQualifyingName = TRUE;
1538 static void processName (statementInfo *const st)
1540 Assert (isType (activeToken (st), TOKEN_NAME));
1541 if (st->gotName && st->declaration == DECL_NONE)
1542 st->declaration = DECL_BASE;
1544 st->haveQualifyingName = TRUE;
1547 static void readOperator (statementInfo *const st)
1549 const char *const acceptable = "+-*/%^&|~!=<>,[]";
1550 const tokenInfo* const prev = prevToken (st,1);
1551 tokenInfo *const token = activeToken (st);
1552 vString *const name = token->name;
1553 int c = skipToNonWhite ();
1555 /* When we arrive here, we have the keyword "operator" in 'name'.
1557 if (isType (prev, TOKEN_KEYWORD) && (prev->keyword == KEYWORD_ENUM ||
1558 prev->keyword == KEYWORD_STRUCT || prev->keyword == KEYWORD_UNION))
1559 ; /* ignore "operator" keyword if preceded by these keywords */
1562 /* Verify whether this is a valid function call (i.e. "()") operator.
1564 if (cppGetc () == ')')
1566 vStringPut (name, ' '); /* always separate operator from keyword */
1567 c = skipToNonWhite ();
1569 vStringCatS (name, "()");
1577 else if (isident1 (c))
1579 /* Handle "new" and "delete" operators, and conversion functions
1580 * (per 13.3.1.1.2 [2] of the C++ spec).
1582 boolean whiteSpace = TRUE; /* default causes insertion of space */
1591 vStringPut (name, ' ');
1594 vStringPut (name, c);
1597 } while (! isOneOf (c, "(;") && c != EOF);
1598 vStringTerminate (name);
1600 else if (isOneOf (c, acceptable))
1602 vStringPut (name, ' '); /* always separate operator from keyword */
1605 vStringPut (name, c);
1607 } while (isOneOf (c, acceptable));
1608 vStringTerminate (name);
1613 token->type = TOKEN_NAME;
1614 token->keyword = KEYWORD_NONE;
1618 static void copyToken (tokenInfo *const dest, const tokenInfo *const src)
1620 dest->type = src->type;
1621 dest->keyword = src->keyword;
1622 dest->filePosition = src->filePosition;
1623 dest->lineNumber = src->lineNumber;
1624 vStringCopy (dest->name, src->name);
1627 static void setAccess (statementInfo *const st, const accessType access)
1631 if (isLanguage (Lang_cpp))
1633 int c = skipToNonWhite ();
1636 reinitStatement (st, FALSE);
1640 st->member.accessDefault = access;
1642 st->member.access = access;
1646 static void discardTypeList (tokenInfo *const token)
1648 int c = skipToNonWhite ();
1649 while (isident1 (c))
1651 readIdentifier (token, c);
1652 c = skipToNonWhite ();
1653 if (c == '.' || c == ',')
1654 c = skipToNonWhite ();
1659 static void addParentClass (statementInfo *const st, tokenInfo *const token)
1661 if (vStringLength (token->name) > 0 &&
1662 vStringLength (st->parentClasses) > 0)
1664 vStringPut (st->parentClasses, ',');
1666 vStringCat (st->parentClasses, token->name);
1669 static void readParents (statementInfo *const st, const int qualifier)
1671 tokenInfo *const token = newToken ();
1672 tokenInfo *const parent = newToken ();
1677 c = skipToNonWhite ();
1680 readIdentifier (token, c);
1681 if (isType (token, TOKEN_NAME))
1682 vStringCat (parent->name, token->name);
1685 addParentClass (st, parent);
1689 else if (c == qualifier)
1690 vStringPut (parent->name, c);
1693 else if (isType (token, TOKEN_NAME))
1695 addParentClass (st, parent);
1698 } while (c != '{' && c != EOF);
1700 deleteToken (parent);
1701 deleteToken (token);
1704 static void skipStatement (statementInfo *const st)
1706 st->declaration = DECL_IGNORE;
1710 static void processInterface (statementInfo *const st)
1712 st->declaration = DECL_INTERFACE;
1715 static void processToken (tokenInfo *const token, statementInfo *const st)
1717 switch (token->keyword) /* is it a reserved word? */
1721 case KEYWORD_NONE: processName (st); break;
1722 case KEYWORD_ABSTRACT: st->implementation = IMP_ABSTRACT; break;
1723 case KEYWORD_ATTRIBUTE: skipParens (); initToken (token); break;
1724 case KEYWORD_BIND: st->declaration = DECL_BASE; break;
1725 case KEYWORD_BIT: st->declaration = DECL_BASE; break;
1726 case KEYWORD_CATCH: skipParens (); skipBraces (); break;
1727 case KEYWORD_CHAR: st->declaration = DECL_BASE; break;
1728 case KEYWORD_CLASS: st->declaration = DECL_CLASS; break;
1729 case KEYWORD_CONST: st->declaration = DECL_BASE; break;
1730 case KEYWORD_DOUBLE: st->declaration = DECL_BASE; break;
1731 case KEYWORD_ENUM: st->declaration = DECL_ENUM; break;
1732 case KEYWORD_EXTENDS: readParents (st, '.');
1733 setToken (st, TOKEN_NONE); break;
1734 case KEYWORD_FLOAT: st->declaration = DECL_BASE; break;
1735 case KEYWORD_FUNCTION: st->declaration = DECL_BASE; break;
1736 case KEYWORD_FRIEND: st->scope = SCOPE_FRIEND; break;
1737 case KEYWORD_GOTO: skipStatement (st); break;
1738 case KEYWORD_IMPLEMENTS:readParents (st, '.');
1739 setToken (st, TOKEN_NONE); break;
1740 case KEYWORD_IMPORT: skipStatement (st); break;
1741 case KEYWORD_INT: st->declaration = DECL_BASE; break;
1742 case KEYWORD_INTEGER: st->declaration = DECL_BASE; break;
1743 case KEYWORD_INTERFACE: processInterface (st); break;
1744 case KEYWORD_LOCAL: setAccess (st, ACCESS_LOCAL); break;
1745 case KEYWORD_LONG: st->declaration = DECL_BASE; break;
1746 case KEYWORD_OPERATOR: readOperator (st); break;
1747 case KEYWORD_PRIVATE: setAccess (st, ACCESS_PRIVATE); break;
1748 case KEYWORD_PROGRAM: st->declaration = DECL_PROGRAM; break;
1749 case KEYWORD_PROTECTED: setAccess (st, ACCESS_PROTECTED); break;
1750 case KEYWORD_PUBLIC: setAccess (st, ACCESS_PUBLIC); break;
1751 case KEYWORD_RETURN: skipStatement (st); break;
1752 case KEYWORD_SHORT: st->declaration = DECL_BASE; break;
1753 case KEYWORD_SIGNED: st->declaration = DECL_BASE; break;
1754 case KEYWORD_STRING: st->declaration = DECL_BASE; break;
1755 case KEYWORD_STRUCT: st->declaration = DECL_STRUCT; break;
1756 case KEYWORD_TASK: st->declaration = DECL_TASK; break;
1757 case KEYWORD_THROWS: discardTypeList (token); break;
1758 case KEYWORD_UNION: st->declaration = DECL_UNION; break;
1759 case KEYWORD_UNSIGNED: st->declaration = DECL_BASE; break;
1760 case KEYWORD_USING: skipStatement (st); break;
1761 case KEYWORD_VOID: st->declaration = DECL_BASE; break;
1762 case KEYWORD_VOLATILE: st->declaration = DECL_BASE; break;
1763 case KEYWORD_VIRTUAL: st->implementation = IMP_VIRTUAL; break;
1764 case KEYWORD_WCHAR_T: st->declaration = DECL_BASE; break;
1766 case KEYWORD_NAMESPACE: readPackageOrNamespace (st, DECL_NAMESPACE); break;
1767 case KEYWORD_PACKAGE: readPackageOrNamespace (st, DECL_PACKAGE); break;
1770 if (isLanguage (Lang_csharp))
1771 st->declaration = DECL_EVENT;
1774 case KEYWORD_TYPEDEF:
1775 reinitStatement (st, FALSE);
1776 st->scope = SCOPE_TYPEDEF;
1779 case KEYWORD_EXTERN:
1780 if (! isLanguage (Lang_csharp) || !st->gotName)
1782 reinitStatement (st, FALSE);
1783 st->scope = SCOPE_EXTERN;
1784 st->declaration = DECL_BASE;
1788 case KEYWORD_STATIC:
1789 if (! (isLanguage (Lang_java) || isLanguage (Lang_csharp)))
1791 reinitStatement (st, FALSE);
1792 st->scope = SCOPE_STATIC;
1793 st->declaration = DECL_BASE;
1798 case KEYWORD_FOREACH:
1800 case KEYWORD_SWITCH:
1803 int c = skipToNonWhite ();
1812 * Parenthesis handling functions
1815 static void restartStatement (statementInfo *const st)
1817 tokenInfo *const save = newToken ();
1818 tokenInfo *token = activeToken (st);
1820 copyToken (save, token);
1821 DebugStatement ( if (debug (DEBUG_PARSE)) printf ("<ES>");)
1822 reinitStatement (st, FALSE);
1823 token = activeToken (st);
1824 copyToken (token, save);
1826 processToken (token, st);
1829 /* Skips over a the mem-initializer-list of a ctor-initializer, defined as:
1831 * mem-initializer-list:
1832 * mem-initializer, mem-initializer-list
1835 * [::] [nested-name-spec] class-name (...)
1838 static void skipMemIntializerList (tokenInfo *const token)
1844 c = skipToNonWhite ();
1845 while (isident1 (c) || c == ':')
1848 readIdentifier (token, c);
1849 c = skipToNonWhite ();
1854 c = skipToNonWhite ();
1859 c = skipToNonWhite ();
1865 static void skipMacro (statementInfo *const st)
1867 tokenInfo *const prev2 = prevToken (st, 2);
1869 if (isType (prev2, TOKEN_NAME))
1874 /* Skips over characters following the parameter list. This will be either
1875 * non-ANSI style function declarations or C++ stuff. Our choices:
1879 * int func (one, two) int one; float two; {...}
1881 * int func (int one, float two);
1882 * int func (int one, float two) {...}
1884 * int foo (...) [const|volatile] [throw (...)];
1885 * int foo (...) [const|volatile] [throw (...)] [ctor-initializer] {...}
1886 * int foo (...) [const|volatile] [throw (...)] try [ctor-initializer] {...}
1889 static boolean skipPostArgumentStuff (
1890 statementInfo *const st, parenInfo *const info)
1892 tokenInfo *const token = activeToken (st);
1893 unsigned int parameters = info->parameterCount;
1894 unsigned int elementCount = 0;
1895 boolean restart = FALSE;
1896 boolean end = FALSE;
1897 int c = skipToNonWhite ();
1904 case ':': skipMemIntializerList (token);break; /* ctor-initializer */
1905 case '[': skipToMatch ("[]"); break;
1906 case '=': cppUngetc (c); end = TRUE; break;
1907 case '{': cppUngetc (c); end = TRUE; break;
1908 case '}': cppUngetc (c); end = TRUE; break;
1911 if (elementCount > 0)
1917 if (parameters == 0 || elementCount < 2)
1922 else if (--parameters == 0)
1929 readIdentifier (token, c);
1930 switch (token->keyword)
1932 case KEYWORD_ATTRIBUTE: skipParens (); break;
1933 case KEYWORD_THROW: skipParens (); break;
1934 case KEYWORD_TRY: break;
1937 case KEYWORD_VOLATILE:
1938 if (vStringLength (Signature) > 0)
1940 vStringPut (Signature, ' ');
1941 vStringCat (Signature, token->name);
1947 case KEYWORD_EXPLICIT:
1948 case KEYWORD_EXTERN:
1949 case KEYWORD_FRIEND:
1950 case KEYWORD_INLINE:
1951 case KEYWORD_MUTABLE:
1952 case KEYWORD_NAMESPACE:
1954 case KEYWORD_NEWCOV:
1955 case KEYWORD_OPERATOR:
1956 case KEYWORD_OVERLOAD:
1957 case KEYWORD_PRIVATE:
1958 case KEYWORD_PROTECTED:
1959 case KEYWORD_PUBLIC:
1960 case KEYWORD_STATIC:
1961 case KEYWORD_TEMPLATE:
1962 case KEYWORD_TYPEDEF:
1963 case KEYWORD_TYPENAME:
1965 case KEYWORD_VIRTUAL:
1966 /* Never allowed within parameter declarations. */
1972 if (isType (token, TOKEN_NONE))
1974 else if (info->isKnrParamList && info->parameterCount > 0)
1978 /* If we encounter any other identifier immediately
1979 * following an empty parameter list, this is almost
1980 * certainly one of those Microsoft macro "thingies"
1981 * that the automatic source code generation sticks
1982 * in. Terminate the current statement.
1993 c = skipToNonWhite ();
2000 restartStatement (st);
2002 setToken (st, TOKEN_NONE);
2004 return (boolean) (c != EOF);
2007 static void skipJavaThrows (statementInfo *const st)
2009 tokenInfo *const token = activeToken (st);
2010 int c = skipToNonWhite ();
2014 readIdentifier (token, c);
2015 if (token->keyword == KEYWORD_THROWS)
2019 c = skipToNonWhite ();
2022 readIdentifier (token, c);
2023 c = skipToNonWhite ();
2025 } while (c == '.' || c == ',');
2029 setToken (st, TOKEN_NONE);
2032 static void analyzePostParens (statementInfo *const st, parenInfo *const info)
2034 const unsigned long inputLineNumber = getInputLineNumber ();
2035 int c = skipToNonWhite ();
2038 if (isOneOf (c, "{;,="))
2040 else if (isLanguage (Lang_java))
2041 skipJavaThrows (st);
2044 if (! skipPostArgumentStuff (st, info))
2047 "%s: confusing argument declarations beginning at line %lu\n",
2048 getInputFileName (), inputLineNumber);
2049 longjmp (Exception, (int) ExceptionFormattingError);
2054 static boolean languageSupportsGenerics (void)
2056 return (boolean) (isLanguage (Lang_cpp) || isLanguage (Lang_csharp) ||
2057 isLanguage (Lang_java));
2060 static void processAngleBracket (void)
2064 /* already found match for template */
2065 } else if (languageSupportsGenerics () && c != '<' && c != '=') {
2066 /* this is a template */
2069 } else if (c == '<') {
2070 /* skip "<<" or "<<=". */
2080 static void parseJavaAnnotation (statementInfo *const st)
2084 * @Target(ElementType.METHOD)
2085 * @SuppressWarnings(value = "unchecked")
2087 * But watch out for "@interface"!
2089 tokenInfo *const token = activeToken (st);
2091 int c = skipToNonWhite ();
2092 readIdentifier (token, c);
2093 if (token->keyword == KEYWORD_INTERFACE)
2095 /* Oops. This was actually "@interface" defining a new annotation. */
2096 processInterface (st);
2100 /* Bug #1691412: skip any annotation arguments. */
2105 static int parseParens (statementInfo *const st, parenInfo *const info)
2107 tokenInfo *const token = activeToken (st);
2108 unsigned int identifierCount = 0;
2109 unsigned int depth = 1;
2110 boolean firstChar = TRUE;
2111 int nextChar = '\0';
2113 CollectingSignature = TRUE;
2114 vStringClear (Signature);
2115 vStringPut (Signature, '(');
2116 info->parameterCount = 1;
2119 int c = skipToNonWhite ();
2120 vStringPut (Signature, c);
2126 info->isPointer = TRUE;
2127 info->isKnrParamList = FALSE;
2128 if (identifierCount == 0)
2129 info->isParamList = FALSE;
2134 info->isKnrParamList = FALSE;
2138 info->isNameCandidate = FALSE;
2143 info->isKnrParamList = FALSE;
2151 info->isKnrParamList = FALSE;
2154 vStringCatS (Signature, "..."); /* variable arg list */
2159 info->isNameCandidate = FALSE;
2160 if (info->isKnrParamList)
2162 ++info->parameterCount;
2163 identifierCount = 0;
2168 info->isKnrParamList = FALSE;
2169 info->isNameCandidate = FALSE;
2172 info->isParamList = FALSE;
2179 info->isKnrParamList = FALSE;
2184 info->isKnrParamList = FALSE;
2185 processAngleBracket ();
2190 info->parameterCount = 0;
2195 info->isKnrParamList = FALSE;
2198 info->isNameCandidate = FALSE;
2200 vStringClear (Signature);
2203 vStringChop (Signature);
2205 else if (isType (token, TOKEN_PAREN_NAME))
2207 c = skipToNonWhite ();
2208 if (c == '*') /* check for function pointer */
2211 c = skipToNonWhite ();
2221 info->nestedArgs = TRUE;
2229 if (c == '@' && isLanguage (Lang_java))
2231 parseJavaAnnotation(st);
2233 else if (isident1 (c))
2235 if (++identifierCount > 1)
2236 info->isKnrParamList = FALSE;
2237 readIdentifier (token, c);
2238 if (isType (token, TOKEN_NAME) && info->isNameCandidate)
2239 token->type = TOKEN_PAREN_NAME;
2240 else if (isType (token, TOKEN_KEYWORD))
2242 if (token->keyword != KEYWORD_CONST &&
2243 token->keyword != KEYWORD_VOLATILE)
2245 info->isKnrParamList = FALSE;
2246 info->isNameCandidate = FALSE;
2252 info->isParamList = FALSE;
2253 info->isKnrParamList = FALSE;
2254 info->isNameCandidate = FALSE;
2255 info->invalidContents = TRUE;
2260 } while (! info->nestedArgs && depth > 0 &&
2261 (info->isKnrParamList || info->isNameCandidate));
2263 if (! info->nestedArgs) while (depth > 0)
2269 if (! info->isNameCandidate)
2272 vStringTerminate (Signature);
2273 if (info->isKnrParamList)
2274 vStringClear (Signature);
2275 CollectingSignature = FALSE;
2279 static void initParenInfo (parenInfo *const info)
2281 info->isPointer = FALSE;
2282 info->isParamList = TRUE;
2283 info->isKnrParamList = isLanguage (Lang_c);
2284 info->isNameCandidate = TRUE;
2285 info->invalidContents = FALSE;
2286 info->nestedArgs = FALSE;
2287 info->parameterCount = 0;
2290 static void analyzeParens (statementInfo *const st)
2292 tokenInfo *const prev = prevToken (st, 1);
2294 if (st->inFunction && ! st->assignment)
2295 st->notVariable = TRUE;
2296 if (! isType (prev, TOKEN_NONE)) /* in case of ignored enclosing macros */
2298 tokenInfo *const token = activeToken (st);
2302 initParenInfo (&info);
2303 parseParens (st, &info);
2304 c = skipToNonWhite ();
2306 if (info.invalidContents)
2307 reinitStatement (st, FALSE);
2308 else if (info.isNameCandidate && isType (token, TOKEN_PAREN_NAME) &&
2309 ! st->gotParenName &&
2310 (! info.isParamList || ! st->haveQualifyingName ||
2312 (c == '=' && st->implementation != IMP_VIRTUAL) ||
2313 (st->declaration == DECL_NONE && isOneOf (c, ",;"))))
2315 token->type = TOKEN_NAME;
2317 st->gotParenName = TRUE;
2318 if (! (c == '(' && info.nestedArgs))
2319 st->isPointer = info.isPointer;
2321 else if (! st->gotArgs && info.isParamList)
2324 setToken (st, TOKEN_ARGS);
2326 if (st->scope != SCOPE_TYPEDEF)
2327 analyzePostParens (st, &info);
2330 setToken (st, TOKEN_NONE);
2335 * Token parsing functions
2338 static void addContext (statementInfo *const st, const tokenInfo* const token)
2340 if (isType (token, TOKEN_NAME))
2342 if (vStringLength (st->context->name) > 0)
2344 if (isLanguage (Lang_c) || isLanguage (Lang_cpp))
2345 vStringCatS (st->context->name, "::");
2346 else if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
2347 vStringCatS (st->context->name, ".");
2349 vStringCat (st->context->name, token->name);
2350 st->context->type = TOKEN_NAME;
2354 static boolean inheritingDeclaration (declType decl)
2356 /* C# supports inheritance for enums. C++0x will too, but not yet. */
2357 if (decl == DECL_ENUM)
2359 return (boolean) (isLanguage (Lang_csharp));
2362 decl == DECL_CLASS ||
2363 decl == DECL_STRUCT ||
2364 decl == DECL_INTERFACE);
2367 static void processColon (statementInfo *const st)
2369 int c = (isLanguage (Lang_cpp) ? cppGetc () : skipToNonWhite ());
2370 const boolean doubleColon = (boolean) (c == ':');
2374 setToken (st, TOKEN_DOUBLE_COLON);
2375 st->haveQualifyingName = FALSE;
2380 if ((isLanguage (Lang_cpp) || isLanguage (Lang_csharp)) &&
2381 inheritingDeclaration (st->declaration))
2383 readParents (st, ':');
2385 else if (parentDecl (st) == DECL_STRUCT)
2387 c = skipToOneOf (",;");
2389 setToken (st, TOKEN_COMMA);
2391 setToken (st, TOKEN_SEMICOLON);
2395 const tokenInfo *const prev = prevToken (st, 1);
2396 const tokenInfo *const prev2 = prevToken (st, 2);
2397 if (prev->keyword == KEYWORD_DEFAULT ||
2398 prev2->keyword == KEYWORD_CASE ||
2401 reinitStatement (st, FALSE);
2407 /* Skips over any initializing value which may follow an '=' character in a
2408 * variable definition.
2410 static int skipInitializer (statementInfo *const st)
2412 boolean done = FALSE;
2417 c = skipToNonWhite ();
2420 longjmp (Exception, (int) ExceptionFormattingError);
2424 case ';': done = TRUE; break;
2427 if (st->implementation == IMP_VIRTUAL)
2428 st->implementation = IMP_PURE_VIRTUAL;
2431 case '[': skipToMatch ("[]"); break;
2432 case '(': skipToMatch ("()"); break;
2433 case '{': skipToMatch ("{}"); break;
2434 case '<': processAngleBracket(); break;
2437 if (insideEnumBody (st))
2439 else if (! isBraceFormat ())
2441 verbose ("%s: unexpected closing brace at line %lu\n",
2442 getInputFileName (), getInputLineNumber ());
2443 longjmp (Exception, (int) ExceptionBraceFormattingError);
2453 static void processInitializer (statementInfo *const st)
2455 const boolean inEnumBody = insideEnumBody (st);
2461 c = skipInitializer (st);
2462 st->assignment = TRUE;
2464 setToken (st, TOKEN_SEMICOLON);
2466 setToken (st, TOKEN_COMMA);
2467 else if (c == '}' && inEnumBody)
2470 setToken (st, TOKEN_COMMA);
2472 if (st->scope == SCOPE_EXTERN)
2473 st->scope = SCOPE_GLOBAL;
2477 static void parseIdentifier (statementInfo *const st, const int c)
2479 tokenInfo *const token = activeToken (st);
2481 readIdentifier (token, c);
2482 if (! isType (token, TOKEN_NONE))
2483 processToken (token, st);
2486 static void parseGeneralToken (statementInfo *const st, const int c)
2488 const tokenInfo *const prev = prevToken (st, 1);
2490 if (isident1 (c) || (isLanguage (Lang_java) && isHighChar (c)))
2492 parseIdentifier (st, c);
2493 if (isType (st->context, TOKEN_NAME) &&
2494 isType (activeToken (st), TOKEN_NAME) && isType (prev, TOKEN_NAME))
2496 initToken (st->context);
2499 else if (c == '.' || c == '-')
2501 if (! st->assignment)
2502 st->notVariable = TRUE;
2505 int c2 = cppGetc ();
2510 else if (c == '!' || c == '>')
2512 int c2 = cppGetc ();
2516 else if (c == '@' && isLanguage (Lang_java))
2518 parseJavaAnnotation (st);
2520 else if (isExternCDecl (st, c))
2522 st->declaration = DECL_NOMANGLE;
2523 st->scope = SCOPE_GLOBAL;
2527 /* Reads characters from the pre-processor and assembles tokens, setting
2528 * the current statement state.
2530 static void nextToken (statementInfo *const st)
2535 int c = skipToNonWhite ();
2538 case EOF: longjmp (Exception, (int) ExceptionEOF); break;
2539 case '(': analyzeParens (st); break;
2540 case '<': processAngleBracket (); break;
2541 case '*': st->haveQualifyingName = FALSE; break;
2542 case ',': setToken (st, TOKEN_COMMA); break;
2543 case ':': processColon (st); break;
2544 case ';': setToken (st, TOKEN_SEMICOLON); break;
2545 case '=': processInitializer (st); break;
2546 case '[': skipToMatch ("[]"); break;
2547 case '{': setToken (st, TOKEN_BRACE_OPEN); break;
2548 case '}': setToken (st, TOKEN_BRACE_CLOSE); break;
2549 default: parseGeneralToken (st, c); break;
2551 token = activeToken (st);
2552 } while (isType (token, TOKEN_NONE));
2556 * Scanning support functions
2559 static statementInfo *CurrentStatement = NULL;
2561 static statementInfo *newStatement (statementInfo *const parent)
2563 statementInfo *const st = xMalloc (1, statementInfo);
2566 for (i = 0 ; i < (unsigned int) NumTokens ; ++i)
2567 st->token [i] = newToken ();
2569 st->context = newToken ();
2570 st->blockName = newToken ();
2571 st->parentClasses = vStringNew ();
2573 initStatement (st, parent);
2574 CurrentStatement = st;
2579 static void deleteStatement (void)
2581 statementInfo *const st = CurrentStatement;
2582 statementInfo *const parent = st->parent;
2585 for (i = 0 ; i < (unsigned int) NumTokens ; ++i)
2587 deleteToken (st->token [i]); st->token [i] = NULL;
2589 deleteToken (st->blockName); st->blockName = NULL;
2590 deleteToken (st->context); st->context = NULL;
2591 vStringDelete (st->parentClasses); st->parentClasses = NULL;
2593 CurrentStatement = parent;
2596 static void deleteAllStatements (void)
2598 while (CurrentStatement != NULL)
2602 static boolean isStatementEnd (const statementInfo *const st)
2604 const tokenInfo *const token = activeToken (st);
2607 if (isType (token, TOKEN_SEMICOLON))
2609 else if (isType (token, TOKEN_BRACE_CLOSE))
2610 /* Java and C# do not require semicolons to end a block. Neither do C++
2611 * namespaces. All other blocks require a semicolon to terminate them.
2613 isEnd = (boolean) (isLanguage (Lang_java) || isLanguage (Lang_csharp) ||
2614 ! isContextualStatement (st));
2621 static void checkStatementEnd (statementInfo *const st)
2623 const tokenInfo *const token = activeToken (st);
2625 if (isType (token, TOKEN_COMMA))
2626 reinitStatement (st, TRUE);
2627 else if (isStatementEnd (st))
2629 DebugStatement ( if (debug (DEBUG_PARSE)) printf ("<ES>"); )
2630 reinitStatement (st, FALSE);
2635 cppBeginStatement ();
2640 static void nest (statementInfo *const st, const unsigned int nestLevel)
2642 switch (st->declaration)
2646 case DECL_INTERFACE:
2647 case DECL_NAMESPACE:
2651 createTags (nestLevel, st);
2656 st->inFunction = TRUE;
2659 if (includeTag (TAG_LOCAL, FALSE))
2660 createTags (nestLevel, st);
2666 setToken (st, TOKEN_BRACE_CLOSE);
2669 static void tagCheck (statementInfo *const st)
2671 const tokenInfo *const token = activeToken (st);
2672 const tokenInfo *const prev = prevToken (st, 1);
2673 const tokenInfo *const prev2 = prevToken (st, 2);
2675 switch (token->type)
2678 if (insideEnumBody (st))
2679 qualifyEnumeratorTag (st, token);
2683 if (st->haveQualifyingName)
2684 makeTag (token, st, FALSE, TAG_PACKAGE);
2687 case TOKEN_BRACE_OPEN:
2688 if (isType (prev, TOKEN_ARGS))
2690 if (st->haveQualifyingName)
2692 if (! isLanguage (Lang_vera))
2693 st->declaration = DECL_FUNCTION;
2694 if (isType (prev2, TOKEN_NAME))
2695 copyToken (st->blockName, prev2);
2696 qualifyFunctionTag (st, prev2);
2699 else if (isContextualStatement (st) ||
2700 st->declaration == DECL_NAMESPACE ||
2701 st->declaration == DECL_PROGRAM)
2703 if (isType (prev, TOKEN_NAME))
2704 copyToken (st->blockName, prev);
2707 /* For an anonymous struct or union we use a unique ID
2708 * a number, so that the members can be found.
2710 char buf [20]; /* length of "_anon" + digits + null */
2711 sprintf (buf, "__anon%d", ++AnonymousID);
2712 vStringCopyS (st->blockName->name, buf);
2713 st->blockName->type = TOKEN_NAME;
2714 st->blockName->keyword = KEYWORD_NONE;
2716 qualifyBlockTag (st, prev);
2718 else if (isLanguage (Lang_csharp))
2719 makeTag (prev, st, FALSE, TAG_PROPERTY);
2722 case TOKEN_SEMICOLON:
2724 if (insideEnumBody (st))
2726 else if (isType (prev, TOKEN_NAME))
2728 if (isContextualKeyword (prev2))
2729 makeTag (prev, st, TRUE, TAG_EXTERN_VAR);
2731 qualifyVariableTag (st, prev);
2733 else if (isType (prev, TOKEN_ARGS) && isType (prev2, TOKEN_NAME))
2736 qualifyVariableTag (st, prev2);
2738 qualifyFunctionDeclTag (st, prev2);
2740 if (isLanguage (Lang_java) && token->type == TOKEN_SEMICOLON && insideEnumBody (st))
2742 /* In Java, after an initial enum-like part,
2743 * a semicolon introduces a class-like part.
2744 * See Bug #1730485 for the full rationale. */
2745 st->parent->declaration = DECL_CLASS;
2753 /* Parses the current file and decides whether to write out and tags that
2756 static void createTags (const unsigned int nestLevel,
2757 statementInfo *const parent)
2759 statementInfo *const st = newStatement (parent);
2761 DebugStatement ( if (nestLevel > 0) debugParseNest (TRUE, nestLevel); )
2767 token = activeToken (st);
2768 if (isType (token, TOKEN_BRACE_CLOSE))
2774 verbose ("%s: unexpected closing brace at line %lu\n",
2775 getInputFileName (), getInputLineNumber ());
2776 longjmp (Exception, (int) ExceptionBraceFormattingError);
2779 else if (isType (token, TOKEN_DOUBLE_COLON))
2781 addContext (st, prevToken (st, 1));
2787 if (isType (token, TOKEN_BRACE_OPEN))
2788 nest (st, nestLevel + 1);
2789 checkStatementEnd (st);
2793 DebugStatement ( if (nestLevel > 0) debugParseNest (FALSE, nestLevel - 1); )
2796 static boolean findCTags (const unsigned int passCount)
2798 exception_t exception;
2801 Assert (passCount < 3);
2802 cppInit ((boolean) (passCount > 1), isLanguage (Lang_csharp));
2803 Signature = vStringNew ();
2805 exception = (exception_t) setjmp (Exception);
2807 if (exception == ExceptionNone)
2808 createTags (0, NULL);
2811 deleteAllStatements ();
2812 if (exception == ExceptionBraceFormattingError && passCount == 1)
2815 verbose ("%s: retrying file with fallback brace matching algorithm\n",
2816 getInputFileName ());
2819 vStringDelete (Signature);
2824 static void buildKeywordHash (const langType language, unsigned int idx)
2826 const size_t count = sizeof (KeywordTable) / sizeof (KeywordTable [0]);
2828 for (i = 0 ; i < count ; ++i)
2830 const keywordDesc* const p = &KeywordTable [i];
2831 if (p->isValid [idx])
2832 addKeyword (p->name, language, (int) p->id);
2836 static void initializeCParser (const langType language)
2839 buildKeywordHash (language, 0);
2842 static void initializeCppParser (const langType language)
2844 Lang_cpp = language;
2845 buildKeywordHash (language, 1);
2848 static void initializeCsharpParser (const langType language)
2850 Lang_csharp = language;
2851 buildKeywordHash (language, 2);
2854 static void initializeJavaParser (const langType language)
2856 Lang_java = language;
2857 buildKeywordHash (language, 3);
2860 static void initializeVeraParser (const langType language)
2862 Lang_vera = language;
2863 buildKeywordHash (language, 4);
2866 extern parserDefinition* CParser (void)
2868 static const char *const extensions [] = { "c", NULL };
2869 parserDefinition* def = parserNew ("C");
2870 def->kinds = CKinds;
2871 def->kindCount = KIND_COUNT (CKinds);
2872 def->extensions = extensions;
2873 def->parser2 = findCTags;
2874 def->initialize = initializeCParser;
2878 extern parserDefinition* CppParser (void)
2880 static const char *const extensions [] = {
2881 "c++", "cc", "cp", "cpp", "cxx", "h", "h++", "hh", "hp", "hpp", "hxx",
2882 #ifndef CASE_INSENSITIVE_FILENAMES
2887 parserDefinition* def = parserNew ("C++");
2888 def->kinds = CKinds;
2889 def->kindCount = KIND_COUNT (CKinds);
2890 def->extensions = extensions;
2891 def->parser2 = findCTags;
2892 def->initialize = initializeCppParser;
2896 extern parserDefinition* CsharpParser (void)
2898 static const char *const extensions [] = { "cs", NULL };
2899 parserDefinition* def = parserNew ("C#");
2900 def->kinds = CsharpKinds;
2901 def->kindCount = KIND_COUNT (CsharpKinds);
2902 def->extensions = extensions;
2903 def->parser2 = findCTags;
2904 def->initialize = initializeCsharpParser;
2908 extern parserDefinition* JavaParser (void)
2910 static const char *const extensions [] = { "java", NULL };
2911 parserDefinition* def = parserNew ("Java");
2912 def->kinds = JavaKinds;
2913 def->kindCount = KIND_COUNT (JavaKinds);
2914 def->extensions = extensions;
2915 def->parser2 = findCTags;
2916 def->initialize = initializeJavaParser;
2920 extern parserDefinition* VeraParser (void)
2922 static const char *const extensions [] = { "vr", "vri", "vrh", NULL };
2923 parserDefinition* def = parserNew ("Vera");
2924 def->kinds = VeraKinds;
2925 def->kindCount = KIND_COUNT (VeraKinds);
2926 def->extensions = extensions;
2927 def->parser2 = findCTags;
2928 def->initialize = initializeVeraParser;
2932 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */