change __unused__ macro to __ctags_unused__
[platform/upstream/ctags.git] / c.c
1 /*
2 *   $Id: c.c 689 2008-12-13 21:17:36Z elliotth $
3 *
4 *   Copyright (c) 1996-2003, Darren Hiebert
5 *
6 *   This source code is released for free distribution under the terms of the
7 *   GNU General Public License.
8 *
9 *   This module contains functions for parsing and scanning C, C++ and Java
10 *   source files.
11 */
12
13 /*
14 *   INCLUDE FILES
15 */
16 #include "general.h"        /* must always come first */
17
18 #include <string.h>
19 #include <setjmp.h>
20
21 #include "debug.h"
22 #include "entry.h"
23 #include "get.h"
24 #include "keyword.h"
25 #include "options.h"
26 #include "parse.h"
27 #include "read.h"
28 #include "routines.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)     (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)
42
43 #define isOneOf(c,s)        (boolean) (strchr ((s), (c)) != NULL)
44
45 #define isHighChar(c)       ((c) != EOF && (unsigned char)(c) >= 0xc0)
46
47 /*
48 *   DATA DECLARATIONS
49 */
50
51 enum { NumTokens = 3 };
52
53 typedef enum eException {
54         ExceptionNone, ExceptionEOF, ExceptionFormattingError,
55         ExceptionBraceFormattingError
56 } exception_t;
57
58 /*  Used to specify type of keyword.
59  */
60 typedef enum eKeywordId {
61         KEYWORD_NONE = -1,
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,
68         KEYWORD_DOUBLE,
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,
73         KEYWORD_GOTO,
74         KEYWORD_IF, KEYWORD_IMPLEMENTS, KEYWORD_IMPORT, KEYWORD_INLINE, KEYWORD_INT,
75         KEYWORD_INOUT, KEYWORD_INPUT, KEYWORD_INTEGER, KEYWORD_INTERFACE,
76         KEYWORD_INTERNAL,
77         KEYWORD_LOCAL, KEYWORD_LONG,
78         KEYWORD_M_BAD_STATE, KEYWORD_M_BAD_TRANS, KEYWORD_M_STATE, KEYWORD_M_TRANS,
79         KEYWORD_MUTABLE,
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,
92         KEYWORD_USING,
93         KEYWORD_VIRTUAL, KEYWORD_VOID, KEYWORD_VOLATILE,
94         KEYWORD_WCHAR_T, KEYWORD_WHILE
95 } keywordId;
96
97 /*  Used to determine whether keyword is valid for the current language and
98  *  what its ID is.
99  */
100 typedef struct sKeywordDesc {
101         const char *name;
102         keywordId id;
103         short isValid [5]; /* indicates languages for which kw is valid */
104 } keywordDesc;
105
106 /*  Used for reporting the type of object parsed by nextToken ().
107  */
108 typedef enum eTokenType {
109         TOKEN_NONE,          /* none */
110         TOKEN_ARGS,          /* a parenthetical pair and its contents */
111         TOKEN_BRACE_CLOSE,
112         TOKEN_BRACE_OPEN,
113         TOKEN_COLON,         /* the colon character */
114         TOKEN_COMMA,         /* the comma character */
115         TOKEN_DOUBLE_COLON,  /* double colon indicates nested-name-specifier */
116         TOKEN_KEYWORD,
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. */
122         TOKEN_COUNT
123 } tokenType;
124
125 /*  This describes the scoping of the current statement.
126  */
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 */
133         SCOPE_COUNT
134 } tagScope;
135
136 typedef enum eDeclaration {
137         DECL_NONE,
138         DECL_BASE,           /* base type (default) */
139         DECL_CLASS,
140         DECL_ENUM,
141         DECL_EVENT,
142         DECL_FUNCTION,
143         DECL_IGNORE,         /* non-taggable "declaration" */
144         DECL_INTERFACE,
145         DECL_NAMESPACE,
146         DECL_NOMANGLE,       /* C++ name demangling block */
147         DECL_PACKAGE,
148         DECL_PROGRAM,        /* Vera program */
149         DECL_STRUCT,
150         DECL_TASK,           /* Vera task */
151         DECL_UNION,
152         DECL_COUNT
153 } declType;
154
155 typedef enum eVisibilityType {
156         ACCESS_UNDEFINED,
157         ACCESS_LOCAL,
158         ACCESS_PRIVATE,
159         ACCESS_PROTECTED,
160         ACCESS_PUBLIC,
161         ACCESS_DEFAULT,      /* Java-specific */
162         ACCESS_COUNT
163 } accessType;
164
165 /*  Information about the parent class of a member (if any).
166  */
167 typedef struct sMemberInfo {
168         accessType access;           /* access of current statement */
169         accessType accessDefault;    /* access default for current statement */
170 } memberInfo;
171
172 typedef struct sTokenInfo {
173         tokenType     type;
174         keywordId     keyword;
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 */
178 } tokenInfo;
179
180 typedef enum eImplementation {
181         IMP_DEFAULT,
182         IMP_ABSTRACT,
183         IMP_VIRTUAL,
184         IMP_PURE_VIRTUAL,
185         IMP_COUNT
186 } impType;
187
188 /*  Describes the statement currently undergoing analysis.
189  */
190 typedef struct sStatementInfo {
191         tagScope        scope;
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 */
209 } statementInfo;
210
211 /*  Describes the type of tag being generated.
212  */
213 typedef enum eTagType {
214         TAG_UNDEFINED,
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 */
237 } tagType;
238
239 typedef struct sParenInfo {
240         boolean isPointer;
241         boolean isParamList;
242         boolean isKnrParamList;
243         boolean isNameCandidate;
244         boolean invalidContents;
245         boolean nestedArgs;
246         unsigned int parameterCount;
247 } parenInfo;
248
249 /*
250 *   DATA DEFINITIONS
251 */
252
253 static jmp_buf Exception;
254
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;
262
263 /* Number used to uniquely identify anonymous structs and unions. */
264 static int AnonymousID = 0;
265
266 /* Used to index into the CKinds table. */
267 typedef enum {
268         CK_UNDEFINED = -1,
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,
272         CK_EXTERN_VARIABLE
273 } cKind;
274
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"},
290 };
291
292 typedef enum {
293         CSK_UNDEFINED = -1,
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
297 } csharpKind;
298
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"},
313 };
314
315 /* Used to index into the JavaKinds table. */
316 typedef enum {
317         JK_UNDEFINED = -1,
318         JK_CLASS, JK_ENUM_CONSTANT, JK_FIELD, JK_ENUM, JK_INTERFACE,
319         JK_LOCAL, JK_METHOD, JK_PACKAGE, JK_ACCESS, JK_CLASS_PREFIX
320 } javaKind;
321
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"},
331 };
332
333 /* Used to index into the VeraKinds table. */
334 typedef enum {
335         VK_UNDEFINED = -1,
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,
339         VK_EXTERN_VARIABLE
340 } veraKind;
341
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"}
356 };
357
358 static const keywordDesc KeywordTable [] = {
359         /*                                              C++            */
360         /*                                       ANSI C  |  C# Java    */
361         /*                                            |  |  |  |  Vera */
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 } }
463 };
464
465 /*
466 *   FUNCTION PROTOTYPES
467 */
468 static void createTags (const unsigned int nestLevel, statementInfo *const parent);
469
470 /*
471 *   FUNCTION DEFINITIONS
472 */
473
474 extern boolean includingDefineTags (void)
475 {
476         return CKinds [CK_DEFINE].enabled;
477 }
478
479 /*
480 *   Token management
481 */
482
483 static void initToken (tokenInfo* const token)
484 {
485         token->type                     = TOKEN_NONE;
486         token->keyword          = KEYWORD_NONE;
487         token->lineNumber       = getSourceLineNumber ();
488         token->filePosition     = getInputFilePosition ();
489         vStringClear (token->name);
490 }
491
492 static void advanceToken (statementInfo* const st)
493 {
494         if (st->tokenIndex >= (unsigned int) NumTokens - 1)
495                 st->tokenIndex = 0;
496         else
497                 ++st->tokenIndex;
498         initToken (st->token [st->tokenIndex]);
499 }
500
501 static tokenInfo *prevToken (const statementInfo *const st, unsigned int n)
502 {
503         unsigned int tokenIndex;
504         unsigned int num = (unsigned int) NumTokens;
505         Assert (n < num);
506         tokenIndex = (st->tokenIndex + num - n) % num;
507         return st->token [tokenIndex];
508 }
509
510 static void setToken (statementInfo *const st, const tokenType type)
511 {
512         tokenInfo *token;
513         token = activeToken (st);
514         initToken (token);
515         token->type = type;
516 }
517
518 static void retardToken (statementInfo *const st)
519 {
520         if (st->tokenIndex == 0)
521                 st->tokenIndex = (unsigned int) NumTokens - 1;
522         else
523                 --st->tokenIndex;
524         setToken (st, TOKEN_NONE);
525 }
526
527 static tokenInfo *newToken (void)
528 {
529         tokenInfo *const token = xMalloc (1, tokenInfo);
530         token->name = vStringNew ();
531         initToken (token);
532         return token;
533 }
534
535 static void deleteToken (tokenInfo *const token)
536 {
537         if (token != NULL)
538         {
539                 vStringDelete (token->name);
540                 eFree (token);
541         }
542 }
543
544 static const char *accessString (const accessType access)
545 {
546         static const char *const names [] = {
547                 "?", "local", "private", "protected", "public", "default"
548         };
549         Assert (sizeof (names) / sizeof (names [0]) == ACCESS_COUNT);
550         Assert ((int) access < ACCESS_COUNT);
551         return names [(int) access];
552 }
553
554 static const char *implementationString (const impType imp)
555 {
556         static const char *const names [] ={
557                 "?", "abstract", "virtual", "pure virtual"
558         };
559         Assert (sizeof (names) / sizeof (names [0]) == IMP_COUNT);
560         Assert ((int) imp < IMP_COUNT);
561         return names [(int) imp];
562 }
563
564 /*
565 *   Debugging functions
566 */
567
568 #ifdef DEBUG
569
570 #define boolString(c)   ((c) ? "TRUE" : "FALSE")
571
572 static const char *tokenString (const tokenType type)
573 {
574         static const char *const names [] = {
575                 "none", "args", "}", "{", "colon", "comma", "double colon", "keyword",
576                 "name", "package", "paren-name", "semicolon", "specifier"
577         };
578         Assert (sizeof (names) / sizeof (names [0]) == TOKEN_COUNT);
579         Assert ((int) type < TOKEN_COUNT);
580         return names [(int) type];
581 }
582
583 static const char *scopeString (const tagScope scope)
584 {
585         static const char *const names [] = {
586                 "global", "static", "extern", "friend", "typedef"
587         };
588         Assert (sizeof (names) / sizeof (names [0]) == SCOPE_COUNT);
589         Assert ((int) scope < SCOPE_COUNT);
590         return names [(int) scope];
591 }
592
593 static const char *declString (const declType declaration)
594 {
595         static const char *const names [] = {
596                 "?", "base", "class", "enum", "event", "function", "ignore",
597                 "interface", "namespace", "no mangle", "package", "program",
598                 "struct", "task", "union",
599         };
600         Assert (sizeof (names) / sizeof (names [0]) == DECL_COUNT);
601         Assert ((int) declaration < DECL_COUNT);
602         return names [(int) declaration];
603 }
604
605 static const char *keywordString (const keywordId keyword)
606 {
607         const size_t count = sizeof (KeywordTable) / sizeof (KeywordTable [0]);
608         const char *name = "none";
609         size_t i;
610         for (i = 0  ;  i < count  ;  ++i)
611         {
612                 const keywordDesc *p = &KeywordTable [i];
613                 if (p->id == keyword)
614                 {
615                         name = p->name;
616                         break;
617                 }
618         }
619         return name;
620 }
621
622 static void __ctags_unused__ pt (tokenInfo *const token)
623 {
624         if (isType (token, TOKEN_NAME))
625                 printf ("type: %-12s: %-13s   line: %lu\n",
626                         tokenString (token->type), vStringValue (token->name),
627                         token->lineNumber);
628         else if (isType (token, TOKEN_KEYWORD))
629                 printf ("type: %-12s: %-13s   line: %lu\n",
630                         tokenString (token->type), keywordString (token->keyword),
631                         token->lineNumber);
632         else
633                 printf ("type: %-12s                  line: %lu\n",
634                         tokenString (token->type), token->lineNumber);
635 }
636
637 static void __ctags_unused__ ps (statementInfo *const st)
638 {
639         unsigned int i;
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));
646         printf ("token  : ");
647         pt (activeToken (st));
648         for (i = 1  ;  i < (unsigned int) NumTokens  ;  ++i)
649         {
650                 printf ("prev %u : ", i);
651                 pt (prevToken (st, i));
652         }
653         printf ("context: ");
654         pt (st->context);
655 }
656
657 #endif
658
659 /*
660 *   Statement management
661 */
662
663 static boolean isContextualKeyword (const tokenInfo *const token)
664 {
665         boolean result;
666         switch (token->keyword)
667         {
668                 case KEYWORD_CLASS:
669                 case KEYWORD_ENUM:
670                 case KEYWORD_INTERFACE:
671                 case KEYWORD_NAMESPACE:
672                 case KEYWORD_STRUCT:
673                 case KEYWORD_UNION:
674                         result = TRUE;
675                         break;
676
677                 default: result = FALSE; break;
678         }
679         return result;
680 }
681
682 static boolean isContextualStatement (const statementInfo *const st)
683 {
684         boolean result = FALSE;
685         if (st != NULL) switch (st->declaration)
686         {
687                 case DECL_CLASS:
688                 case DECL_ENUM:
689                 case DECL_INTERFACE:
690                 case DECL_NAMESPACE:
691                 case DECL_STRUCT:
692                 case DECL_UNION:
693                         result = TRUE;
694                         break;
695
696                 default: result = FALSE; break;
697         }
698         return result;
699 }
700
701 static boolean isMember (const statementInfo *const st)
702 {
703         boolean result;
704         if (isType (st->context, TOKEN_NAME))
705                 result = TRUE;
706         else
707                 result = (boolean)
708                         (st->parent != NULL && isContextualStatement (st->parent));
709         return result;
710 }
711
712 static void initMemberInfo (statementInfo *const st)
713 {
714         accessType accessDefault = ACCESS_UNDEFINED;
715
716         if (st->parent != NULL) switch (st->parent->declaration)
717         {
718                 case DECL_ENUM:
719                         accessDefault = (isLanguage (Lang_java) ? ACCESS_PUBLIC : ACCESS_UNDEFINED);
720                         break;
721                 case DECL_NAMESPACE:
722                         accessDefault = ACCESS_UNDEFINED;
723                         break;
724
725                 case DECL_CLASS:
726                         if (isLanguage (Lang_java))
727                                 accessDefault = ACCESS_DEFAULT;
728                         else
729                                 accessDefault = ACCESS_PRIVATE;
730                         break;
731
732                 case DECL_INTERFACE:
733                 case DECL_STRUCT:
734                 case DECL_UNION:
735                         accessDefault = ACCESS_PUBLIC;
736                         break;
737
738                 default: break;
739         }
740         st->member.accessDefault = accessDefault;
741         st->member.access                = accessDefault;
742 }
743
744 static void reinitStatement (statementInfo *const st, const boolean partial)
745 {
746         unsigned int i;
747
748         if (! partial)
749         {
750                 st->scope = SCOPE_GLOBAL;
751                 if (isContextualStatement (st->parent))
752                         st->declaration = DECL_BASE;
753                 else
754                         st->declaration = DECL_NONE;
755         }
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;
762         st->gotArgs                     = FALSE;
763         st->gotName                     = FALSE;
764         st->haveQualifyingName = FALSE;
765         st->tokenIndex          = 0;
766
767         if (st->parent != NULL)
768                 st->inFunction = st->parent->inFunction;
769
770         for (i = 0  ;  i < (unsigned int) NumTokens  ;  ++i)
771                 initToken (st->token [i]);
772
773         initToken (st->context);
774
775         /*      Keep the block name, so that a variable following after a comma will
776          *      still have the structure name.
777          */
778         if (! partial)
779                 initToken (st->blockName);
780
781         vStringClear (st->parentClasses);
782
783         /*  Init member info.
784          */
785         if (! partial)
786                 st->member.access = st->member.accessDefault;
787 }
788
789 static void initStatement (statementInfo *const st, statementInfo *const parent)
790 {
791         st->parent = parent;
792         initMemberInfo (st);
793         reinitStatement (st, FALSE);
794 }
795
796 /*
797 *   Tag generation functions
798 */
799 static cKind cTagKind (const tagType type)
800 {
801         cKind result = CK_UNDEFINED;
802         switch (type)
803         {
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;
817
818                 default: Assert ("Bad C tag type" == NULL); break;
819         }
820         return result;
821 }
822
823 static csharpKind csharpTagKind (const tagType type)
824 {
825         csharpKind result = CSK_UNDEFINED;
826         switch (type)
827         {
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;
840
841                 default: Assert ("Bad C# tag type" == NULL); break;
842         }
843         return result;
844 }
845
846 static javaKind javaTagKind (const tagType type)
847 {
848         javaKind result = JK_UNDEFINED;
849         switch (type)
850         {
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;
859
860                 default: Assert ("Bad Java tag type" == NULL); break;
861         }
862         return result;
863 }
864
865 static veraKind veraTagKind (const tagType type) {
866         veraKind result = VK_UNDEFINED;
867         switch (type)
868         {
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;
881
882                 default: Assert ("Bad Vera tag type" == NULL); break;
883         }
884         return result;
885 }
886
887 static const char *tagName (const tagType type)
888 {
889         const char* result;
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;
896         else
897                 result = CKinds [cTagKind (type)].name;
898         return result;
899 }
900
901 static int tagLetter (const tagType type)
902 {
903         int result;
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;
910         else
911                 result = CKinds [cTagKind (type)].letter;
912         return result;
913 }
914
915 static boolean includeTag (const tagType type, const boolean isFileScope)
916 {
917         boolean result;
918         if (isFileScope  &&  ! Option.include.fileScope)
919                 result = FALSE;
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;
926         else
927                 result = CKinds [cTagKind (type)].enabled;
928         return result;
929 }
930
931 static tagType declToTagType (const declType declaration)
932 {
933         tagType type = TAG_UNDEFINED;
934
935         switch (declaration)
936         {
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;
947
948                 default: Assert ("Unexpected declaration" == NULL); break;
949         }
950         return type;
951 }
952
953 static const char* accessField (const statementInfo *const st)
954 {
955         const char* result = NULL;
956         if (isLanguage (Lang_cpp)  &&  st->scope == SCOPE_FRIEND)
957                 result = "friend";
958         else if (st->member.access != ACCESS_UNDEFINED)
959                 result = accessString (st->member.access);
960         return result;
961 }
962
963 static void addContextSeparator (vString *const scope)
964 {
965         if (isLanguage (Lang_c)  ||  isLanguage (Lang_cpp))
966                 vStringCatS (scope, "::");
967         else if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
968                 vStringCatS (scope, ".");
969 }
970
971 static void addOtherFields (tagEntryInfo* const tag, const tagType type,
972                                                         const statementInfo *const st,
973                                                         vString *const scope, vString *const typeRef)
974 {
975         /*  For selected tag types, append an extension flag designating the
976          *  parent object in which the tag is defined.
977          */
978         switch (type)
979         {
980                 default: break;
981
982                 case TAG_FUNCTION:
983                 case TAG_METHOD:
984                 case TAG_PROTOTYPE:
985                         if (vStringLength (Signature) > 0)
986                                 tag->extensionFields.signature = vStringValue (Signature);
987                 case TAG_CLASS:
988                 case TAG_ENUM:
989                 case TAG_ENUMERATOR:
990                 case TAG_EVENT:
991                 case TAG_FIELD:
992                 case TAG_INTERFACE:
993                 case TAG_MEMBER:
994                 case TAG_NAMESPACE:
995                 case TAG_PROPERTY:
996                 case TAG_STRUCT:
997                 case TAG_TASK:
998                 case TAG_TYPEDEF:
999                 case TAG_UNION:
1000                         if (vStringLength (scope) > 0  &&
1001                                 (isMember (st) || st->parent->declaration == DECL_NAMESPACE))
1002                         {
1003                                 if (isType (st->context, TOKEN_NAME))
1004                                         tag->extensionFields.scope [0] = tagName (TAG_CLASS);
1005                                 else
1006                                         tag->extensionFields.scope [0] =
1007                                                 tagName (declToTagType (parentDecl (st)));
1008                                 tag->extensionFields.scope [1] = vStringValue (scope);
1009                         }
1010                         if ((type == TAG_CLASS  ||  type == TAG_INTERFACE  ||
1011                                  type == TAG_STRUCT) && vStringLength (st->parentClasses) > 0)
1012                         {
1013
1014                                 tag->extensionFields.inheritance =
1015                                                 vStringValue (st->parentClasses);
1016                         }
1017                         if (st->implementation != IMP_DEFAULT &&
1018                                 (isLanguage (Lang_cpp) || isLanguage (Lang_csharp) ||
1019                                  isLanguage (Lang_java)))
1020                         {
1021                                 tag->extensionFields.implementation =
1022                                                 implementationString (st->implementation);
1023                         }
1024                         if (isMember (st))
1025                         {
1026                                 tag->extensionFields.access = accessField (st);
1027                         }
1028                         break;
1029         }
1030
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))
1034         {
1035                 char *p;
1036
1037                 tag->extensionFields.typeRef [0] =
1038                                                 tagName (declToTagType (st->declaration));
1039                 p = vStringValue (st->blockName->name);
1040
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).
1043                  */
1044                 if (p == NULL || *p == '\0')
1045                 {
1046                         tokenInfo *const prev2 = prevToken (st, 2);
1047                         if (isType (prev2, TOKEN_NAME))
1048                                 p = vStringValue (prev2->name);
1049                 }
1050
1051                 /* Prepend the scope name if there is one. */
1052                 if (vStringLength (scope) > 0)
1053                 {
1054                         vStringCopy(typeRef, scope);
1055                         addContextSeparator (typeRef);
1056                         vStringCatS(typeRef, p);
1057                         p = vStringValue (typeRef);
1058                 }
1059                 tag->extensionFields.typeRef [1] = p;
1060         }
1061 }
1062
1063 static void findScopeHierarchy (vString *const string,
1064                                                                 const statementInfo *const st)
1065 {
1066         vStringClear (string);
1067         if (isType (st->context, TOKEN_NAME))
1068                 vStringCopy (string, st->context->name);
1069         if (st->parent != NULL)
1070         {
1071                 vString *temp = vStringNew ();
1072                 const statementInfo *s;
1073                 for (s = st->parent  ;  s != NULL  ;  s = s->parent)
1074                 {
1075                         if (isContextualStatement (s) ||
1076                                 s->declaration == DECL_NAMESPACE ||
1077                                 s->declaration == DECL_PROGRAM)
1078                         {
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)
1084                                 {
1085                                         vStringCat (string, s->context->name);
1086                                         addContextSeparator (string);
1087                                 }
1088                                 vStringCat (string, s->blockName->name);
1089                                 if (vStringLength (temp) > 0)
1090                                         addContextSeparator (string);
1091                                 vStringCat (string, temp);
1092                         }
1093                 }
1094                 vStringDelete (temp);
1095         }
1096 }
1097
1098 static void makeExtraTagEntry (const tagType type, tagEntryInfo *const e,
1099                                                            vString *const scope)
1100 {
1101         if (Option.include.qualifiedTags  &&
1102                 scope != NULL  &&  vStringLength (scope) > 0)
1103         {
1104                 vString *const scopedName = vStringNew ();
1105
1106                 if (type != TAG_ENUMERATOR)
1107                         vStringCopy (scopedName, scope);
1108                 else
1109                 {
1110                         /* remove last component (i.e. enumeration name) from scope */
1111                         const char* const sc = vStringValue (scope);
1112                         const char* colon = strrchr (sc, ':');
1113                         if (colon != NULL)
1114                         {
1115                                 while (*colon == ':'  &&  colon > sc)
1116                                         --colon;
1117                                 vStringNCopy (scopedName, scope, colon + 1 - sc);
1118                         }
1119                 }
1120                 if (vStringLength (scopedName) > 0)
1121                 {
1122                         addContextSeparator (scopedName);
1123                         vStringCatS (scopedName, e->name);
1124                         e->name = vStringValue (scopedName);
1125                         makeTagEntry (e);
1126                 }
1127                 vStringDelete (scopedName);
1128         }
1129 }
1130
1131 static void makeTag (const tokenInfo *const token,
1132                                          const statementInfo *const st,
1133                                          boolean isFileScope, const tagType type)
1134 {
1135         /*  Nothing is really of file scope when it appears in a header file.
1136          */
1137         isFileScope = (boolean) (isFileScope && ! isHeaderFile ());
1138
1139         if (isType (token, TOKEN_NAME)  &&  vStringLength (token->name) > 0  &&
1140                 includeTag (type, isFileScope))
1141         {
1142                 vString *scope = vStringNew ();
1143                 /* Use "typeRef" to store the typename from addOtherFields() until
1144                  * it's used in makeTagEntry().
1145                  */
1146                 vString *typeRef = vStringNew ();
1147                 tagEntryInfo e;
1148
1149                 initTagEntry (&e, vStringValue (token->name));
1150
1151                 e.lineNumber    = token->lineNumber;
1152                 e.filePosition  = token->filePosition;
1153                 e.isFileScope   = isFileScope;
1154                 e.kindName              = tagName (type);
1155                 e.kind                  = tagLetter (type);
1156
1157                 findScopeHierarchy (scope, st);
1158                 addOtherFields (&e, type, st, scope, typeRef);
1159
1160                 makeTagEntry (&e);
1161                 makeExtraTagEntry (type, &e, scope);
1162                 vStringDelete (scope);
1163                 vStringDelete (typeRef);
1164         }
1165 }
1166
1167 static boolean isValidTypeSpecifier (const declType declaration)
1168 {
1169         boolean result;
1170         switch (declaration)
1171         {
1172                 case DECL_BASE:
1173                 case DECL_CLASS:
1174                 case DECL_ENUM:
1175                 case DECL_EVENT:
1176                 case DECL_STRUCT:
1177                 case DECL_UNION:
1178                         result = TRUE;
1179                         break;
1180
1181                 default:
1182                         result = FALSE;
1183                         break;
1184         }
1185         return result;
1186 }
1187
1188 static void qualifyEnumeratorTag (const statementInfo *const st,
1189                                                                   const tokenInfo *const nameToken)
1190 {
1191         if (isType (nameToken, TOKEN_NAME))
1192                 makeTag (nameToken, st, TRUE, TAG_ENUMERATOR);
1193 }
1194
1195 static void qualifyFunctionTag (const statementInfo *const st,
1196                                                                 const tokenInfo *const nameToken)
1197 {
1198         if (isType (nameToken, TOKEN_NAME))
1199         {
1200                 tagType type;
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))
1205                         type = TAG_METHOD;
1206                 else if (isLanguage (Lang_vera)  &&  st->declaration == DECL_TASK)
1207                         type = TAG_TASK;
1208                 else
1209                         type = TAG_FUNCTION;
1210                 makeTag (nameToken, st, isFileScope, type);
1211         }
1212 }
1213
1214 static void qualifyFunctionDeclTag (const statementInfo *const st,
1215                                                                         const tokenInfo *const nameToken)
1216 {
1217         if (! isType (nameToken, TOKEN_NAME))
1218                 ;
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);
1225 }
1226
1227 static void qualifyCompoundTag (const statementInfo *const st,
1228                                                                 const tokenInfo *const nameToken)
1229 {
1230         if (isType (nameToken, TOKEN_NAME))
1231         {
1232                 const tagType type = declToTagType (st->declaration);
1233                 const boolean fileScoped = (boolean)
1234                                 (!(isLanguage (Lang_java) ||
1235                                    isLanguage (Lang_csharp) ||
1236                                    isLanguage (Lang_vera)));
1237
1238                 if (type != TAG_UNDEFINED)
1239                         makeTag (nameToken, st, fileScoped, type);
1240         }
1241 }
1242
1243 static void qualifyBlockTag (statementInfo *const st,
1244                                                          const tokenInfo *const nameToken)
1245 {
1246         switch (st->declaration)
1247         {
1248                 case DECL_CLASS:
1249                 case DECL_ENUM:
1250                 case DECL_INTERFACE:
1251                 case DECL_NAMESPACE:
1252                 case DECL_PROGRAM:
1253                 case DECL_STRUCT:
1254                 case DECL_UNION:
1255                         qualifyCompoundTag (st, nameToken);
1256                         break;
1257                 default: break;
1258         }
1259 }
1260
1261 static void qualifyVariableTag (const statementInfo *const st,
1262                                                                 const tokenInfo *const nameToken)
1263 {
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.
1267          */
1268         if (! isType (nameToken, TOKEN_NAME))
1269                 ;
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),
1274                                 TAG_EVENT);
1275         else if (st->declaration == DECL_PACKAGE)
1276                 makeTag (nameToken, st, FALSE, TAG_PACKAGE);
1277         else if (isValidTypeSpecifier (st->declaration))
1278         {
1279                 if (st->notVariable)
1280                         ;
1281                 else if (isMember (st))
1282                 {
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);
1288                 }
1289                 else
1290                 {
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),
1295                                                 TAG_LOCAL);
1296                         else
1297                                 makeTag (nameToken, st, (boolean) (st->scope == SCOPE_STATIC),
1298                                                 TAG_VARIABLE);
1299                 }
1300         }
1301 }
1302
1303 /*
1304 *   Parsing functions
1305 */
1306
1307 static int skipToOneOf (const char *const chars)
1308 {
1309         int c;
1310         do
1311                 c = cppGetc ();
1312         while (c != EOF  &&  c != '\0'  &&  strchr (chars, c) == NULL);
1313         return c;
1314 }
1315
1316 /*  Skip to the next non-white character.
1317  */
1318 static int skipToNonWhite (void)
1319 {
1320         boolean found = FALSE;
1321         int c;
1322
1323 #if 0
1324         do
1325                 c = cppGetc ();
1326         while (isspace (c));
1327 #else
1328         while (1)
1329         {
1330                 c = cppGetc ();
1331                 if (isspace (c))
1332                         found = TRUE;
1333                 else
1334                         break;
1335         }
1336         if (CollectingSignature && found)
1337                 vStringPut (Signature, ' ');
1338 #endif
1339
1340         return c;
1341 }
1342
1343 /*  Skips to the next brace in column 1. This is intended for cases where
1344  *  preprocessor constructs result in unbalanced braces.
1345  */
1346 static void skipToFormattedBraceMatch (void)
1347 {
1348         int c, next;
1349
1350         c = cppGetc ();
1351         next = cppGetc ();
1352         while (c != EOF  &&  (c != '\n'  ||  next != '}'))
1353         {
1354                 c = next;
1355                 next = cppGetc ();
1356         }
1357 }
1358
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
1364  *  "pair").
1365  */
1366 static void skipToMatch (const char *const pair)
1367 {
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 ();
1373         int matchLevel = 1;
1374         int c = '\0';
1375
1376         while (matchLevel > 0  &&  (c = skipToNonWhite ()) != EOF)
1377         {
1378                 if (CollectingSignature)
1379                         vStringPut (Signature, c);
1380                 if (c == begin)
1381                 {
1382                         ++matchLevel;
1383                         if (braceFormatting  &&  getDirectiveNestLevel () != initialLevel)
1384                         {
1385                                 skipToFormattedBraceMatch ();
1386                                 break;
1387                         }
1388                 }
1389                 else if (c == end)
1390                 {
1391                         --matchLevel;
1392                         if (braceFormatting  &&  getDirectiveNestLevel () != initialLevel)
1393                         {
1394                                 skipToFormattedBraceMatch ();
1395                                 break;
1396                         }
1397                 }
1398         }
1399         if (c == EOF)
1400         {
1401                 verbose ("%s: failed to find match for '%c' at line %lu\n",
1402                                 getInputFileName (), begin, inputLineNumber);
1403                 if (braceMatching)
1404                         longjmp (Exception, (int) ExceptionBraceFormattingError);
1405                 else
1406                         longjmp (Exception, (int) ExceptionFormattingError);
1407         }
1408 }
1409
1410 static void skipParens (void)
1411 {
1412         const int c = skipToNonWhite ();
1413
1414         if (c == '(')
1415                 skipToMatch ("()");
1416         else
1417                 cppUngetc (c);
1418 }
1419
1420 static void skipBraces (void)
1421 {
1422         const int c = skipToNonWhite ();
1423
1424         if (c == '{')
1425                 skipToMatch ("{}");
1426         else
1427                 cppUngetc (c);
1428 }
1429
1430 static keywordId analyzeKeyword (const char *const name)
1431 {
1432         const keywordId id = (keywordId) lookupKeyword (name, getSourceLanguage ());
1433         return id;
1434 }
1435
1436 static void analyzeIdentifier (tokenInfo *const token)
1437 {
1438         char *const name = vStringValue (token->name);
1439         const char *replacement = NULL;
1440         boolean parensToo = FALSE;
1441
1442         if (isLanguage (Lang_java)  ||
1443                 ! isIgnoreToken (name, &parensToo, &replacement))
1444         {
1445                 if (replacement != NULL)
1446                         token->keyword = analyzeKeyword (replacement);
1447                 else
1448                         token->keyword = analyzeKeyword (vStringValue (token->name));
1449
1450                 if (token->keyword == KEYWORD_NONE)
1451                         token->type = TOKEN_NAME;
1452                 else
1453                         token->type = TOKEN_KEYWORD;
1454         }
1455         else
1456         {
1457                 initToken (token);
1458                 if (parensToo)
1459                 {
1460                         int c = skipToNonWhite ();
1461
1462                         if (c == '(')
1463                                 skipToMatch ("()");
1464                 }
1465         }
1466 }
1467
1468 static void readIdentifier (tokenInfo *const token, const int firstChar)
1469 {
1470         vString *const name = token->name;
1471         int c = firstChar;
1472         boolean first = TRUE;
1473
1474         initToken (token);
1475
1476         /* Bug #1585745: strangely, C++ destructors allow whitespace between
1477          * the ~ and the class name. */
1478         if (isLanguage (Lang_cpp) && firstChar == '~')
1479         {
1480                 vStringPut (name, c);
1481                 c = skipToNonWhite ();
1482         }
1483
1484         do
1485         {
1486                 vStringPut (name, c);
1487                 if (CollectingSignature)
1488                 {
1489                         if (!first)
1490                                 vStringPut (Signature, c);
1491                         first = FALSE;
1492                 }
1493                 c = cppGetc ();
1494         } while (isident (c) || ((isLanguage (Lang_java) || isLanguage (Lang_csharp)) && (isHighChar (c) || c == '.')));
1495         vStringTerminate (name);
1496         cppUngetc (c);        /* unget non-identifier character */
1497
1498         analyzeIdentifier (token);
1499 }
1500
1501 static void readPackageName (tokenInfo *const token, const int firstChar)
1502 {
1503         vString *const name = token->name;
1504         int c = firstChar;
1505
1506         initToken (token);
1507
1508         while (isident (c)  ||  c == '.')
1509         {
1510                 vStringPut (name, c);
1511                 c = cppGetc ();
1512         }
1513         vStringTerminate (name);
1514         cppUngetc (c);        /* unget non-package character */
1515 }
1516
1517 static void readPackageOrNamespace (statementInfo *const st, const declType declaration)
1518 {
1519         st->declaration = declaration;
1520         
1521         if (declaration == DECL_NAMESPACE && !isLanguage (Lang_csharp))
1522         {
1523                 /* In C++ a namespace is specified one level at a time. */
1524                 return;
1525         }
1526         else
1527         {
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;
1533                 st->gotName = TRUE;
1534                 st->haveQualifyingName = TRUE;
1535         }
1536 }
1537
1538 static void processName (statementInfo *const st)
1539 {
1540         Assert (isType (activeToken (st), TOKEN_NAME));
1541         if (st->gotName  &&  st->declaration == DECL_NONE)
1542                 st->declaration = DECL_BASE;
1543         st->gotName = TRUE;
1544         st->haveQualifyingName = TRUE;
1545 }
1546
1547 static void readOperator (statementInfo *const st)
1548 {
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 ();
1554
1555         /*  When we arrive here, we have the keyword "operator" in 'name'.
1556          */
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 */
1560         else if (c == '(')
1561         {
1562                 /*  Verify whether this is a valid function call (i.e. "()") operator.
1563                  */
1564                 if (cppGetc () == ')')
1565                 {
1566                         vStringPut (name, ' ');  /* always separate operator from keyword */
1567                         c = skipToNonWhite ();
1568                         if (c == '(')
1569                                 vStringCatS (name, "()");
1570                 }
1571                 else
1572                 {
1573                         skipToMatch ("()");
1574                         c = cppGetc ();
1575                 }
1576         }
1577         else if (isident1 (c))
1578         {
1579                 /*  Handle "new" and "delete" operators, and conversion functions
1580                  *  (per 13.3.1.1.2 [2] of the C++ spec).
1581                  */
1582                 boolean whiteSpace = TRUE;  /* default causes insertion of space */
1583                 do
1584                 {
1585                         if (isspace (c))
1586                                 whiteSpace = TRUE;
1587                         else
1588                         {
1589                                 if (whiteSpace)
1590                                 {
1591                                         vStringPut (name, ' ');
1592                                         whiteSpace = FALSE;
1593                                 }
1594                                 vStringPut (name, c);
1595                         }
1596                         c = cppGetc ();
1597                 } while (! isOneOf (c, "(;")  &&  c != EOF);
1598                 vStringTerminate (name);
1599         }
1600         else if (isOneOf (c, acceptable))
1601         {
1602                 vStringPut (name, ' ');  /* always separate operator from keyword */
1603                 do
1604                 {
1605                         vStringPut (name, c);
1606                         c = cppGetc ();
1607                 } while (isOneOf (c, acceptable));
1608                 vStringTerminate (name);
1609         }
1610
1611         cppUngetc (c);
1612
1613         token->type     = TOKEN_NAME;
1614         token->keyword = KEYWORD_NONE;
1615         processName (st);
1616 }
1617
1618 static void copyToken (tokenInfo *const dest, const tokenInfo *const src)
1619 {
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);
1625 }
1626
1627 static void setAccess (statementInfo *const st, const accessType access)
1628 {
1629         if (isMember (st))
1630         {
1631                 if (isLanguage (Lang_cpp))
1632                 {
1633                         int c = skipToNonWhite ();
1634
1635                         if (c == ':')
1636                                 reinitStatement (st, FALSE);
1637                         else
1638                                 cppUngetc (c);
1639
1640                         st->member.accessDefault = access;
1641                 }
1642                 st->member.access = access;
1643         }
1644 }
1645
1646 static void discardTypeList (tokenInfo *const token)
1647 {
1648         int c = skipToNonWhite ();
1649         while (isident1 (c))
1650         {
1651                 readIdentifier (token, c);
1652                 c = skipToNonWhite ();
1653                 if (c == '.'  ||  c == ',')
1654                         c = skipToNonWhite ();
1655         }
1656         cppUngetc (c);
1657 }
1658
1659 static void addParentClass (statementInfo *const st, tokenInfo *const token)
1660 {
1661         if (vStringLength (token->name) > 0  &&
1662                 vStringLength (st->parentClasses) > 0)
1663         {
1664                 vStringPut (st->parentClasses, ',');
1665         }
1666         vStringCat (st->parentClasses, token->name);
1667 }
1668
1669 static void readParents (statementInfo *const st, const int qualifier)
1670 {
1671         tokenInfo *const token = newToken ();
1672         tokenInfo *const parent = newToken ();
1673         int c;
1674
1675         do
1676         {
1677                 c = skipToNonWhite ();
1678                 if (isident1 (c))
1679                 {
1680                         readIdentifier (token, c);
1681                         if (isType (token, TOKEN_NAME))
1682                                 vStringCat (parent->name, token->name);
1683                         else
1684                         {
1685                                 addParentClass (st, parent);
1686                                 initToken (parent);
1687                         }
1688                 }
1689                 else if (c == qualifier)
1690                         vStringPut (parent->name, c);
1691                 else if (c == '<')
1692                         skipToMatch ("<>");
1693                 else if (isType (token, TOKEN_NAME))
1694                 {
1695                         addParentClass (st, parent);
1696                         initToken (parent);
1697                 }
1698         } while (c != '{'  &&  c != EOF);
1699         cppUngetc (c);
1700         deleteToken (parent);
1701         deleteToken (token);
1702 }
1703
1704 static void skipStatement (statementInfo *const st)
1705 {
1706         st->declaration = DECL_IGNORE;
1707         skipToOneOf (";");
1708 }
1709
1710 static void processInterface (statementInfo *const st)
1711 {
1712         st->declaration = DECL_INTERFACE;
1713 }
1714
1715 static void processToken (tokenInfo *const token, statementInfo *const st)
1716 {
1717         switch (token->keyword)        /* is it a reserved word? */
1718         {
1719                 default: break;
1720
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;
1765                 
1766                 case KEYWORD_NAMESPACE: readPackageOrNamespace (st, DECL_NAMESPACE); break;
1767                 case KEYWORD_PACKAGE:   readPackageOrNamespace (st, DECL_PACKAGE);   break;
1768                 
1769                 case KEYWORD_EVENT:
1770                         if (isLanguage (Lang_csharp))
1771                                 st->declaration = DECL_EVENT;
1772                         break;
1773
1774                 case KEYWORD_TYPEDEF:
1775                         reinitStatement (st, FALSE);
1776                         st->scope = SCOPE_TYPEDEF;
1777                         break;
1778
1779                 case KEYWORD_EXTERN:
1780                         if (! isLanguage (Lang_csharp) || !st->gotName)
1781                         {
1782                                 reinitStatement (st, FALSE);
1783                                 st->scope = SCOPE_EXTERN;
1784                                 st->declaration = DECL_BASE;
1785                         }
1786                         break;
1787
1788                 case KEYWORD_STATIC:
1789                         if (! (isLanguage (Lang_java) || isLanguage (Lang_csharp)))
1790                         {
1791                                 reinitStatement (st, FALSE);
1792                                 st->scope = SCOPE_STATIC;
1793                                 st->declaration = DECL_BASE;
1794                         }
1795                         break;
1796
1797                 case KEYWORD_FOR:
1798                 case KEYWORD_FOREACH:
1799                 case KEYWORD_IF:
1800                 case KEYWORD_SWITCH:
1801                 case KEYWORD_WHILE:
1802                 {
1803                         int c = skipToNonWhite ();
1804                         if (c == '(')
1805                                 skipToMatch ("()");
1806                         break;
1807                 }
1808         }
1809 }
1810
1811 /*
1812 *   Parenthesis handling functions
1813 */
1814
1815 static void restartStatement (statementInfo *const st)
1816 {
1817         tokenInfo *const save = newToken ();
1818         tokenInfo *token = activeToken (st);
1819
1820         copyToken (save, token);
1821         DebugStatement ( if (debug (DEBUG_PARSE)) printf ("<ES>");)
1822         reinitStatement (st, FALSE);
1823         token = activeToken (st);
1824         copyToken (token, save);
1825         deleteToken (save);
1826         processToken (token, st);
1827 }
1828
1829 /*  Skips over a the mem-initializer-list of a ctor-initializer, defined as:
1830  *
1831  *  mem-initializer-list:
1832  *    mem-initializer, mem-initializer-list
1833  *
1834  *  mem-initializer:
1835  *    [::] [nested-name-spec] class-name (...)
1836  *    identifier
1837  */
1838 static void skipMemIntializerList (tokenInfo *const token)
1839 {
1840         int c;
1841
1842         do
1843         {
1844                 c = skipToNonWhite ();
1845                 while (isident1 (c)  ||  c == ':')
1846                 {
1847                         if (c != ':')
1848                                 readIdentifier (token, c);
1849                         c = skipToNonWhite ();
1850                 }
1851                 if (c == '<')
1852                 {
1853                         skipToMatch ("<>");
1854                         c = skipToNonWhite ();
1855                 }
1856                 if (c == '(')
1857                 {
1858                         skipToMatch ("()");
1859                         c = skipToNonWhite ();
1860                 }
1861         } while (c == ',');
1862         cppUngetc (c);
1863 }
1864
1865 static void skipMacro (statementInfo *const st)
1866 {
1867         tokenInfo *const prev2 = prevToken (st, 2);
1868
1869         if (isType (prev2, TOKEN_NAME))
1870                 retardToken (st);
1871         skipToMatch ("()");
1872 }
1873
1874 /*  Skips over characters following the parameter list. This will be either
1875  *  non-ANSI style function declarations or C++ stuff. Our choices:
1876  *
1877  *  C (K&R):
1878  *    int func ();
1879  *    int func (one, two) int one; float two; {...}
1880  *  C (ANSI):
1881  *    int func (int one, float two);
1882  *    int func (int one, float two) {...}
1883  *  C++:
1884  *    int foo (...) [const|volatile] [throw (...)];
1885  *    int foo (...) [const|volatile] [throw (...)] [ctor-initializer] {...}
1886  *    int foo (...) [const|volatile] [throw (...)] try [ctor-initializer] {...}
1887  *        catch (...) {...}
1888  */
1889 static boolean skipPostArgumentStuff (
1890                 statementInfo *const st, parenInfo *const info)
1891 {
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 ();
1898
1899         do
1900         {
1901                 switch (c)
1902                 {
1903                 case ')':                               break;
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;
1909
1910                 case '(':
1911                         if (elementCount > 0)
1912                                 ++elementCount;
1913                         skipToMatch ("()");
1914                         break;
1915
1916                 case ';':
1917                         if (parameters == 0  ||  elementCount < 2)
1918                         {
1919                                 cppUngetc (c);
1920                                 end = TRUE;
1921                         }
1922                         else if (--parameters == 0)
1923                                 end = TRUE;
1924                         break;
1925
1926                 default:
1927                         if (isident1 (c))
1928                         {
1929                                 readIdentifier (token, c);
1930                                 switch (token->keyword)
1931                                 {
1932                                 case KEYWORD_ATTRIBUTE: skipParens ();  break;
1933                                 case KEYWORD_THROW:     skipParens ();  break;
1934                                 case KEYWORD_TRY:                       break;
1935
1936                                 case KEYWORD_CONST:
1937                                 case KEYWORD_VOLATILE:
1938                                         if (vStringLength (Signature) > 0)
1939                                         {
1940                                                 vStringPut (Signature, ' ');
1941                                                 vStringCat (Signature, token->name);
1942                                         }
1943                                         break;
1944
1945                                 case KEYWORD_CATCH:
1946                                 case KEYWORD_CLASS:
1947                                 case KEYWORD_EXPLICIT:
1948                                 case KEYWORD_EXTERN:
1949                                 case KEYWORD_FRIEND:
1950                                 case KEYWORD_INLINE:
1951                                 case KEYWORD_MUTABLE:
1952                                 case KEYWORD_NAMESPACE:
1953                                 case KEYWORD_NEW:
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:
1964                                 case KEYWORD_USING:
1965                                 case KEYWORD_VIRTUAL:
1966                                         /* Never allowed within parameter declarations. */
1967                                         restart = TRUE;
1968                                         end = TRUE;
1969                                         break;
1970
1971                                 default:
1972                                         if (isType (token, TOKEN_NONE))
1973                                                 ;
1974                                         else if (info->isKnrParamList  &&  info->parameterCount > 0)
1975                                                 ++elementCount;
1976                                         else
1977                                         {
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.
1983                                                  */
1984                                                 restart = TRUE;
1985                                                 end = TRUE;
1986                                         }
1987                                         break;
1988                                 }
1989                         }
1990                 }
1991                 if (! end)
1992                 {
1993                         c = skipToNonWhite ();
1994                         if (c == EOF)
1995                                 end = TRUE;
1996                 }
1997         } while (! end);
1998
1999         if (restart)
2000                 restartStatement (st);
2001         else
2002                 setToken (st, TOKEN_NONE);
2003
2004         return (boolean) (c != EOF);
2005 }
2006
2007 static void skipJavaThrows (statementInfo *const st)
2008 {
2009         tokenInfo *const token = activeToken (st);
2010         int c = skipToNonWhite ();
2011
2012         if (isident1 (c))
2013         {
2014                 readIdentifier (token, c);
2015                 if (token->keyword == KEYWORD_THROWS)
2016                 {
2017                         do
2018                         {
2019                                 c = skipToNonWhite ();
2020                                 if (isident1 (c))
2021                                 {
2022                                         readIdentifier (token, c);
2023                                         c = skipToNonWhite ();
2024                                 }
2025                         } while (c == '.'  ||  c == ',');
2026                 }
2027         }
2028         cppUngetc (c);
2029         setToken (st, TOKEN_NONE);
2030 }
2031
2032 static void analyzePostParens (statementInfo *const st, parenInfo *const info)
2033 {
2034         const unsigned long inputLineNumber = getInputLineNumber ();
2035         int c = skipToNonWhite ();
2036
2037         cppUngetc (c);
2038         if (isOneOf (c, "{;,="))
2039                 ;
2040         else if (isLanguage (Lang_java))
2041                 skipJavaThrows (st);
2042         else
2043         {
2044                 if (! skipPostArgumentStuff (st, info))
2045                 {
2046                         verbose (
2047                                 "%s: confusing argument declarations beginning at line %lu\n",
2048                                 getInputFileName (), inputLineNumber);
2049                         longjmp (Exception, (int) ExceptionFormattingError);
2050                 }
2051         }
2052 }
2053
2054 static boolean languageSupportsGenerics (void)
2055 {
2056         return (boolean) (isLanguage (Lang_cpp) || isLanguage (Lang_csharp) ||
2057                 isLanguage (Lang_java));
2058 }
2059
2060 static void processAngleBracket (void)
2061 {
2062         int c = cppGetc ();
2063         if (c == '>') {
2064                 /* already found match for template */
2065         } else if (languageSupportsGenerics () && c != '<' && c != '=') {
2066                 /* this is a template */
2067                 cppUngetc (c);
2068                 skipToMatch ("<>");
2069         } else if (c == '<') {
2070                 /* skip "<<" or "<<=". */
2071                 c = cppGetc ();
2072                 if (c != '=') {
2073                         cppUngetc (c);
2074                 }
2075         } else {
2076                 cppUngetc (c);
2077         }
2078 }
2079
2080 static void parseJavaAnnotation (statementInfo *const st)
2081 {
2082         /*
2083          * @Override
2084          * @Target(ElementType.METHOD)
2085          * @SuppressWarnings(value = "unchecked")
2086          *
2087          * But watch out for "@interface"!
2088          */
2089         tokenInfo *const token = activeToken (st);
2090         
2091         int c = skipToNonWhite ();
2092         readIdentifier (token, c);
2093         if (token->keyword == KEYWORD_INTERFACE)
2094         {
2095                 /* Oops. This was actually "@interface" defining a new annotation. */
2096                 processInterface (st);
2097         }
2098         else
2099         {
2100                 /* Bug #1691412: skip any annotation arguments. */
2101                 skipParens ();
2102         }
2103 }
2104
2105 static int parseParens (statementInfo *const st, parenInfo *const info)
2106 {
2107         tokenInfo *const token = activeToken (st);
2108         unsigned int identifierCount = 0;
2109         unsigned int depth = 1;
2110         boolean firstChar = TRUE;
2111         int nextChar = '\0';
2112
2113         CollectingSignature = TRUE;
2114         vStringClear (Signature);
2115         vStringPut (Signature, '(');
2116         info->parameterCount = 1;
2117         do
2118         {
2119                 int c = skipToNonWhite ();
2120                 vStringPut (Signature, c);
2121
2122                 switch (c)
2123                 {
2124                         case '&':
2125                         case '*':
2126                                 info->isPointer = TRUE;
2127                                 info->isKnrParamList = FALSE;
2128                                 if (identifierCount == 0)
2129                                         info->isParamList = FALSE;
2130                                 initToken (token);
2131                                 break;
2132
2133                         case ':':
2134                                 info->isKnrParamList = FALSE;
2135                                 break;
2136
2137                         case '.':
2138                                 info->isNameCandidate = FALSE;
2139                                 c = cppGetc ();
2140                                 if (c != '.')
2141                                 {
2142                                         cppUngetc (c);
2143                                         info->isKnrParamList = FALSE;
2144                                 }
2145                                 else
2146                                 {
2147                                         c = cppGetc ();
2148                                         if (c != '.')
2149                                         {
2150                                                 cppUngetc (c);
2151                                                 info->isKnrParamList = FALSE;
2152                                         }
2153                                         else
2154                                                 vStringCatS (Signature, "..."); /* variable arg list */
2155                                 }
2156                                 break;
2157
2158                         case ',':
2159                                 info->isNameCandidate = FALSE;
2160                                 if (info->isKnrParamList)
2161                                 {
2162                                         ++info->parameterCount;
2163                                         identifierCount = 0;
2164                                 }
2165                                 break;
2166
2167                         case '=':
2168                                 info->isKnrParamList = FALSE;
2169                                 info->isNameCandidate = FALSE;
2170                                 if (firstChar)
2171                                 {
2172                                         info->isParamList = FALSE;
2173                                         skipMacro (st);
2174                                         depth = 0;
2175                                 }
2176                                 break;
2177
2178                         case '[':
2179                                 info->isKnrParamList = FALSE;
2180                                 skipToMatch ("[]");
2181                                 break;
2182
2183                         case '<':
2184                                 info->isKnrParamList = FALSE;
2185                                 processAngleBracket ();
2186                                 break;
2187
2188                         case ')':
2189                                 if (firstChar)
2190                                         info->parameterCount = 0;
2191                                 --depth;
2192                                 break;
2193
2194                         case '(':
2195                                 info->isKnrParamList = FALSE;
2196                                 if (firstChar)
2197                                 {
2198                                         info->isNameCandidate = FALSE;
2199                                         cppUngetc (c);
2200                                         vStringClear (Signature);
2201                                         skipMacro (st);
2202                                         depth = 0;
2203                                         vStringChop (Signature);
2204                                 }
2205                                 else if (isType (token, TOKEN_PAREN_NAME))
2206                                 {
2207                                         c = skipToNonWhite ();
2208                                         if (c == '*')        /* check for function pointer */
2209                                         {
2210                                                 skipToMatch ("()");
2211                                                 c = skipToNonWhite ();
2212                                                 if (c == '(')
2213                                                         skipToMatch ("()");
2214                                                 else
2215                                                         cppUngetc (c);
2216                                         }
2217                                         else
2218                                         {
2219                                                 cppUngetc (c);
2220                                                 cppUngetc ('(');
2221                                                 info->nestedArgs = TRUE;
2222                                         }
2223                                 }
2224                                 else
2225                                         ++depth;
2226                                 break;
2227
2228                         default:
2229                                 if (c == '@' && isLanguage (Lang_java))
2230                                 {
2231                                         parseJavaAnnotation(st);
2232                                 }
2233                                 else if (isident1 (c))
2234                                 {
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))
2241                                         {
2242                                                 if (token->keyword != KEYWORD_CONST &&
2243                                                         token->keyword != KEYWORD_VOLATILE)
2244                                                 {
2245                                                         info->isKnrParamList = FALSE;
2246                                                         info->isNameCandidate = FALSE;
2247                                                 }
2248                                         }
2249                                 }
2250                                 else
2251                                 {
2252                                         info->isParamList     = FALSE;
2253                                         info->isKnrParamList  = FALSE;
2254                                         info->isNameCandidate = FALSE;
2255                                         info->invalidContents = TRUE;
2256                                 }
2257                                 break;
2258                 }
2259                 firstChar = FALSE;
2260         } while (! info->nestedArgs  &&  depth > 0  &&
2261                          (info->isKnrParamList  ||  info->isNameCandidate));
2262
2263         if (! info->nestedArgs) while (depth > 0)
2264         {
2265                 skipToMatch ("()");
2266                 --depth;
2267         }
2268
2269         if (! info->isNameCandidate)
2270                 initToken (token);
2271
2272         vStringTerminate (Signature);
2273         if (info->isKnrParamList)
2274                 vStringClear (Signature);
2275         CollectingSignature = FALSE;
2276         return nextChar;
2277 }
2278
2279 static void initParenInfo (parenInfo *const info)
2280 {
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;
2288 }
2289
2290 static void analyzeParens (statementInfo *const st)
2291 {
2292         tokenInfo *const prev = prevToken (st, 1);
2293
2294         if (st->inFunction  &&  ! st->assignment)
2295                 st->notVariable = TRUE;
2296         if (! isType (prev, TOKEN_NONE))  /* in case of ignored enclosing macros */
2297         {
2298                 tokenInfo *const token = activeToken (st);
2299                 parenInfo info;
2300                 int c;
2301
2302                 initParenInfo (&info);
2303                 parseParens (st, &info);
2304                 c = skipToNonWhite ();
2305                 cppUngetc (c);
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  ||
2311                                   c == '('  ||
2312                                   (c == '='  &&  st->implementation != IMP_VIRTUAL) ||
2313                                   (st->declaration == DECL_NONE  &&  isOneOf (c, ",;"))))
2314                 {
2315                         token->type = TOKEN_NAME;
2316                         processName (st);
2317                         st->gotParenName = TRUE;
2318                         if (! (c == '('  &&  info.nestedArgs))
2319                                 st->isPointer = info.isPointer;
2320                 }
2321                 else if (! st->gotArgs  &&  info.isParamList)
2322                 {
2323                         st->gotArgs = TRUE;
2324                         setToken (st, TOKEN_ARGS);
2325                         advanceToken (st);
2326                         if (st->scope != SCOPE_TYPEDEF)
2327                                 analyzePostParens (st, &info);
2328                 }
2329                 else
2330                         setToken (st, TOKEN_NONE);
2331         }
2332 }
2333
2334 /*
2335 *   Token parsing functions
2336 */
2337
2338 static void addContext (statementInfo *const st, const tokenInfo* const token)
2339 {
2340         if (isType (token, TOKEN_NAME))
2341         {
2342                 if (vStringLength (st->context->name) > 0)
2343                 {
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, ".");
2348                 }
2349                 vStringCat (st->context->name, token->name);
2350                 st->context->type = TOKEN_NAME;
2351         }
2352 }
2353
2354 static boolean inheritingDeclaration (declType decl)
2355 {
2356         /* C# supports inheritance for enums. C++0x will too, but not yet. */
2357         if (decl == DECL_ENUM)
2358         {
2359                 return (boolean) (isLanguage (Lang_csharp));
2360         }
2361         return (boolean) (
2362                 decl == DECL_CLASS ||
2363                 decl == DECL_STRUCT ||
2364                 decl == DECL_INTERFACE);
2365 }
2366
2367 static void processColon (statementInfo *const st)
2368 {
2369         int c = (isLanguage (Lang_cpp) ? cppGetc () : skipToNonWhite ());
2370         const boolean doubleColon = (boolean) (c == ':');
2371
2372         if (doubleColon)
2373         {
2374                 setToken (st, TOKEN_DOUBLE_COLON);
2375                 st->haveQualifyingName = FALSE;
2376         }
2377         else
2378         {
2379                 cppUngetc (c);
2380                 if ((isLanguage (Lang_cpp) || isLanguage (Lang_csharp))  &&
2381                         inheritingDeclaration (st->declaration))
2382                 {
2383                         readParents (st, ':');
2384                 }
2385                 else if (parentDecl (st) == DECL_STRUCT)
2386                 {
2387                         c = skipToOneOf (",;");
2388                         if (c == ',')
2389                                 setToken (st, TOKEN_COMMA);
2390                         else if (c == ';')
2391                                 setToken (st, TOKEN_SEMICOLON);
2392                 }
2393                 else
2394                 {
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 ||
2399                                 st->parent != NULL)
2400                         {
2401                                 reinitStatement (st, FALSE);
2402                         }
2403                 }
2404         }
2405 }
2406
2407 /*  Skips over any initializing value which may follow an '=' character in a
2408  *  variable definition.
2409  */
2410 static int skipInitializer (statementInfo *const st)
2411 {
2412         boolean done = FALSE;
2413         int c;
2414
2415         while (! done)
2416         {
2417                 c = skipToNonWhite ();
2418
2419                 if (c == EOF)
2420                         longjmp (Exception, (int) ExceptionFormattingError);
2421                 else switch (c)
2422                 {
2423                         case ',':
2424                         case ';': done = TRUE; break;
2425
2426                         case '0':
2427                                 if (st->implementation == IMP_VIRTUAL)
2428                                         st->implementation = IMP_PURE_VIRTUAL;
2429                                 break;
2430
2431                         case '[': skipToMatch ("[]"); break;
2432                         case '(': skipToMatch ("()"); break;
2433                         case '{': skipToMatch ("{}"); break;
2434                         case '<': processAngleBracket(); break;
2435
2436                         case '}':
2437                                 if (insideEnumBody (st))
2438                                         done = TRUE;
2439                                 else if (! isBraceFormat ())
2440                                 {
2441                                         verbose ("%s: unexpected closing brace at line %lu\n",
2442                                                         getInputFileName (), getInputLineNumber ());
2443                                         longjmp (Exception, (int) ExceptionBraceFormattingError);
2444                                 }
2445                                 break;
2446
2447                         default: break;
2448                 }
2449         }
2450         return c;
2451 }
2452
2453 static void processInitializer (statementInfo *const st)
2454 {
2455         const boolean inEnumBody = insideEnumBody (st);
2456         int c = cppGetc ();
2457
2458         if (c != '=')
2459         {
2460                 cppUngetc (c);
2461                 c = skipInitializer (st);
2462                 st->assignment = TRUE;
2463                 if (c == ';')
2464                         setToken (st, TOKEN_SEMICOLON);
2465                 else if (c == ',')
2466                         setToken (st, TOKEN_COMMA);
2467                 else if (c == '}'  &&  inEnumBody)
2468                 {
2469                         cppUngetc (c);
2470                         setToken (st, TOKEN_COMMA);
2471                 }
2472                 if (st->scope == SCOPE_EXTERN)
2473                         st->scope = SCOPE_GLOBAL;
2474         }
2475 }
2476
2477 static void parseIdentifier (statementInfo *const st, const int c)
2478 {
2479         tokenInfo *const token = activeToken (st);
2480
2481         readIdentifier (token, c);
2482         if (! isType (token, TOKEN_NONE))
2483                 processToken (token, st);
2484 }
2485
2486 static void parseGeneralToken (statementInfo *const st, const int c)
2487 {
2488         const tokenInfo *const prev = prevToken (st, 1);
2489         
2490         if (isident1 (c) || (isLanguage (Lang_java) && isHighChar (c)))
2491         {
2492                 parseIdentifier (st, c);
2493                 if (isType (st->context, TOKEN_NAME) &&
2494                         isType (activeToken (st), TOKEN_NAME) && isType (prev, TOKEN_NAME))
2495                 {
2496                         initToken (st->context);
2497                 }
2498         }
2499         else if (c == '.' || c == '-')
2500         {
2501                 if (! st->assignment)
2502                         st->notVariable = TRUE;
2503                 if (c == '-')
2504                 {
2505                         int c2 = cppGetc ();
2506                         if (c2 != '>')
2507                                 cppUngetc (c2);
2508                 }
2509         }
2510         else if (c == '!' || c == '>')
2511         {
2512                 int c2 = cppGetc ();
2513                 if (c2 != '=')
2514                         cppUngetc (c2);
2515         }
2516         else if (c == '@' && isLanguage (Lang_java))
2517         {
2518                 parseJavaAnnotation (st);
2519         }
2520         else if (isExternCDecl (st, c))
2521         {
2522                 st->declaration = DECL_NOMANGLE;
2523                 st->scope = SCOPE_GLOBAL;
2524         }
2525 }
2526
2527 /*  Reads characters from the pre-processor and assembles tokens, setting
2528  *  the current statement state.
2529  */
2530 static void nextToken (statementInfo *const st)
2531 {
2532         tokenInfo *token;
2533         do
2534         {
2535                 int c = skipToNonWhite ();
2536                 switch (c)
2537                 {
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;
2550                 }
2551                 token = activeToken (st);
2552         } while (isType (token, TOKEN_NONE));
2553 }
2554
2555 /*
2556 *   Scanning support functions
2557 */
2558
2559 static statementInfo *CurrentStatement = NULL;
2560
2561 static statementInfo *newStatement (statementInfo *const parent)
2562 {
2563         statementInfo *const st = xMalloc (1, statementInfo);
2564         unsigned int i;
2565
2566         for (i = 0  ;  i < (unsigned int) NumTokens  ;  ++i)
2567                 st->token [i] = newToken ();
2568
2569         st->context = newToken ();
2570         st->blockName = newToken ();
2571         st->parentClasses = vStringNew ();
2572
2573         initStatement (st, parent);
2574         CurrentStatement = st;
2575
2576         return st;
2577 }
2578
2579 static void deleteStatement (void)
2580 {
2581         statementInfo *const st = CurrentStatement;
2582         statementInfo *const parent = st->parent;
2583         unsigned int i;
2584
2585         for (i = 0  ;  i < (unsigned int) NumTokens  ;  ++i)
2586         {
2587                 deleteToken (st->token [i]);       st->token [i] = NULL;
2588         }
2589         deleteToken (st->blockName);           st->blockName = NULL;
2590         deleteToken (st->context);             st->context = NULL;
2591         vStringDelete (st->parentClasses);     st->parentClasses = NULL;
2592         eFree (st);
2593         CurrentStatement = parent;
2594 }
2595
2596 static void deleteAllStatements (void)
2597 {
2598         while (CurrentStatement != NULL)
2599                 deleteStatement ();
2600 }
2601
2602 static boolean isStatementEnd (const statementInfo *const st)
2603 {
2604         const tokenInfo *const token = activeToken (st);
2605         boolean isEnd;
2606
2607         if (isType (token, TOKEN_SEMICOLON))
2608                 isEnd = TRUE;
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.
2612                  */
2613                 isEnd = (boolean) (isLanguage (Lang_java) || isLanguage (Lang_csharp) ||
2614                                 ! isContextualStatement (st));
2615         else
2616                 isEnd = FALSE;
2617
2618         return isEnd;
2619 }
2620
2621 static void checkStatementEnd (statementInfo *const st)
2622 {
2623         const tokenInfo *const token = activeToken (st);
2624
2625         if (isType (token, TOKEN_COMMA))
2626                 reinitStatement (st, TRUE);
2627         else if (isStatementEnd (st))
2628         {
2629                 DebugStatement ( if (debug (DEBUG_PARSE)) printf ("<ES>"); )
2630                 reinitStatement (st, FALSE);
2631                 cppEndStatement ();
2632         }
2633         else
2634         {
2635                 cppBeginStatement ();
2636                 advanceToken (st);
2637         }
2638 }
2639
2640 static void nest (statementInfo *const st, const unsigned int nestLevel)
2641 {
2642         switch (st->declaration)
2643         {
2644                 case DECL_CLASS:
2645                 case DECL_ENUM:
2646                 case DECL_INTERFACE:
2647                 case DECL_NAMESPACE:
2648                 case DECL_NOMANGLE:
2649                 case DECL_STRUCT:
2650                 case DECL_UNION:
2651                         createTags (nestLevel, st);
2652                         break;
2653
2654                 case DECL_FUNCTION:
2655                 case DECL_TASK:
2656                         st->inFunction = TRUE;
2657                         /* fall through */
2658                 default:
2659                         if (includeTag (TAG_LOCAL, FALSE))
2660                                 createTags (nestLevel, st);
2661                         else
2662                                 skipToMatch ("{}");
2663                         break;
2664         }
2665         advanceToken (st);
2666         setToken (st, TOKEN_BRACE_CLOSE);
2667 }
2668
2669 static void tagCheck (statementInfo *const st)
2670 {
2671         const tokenInfo *const token = activeToken (st);
2672         const tokenInfo *const prev  = prevToken (st, 1);
2673         const tokenInfo *const prev2 = prevToken (st, 2);
2674
2675         switch (token->type)
2676         {
2677                 case TOKEN_NAME:
2678                         if (insideEnumBody (st))
2679                                 qualifyEnumeratorTag (st, token);
2680                         break;
2681 #if 0
2682                 case TOKEN_PACKAGE:
2683                         if (st->haveQualifyingName)
2684                                 makeTag (token, st, FALSE, TAG_PACKAGE);
2685                         break;
2686 #endif
2687                 case TOKEN_BRACE_OPEN:
2688                         if (isType (prev, TOKEN_ARGS))
2689                         {
2690                                 if (st->haveQualifyingName)
2691                                 {
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);
2697                                 }
2698                         }
2699                         else if (isContextualStatement (st) ||
2700                                         st->declaration == DECL_NAMESPACE ||
2701                                         st->declaration == DECL_PROGRAM)
2702                         {
2703                                 if (isType (prev, TOKEN_NAME))
2704                                         copyToken (st->blockName, prev);
2705                                 else
2706                                 {
2707                                         /*  For an anonymous struct or union we use a unique ID
2708                                          *  a number, so that the members can be found.
2709                                          */
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;
2715                                 }
2716                                 qualifyBlockTag (st, prev);
2717                         }
2718                         else if (isLanguage (Lang_csharp))
2719                                 makeTag (prev, st, FALSE, TAG_PROPERTY);
2720                         break;
2721
2722                 case TOKEN_SEMICOLON:
2723                 case TOKEN_COMMA:
2724                         if (insideEnumBody (st))
2725                                 ;
2726                         else if (isType (prev, TOKEN_NAME))
2727                         {
2728                                 if (isContextualKeyword (prev2))
2729                                         makeTag (prev, st, TRUE, TAG_EXTERN_VAR);
2730                                 else
2731                                         qualifyVariableTag (st, prev);
2732                         }
2733                         else if (isType (prev, TOKEN_ARGS)  &&  isType (prev2, TOKEN_NAME))
2734                         {
2735                                 if (st->isPointer)
2736                                         qualifyVariableTag (st, prev2);
2737                                 else
2738                                         qualifyFunctionDeclTag (st, prev2);
2739                         }
2740                         if (isLanguage (Lang_java) && token->type == TOKEN_SEMICOLON && insideEnumBody (st))
2741                         {
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;
2746                         }
2747                         break;
2748
2749                 default: break;
2750         }
2751 }
2752
2753 /*  Parses the current file and decides whether to write out and tags that
2754  *  are discovered.
2755  */
2756 static void createTags (const unsigned int nestLevel,
2757                                                 statementInfo *const parent)
2758 {
2759         statementInfo *const st = newStatement (parent);
2760
2761         DebugStatement ( if (nestLevel > 0) debugParseNest (TRUE, nestLevel); )
2762         while (TRUE)
2763         {
2764                 tokenInfo *token;
2765
2766                 nextToken (st);
2767                 token = activeToken (st);
2768                 if (isType (token, TOKEN_BRACE_CLOSE))
2769                 {
2770                         if (nestLevel > 0)
2771                                 break;
2772                         else
2773                         {
2774                                 verbose ("%s: unexpected closing brace at line %lu\n",
2775                                                 getInputFileName (), getInputLineNumber ());
2776                                 longjmp (Exception, (int) ExceptionBraceFormattingError);
2777                         }
2778                 }
2779                 else if (isType (token, TOKEN_DOUBLE_COLON))
2780                 {
2781                         addContext (st, prevToken (st, 1));
2782                         advanceToken (st);
2783                 }
2784                 else
2785                 {
2786                         tagCheck (st);
2787                         if (isType (token, TOKEN_BRACE_OPEN))
2788                                 nest (st, nestLevel + 1);
2789                         checkStatementEnd (st);
2790                 }
2791         }
2792         deleteStatement ();
2793         DebugStatement ( if (nestLevel > 0) debugParseNest (FALSE, nestLevel - 1); )
2794 }
2795
2796 static boolean findCTags (const unsigned int passCount)
2797 {
2798         exception_t exception;
2799         boolean retry;
2800
2801         Assert (passCount < 3);
2802         cppInit ((boolean) (passCount > 1), isLanguage (Lang_csharp));
2803         Signature = vStringNew ();
2804
2805         exception = (exception_t) setjmp (Exception);
2806         retry = FALSE;
2807         if (exception == ExceptionNone)
2808                 createTags (0, NULL);
2809         else
2810         {
2811                 deleteAllStatements ();
2812                 if (exception == ExceptionBraceFormattingError  &&  passCount == 1)
2813                 {
2814                         retry = TRUE;
2815                    verbose ("%s: retrying file with fallback brace matching algorithm\n",
2816                                         getInputFileName ());
2817                 }
2818         }
2819         vStringDelete (Signature);
2820         cppTerminate ();
2821         return retry;
2822 }
2823
2824 static void buildKeywordHash (const langType language, unsigned int idx)
2825 {
2826         const size_t count = sizeof (KeywordTable) / sizeof (KeywordTable [0]);
2827         size_t i;
2828         for (i = 0  ;  i < count  ;  ++i)
2829         {
2830                 const keywordDesc* const p = &KeywordTable [i];
2831                 if (p->isValid [idx])
2832                         addKeyword (p->name, language, (int) p->id);
2833         }
2834 }
2835
2836 static void initializeCParser (const langType language)
2837 {
2838         Lang_c = language;
2839         buildKeywordHash (language, 0);
2840 }
2841
2842 static void initializeCppParser (const langType language)
2843 {
2844         Lang_cpp = language;
2845         buildKeywordHash (language, 1);
2846 }
2847
2848 static void initializeCsharpParser (const langType language)
2849 {
2850         Lang_csharp = language;
2851         buildKeywordHash (language, 2);
2852 }
2853
2854 static void initializeJavaParser (const langType language)
2855 {
2856         Lang_java = language;
2857         buildKeywordHash (language, 3);
2858 }
2859
2860 static void initializeVeraParser (const langType language)
2861 {
2862         Lang_vera = language;
2863         buildKeywordHash (language, 4);
2864 }
2865
2866 extern parserDefinition* CParser (void)
2867 {
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;
2875         return def;
2876 }
2877
2878 extern parserDefinition* CppParser (void)
2879 {
2880         static const char *const extensions [] = {
2881                 "c++", "cc", "cp", "cpp", "cxx", "h", "h++", "hh", "hp", "hpp", "hxx",
2882 #ifndef CASE_INSENSITIVE_FILENAMES
2883                 "C", "H",
2884 #endif
2885                 NULL
2886         };
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;
2893         return def;
2894 }
2895
2896 extern parserDefinition* CsharpParser (void)
2897 {
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;
2905         return def;
2906 }
2907
2908 extern parserDefinition* JavaParser (void)
2909 {
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;
2917         return def;
2918 }
2919
2920 extern parserDefinition* VeraParser (void)
2921 {
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;
2929         return def;
2930 }
2931
2932 /* vi:set tabstop=4 shiftwidth=4 noexpandtab: */