Page Menu
Home
Software Heritage
Search
Configure Global Search
Log In
Files
F9340515
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
81 KB
Subscribers
None
View Options
diff --git a/Test/bug1585745.cpp b/Test/bug1585745.cpp
new file mode 100644
index 00000000..410aab16
--- /dev/null
+++ b/Test/bug1585745.cpp
@@ -0,0 +1,8 @@
+// Despite the weird whitespace, all these destructors should be recognized and tagged.
+Class1::~Class1() { }
+Class2::~ Class2() { }
+Class3:: ~Class3() { }
+Class4:: ~ Class4() { }
+class Class5 {
+ public: ~ Class5() { }
+};
diff --git a/c.c b/c.c
index dfd2a0a4..baad4ddf 100644
--- a/c.c
+++ b/c.c
@@ -1,2900 +1,2908 @@
/*
* $Id$
*
* Copyright (c) 1996-2003, Darren Hiebert
*
* This source code is released for free distribution under the terms of the
* GNU General Public License.
*
* This module contains functions for parsing and scanning C, C++ and Java
* source files.
*/
/*
* INCLUDE FILES
*/
#include "general.h" /* must always come first */
#include <string.h>
#include <setjmp.h>
#include "debug.h"
#include "entry.h"
#include "get.h"
#include "keyword.h"
#include "options.h"
#include "parse.h"
#include "read.h"
#include "routines.h"
/*
* MACROS
*/
#define MaxFields 6
#define stringValue(a) #a
#define activeToken(st) ((st)->token [(int) (st)->tokenIndex])
#define parentDecl(st) ((st)->parent == NULL ? \
DECL_NONE : (st)->parent->declaration)
#define isType(token,t) (boolean) ((token)->type == (t))
#define insideEnumBody(st) ((st)->parent == NULL ? FALSE : \
(boolean) ((st)->parent->declaration == DECL_ENUM))
#define isExternCDecl(st,c) (boolean) ((c) == STRING_SYMBOL && \
! (st)->haveQualifyingName && (st)->scope == SCOPE_EXTERN)
#define isOneOf(c,s) (boolean) (strchr ((s), (c)) != NULL)
#define isHighChar(c) ((c) != EOF && (unsigned char)(c) >= 0xc0)
/*
* DATA DECLARATIONS
*/
enum { NumTokens = 3 };
typedef enum eException {
ExceptionNone, ExceptionEOF, ExceptionFormattingError,
ExceptionBraceFormattingError
} exception_t;
/* Used to specify type of keyword.
*/
typedef enum eKeywordId {
KEYWORD_NONE = -1,
KEYWORD_ATTRIBUTE, KEYWORD_ABSTRACT,
KEYWORD_BOOLEAN, KEYWORD_BYTE, KEYWORD_BAD_STATE, KEYWORD_BAD_TRANS,
KEYWORD_BIND, KEYWORD_BIND_VAR, KEYWORD_BIT,
KEYWORD_CASE, KEYWORD_CATCH, KEYWORD_CHAR, KEYWORD_CLASS, KEYWORD_CONST,
KEYWORD_CONSTRAINT, KEYWORD_COVERAGE_BLOCK, KEYWORD_COVERAGE_DEF,
KEYWORD_DEFAULT, KEYWORD_DELEGATE, KEYWORD_DELETE, KEYWORD_DO,
KEYWORD_DOUBLE,
KEYWORD_ELSE, KEYWORD_ENUM, KEYWORD_EXPLICIT, KEYWORD_EXTERN,
KEYWORD_EXTENDS, KEYWORD_EVENT,
KEYWORD_FINAL, KEYWORD_FLOAT, KEYWORD_FOR, KEYWORD_FRIEND, KEYWORD_FUNCTION,
KEYWORD_GOTO,
KEYWORD_IF, KEYWORD_IMPLEMENTS, KEYWORD_IMPORT, KEYWORD_INLINE, KEYWORD_INT,
KEYWORD_INOUT, KEYWORD_INPUT, KEYWORD_INTEGER, KEYWORD_INTERFACE,
KEYWORD_INTERNAL,
KEYWORD_LOCAL, KEYWORD_LONG,
KEYWORD_M_BAD_STATE, KEYWORD_M_BAD_TRANS, KEYWORD_M_STATE, KEYWORD_M_TRANS,
KEYWORD_MUTABLE,
KEYWORD_NAMESPACE, KEYWORD_NEW, KEYWORD_NEWCOV, KEYWORD_NATIVE,
KEYWORD_OPERATOR, KEYWORD_OUTPUT, KEYWORD_OVERLOAD, KEYWORD_OVERRIDE,
KEYWORD_PACKED, KEYWORD_PORT, KEYWORD_PACKAGE, KEYWORD_PRIVATE,
KEYWORD_PROGRAM, KEYWORD_PROTECTED, KEYWORD_PUBLIC,
KEYWORD_REGISTER, KEYWORD_RETURN,
KEYWORD_SHADOW, KEYWORD_STATE,
KEYWORD_SHORT, KEYWORD_SIGNED, KEYWORD_STATIC, KEYWORD_STRING,
KEYWORD_STRUCT, KEYWORD_SWITCH, KEYWORD_SYNCHRONIZED,
KEYWORD_TASK, KEYWORD_TEMPLATE, KEYWORD_THIS, KEYWORD_THROW,
KEYWORD_THROWS, KEYWORD_TRANSIENT, KEYWORD_TRANS, KEYWORD_TRANSITION,
KEYWORD_TRY, KEYWORD_TYPEDEF, KEYWORD_TYPENAME,
KEYWORD_UINT, KEYWORD_ULONG, KEYWORD_UNION, KEYWORD_UNSIGNED, KEYWORD_USHORT,
KEYWORD_USING,
KEYWORD_VIRTUAL, KEYWORD_VOID, KEYWORD_VOLATILE,
KEYWORD_WCHAR_T, KEYWORD_WHILE
} keywordId;
/* Used to determine whether keyword is valid for the current language and
* what its ID is.
*/
typedef struct sKeywordDesc {
const char *name;
keywordId id;
short isValid [5]; /* indicates languages for which kw is valid */
} keywordDesc;
/* Used for reporting the type of object parsed by nextToken ().
*/
typedef enum eTokenType {
TOKEN_NONE, /* none */
TOKEN_ARGS, /* a parenthetical pair and its contents */
TOKEN_BRACE_CLOSE,
TOKEN_BRACE_OPEN,
TOKEN_COLON, /* the colon character */
TOKEN_COMMA, /* the comma character */
TOKEN_DOUBLE_COLON, /* double colon indicates nested-name-specifier */
TOKEN_KEYWORD,
TOKEN_NAME, /* an unknown name */
TOKEN_PACKAGE, /* a Java package name */
TOKEN_PAREN_NAME, /* a single name in parentheses */
TOKEN_SEMICOLON, /* the semicolon character */
TOKEN_SPEC, /* a storage class specifier, qualifier, type, etc. */
TOKEN_COUNT
} tokenType;
/* This describes the scoping of the current statement.
*/
typedef enum eTagScope {
SCOPE_GLOBAL, /* no storage class specified */
SCOPE_STATIC, /* static storage class */
SCOPE_EXTERN, /* external storage class */
SCOPE_FRIEND, /* declares access only */
SCOPE_TYPEDEF, /* scoping depends upon context */
SCOPE_COUNT
} tagScope;
typedef enum eDeclaration {
DECL_NONE,
DECL_BASE, /* base type (default) */
DECL_CLASS,
DECL_ENUM,
DECL_EVENT,
DECL_FUNCTION,
DECL_IGNORE, /* non-taggable "declaration" */
DECL_INTERFACE,
DECL_NAMESPACE,
DECL_NOMANGLE, /* C++ name demangling block */
DECL_PACKAGE,
DECL_PROGRAM, /* Vera program */
DECL_STRUCT,
DECL_TASK, /* Vera task */
DECL_UNION,
DECL_COUNT
} declType;
typedef enum eVisibilityType {
ACCESS_UNDEFINED,
ACCESS_LOCAL,
ACCESS_PRIVATE,
ACCESS_PROTECTED,
ACCESS_PUBLIC,
ACCESS_DEFAULT, /* Java-specific */
ACCESS_COUNT
} accessType;
/* Information about the parent class of a member (if any).
*/
typedef struct sMemberInfo {
accessType access; /* access of current statement */
accessType accessDefault; /* access default for current statement */
} memberInfo;
typedef struct sTokenInfo {
tokenType type;
keywordId keyword;
vString* name; /* the name of the token */
unsigned long lineNumber; /* line number of tag */
fpos_t filePosition; /* file position of line containing name */
} tokenInfo;
typedef enum eImplementation {
IMP_DEFAULT,
IMP_ABSTRACT,
IMP_VIRTUAL,
IMP_PURE_VIRTUAL,
IMP_COUNT
} impType;
/* Describes the statement currently undergoing analysis.
*/
typedef struct sStatementInfo {
tagScope scope;
declType declaration; /* specifier associated with TOKEN_SPEC */
boolean gotName; /* was a name parsed yet? */
boolean haveQualifyingName; /* do we have a name we are considering? */
boolean gotParenName; /* was a name inside parentheses parsed yet? */
boolean gotArgs; /* was a list of parameters parsed yet? */
boolean isPointer; /* is 'name' a pointer? */
boolean inFunction; /* are we inside of a function? */
boolean assignment; /* have we handled an '='? */
boolean notVariable; /* has a variable declaration been disqualified ? */
impType implementation; /* abstract or concrete implementation? */
unsigned int tokenIndex; /* currently active token */
tokenInfo* token [(int) NumTokens];
tokenInfo* context; /* accumulated scope of current statement */
tokenInfo* blockName; /* name of current block */
memberInfo member; /* information regarding parent class/struct */
vString* parentClasses; /* parent classes */
struct sStatementInfo *parent; /* statement we are nested within */
} statementInfo;
/* Describes the type of tag being generated.
*/
typedef enum eTagType {
TAG_UNDEFINED,
TAG_CLASS, /* class name */
TAG_ENUM, /* enumeration name */
TAG_ENUMERATOR, /* enumerator (enumeration value) */
TAG_EVENT, /* event */
TAG_FIELD, /* field (Java) */
TAG_FUNCTION, /* function definition */
TAG_INTERFACE, /* interface declaration */
TAG_LOCAL, /* local variable definition */
TAG_MEMBER, /* structure, class or interface member */
TAG_METHOD, /* method declaration */
TAG_NAMESPACE, /* namespace name */
TAG_PACKAGE, /* package name */
TAG_PROGRAM, /* program name */
TAG_PROPERTY, /* property name */
TAG_PROTOTYPE, /* function prototype or declaration */
TAG_STRUCT, /* structure name */
TAG_TASK, /* task name */
TAG_TYPEDEF, /* typedef name */
TAG_UNION, /* union name */
TAG_VARIABLE, /* variable definition */
TAG_EXTERN_VAR, /* external variable declaration */
TAG_COUNT /* must be last */
} tagType;
typedef struct sParenInfo {
boolean isPointer;
boolean isParamList;
boolean isKnrParamList;
boolean isNameCandidate;
boolean invalidContents;
boolean nestedArgs;
unsigned int parameterCount;
} parenInfo;
/*
* DATA DEFINITIONS
*/
static jmp_buf Exception;
static langType Lang_c;
static langType Lang_cpp;
static langType Lang_csharp;
static langType Lang_java;
static langType Lang_vera;
static vString *Signature;
static boolean CollectingSignature;
/* Number used to uniquely identify anonymous structs and unions. */
static int AnonymousID = 0;
/* Used to index into the CKinds table. */
typedef enum {
CK_UNDEFINED = -1,
CK_CLASS, CK_DEFINE, CK_ENUMERATOR, CK_FUNCTION,
CK_ENUMERATION, CK_LOCAL, CK_MEMBER, CK_NAMESPACE, CK_PROTOTYPE,
CK_STRUCT, CK_TYPEDEF, CK_UNION, CK_VARIABLE,
CK_EXTERN_VARIABLE
} cKind;
static kindOption CKinds [] = {
{ TRUE, 'c', "class", "classes"},
{ TRUE, 'd', "macro", "macro definitions"},
{ TRUE, 'e', "enumerator", "enumerators (values inside an enumeration)"},
{ TRUE, 'f', "function", "function definitions"},
{ TRUE, 'g', "enum", "enumeration names"},
{ FALSE, 'l', "local", "local variables"},
{ TRUE, 'm', "member", "class, struct, and union members"},
{ TRUE, 'n', "namespace", "namespaces"},
{ FALSE, 'p', "prototype", "function prototypes"},
{ TRUE, 's', "struct", "structure names"},
{ TRUE, 't', "typedef", "typedefs"},
{ TRUE, 'u', "union", "union names"},
{ TRUE, 'v', "variable", "variable definitions"},
{ FALSE, 'x', "externvar", "external and forward variable declarations"},
};
typedef enum {
CSK_UNDEFINED = -1,
CSK_CLASS, CSK_DEFINE, CSK_ENUMERATOR, CSK_EVENT, CSK_FIELD,
CSK_ENUMERATION, CSK_INTERFACE, CSK_LOCAL, CSK_METHOD,
CSK_NAMESPACE, CSK_PROPERTY, CSK_STRUCT, CSK_TYPEDEF
} csharpKind;
static kindOption CsharpKinds [] = {
{ TRUE, 'c', "class", "classes"},
{ TRUE, 'd', "macro", "macro definitions"},
{ TRUE, 'e', "enumerator", "enumerators (values inside an enumeration)"},
{ TRUE, 'E', "event", "events"},
{ TRUE, 'f', "field", "fields"},
{ TRUE, 'g', "enum", "enumeration names"},
{ TRUE, 'i', "interface", "interfaces"},
{ FALSE, 'l', "local", "local variables"},
{ TRUE, 'm', "method", "methods"},
{ TRUE, 'n', "namespace", "namespaces"},
{ TRUE, 'p', "property", "properties"},
{ TRUE, 's', "struct", "structure names"},
{ TRUE, 't', "typedef", "typedefs"},
};
/* Used to index into the JavaKinds table. */
typedef enum {
JK_UNDEFINED = -1,
JK_CLASS, JK_ENUM_CONSTANT, JK_FIELD, JK_ENUM, JK_INTERFACE,
JK_LOCAL, JK_METHOD, JK_PACKAGE, JK_ACCESS, JK_CLASS_PREFIX
} javaKind;
static kindOption JavaKinds [] = {
{ TRUE, 'c', "class", "classes"},
{ TRUE, 'e', "enum constant", "enum constants"},
{ TRUE, 'f', "field", "fields"},
{ TRUE, 'g', "enum", "enum types"},
{ TRUE, 'i', "interface", "interfaces"},
{ FALSE, 'l', "local", "local variables"},
{ TRUE, 'm', "method", "methods"},
{ TRUE, 'p', "package", "packages"},
};
/* Used to index into the VeraKinds table. */
typedef enum {
VK_UNDEFINED = -1,
VK_CLASS, VK_DEFINE, VK_ENUMERATOR, VK_FUNCTION,
VK_ENUMERATION, VK_LOCAL, VK_MEMBER, VK_PROGRAM, VK_PROTOTYPE,
VK_TASK, VK_TYPEDEF, VK_VARIABLE,
VK_EXTERN_VARIABLE
} veraKind;
static kindOption VeraKinds [] = {
{ TRUE, 'c', "class", "classes"},
{ TRUE, 'd', "macro", "macro definitions"},
{ TRUE, 'e', "enumerator", "enumerators (values inside an enumeration)"},
{ TRUE, 'f', "function", "function definitions"},
{ TRUE, 'g', "enum", "enumeration names"},
{ FALSE, 'l', "local", "local variables"},
{ TRUE, 'm', "member", "class, struct, and union members"},
{ TRUE, 'p', "program", "programs"},
{ FALSE, 'P', "prototype", "function prototypes"},
{ TRUE, 't', "task", "tasks"},
{ TRUE, 'T', "typedef", "typedefs"},
{ TRUE, 'v', "variable", "variable definitions"},
{ FALSE, 'x', "externvar", "external variable declarations"}
};
static const keywordDesc KeywordTable [] = {
/* C++ */
/* ANSI C | C# Java */
/* | | | | Vera */
/* keyword keyword ID | | | | | */
{ "__attribute__", KEYWORD_ATTRIBUTE, { 1, 1, 1, 0, 0 } },
{ "abstract", KEYWORD_ABSTRACT, { 0, 0, 1, 1, 0 } },
{ "bad_state", KEYWORD_BAD_STATE, { 0, 0, 0, 0, 1 } },
{ "bad_trans", KEYWORD_BAD_TRANS, { 0, 0, 0, 0, 1 } },
{ "bind", KEYWORD_BIND, { 0, 0, 0, 0, 1 } },
{ "bind_var", KEYWORD_BIND_VAR, { 0, 0, 0, 0, 1 } },
{ "bit", KEYWORD_BIT, { 0, 0, 0, 0, 1 } },
{ "boolean", KEYWORD_BOOLEAN, { 0, 0, 0, 1, 0 } },
{ "byte", KEYWORD_BYTE, { 0, 0, 0, 1, 0 } },
{ "case", KEYWORD_CASE, { 1, 1, 1, 1, 0 } },
{ "catch", KEYWORD_CATCH, { 0, 1, 1, 0, 0 } },
{ "char", KEYWORD_CHAR, { 1, 1, 1, 1, 0 } },
{ "class", KEYWORD_CLASS, { 0, 1, 1, 1, 1 } },
{ "const", KEYWORD_CONST, { 1, 1, 1, 1, 0 } },
{ "constraint", KEYWORD_CONSTRAINT, { 0, 0, 0, 0, 1 } },
{ "coverage_block", KEYWORD_COVERAGE_BLOCK, { 0, 0, 0, 0, 1 } },
{ "coverage_def", KEYWORD_COVERAGE_DEF, { 0, 0, 0, 0, 1 } },
{ "do", KEYWORD_DO, { 1, 1, 1, 1, 0 } },
{ "default", KEYWORD_DEFAULT, { 1, 1, 1, 1, 0 } },
{ "delegate", KEYWORD_DELEGATE, { 0, 0, 1, 0, 0 } },
{ "delete", KEYWORD_DELETE, { 0, 1, 0, 0, 0 } },
{ "double", KEYWORD_DOUBLE, { 1, 1, 1, 1, 0 } },
{ "else", KEYWORD_ELSE, { 1, 1, 0, 1, 0 } },
{ "enum", KEYWORD_ENUM, { 1, 1, 1, 1, 1 } },
{ "event", KEYWORD_EVENT, { 0, 0, 1, 0, 1 } },
{ "explicit", KEYWORD_EXPLICIT, { 0, 1, 1, 0, 0 } },
{ "extends", KEYWORD_EXTENDS, { 0, 0, 0, 1, 1 } },
{ "extern", KEYWORD_EXTERN, { 1, 1, 1, 0, 1 } },
{ "final", KEYWORD_FINAL, { 0, 0, 0, 1, 0 } },
{ "float", KEYWORD_FLOAT, { 1, 1, 1, 1, 0 } },
{ "for", KEYWORD_FOR, { 1, 1, 1, 1, 0 } },
{ "friend", KEYWORD_FRIEND, { 0, 1, 0, 0, 0 } },
{ "function", KEYWORD_FUNCTION, { 0, 0, 0, 0, 1 } },
{ "goto", KEYWORD_GOTO, { 1, 1, 1, 1, 0 } },
{ "if", KEYWORD_IF, { 1, 1, 1, 1, 0 } },
{ "implements", KEYWORD_IMPLEMENTS, { 0, 0, 0, 1, 0 } },
{ "import", KEYWORD_IMPORT, { 0, 0, 0, 1, 0 } },
{ "inline", KEYWORD_INLINE, { 0, 1, 0, 0, 0 } },
{ "inout", KEYWORD_INOUT, { 0, 0, 0, 0, 1 } },
{ "input", KEYWORD_INPUT, { 0, 0, 0, 0, 1 } },
{ "int", KEYWORD_INT, { 1, 1, 1, 1, 0 } },
{ "integer", KEYWORD_INTEGER, { 0, 0, 0, 0, 1 } },
{ "interface", KEYWORD_INTERFACE, { 0, 0, 1, 1, 1 } },
{ "internal", KEYWORD_INTERNAL, { 0, 0, 1, 0, 0 } },
{ "local", KEYWORD_LOCAL, { 0, 0, 0, 0, 1 } },
{ "long", KEYWORD_LONG, { 1, 1, 1, 1, 0 } },
{ "m_bad_state", KEYWORD_M_BAD_STATE, { 0, 0, 0, 0, 1 } },
{ "m_bad_trans", KEYWORD_M_BAD_TRANS, { 0, 0, 0, 0, 1 } },
{ "m_state", KEYWORD_M_STATE, { 0, 0, 0, 0, 1 } },
{ "m_trans", KEYWORD_M_TRANS, { 0, 0, 0, 0, 1 } },
{ "mutable", KEYWORD_MUTABLE, { 0, 1, 0, 0, 0 } },
{ "namespace", KEYWORD_NAMESPACE, { 0, 1, 1, 0, 0 } },
{ "native", KEYWORD_NATIVE, { 0, 0, 0, 1, 0 } },
{ "new", KEYWORD_NEW, { 0, 1, 1, 1, 0 } },
{ "newcov", KEYWORD_NEWCOV, { 0, 0, 0, 0, 1 } },
{ "operator", KEYWORD_OPERATOR, { 0, 1, 1, 0, 0 } },
{ "output", KEYWORD_OUTPUT, { 0, 0, 0, 0, 1 } },
{ "overload", KEYWORD_OVERLOAD, { 0, 1, 0, 0, 0 } },
{ "override", KEYWORD_OVERRIDE, { 0, 0, 1, 0, 0 } },
{ "package", KEYWORD_PACKAGE, { 0, 0, 0, 1, 0 } },
{ "packed", KEYWORD_PACKED, { 0, 0, 0, 0, 1 } },
{ "port", KEYWORD_PORT, { 0, 0, 0, 0, 1 } },
{ "private", KEYWORD_PRIVATE, { 0, 1, 1, 1, 0 } },
{ "program", KEYWORD_PROGRAM, { 0, 0, 0, 0, 1 } },
{ "protected", KEYWORD_PROTECTED, { 0, 1, 1, 1, 1 } },
{ "public", KEYWORD_PUBLIC, { 0, 1, 1, 1, 1 } },
{ "register", KEYWORD_REGISTER, { 1, 1, 0, 0, 0 } },
{ "return", KEYWORD_RETURN, { 1, 1, 1, 1, 0 } },
{ "shadow", KEYWORD_SHADOW, { 0, 0, 0, 0, 1 } },
{ "short", KEYWORD_SHORT, { 1, 1, 1, 1, 0 } },
{ "signed", KEYWORD_SIGNED, { 1, 1, 0, 0, 0 } },
{ "state", KEYWORD_STATE, { 0, 0, 0, 0, 1 } },
{ "static", KEYWORD_STATIC, { 1, 1, 1, 1, 1 } },
{ "string", KEYWORD_STRING, { 0, 0, 1, 0, 1 } },
{ "struct", KEYWORD_STRUCT, { 1, 1, 1, 0, 0 } },
{ "switch", KEYWORD_SWITCH, { 1, 1, 1, 1, 0 } },
{ "synchronized", KEYWORD_SYNCHRONIZED, { 0, 0, 0, 1, 0 } },
{ "task", KEYWORD_TASK, { 0, 0, 0, 0, 1 } },
{ "template", KEYWORD_TEMPLATE, { 0, 1, 0, 0, 0 } },
{ "this", KEYWORD_THIS, { 0, 1, 1, 1, 0 } },
{ "throw", KEYWORD_THROW, { 0, 1, 1, 1, 0 } },
{ "throws", KEYWORD_THROWS, { 0, 0, 0, 1, 0 } },
{ "trans", KEYWORD_TRANS, { 0, 0, 0, 0, 1 } },
{ "transition", KEYWORD_TRANSITION, { 0, 0, 0, 0, 1 } },
{ "transient", KEYWORD_TRANSIENT, { 0, 0, 0, 1, 0 } },
{ "try", KEYWORD_TRY, { 0, 1, 1, 0, 0 } },
{ "typedef", KEYWORD_TYPEDEF, { 1, 1, 1, 0, 1 } },
{ "typename", KEYWORD_TYPENAME, { 0, 1, 0, 0, 0 } },
{ "uint", KEYWORD_UINT, { 0, 0, 1, 0, 0 } },
{ "ulong", KEYWORD_ULONG, { 0, 0, 1, 0, 0 } },
{ "union", KEYWORD_UNION, { 1, 1, 0, 0, 0 } },
{ "unsigned", KEYWORD_UNSIGNED, { 1, 1, 1, 0, 0 } },
{ "ushort", KEYWORD_USHORT, { 0, 0, 1, 0, 0 } },
{ "using", KEYWORD_USING, { 0, 1, 1, 0, 0 } },
{ "virtual", KEYWORD_VIRTUAL, { 0, 1, 1, 0, 1 } },
{ "void", KEYWORD_VOID, { 1, 1, 1, 1, 1 } },
{ "volatile", KEYWORD_VOLATILE, { 1, 1, 1, 1, 0 } },
{ "wchar_t", KEYWORD_WCHAR_T, { 1, 1, 1, 0, 0 } },
{ "while", KEYWORD_WHILE, { 1, 1, 1, 1, 0 } }
};
/*
* FUNCTION PROTOTYPES
*/
static void createTags (const unsigned int nestLevel, statementInfo *const parent);
/*
* FUNCTION DEFINITIONS
*/
extern boolean includingDefineTags (void)
{
return CKinds [CK_DEFINE].enabled;
}
/*
* Token management
*/
static void initToken (tokenInfo* const token)
{
token->type = TOKEN_NONE;
token->keyword = KEYWORD_NONE;
token->lineNumber = getSourceLineNumber ();
token->filePosition = getInputFilePosition ();
vStringClear (token->name);
}
static void advanceToken (statementInfo* const st)
{
if (st->tokenIndex >= (unsigned int) NumTokens - 1)
st->tokenIndex = 0;
else
++st->tokenIndex;
initToken (st->token [st->tokenIndex]);
}
static tokenInfo *prevToken (const statementInfo *const st, unsigned int n)
{
unsigned int tokenIndex;
unsigned int num = (unsigned int) NumTokens;
Assert (n < num);
tokenIndex = (st->tokenIndex + num - n) % num;
return st->token [tokenIndex];
}
static void setToken (statementInfo *const st, const tokenType type)
{
tokenInfo *token;
token = activeToken (st);
initToken (token);
token->type = type;
}
static void retardToken (statementInfo *const st)
{
if (st->tokenIndex == 0)
st->tokenIndex = (unsigned int) NumTokens - 1;
else
--st->tokenIndex;
setToken (st, TOKEN_NONE);
}
static tokenInfo *newToken (void)
{
tokenInfo *const token = xMalloc (1, tokenInfo);
token->name = vStringNew ();
initToken (token);
return token;
}
static void deleteToken (tokenInfo *const token)
{
if (token != NULL)
{
vStringDelete (token->name);
eFree (token);
}
}
static const char *accessString (const accessType access)
{
static const char *const names [] = {
"?", "local", "private", "protected", "public", "default"
};
Assert (sizeof (names) / sizeof (names [0]) == ACCESS_COUNT);
Assert ((int) access < ACCESS_COUNT);
return names [(int) access];
}
static const char *implementationString (const impType imp)
{
static const char *const names [] ={
"?", "abstract", "virtual", "pure virtual"
};
Assert (sizeof (names) / sizeof (names [0]) == IMP_COUNT);
Assert ((int) imp < IMP_COUNT);
return names [(int) imp];
}
/*
* Debugging functions
*/
#ifdef DEBUG
#define boolString(c) ((c) ? "TRUE" : "FALSE")
static const char *tokenString (const tokenType type)
{
static const char *const names [] = {
"none", "args", "}", "{", "colon", "comma", "double colon", "keyword",
"name", "package", "paren-name", "semicolon", "specifier"
};
Assert (sizeof (names) / sizeof (names [0]) == TOKEN_COUNT);
Assert ((int) type < TOKEN_COUNT);
return names [(int) type];
}
static const char *scopeString (const tagScope scope)
{
static const char *const names [] = {
"global", "static", "extern", "friend", "typedef"
};
Assert (sizeof (names) / sizeof (names [0]) == SCOPE_COUNT);
Assert ((int) scope < SCOPE_COUNT);
return names [(int) scope];
}
static const char *declString (const declType declaration)
{
static const char *const names [] = {
"?", "base", "class", "enum", "event", "function", "ignore",
"interface", "namespace", "no mangle", "package", "program",
"struct", "task", "union",
};
Assert (sizeof (names) / sizeof (names [0]) == DECL_COUNT);
Assert ((int) declaration < DECL_COUNT);
return names [(int) declaration];
}
static const char *keywordString (const keywordId keyword)
{
const size_t count = sizeof (KeywordTable) / sizeof (KeywordTable [0]);
const char *name = "none";
size_t i;
for (i = 0 ; i < count ; ++i)
{
const keywordDesc *p = &KeywordTable [i];
if (p->id == keyword)
{
name = p->name;
break;
}
}
return name;
}
static void __unused__ pt (tokenInfo *const token)
{
if (isType (token, TOKEN_NAME))
printf ("type: %-12s: %-13s line: %lu\n",
tokenString (token->type), vStringValue (token->name),
token->lineNumber);
else if (isType (token, TOKEN_KEYWORD))
printf ("type: %-12s: %-13s line: %lu\n",
tokenString (token->type), keywordString (token->keyword),
token->lineNumber);
else
printf ("type: %-12s line: %lu\n",
tokenString (token->type), token->lineNumber);
}
static void __unused__ ps (statementInfo *const st)
{
unsigned int i;
printf ("scope: %s decl: %s gotName: %s gotParenName: %s\n",
scopeString (st->scope), declString (st->declaration),
boolString (st->gotName), boolString (st->gotParenName));
printf ("haveQualifyingName: %s\n", boolString (st->haveQualifyingName));
printf ("access: %s default: %s\n", accessString (st->member.access),
accessString (st->member.accessDefault));
printf ("token : ");
pt (activeToken (st));
for (i = 1 ; i < (unsigned int) NumTokens ; ++i)
{
printf ("prev %u : ", i);
pt (prevToken (st, i));
}
printf ("context: ");
pt (st->context);
}
#endif
/*
* Statement management
*/
static boolean isContextualKeyword (const tokenInfo *const token)
{
boolean result;
switch (token->keyword)
{
case KEYWORD_CLASS:
case KEYWORD_ENUM:
case KEYWORD_INTERFACE:
case KEYWORD_NAMESPACE:
case KEYWORD_STRUCT:
case KEYWORD_UNION:
result = TRUE;
break;
default: result = FALSE; break;
}
return result;
}
static boolean isContextualStatement (const statementInfo *const st)
{
boolean result = FALSE;
if (st != NULL) switch (st->declaration)
{
case DECL_CLASS:
case DECL_ENUM:
case DECL_INTERFACE:
case DECL_STRUCT:
case DECL_UNION:
result = TRUE;
break;
default: result = FALSE; break;
}
return result;
}
static boolean isMember (const statementInfo *const st)
{
boolean result;
if (isType (st->context, TOKEN_NAME))
result = TRUE;
else
result = (boolean)
(st->parent != NULL && isContextualStatement (st->parent));
return result;
}
static void initMemberInfo (statementInfo *const st)
{
accessType accessDefault = ACCESS_UNDEFINED;
if (st->parent != NULL) switch (st->parent->declaration)
{
case DECL_ENUM:
accessDefault = (isLanguage (Lang_java) ? ACCESS_PUBLIC : ACCESS_UNDEFINED);
break;
case DECL_NAMESPACE:
case DECL_UNION:
accessDefault = ACCESS_UNDEFINED;
break;
case DECL_CLASS:
if (isLanguage (Lang_java))
accessDefault = ACCESS_DEFAULT;
else
accessDefault = ACCESS_PRIVATE;
break;
case DECL_INTERFACE:
case DECL_STRUCT:
accessDefault = ACCESS_PUBLIC;
break;
default: break;
}
st->member.accessDefault = accessDefault;
st->member.access = accessDefault;
}
static void reinitStatement (statementInfo *const st, const boolean partial)
{
unsigned int i;
if (! partial)
{
st->scope = SCOPE_GLOBAL;
if (isContextualStatement (st->parent))
st->declaration = DECL_BASE;
else
st->declaration = DECL_NONE;
}
st->gotParenName = FALSE;
st->isPointer = FALSE;
st->inFunction = FALSE;
st->assignment = FALSE;
st->notVariable = FALSE;
st->implementation = IMP_DEFAULT;
st->gotArgs = FALSE;
st->gotName = FALSE;
st->haveQualifyingName = FALSE;
st->tokenIndex = 0;
if (st->parent != NULL)
st->inFunction = st->parent->inFunction;
for (i = 0 ; i < (unsigned int) NumTokens ; ++i)
initToken (st->token [i]);
initToken (st->context);
/* Keep the block name, so that a variable following after a comma will
* still have the structure name.
*/
if (! partial)
initToken (st->blockName);
vStringClear (st->parentClasses);
/* Init member info.
*/
if (! partial)
st->member.access = st->member.accessDefault;
}
static void initStatement (statementInfo *const st, statementInfo *const parent)
{
st->parent = parent;
initMemberInfo (st);
reinitStatement (st, FALSE);
}
/*
* Tag generation functions
*/
static cKind cTagKind (const tagType type)
{
cKind result = CK_UNDEFINED;
switch (type)
{
case TAG_CLASS: result = CK_CLASS; break;
case TAG_ENUM: result = CK_ENUMERATION; break;
case TAG_ENUMERATOR: result = CK_ENUMERATOR; break;
case TAG_FUNCTION: result = CK_FUNCTION; break;
case TAG_LOCAL: result = CK_LOCAL; break;
case TAG_MEMBER: result = CK_MEMBER; break;
case TAG_NAMESPACE: result = CK_NAMESPACE; break;
case TAG_PROTOTYPE: result = CK_PROTOTYPE; break;
case TAG_STRUCT: result = CK_STRUCT; break;
case TAG_TYPEDEF: result = CK_TYPEDEF; break;
case TAG_UNION: result = CK_UNION; break;
case TAG_VARIABLE: result = CK_VARIABLE; break;
case TAG_EXTERN_VAR: result = CK_EXTERN_VARIABLE; break;
default: Assert ("Bad C tag type" == NULL); break;
}
return result;
}
static csharpKind csharpTagKind (const tagType type)
{
csharpKind result = CSK_UNDEFINED;
switch (type)
{
case TAG_CLASS: result = CSK_CLASS; break;
case TAG_ENUM: result = CSK_ENUMERATION; break;
case TAG_ENUMERATOR: result = CSK_ENUMERATOR; break;
case TAG_EVENT: result = CSK_EVENT; break;
case TAG_FIELD: result = CSK_FIELD ; break;
case TAG_INTERFACE: result = CSK_INTERFACE; break;
case TAG_LOCAL: result = CSK_LOCAL; break;
case TAG_METHOD: result = CSK_METHOD; break;
case TAG_NAMESPACE: result = CSK_NAMESPACE; break;
case TAG_PROPERTY: result = CSK_PROPERTY; break;
case TAG_STRUCT: result = CSK_STRUCT; break;
case TAG_TYPEDEF: result = CSK_TYPEDEF; break;
default: Assert ("Bad C# tag type" == NULL); break;
}
return result;
}
static javaKind javaTagKind (const tagType type)
{
javaKind result = JK_UNDEFINED;
switch (type)
{
case TAG_CLASS: result = JK_CLASS; break;
case TAG_ENUM: result = JK_ENUM; break;
case TAG_ENUMERATOR: result = JK_ENUM_CONSTANT; break;
case TAG_FIELD: result = JK_FIELD; break;
case TAG_INTERFACE: result = JK_INTERFACE; break;
case TAG_LOCAL: result = JK_LOCAL; break;
case TAG_METHOD: result = JK_METHOD; break;
case TAG_PACKAGE: result = JK_PACKAGE; break;
default: Assert ("Bad Java tag type" == NULL); break;
}
return result;
}
static veraKind veraTagKind (const tagType type) {
veraKind result = VK_UNDEFINED;
switch (type)
{
case TAG_CLASS: result = VK_CLASS; break;
case TAG_ENUM: result = VK_ENUMERATION; break;
case TAG_ENUMERATOR: result = VK_ENUMERATOR; break;
case TAG_FUNCTION: result = VK_FUNCTION; break;
case TAG_LOCAL: result = VK_LOCAL; break;
case TAG_MEMBER: result = VK_MEMBER; break;
case TAG_PROGRAM: result = VK_PROGRAM; break;
case TAG_PROTOTYPE: result = VK_PROTOTYPE; break;
case TAG_TASK: result = VK_TASK; break;
case TAG_TYPEDEF: result = VK_TYPEDEF; break;
case TAG_VARIABLE: result = VK_VARIABLE; break;
case TAG_EXTERN_VAR: result = VK_EXTERN_VARIABLE; break;
default: Assert ("Bad Vera tag type" == NULL); break;
}
return result;
}
static const char *tagName (const tagType type)
{
const char* result;
if (isLanguage (Lang_csharp))
result = CsharpKinds [csharpTagKind (type)].name;
else if (isLanguage (Lang_java))
result = JavaKinds [javaTagKind (type)].name;
else if (isLanguage (Lang_vera))
result = VeraKinds [veraTagKind (type)].name;
else
result = CKinds [cTagKind (type)].name;
return result;
}
static int tagLetter (const tagType type)
{
int result;
if (isLanguage (Lang_csharp))
result = CsharpKinds [csharpTagKind (type)].letter;
else if (isLanguage (Lang_java))
result = JavaKinds [javaTagKind (type)].letter;
else if (isLanguage (Lang_vera))
result = VeraKinds [veraTagKind (type)].letter;
else
result = CKinds [cTagKind (type)].letter;
return result;
}
static boolean includeTag (const tagType type, const boolean isFileScope)
{
boolean result;
if (isFileScope && ! Option.include.fileScope)
result = FALSE;
else if (isLanguage (Lang_csharp))
result = CsharpKinds [csharpTagKind (type)].enabled;
else if (isLanguage (Lang_java))
result = JavaKinds [javaTagKind (type)].enabled;
else if (isLanguage (Lang_vera))
result = VeraKinds [veraTagKind (type)].enabled;
else
result = CKinds [cTagKind (type)].enabled;
return result;
}
static tagType declToTagType (const declType declaration)
{
tagType type = TAG_UNDEFINED;
switch (declaration)
{
case DECL_CLASS: type = TAG_CLASS; break;
case DECL_ENUM: type = TAG_ENUM; break;
case DECL_EVENT: type = TAG_EVENT; break;
case DECL_FUNCTION: type = TAG_FUNCTION; break;
case DECL_INTERFACE: type = TAG_INTERFACE; break;
case DECL_NAMESPACE: type = TAG_NAMESPACE; break;
case DECL_PROGRAM: type = TAG_PROGRAM; break;
case DECL_TASK: type = TAG_TASK; break;
case DECL_STRUCT: type = TAG_STRUCT; break;
case DECL_UNION: type = TAG_UNION; break;
default: Assert ("Unexpected declaration" == NULL); break;
}
return type;
}
static const char* accessField (const statementInfo *const st)
{
const char* result = NULL;
if (isLanguage (Lang_cpp) && st->scope == SCOPE_FRIEND)
result = "friend";
else if (st->member.access != ACCESS_UNDEFINED)
result = accessString (st->member.access);
return result;
}
static void addContextSeparator (vString *const scope)
{
if (isLanguage (Lang_c) || isLanguage (Lang_cpp))
vStringCatS (scope, "::");
else if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
vStringCatS (scope, ".");
}
static void addOtherFields (tagEntryInfo* const tag, const tagType type,
const statementInfo *const st,
vString *const scope, vString *const typeRef)
{
/* For selected tag types, append an extension flag designating the
* parent object in which the tag is defined.
*/
switch (type)
{
default: break;
case TAG_FUNCTION:
case TAG_METHOD:
case TAG_PROTOTYPE:
if (vStringLength (Signature) > 0)
tag->extensionFields.signature = vStringValue (Signature);
case TAG_CLASS:
case TAG_ENUM:
case TAG_ENUMERATOR:
case TAG_EVENT:
case TAG_FIELD:
case TAG_INTERFACE:
case TAG_MEMBER:
case TAG_NAMESPACE:
case TAG_PROPERTY:
case TAG_STRUCT:
case TAG_TASK:
case TAG_TYPEDEF:
case TAG_UNION:
if (vStringLength (scope) > 0 &&
(isMember (st) || st->parent->declaration == DECL_NAMESPACE))
{
if (isType (st->context, TOKEN_NAME))
tag->extensionFields.scope [0] = tagName (TAG_CLASS);
else
tag->extensionFields.scope [0] =
tagName (declToTagType (parentDecl (st)));
tag->extensionFields.scope [1] = vStringValue (scope);
}
if ((type == TAG_CLASS || type == TAG_INTERFACE ||
type == TAG_STRUCT) && vStringLength (st->parentClasses) > 0)
{
tag->extensionFields.inheritance =
vStringValue (st->parentClasses);
}
if (st->implementation != IMP_DEFAULT &&
(isLanguage (Lang_cpp) || isLanguage (Lang_csharp) ||
isLanguage (Lang_java)))
{
tag->extensionFields.implementation =
implementationString (st->implementation);
}
if (isMember (st))
{
tag->extensionFields.access = accessField (st);
}
break;
}
/* Add typename info, type of the tag and name of struct/union/etc. */
if ((type == TAG_TYPEDEF || type == TAG_VARIABLE || type == TAG_MEMBER)
&& isContextualStatement(st))
{
char *p;
tag->extensionFields.typeRef [0] =
tagName (declToTagType (st->declaration));
p = vStringValue (st->blockName->name);
/* If there was no {} block get the name from the token before the
* name (current token is ';' or ',', previous token is the name).
*/
if (p == NULL || *p == '\0')
{
tokenInfo *const prev2 = prevToken (st, 2);
if (isType (prev2, TOKEN_NAME))
p = vStringValue (prev2->name);
}
/* Prepend the scope name if there is one. */
if (vStringLength (scope) > 0)
{
vStringCopy(typeRef, scope);
addContextSeparator (typeRef);
vStringCatS(typeRef, p);
p = vStringValue (typeRef);
}
tag->extensionFields.typeRef [1] = p;
}
}
static void findScopeHierarchy (vString *const string,
const statementInfo *const st)
{
vStringClear (string);
if (isType (st->context, TOKEN_NAME))
vStringCopy (string, st->context->name);
if (st->parent != NULL)
{
vString *temp = vStringNew ();
const statementInfo *s;
for (s = st->parent ; s != NULL ; s = s->parent)
{
if (isContextualStatement (s) ||
s->declaration == DECL_NAMESPACE ||
s->declaration == DECL_PROGRAM)
{
vStringCopy (temp, string);
vStringClear (string);
Assert (isType (s->blockName, TOKEN_NAME));
if (isType (s->context, TOKEN_NAME) &&
vStringLength (s->context->name) > 0)
{
vStringCat (string, s->context->name);
addContextSeparator (string);
}
vStringCat (string, s->blockName->name);
if (vStringLength (temp) > 0)
addContextSeparator (string);
vStringCat (string, temp);
}
}
vStringDelete (temp);
}
}
static void makeExtraTagEntry (const tagType type, tagEntryInfo *const e,
vString *const scope)
{
if (Option.include.qualifiedTags &&
scope != NULL && vStringLength (scope) > 0)
{
vString *const scopedName = vStringNew ();
if (type != TAG_ENUMERATOR)
vStringCopy (scopedName, scope);
else
{
/* remove last component (i.e. enumeration name) from scope */
const char* const sc = vStringValue (scope);
const char* colon = strrchr (sc, ':');
if (colon != NULL)
{
while (*colon == ':' && colon > sc)
--colon;
vStringNCopy (scopedName, scope, colon + 1 - sc);
}
}
if (vStringLength (scopedName) > 0)
{
addContextSeparator (scopedName);
vStringCatS (scopedName, e->name);
e->name = vStringValue (scopedName);
makeTagEntry (e);
}
vStringDelete (scopedName);
}
}
static void makeTag (const tokenInfo *const token,
const statementInfo *const st,
boolean isFileScope, const tagType type)
{
/* Nothing is really of file scope when it appears in a header file.
*/
isFileScope = (boolean) (isFileScope && ! isHeaderFile ());
if (isType (token, TOKEN_NAME) && vStringLength (token->name) > 0 &&
includeTag (type, isFileScope))
{
vString *scope = vStringNew ();
/* Use "typeRef" to store the typename from addOtherFields() until
* it's used in makeTagEntry().
*/
vString *typeRef = vStringNew ();
tagEntryInfo e;
initTagEntry (&e, vStringValue (token->name));
e.lineNumber = token->lineNumber;
e.filePosition = token->filePosition;
e.isFileScope = isFileScope;
e.kindName = tagName (type);
e.kind = tagLetter (type);
findScopeHierarchy (scope, st);
addOtherFields (&e, type, st, scope, typeRef);
makeTagEntry (&e);
makeExtraTagEntry (type, &e, scope);
vStringDelete (scope);
vStringDelete (typeRef);
}
}
static boolean isValidTypeSpecifier (const declType declaration)
{
boolean result;
switch (declaration)
{
case DECL_BASE:
case DECL_CLASS:
case DECL_ENUM:
case DECL_EVENT:
case DECL_STRUCT:
case DECL_UNION:
result = TRUE;
break;
default:
result = FALSE;
break;
}
return result;
}
static void qualifyEnumeratorTag (const statementInfo *const st,
const tokenInfo *const nameToken)
{
if (isType (nameToken, TOKEN_NAME))
makeTag (nameToken, st, TRUE, TAG_ENUMERATOR);
}
static void qualifyFunctionTag (const statementInfo *const st,
const tokenInfo *const nameToken)
{
if (isType (nameToken, TOKEN_NAME))
{
tagType type;
const boolean isFileScope =
(boolean) (st->member.access == ACCESS_PRIVATE ||
(!isMember (st) && st->scope == SCOPE_STATIC));
if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
type = TAG_METHOD;
else if (isLanguage (Lang_vera) && st->declaration == DECL_TASK)
type = TAG_TASK;
else
type = TAG_FUNCTION;
makeTag (nameToken, st, isFileScope, type);
}
}
static void qualifyFunctionDeclTag (const statementInfo *const st,
const tokenInfo *const nameToken)
{
if (! isType (nameToken, TOKEN_NAME))
;
else if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
qualifyFunctionTag (st, nameToken);
else if (st->scope == SCOPE_TYPEDEF)
makeTag (nameToken, st, TRUE, TAG_TYPEDEF);
else if (isValidTypeSpecifier (st->declaration) && ! isLanguage (Lang_csharp))
makeTag (nameToken, st, TRUE, TAG_PROTOTYPE);
}
static void qualifyCompoundTag (const statementInfo *const st,
const tokenInfo *const nameToken)
{
if (isType (nameToken, TOKEN_NAME))
{
const tagType type = declToTagType (st->declaration);
const boolean fileScoped = (boolean)
(!(isLanguage (Lang_java) ||
isLanguage (Lang_csharp) ||
isLanguage (Lang_vera)));
if (type != TAG_UNDEFINED)
makeTag (nameToken, st, fileScoped, type);
}
}
static void qualifyBlockTag (statementInfo *const st,
const tokenInfo *const nameToken)
{
switch (st->declaration)
{
case DECL_CLASS:
case DECL_ENUM:
case DECL_INTERFACE:
case DECL_NAMESPACE:
case DECL_PROGRAM:
case DECL_STRUCT:
case DECL_UNION:
qualifyCompoundTag (st, nameToken);
break;
default: break;
}
}
static void qualifyVariableTag (const statementInfo *const st,
const tokenInfo *const nameToken)
{
/* We have to watch that we do not interpret a declaration of the
* form "struct tag;" as a variable definition. In such a case, the
* token preceding the name will be a keyword.
*/
if (! isType (nameToken, TOKEN_NAME))
;
else if (st->scope == SCOPE_TYPEDEF)
makeTag (nameToken, st, TRUE, TAG_TYPEDEF);
else if (st->declaration == DECL_EVENT)
makeTag (nameToken, st, (boolean) (st->member.access == ACCESS_PRIVATE),
TAG_EVENT);
else if (st->declaration == DECL_PACKAGE)
makeTag (nameToken, st, FALSE, TAG_PACKAGE);
else if (isValidTypeSpecifier (st->declaration))
{
if (st->notVariable)
;
else if (isMember (st))
{
if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
makeTag (nameToken, st,
(boolean) (st->member.access == ACCESS_PRIVATE), TAG_FIELD);
else if (st->scope == SCOPE_GLOBAL || st->scope == SCOPE_STATIC)
makeTag (nameToken, st, TRUE, TAG_MEMBER);
}
else
{
if (st->scope == SCOPE_EXTERN || ! st->haveQualifyingName)
makeTag (nameToken, st, FALSE, TAG_EXTERN_VAR);
else if (st->inFunction)
makeTag (nameToken, st, (boolean) (st->scope == SCOPE_STATIC),
TAG_LOCAL);
else
makeTag (nameToken, st, (boolean) (st->scope == SCOPE_STATIC),
TAG_VARIABLE);
}
}
}
/*
* Parsing functions
*/
static int skipToOneOf (const char *const chars)
{
int c;
do
c = cppGetc ();
while (c != EOF && c != '\0' && strchr (chars, c) == NULL);
return c;
}
/* Skip to the next non-white character.
*/
static int skipToNonWhite (void)
{
boolean found = FALSE;
int c;
#if 0
do
c = cppGetc ();
while (isspace (c));
#else
while (1)
{
c = cppGetc ();
if (isspace (c))
found = TRUE;
else
break;
}
if (CollectingSignature && found)
vStringPut (Signature, ' ');
#endif
return c;
}
/* Skips to the next brace in column 1. This is intended for cases where
* preprocessor constructs result in unbalanced braces.
*/
static void skipToFormattedBraceMatch (void)
{
int c, next;
c = cppGetc ();
next = cppGetc ();
while (c != EOF && (c != '\n' || next != '}'))
{
c = next;
next = cppGetc ();
}
}
/* Skip to the matching character indicated by the pair string. If skipping
* to a matching brace and any brace is found within a different level of a
* #if conditional statement while brace formatting is in effect, we skip to
* the brace matched by its formatting. It is assumed that we have already
* read the character which starts the group (i.e. the first character of
* "pair").
*/
static void skipToMatch (const char *const pair)
{
const boolean braceMatching = (boolean) (strcmp ("{}", pair) == 0);
const boolean braceFormatting = (boolean) (isBraceFormat () && braceMatching);
const unsigned int initialLevel = getDirectiveNestLevel ();
const int begin = pair [0], end = pair [1];
const unsigned long inputLineNumber = getInputLineNumber ();
int matchLevel = 1;
int c = '\0';
while (matchLevel > 0 && (c = skipToNonWhite ()) != EOF)
{
if (CollectingSignature)
vStringPut (Signature, c);
if (c == begin)
{
++matchLevel;
if (braceFormatting && getDirectiveNestLevel () != initialLevel)
{
skipToFormattedBraceMatch ();
break;
}
}
else if (c == end)
{
--matchLevel;
if (braceFormatting && getDirectiveNestLevel () != initialLevel)
{
skipToFormattedBraceMatch ();
break;
}
}
}
if (c == EOF)
{
verbose ("%s: failed to find match for '%c' at line %lu\n",
getInputFileName (), begin, inputLineNumber);
if (braceMatching)
longjmp (Exception, (int) ExceptionBraceFormattingError);
else
longjmp (Exception, (int) ExceptionFormattingError);
}
}
static void skipParens (void)
{
const int c = skipToNonWhite ();
if (c == '(')
skipToMatch ("()");
else
cppUngetc (c);
}
static void skipBraces (void)
{
const int c = skipToNonWhite ();
if (c == '{')
skipToMatch ("{}");
else
cppUngetc (c);
}
static keywordId analyzeKeyword (const char *const name)
{
const keywordId id = (keywordId) lookupKeyword (name, getSourceLanguage ());
return id;
}
static void analyzeIdentifier (tokenInfo *const token)
{
char *const name = vStringValue (token->name);
const char *replacement = NULL;
boolean parensToo = FALSE;
if (isLanguage (Lang_java) ||
! isIgnoreToken (name, &parensToo, &replacement))
{
if (replacement != NULL)
token->keyword = analyzeKeyword (replacement);
else
token->keyword = analyzeKeyword (vStringValue (token->name));
if (token->keyword == KEYWORD_NONE)
token->type = TOKEN_NAME;
else
token->type = TOKEN_KEYWORD;
}
else
{
initToken (token);
if (parensToo)
{
int c = skipToNonWhite ();
if (c == '(')
skipToMatch ("()");
}
}
}
static void readIdentifier (tokenInfo *const token, const int firstChar)
{
vString *const name = token->name;
int c = firstChar;
boolean first = TRUE;
initToken (token);
+ /* Bug #1585745: strangely, C++ destructors allow whitespace between
+ * the ~ and the class name. */
+ if (isLanguage (Lang_cpp) && firstChar == '~')
+ {
+ vStringPut (name, c);
+ c = skipToNonWhite ();
+ }
+
do
{
vStringPut (name, c);
if (CollectingSignature)
{
if (!first)
vStringPut (Signature, c);
first = FALSE;
}
c = cppGetc ();
} while (isident (c) || (isLanguage (Lang_java) && isHighChar (c)));
vStringTerminate (name);
cppUngetc (c); /* unget non-identifier character */
analyzeIdentifier (token);
}
static void readPackageName (tokenInfo *const token, const int firstChar)
{
vString *const name = token->name;
int c = firstChar;
initToken (token);
while (isident (c) || c == '.')
{
vStringPut (name, c);
c = cppGetc ();
}
vStringTerminate (name);
cppUngetc (c); /* unget non-package character */
}
static void readPackageOrNamespace (statementInfo *const st, const declType declaration)
{
st->declaration = declaration;
if (declaration == DECL_NAMESPACE && !isLanguage (Lang_csharp))
{
/* In C++ a namespace is specified one level at a time. */
return;
}
else
{
/* In C#, a namespace can also be specified like a Java package name. */
tokenInfo *const token = activeToken (st);
Assert (isType (token, TOKEN_KEYWORD));
readPackageName (token, skipToNonWhite ());
token->type = TOKEN_NAME;
st->gotName = TRUE;
st->haveQualifyingName = TRUE;
}
}
static void processName (statementInfo *const st)
{
Assert (isType (activeToken (st), TOKEN_NAME));
if (st->gotName && st->declaration == DECL_NONE)
st->declaration = DECL_BASE;
st->gotName = TRUE;
st->haveQualifyingName = TRUE;
}
static void readOperator (statementInfo *const st)
{
const char *const acceptable = "+-*/%^&|~!=<>,[]";
const tokenInfo* const prev = prevToken (st,1);
tokenInfo *const token = activeToken (st);
vString *const name = token->name;
int c = skipToNonWhite ();
/* When we arrive here, we have the keyword "operator" in 'name'.
*/
if (isType (prev, TOKEN_KEYWORD) && (prev->keyword == KEYWORD_ENUM ||
prev->keyword == KEYWORD_STRUCT || prev->keyword == KEYWORD_UNION))
; /* ignore "operator" keyword if preceded by these keywords */
else if (c == '(')
{
/* Verify whether this is a valid function call (i.e. "()") operator.
*/
if (cppGetc () == ')')
{
vStringPut (name, ' '); /* always separate operator from keyword */
c = skipToNonWhite ();
if (c == '(')
vStringCatS (name, "()");
}
else
{
skipToMatch ("()");
c = cppGetc ();
}
}
else if (isident1 (c))
{
/* Handle "new" and "delete" operators, and conversion functions
* (per 13.3.1.1.2 [2] of the C++ spec).
*/
boolean whiteSpace = TRUE; /* default causes insertion of space */
do
{
if (isspace (c))
whiteSpace = TRUE;
else
{
if (whiteSpace)
{
vStringPut (name, ' ');
whiteSpace = FALSE;
}
vStringPut (name, c);
}
c = cppGetc ();
} while (! isOneOf (c, "(;") && c != EOF);
vStringTerminate (name);
}
else if (isOneOf (c, acceptable))
{
vStringPut (name, ' '); /* always separate operator from keyword */
do
{
vStringPut (name, c);
c = cppGetc ();
} while (isOneOf (c, acceptable));
vStringTerminate (name);
}
cppUngetc (c);
token->type = TOKEN_NAME;
token->keyword = KEYWORD_NONE;
processName (st);
}
static void copyToken (tokenInfo *const dest, const tokenInfo *const src)
{
dest->type = src->type;
dest->keyword = src->keyword;
dest->filePosition = src->filePosition;
dest->lineNumber = src->lineNumber;
vStringCopy (dest->name, src->name);
}
static void setAccess (statementInfo *const st, const accessType access)
{
if (isMember (st))
{
if (isLanguage (Lang_cpp))
{
int c = skipToNonWhite ();
if (c == ':')
reinitStatement (st, FALSE);
else
cppUngetc (c);
st->member.accessDefault = access;
}
st->member.access = access;
}
}
static void discardTypeList (tokenInfo *const token)
{
int c = skipToNonWhite ();
while (isident1 (c))
{
readIdentifier (token, c);
c = skipToNonWhite ();
if (c == '.' || c == ',')
c = skipToNonWhite ();
}
cppUngetc (c);
}
static void addParentClass (statementInfo *const st, tokenInfo *const token)
{
if (vStringLength (token->name) > 0 &&
vStringLength (st->parentClasses) > 0)
{
vStringPut (st->parentClasses, ',');
}
vStringCat (st->parentClasses, token->name);
}
static void readParents (statementInfo *const st, const int qualifier)
{
tokenInfo *const token = newToken ();
tokenInfo *const parent = newToken ();
int c;
do
{
c = skipToNonWhite ();
if (isident1 (c))
{
readIdentifier (token, c);
if (isType (token, TOKEN_NAME))
vStringCat (parent->name, token->name);
else
{
addParentClass (st, parent);
initToken (parent);
}
}
else if (c == qualifier)
vStringPut (parent->name, c);
else if (c == '<')
skipToMatch ("<>");
else if (isType (token, TOKEN_NAME))
{
addParentClass (st, parent);
initToken (parent);
}
} while (c != '{' && c != EOF);
cppUngetc (c);
deleteToken (parent);
deleteToken (token);
}
static void skipStatement (statementInfo *const st)
{
st->declaration = DECL_IGNORE;
skipToOneOf (";");
}
static void processInterface (statementInfo *const st)
{
st->declaration = DECL_INTERFACE;
}
static void processToken (tokenInfo *const token, statementInfo *const st)
{
switch (token->keyword) /* is it a reserved word? */
{
default: break;
case KEYWORD_NONE: processName (st); break;
case KEYWORD_ABSTRACT: st->implementation = IMP_ABSTRACT; break;
case KEYWORD_ATTRIBUTE: skipParens (); initToken (token); break;
case KEYWORD_BIND: st->declaration = DECL_BASE; break;
case KEYWORD_BIT: st->declaration = DECL_BASE; break;
case KEYWORD_CATCH: skipParens (); skipBraces (); break;
case KEYWORD_CHAR: st->declaration = DECL_BASE; break;
case KEYWORD_CLASS: st->declaration = DECL_CLASS; break;
case KEYWORD_CONST: st->declaration = DECL_BASE; break;
case KEYWORD_DOUBLE: st->declaration = DECL_BASE; break;
case KEYWORD_ENUM: st->declaration = DECL_ENUM; break;
case KEYWORD_EXTENDS: readParents (st, '.');
setToken (st, TOKEN_NONE); break;
case KEYWORD_FLOAT: st->declaration = DECL_BASE; break;
case KEYWORD_FUNCTION: st->declaration = DECL_BASE; break;
case KEYWORD_FRIEND: st->scope = SCOPE_FRIEND; break;
case KEYWORD_GOTO: skipStatement (st); break;
case KEYWORD_IMPLEMENTS:readParents (st, '.');
setToken (st, TOKEN_NONE); break;
case KEYWORD_IMPORT: skipStatement (st); break;
case KEYWORD_INT: st->declaration = DECL_BASE; break;
case KEYWORD_INTEGER: st->declaration = DECL_BASE; break;
case KEYWORD_INTERFACE: processInterface (st); break;
case KEYWORD_LOCAL: setAccess (st, ACCESS_LOCAL); break;
case KEYWORD_LONG: st->declaration = DECL_BASE; break;
case KEYWORD_OPERATOR: readOperator (st); break;
case KEYWORD_PRIVATE: setAccess (st, ACCESS_PRIVATE); break;
case KEYWORD_PROGRAM: st->declaration = DECL_PROGRAM; break;
case KEYWORD_PROTECTED: setAccess (st, ACCESS_PROTECTED); break;
case KEYWORD_PUBLIC: setAccess (st, ACCESS_PUBLIC); break;
case KEYWORD_RETURN: skipStatement (st); break;
case KEYWORD_SHORT: st->declaration = DECL_BASE; break;
case KEYWORD_SIGNED: st->declaration = DECL_BASE; break;
case KEYWORD_STRING: st->declaration = DECL_BASE; break;
case KEYWORD_STRUCT: st->declaration = DECL_STRUCT; break;
case KEYWORD_TASK: st->declaration = DECL_TASK; break;
case KEYWORD_THROWS: discardTypeList (token); break;
case KEYWORD_UNION: st->declaration = DECL_UNION; break;
case KEYWORD_UNSIGNED: st->declaration = DECL_BASE; break;
case KEYWORD_USING: skipStatement (st); break;
case KEYWORD_VOID: st->declaration = DECL_BASE; break;
case KEYWORD_VOLATILE: st->declaration = DECL_BASE; break;
case KEYWORD_VIRTUAL: st->implementation = IMP_VIRTUAL; break;
case KEYWORD_WCHAR_T: st->declaration = DECL_BASE; break;
case KEYWORD_NAMESPACE: readPackageOrNamespace (st, DECL_NAMESPACE); break;
case KEYWORD_PACKAGE: readPackageOrNamespace (st, DECL_PACKAGE); break;
case KEYWORD_EVENT:
if (isLanguage (Lang_csharp))
st->declaration = DECL_EVENT;
break;
case KEYWORD_TYPEDEF:
reinitStatement (st, FALSE);
st->scope = SCOPE_TYPEDEF;
break;
case KEYWORD_EXTERN:
if (! isLanguage (Lang_csharp) || !st->gotName)
{
reinitStatement (st, FALSE);
st->scope = SCOPE_EXTERN;
st->declaration = DECL_BASE;
}
break;
case KEYWORD_STATIC:
if (! (isLanguage (Lang_java) || isLanguage (Lang_csharp)))
{
reinitStatement (st, FALSE);
st->scope = SCOPE_STATIC;
st->declaration = DECL_BASE;
}
break;
case KEYWORD_FOR:
case KEYWORD_IF:
case KEYWORD_SWITCH:
case KEYWORD_WHILE:
{
int c = skipToNonWhite ();
if (c == '(')
skipToMatch ("()");
break;
}
}
}
/*
* Parenthesis handling functions
*/
static void restartStatement (statementInfo *const st)
{
tokenInfo *const save = newToken ();
tokenInfo *token = activeToken (st);
copyToken (save, token);
DebugStatement ( if (debug (DEBUG_PARSE)) printf ("<ES>");)
reinitStatement (st, FALSE);
token = activeToken (st);
copyToken (token, save);
deleteToken (save);
processToken (token, st);
}
/* Skips over a the mem-initializer-list of a ctor-initializer, defined as:
*
* mem-initializer-list:
* mem-initializer, mem-initializer-list
*
* mem-initializer:
* [::] [nested-name-spec] class-name (...)
* identifier
*/
static void skipMemIntializerList (tokenInfo *const token)
{
int c;
do
{
c = skipToNonWhite ();
while (isident1 (c) || c == ':')
{
if (c != ':')
readIdentifier (token, c);
c = skipToNonWhite ();
}
if (c == '<')
{
skipToMatch ("<>");
c = skipToNonWhite ();
}
if (c == '(')
{
skipToMatch ("()");
c = skipToNonWhite ();
}
} while (c == ',');
cppUngetc (c);
}
static void skipMacro (statementInfo *const st)
{
tokenInfo *const prev2 = prevToken (st, 2);
if (isType (prev2, TOKEN_NAME))
retardToken (st);
skipToMatch ("()");
}
/* Skips over characters following the parameter list. This will be either
* non-ANSI style function declarations or C++ stuff. Our choices:
*
* C (K&R):
* int func ();
* int func (one, two) int one; float two; {...}
* C (ANSI):
* int func (int one, float two);
* int func (int one, float two) {...}
* C++:
* int foo (...) [const|volatile] [throw (...)];
* int foo (...) [const|volatile] [throw (...)] [ctor-initializer] {...}
* int foo (...) [const|volatile] [throw (...)] try [ctor-initializer] {...}
* catch (...) {...}
*/
static boolean skipPostArgumentStuff (
statementInfo *const st, parenInfo *const info)
{
tokenInfo *const token = activeToken (st);
unsigned int parameters = info->parameterCount;
unsigned int elementCount = 0;
boolean restart = FALSE;
boolean end = FALSE;
int c = skipToNonWhite ();
do
{
switch (c)
{
case ')': break;
case ':': skipMemIntializerList (token);break; /* ctor-initializer */
case '[': skipToMatch ("[]"); break;
case '=': cppUngetc (c); end = TRUE; break;
case '{': cppUngetc (c); end = TRUE; break;
case '}': cppUngetc (c); end = TRUE; break;
case '(':
if (elementCount > 0)
++elementCount;
skipToMatch ("()");
break;
case ';':
if (parameters == 0 || elementCount < 2)
{
cppUngetc (c);
end = TRUE;
}
else if (--parameters == 0)
end = TRUE;
break;
default:
if (isident1 (c))
{
readIdentifier (token, c);
switch (token->keyword)
{
case KEYWORD_ATTRIBUTE: skipParens (); break;
case KEYWORD_THROW: skipParens (); break;
case KEYWORD_TRY: break;
case KEYWORD_CONST:
case KEYWORD_VOLATILE:
if (vStringLength (Signature) > 0)
{
vStringPut (Signature, ' ');
vStringCat (Signature, token->name);
}
break;
case KEYWORD_CATCH:
case KEYWORD_CLASS:
case KEYWORD_EXPLICIT:
case KEYWORD_EXTERN:
case KEYWORD_FRIEND:
case KEYWORD_INLINE:
case KEYWORD_MUTABLE:
case KEYWORD_NAMESPACE:
case KEYWORD_NEW:
case KEYWORD_NEWCOV:
case KEYWORD_OPERATOR:
case KEYWORD_OVERLOAD:
case KEYWORD_PRIVATE:
case KEYWORD_PROTECTED:
case KEYWORD_PUBLIC:
case KEYWORD_STATIC:
case KEYWORD_TEMPLATE:
case KEYWORD_TYPEDEF:
case KEYWORD_TYPENAME:
case KEYWORD_USING:
case KEYWORD_VIRTUAL:
/* Never allowed within parameter declarations. */
restart = TRUE;
end = TRUE;
break;
default:
if (isType (token, TOKEN_NONE))
;
else if (info->isKnrParamList && info->parameterCount > 0)
++elementCount;
else
{
/* If we encounter any other identifier immediately
* following an empty parameter list, this is almost
* certainly one of those Microsoft macro "thingies"
* that the automatic source code generation sticks
* in. Terminate the current statement.
*/
restart = TRUE;
end = TRUE;
}
break;
}
}
}
if (! end)
{
c = skipToNonWhite ();
if (c == EOF)
end = TRUE;
}
} while (! end);
if (restart)
restartStatement (st);
else
setToken (st, TOKEN_NONE);
return (boolean) (c != EOF);
}
static void skipJavaThrows (statementInfo *const st)
{
tokenInfo *const token = activeToken (st);
int c = skipToNonWhite ();
if (isident1 (c))
{
readIdentifier (token, c);
if (token->keyword == KEYWORD_THROWS)
{
do
{
c = skipToNonWhite ();
if (isident1 (c))
{
readIdentifier (token, c);
c = skipToNonWhite ();
}
} while (c == '.' || c == ',');
}
}
cppUngetc (c);
setToken (st, TOKEN_NONE);
}
static void analyzePostParens (statementInfo *const st, parenInfo *const info)
{
const unsigned long inputLineNumber = getInputLineNumber ();
int c = skipToNonWhite ();
cppUngetc (c);
if (isOneOf (c, "{;,="))
;
else if (isLanguage (Lang_java))
skipJavaThrows (st);
else
{
if (! skipPostArgumentStuff (st, info))
{
verbose (
"%s: confusing argument declarations beginning at line %lu\n",
getInputFileName (), inputLineNumber);
longjmp (Exception, (int) ExceptionFormattingError);
}
}
}
static int parseParens (statementInfo *const st, parenInfo *const info)
{
tokenInfo *const token = activeToken (st);
unsigned int identifierCount = 0;
unsigned int depth = 1;
boolean firstChar = TRUE;
int nextChar = '\0';
CollectingSignature = TRUE;
vStringClear (Signature);
vStringPut (Signature, '(');
info->parameterCount = 1;
do
{
int c = skipToNonWhite ();
vStringPut (Signature, c);
switch (c)
{
case '&':
case '*':
info->isPointer = TRUE;
info->isKnrParamList = FALSE;
if (identifierCount == 0)
info->isParamList = FALSE;
initToken (token);
break;
case ':':
info->isKnrParamList = FALSE;
break;
case '.':
info->isNameCandidate = FALSE;
c = cppGetc ();
if (c != '.')
{
cppUngetc (c);
info->isKnrParamList = FALSE;
}
else
{
c = cppGetc ();
if (c != '.')
{
cppUngetc (c);
info->isKnrParamList = FALSE;
}
else
vStringCatS (Signature, "..."); /* variable arg list */
}
break;
case ',':
info->isNameCandidate = FALSE;
if (info->isKnrParamList)
{
++info->parameterCount;
identifierCount = 0;
}
break;
case '=':
info->isKnrParamList = FALSE;
info->isNameCandidate = FALSE;
if (firstChar)
{
info->isParamList = FALSE;
skipMacro (st);
depth = 0;
}
break;
case '[':
info->isKnrParamList = FALSE;
skipToMatch ("[]");
break;
case '<':
info->isKnrParamList = FALSE;
skipToMatch ("<>");
break;
case ')':
if (firstChar)
info->parameterCount = 0;
--depth;
break;
case '(':
info->isKnrParamList = FALSE;
if (firstChar)
{
info->isNameCandidate = FALSE;
cppUngetc (c);
vStringClear (Signature);
skipMacro (st);
depth = 0;
vStringChop (Signature);
}
else if (isType (token, TOKEN_PAREN_NAME))
{
c = skipToNonWhite ();
if (c == '*') /* check for function pointer */
{
skipToMatch ("()");
c = skipToNonWhite ();
if (c == '(')
skipToMatch ("()");
else
cppUngetc (c);
}
else
{
cppUngetc (c);
cppUngetc ('(');
info->nestedArgs = TRUE;
}
}
else
++depth;
break;
default:
if (isident1 (c))
{
if (++identifierCount > 1)
info->isKnrParamList = FALSE;
readIdentifier (token, c);
if (isType (token, TOKEN_NAME) && info->isNameCandidate)
token->type = TOKEN_PAREN_NAME;
else if (isType (token, TOKEN_KEYWORD))
{
if (token->keyword != KEYWORD_CONST &&
token->keyword != KEYWORD_VOLATILE)
{
info->isKnrParamList = FALSE;
info->isNameCandidate = FALSE;
}
}
}
else
{
info->isParamList = FALSE;
info->isKnrParamList = FALSE;
info->isNameCandidate = FALSE;
info->invalidContents = TRUE;
}
break;
}
firstChar = FALSE;
} while (! info->nestedArgs && depth > 0 &&
(info->isKnrParamList || info->isNameCandidate));
if (! info->nestedArgs) while (depth > 0)
{
skipToMatch ("()");
--depth;
}
if (! info->isNameCandidate)
initToken (token);
vStringTerminate (Signature);
if (info->isKnrParamList)
vStringClear (Signature);
CollectingSignature = FALSE;
return nextChar;
}
static void initParenInfo (parenInfo *const info)
{
info->isPointer = FALSE;
info->isParamList = TRUE;
info->isKnrParamList = isLanguage (Lang_c);
info->isNameCandidate = TRUE;
info->invalidContents = FALSE;
info->nestedArgs = FALSE;
info->parameterCount = 0;
}
static void analyzeParens (statementInfo *const st)
{
tokenInfo *const prev = prevToken (st, 1);
if (st->inFunction && ! st->assignment)
st->notVariable = TRUE;
if (! isType (prev, TOKEN_NONE)) /* in case of ignored enclosing macros */
{
tokenInfo *const token = activeToken (st);
parenInfo info;
int c;
initParenInfo (&info);
parseParens (st, &info);
c = skipToNonWhite ();
cppUngetc (c);
if (info.invalidContents)
reinitStatement (st, FALSE);
else if (info.isNameCandidate && isType (token, TOKEN_PAREN_NAME) &&
! st->gotParenName &&
(! info.isParamList || ! st->haveQualifyingName ||
c == '(' ||
(c == '=' && st->implementation != IMP_VIRTUAL) ||
(st->declaration == DECL_NONE && isOneOf (c, ",;"))))
{
token->type = TOKEN_NAME;
processName (st);
st->gotParenName = TRUE;
if (! (c == '(' && info.nestedArgs))
st->isPointer = info.isPointer;
}
else if (! st->gotArgs && info.isParamList)
{
st->gotArgs = TRUE;
setToken (st, TOKEN_ARGS);
advanceToken (st);
if (st->scope != SCOPE_TYPEDEF)
analyzePostParens (st, &info);
}
else
setToken (st, TOKEN_NONE);
}
}
/*
* Token parsing functions
*/
static void addContext (statementInfo *const st, const tokenInfo* const token)
{
if (isType (token, TOKEN_NAME))
{
if (vStringLength (st->context->name) > 0)
{
if (isLanguage (Lang_c) || isLanguage (Lang_cpp))
vStringCatS (st->context->name, "::");
else if (isLanguage (Lang_java) || isLanguage (Lang_csharp))
vStringCatS (st->context->name, ".");
}
vStringCat (st->context->name, token->name);
st->context->type = TOKEN_NAME;
}
}
static boolean inheritingDeclaration (declType decl)
{
return (boolean) (
decl == DECL_CLASS ||
decl == DECL_STRUCT ||
decl == DECL_INTERFACE);
}
static void processColon (statementInfo *const st)
{
int c = skipToNonWhite ();
const boolean doubleColon = (boolean) (c == ':');
if (doubleColon)
{
setToken (st, TOKEN_DOUBLE_COLON);
st->haveQualifyingName = FALSE;
}
else
{
cppUngetc (c);
if ((isLanguage (Lang_cpp) || isLanguage (Lang_csharp)) &&
inheritingDeclaration (st->declaration))
{
readParents (st, ':');
}
else if (parentDecl (st) == DECL_STRUCT)
{
c = skipToOneOf (",;");
if (c == ',')
setToken (st, TOKEN_COMMA);
else if (c == ';')
setToken (st, TOKEN_SEMICOLON);
}
else
{
const tokenInfo *const prev = prevToken (st, 1);
const tokenInfo *const prev2 = prevToken (st, 2);
if (prev->keyword == KEYWORD_DEFAULT ||
prev2->keyword == KEYWORD_CASE ||
st->parent != NULL)
{
reinitStatement (st, FALSE);
}
}
}
}
static void processAngleBracket (void)
{
int c = cppGetc ();
if (c == '>')
; /* already found match for template */
else if ((isLanguage (Lang_cpp) || isLanguage (Lang_java)) &&
c != '<' && c != '=')
{
skipToMatch ("<>"); /* this is a template */
}
else
cppUngetc (c);
}
/* Skips over any initializing value which may follow an '=' character in a
* variable definition.
*/
static int skipInitializer (statementInfo *const st)
{
boolean done = FALSE;
int c;
while (! done)
{
c = skipToNonWhite ();
if (c == EOF)
longjmp (Exception, (int) ExceptionFormattingError);
else switch (c)
{
case ',':
case ';': done = TRUE; break;
case '0':
if (st->implementation == IMP_VIRTUAL)
st->implementation = IMP_PURE_VIRTUAL;
break;
case '[': skipToMatch ("[]"); break;
case '(': skipToMatch ("()"); break;
case '{': skipToMatch ("{}"); break;
case '}':
if (insideEnumBody (st))
done = TRUE;
else if (! isBraceFormat ())
{
verbose ("%s: unexpected closing brace at line %lu\n",
getInputFileName (), getInputLineNumber ());
longjmp (Exception, (int) ExceptionBraceFormattingError);
}
break;
default: break;
}
}
return c;
}
static void processInitializer (statementInfo *const st)
{
const boolean inEnumBody = insideEnumBody (st);
int c = cppGetc ();
if (c != '=')
{
cppUngetc (c);
c = skipInitializer (st);
st->assignment = TRUE;
if (c == ';')
setToken (st, TOKEN_SEMICOLON);
else if (c == ',')
setToken (st, TOKEN_COMMA);
else if (c == '}' && inEnumBody)
{
cppUngetc (c);
setToken (st, TOKEN_COMMA);
}
if (st->scope == SCOPE_EXTERN)
st->scope = SCOPE_GLOBAL;
}
}
static void parseIdentifier (statementInfo *const st, const int c)
{
tokenInfo *const token = activeToken (st);
readIdentifier (token, c);
if (! isType (token, TOKEN_NONE))
processToken (token, st);
}
static void parseJavaAnnotation (statementInfo *const st)
{
/*
* @Override
* @Target(ElementType.METHOD)
* @SuppressWarnings(value = "unchecked")
*
* But watch out for "@interface"!
*/
tokenInfo *const token = activeToken (st);
int c = skipToNonWhite ();
readIdentifier (token, c);
if (token->keyword == KEYWORD_INTERFACE)
{
/* Oops. This was actually "@interface" defining a new annotation. */
processInterface (st);
}
else
{
/* Bug #1691412: skip any annotation arguments. */
skipParens ();
}
}
static void parseGeneralToken (statementInfo *const st, const int c)
{
const tokenInfo *const prev = prevToken (st, 1);
if (isident1 (c) || (isLanguage (Lang_java) && isHighChar (c)))
{
parseIdentifier (st, c);
if (isType (st->context, TOKEN_NAME) &&
isType (activeToken (st), TOKEN_NAME) && isType (prev, TOKEN_NAME))
{
initToken (st->context);
}
}
else if (c == '.' || c == '-')
{
if (! st->assignment)
st->notVariable = TRUE;
if (c == '-')
{
int c2 = cppGetc ();
if (c2 != '>')
cppUngetc (c2);
}
}
else if (c == '!' || c == '>')
{
int c2 = cppGetc ();
if (c2 != '=')
cppUngetc (c2);
}
else if (c == '@' && isLanguage (Lang_java))
{
parseJavaAnnotation (st);
}
else if (isExternCDecl (st, c))
{
st->declaration = DECL_NOMANGLE;
st->scope = SCOPE_GLOBAL;
}
}
/* Reads characters from the pre-processor and assembles tokens, setting
* the current statement state.
*/
static void nextToken (statementInfo *const st)
{
tokenInfo *token;
do
{
int c = skipToNonWhite ();
switch (c)
{
case EOF: longjmp (Exception, (int) ExceptionEOF); break;
case '(': analyzeParens (st); break;
case '<': processAngleBracket (); break;
case '*': st->haveQualifyingName = FALSE; break;
case ',': setToken (st, TOKEN_COMMA); break;
case ':': processColon (st); break;
case ';': setToken (st, TOKEN_SEMICOLON); break;
case '=': processInitializer (st); break;
case '[': skipToMatch ("[]"); break;
case '{': setToken (st, TOKEN_BRACE_OPEN); break;
case '}': setToken (st, TOKEN_BRACE_CLOSE); break;
default: parseGeneralToken (st, c); break;
}
token = activeToken (st);
} while (isType (token, TOKEN_NONE));
}
/*
* Scanning support functions
*/
static statementInfo *CurrentStatement = NULL;
static statementInfo *newStatement (statementInfo *const parent)
{
statementInfo *const st = xMalloc (1, statementInfo);
unsigned int i;
for (i = 0 ; i < (unsigned int) NumTokens ; ++i)
st->token [i] = newToken ();
st->context = newToken ();
st->blockName = newToken ();
st->parentClasses = vStringNew ();
initStatement (st, parent);
CurrentStatement = st;
return st;
}
static void deleteStatement (void)
{
statementInfo *const st = CurrentStatement;
statementInfo *const parent = st->parent;
unsigned int i;
for (i = 0 ; i < (unsigned int) NumTokens ; ++i)
{
deleteToken (st->token [i]); st->token [i] = NULL;
}
deleteToken (st->blockName); st->blockName = NULL;
deleteToken (st->context); st->context = NULL;
vStringDelete (st->parentClasses); st->parentClasses = NULL;
eFree (st);
CurrentStatement = parent;
}
static void deleteAllStatements (void)
{
while (CurrentStatement != NULL)
deleteStatement ();
}
static boolean isStatementEnd (const statementInfo *const st)
{
const tokenInfo *const token = activeToken (st);
boolean isEnd;
if (isType (token, TOKEN_SEMICOLON))
isEnd = TRUE;
else if (isType (token, TOKEN_BRACE_CLOSE))
/* Java and C# do not require semicolons to end a block. Neither do C++
* namespaces. All other blocks require a semicolon to terminate them.
*/
isEnd = (boolean) (isLanguage (Lang_java) || isLanguage (Lang_csharp) ||
! isContextualStatement (st));
else
isEnd = FALSE;
return isEnd;
}
static void checkStatementEnd (statementInfo *const st)
{
const tokenInfo *const token = activeToken (st);
if (isType (token, TOKEN_COMMA))
reinitStatement (st, TRUE);
else if (isStatementEnd (st))
{
DebugStatement ( if (debug (DEBUG_PARSE)) printf ("<ES>"); )
reinitStatement (st, FALSE);
cppEndStatement ();
}
else
{
cppBeginStatement ();
advanceToken (st);
}
}
static void nest (statementInfo *const st, const unsigned int nestLevel)
{
switch (st->declaration)
{
case DECL_CLASS:
case DECL_ENUM:
case DECL_INTERFACE:
case DECL_NAMESPACE:
case DECL_NOMANGLE:
case DECL_STRUCT:
case DECL_UNION:
createTags (nestLevel, st);
break;
case DECL_FUNCTION:
case DECL_TASK:
st->inFunction = TRUE;
/* fall through */
default:
if (includeTag (TAG_LOCAL, FALSE))
createTags (nestLevel, st);
else
skipToMatch ("{}");
break;
}
advanceToken (st);
setToken (st, TOKEN_BRACE_CLOSE);
}
static void tagCheck (statementInfo *const st)
{
const tokenInfo *const token = activeToken (st);
const tokenInfo *const prev = prevToken (st, 1);
const tokenInfo *const prev2 = prevToken (st, 2);
switch (token->type)
{
case TOKEN_NAME:
if (insideEnumBody (st))
qualifyEnumeratorTag (st, token);
break;
#if 0
case TOKEN_PACKAGE:
if (st->haveQualifyingName)
makeTag (token, st, FALSE, TAG_PACKAGE);
break;
#endif
case TOKEN_BRACE_OPEN:
if (isType (prev, TOKEN_ARGS))
{
if (st->haveQualifyingName)
{
if (! isLanguage (Lang_vera))
st->declaration = DECL_FUNCTION;
if (isType (prev2, TOKEN_NAME))
copyToken (st->blockName, prev2);
qualifyFunctionTag (st, prev2);
}
}
else if (isContextualStatement (st) ||
st->declaration == DECL_NAMESPACE ||
st->declaration == DECL_PROGRAM)
{
if (isType (prev, TOKEN_NAME))
copyToken (st->blockName, prev);
else
{
/* For an anonymous struct or union we use a unique ID
* a number, so that the members can be found.
*/
char buf [20]; /* length of "_anon" + digits + null */
sprintf (buf, "__anon%d", ++AnonymousID);
vStringCopyS (st->blockName->name, buf);
st->blockName->type = TOKEN_NAME;
st->blockName->keyword = KEYWORD_NONE;
}
qualifyBlockTag (st, prev);
}
else if (isLanguage (Lang_csharp))
makeTag (prev, st, FALSE, TAG_PROPERTY);
break;
case TOKEN_SEMICOLON:
case TOKEN_COMMA:
if (insideEnumBody (st))
;
else if (isType (prev, TOKEN_NAME))
{
if (isContextualKeyword (prev2))
makeTag (prev, st, TRUE, TAG_EXTERN_VAR);
else
qualifyVariableTag (st, prev);
}
else if (isType (prev, TOKEN_ARGS) && isType (prev2, TOKEN_NAME))
{
if (st->isPointer)
qualifyVariableTag (st, prev2);
else
qualifyFunctionDeclTag (st, prev2);
}
if (isLanguage (Lang_java) && token->type == TOKEN_SEMICOLON && insideEnumBody (st))
{
/* In Java, after an initial enum-like part,
* a semicolon introduces a class-like part.
* See Bug #1730485 for the full rationale. */
st->parent->declaration = DECL_CLASS;
}
break;
default: break;
}
}
/* Parses the current file and decides whether to write out and tags that
* are discovered.
*/
static void createTags (const unsigned int nestLevel,
statementInfo *const parent)
{
statementInfo *const st = newStatement (parent);
DebugStatement ( if (nestLevel > 0) debugParseNest (TRUE, nestLevel); )
while (TRUE)
{
tokenInfo *token;
nextToken (st);
token = activeToken (st);
if (isType (token, TOKEN_BRACE_CLOSE))
{
if (nestLevel > 0)
break;
else
{
verbose ("%s: unexpected closing brace at line %lu\n",
getInputFileName (), getInputLineNumber ());
longjmp (Exception, (int) ExceptionBraceFormattingError);
}
}
else if (isType (token, TOKEN_DOUBLE_COLON))
{
addContext (st, prevToken (st, 1));
advanceToken (st);
}
else
{
tagCheck (st);
if (isType (token, TOKEN_BRACE_OPEN))
nest (st, nestLevel + 1);
checkStatementEnd (st);
}
}
deleteStatement ();
DebugStatement ( if (nestLevel > 0) debugParseNest (FALSE, nestLevel - 1); )
}
static boolean findCTags (const unsigned int passCount)
{
exception_t exception;
boolean retry;
Assert (passCount < 3);
cppInit ((boolean) (passCount > 1), isLanguage (Lang_csharp));
Signature = vStringNew ();
exception = (exception_t) setjmp (Exception);
retry = FALSE;
if (exception == ExceptionNone)
createTags (0, NULL);
else
{
deleteAllStatements ();
if (exception == ExceptionBraceFormattingError && passCount == 1)
{
retry = TRUE;
verbose ("%s: retrying file with fallback brace matching algorithm\n",
getInputFileName ());
}
}
vStringDelete (Signature);
cppTerminate ();
return retry;
}
static void buildKeywordHash (const langType language, unsigned int idx)
{
const size_t count = sizeof (KeywordTable) / sizeof (KeywordTable [0]);
size_t i;
for (i = 0 ; i < count ; ++i)
{
const keywordDesc* const p = &KeywordTable [i];
if (p->isValid [idx])
addKeyword (p->name, language, (int) p->id);
}
}
static void initializeCParser (const langType language)
{
Lang_c = language;
buildKeywordHash (language, 0);
}
static void initializeCppParser (const langType language)
{
Lang_cpp = language;
buildKeywordHash (language, 1);
}
static void initializeCsharpParser (const langType language)
{
Lang_csharp = language;
buildKeywordHash (language, 2);
}
static void initializeJavaParser (const langType language)
{
Lang_java = language;
buildKeywordHash (language, 3);
}
static void initializeVeraParser (const langType language)
{
Lang_vera = language;
buildKeywordHash (language, 4);
}
extern parserDefinition* CParser (void)
{
static const char *const extensions [] = { "c", NULL };
parserDefinition* def = parserNew ("C");
def->kinds = CKinds;
def->kindCount = KIND_COUNT (CKinds);
def->extensions = extensions;
def->parser2 = findCTags;
def->initialize = initializeCParser;
return def;
}
extern parserDefinition* CppParser (void)
{
static const char *const extensions [] = {
"c++", "cc", "cp", "cpp", "cxx", "h", "h++", "hh", "hp", "hpp", "hxx",
#ifndef CASE_INSENSITIVE_FILENAMES
"C", "H",
#endif
NULL
};
parserDefinition* def = parserNew ("C++");
def->kinds = CKinds;
def->kindCount = KIND_COUNT (CKinds);
def->extensions = extensions;
def->parser2 = findCTags;
def->initialize = initializeCppParser;
return def;
}
extern parserDefinition* CsharpParser (void)
{
static const char *const extensions [] = { "cs", NULL };
parserDefinition* def = parserNew ("C#");
def->kinds = CsharpKinds;
def->kindCount = KIND_COUNT (CsharpKinds);
def->extensions = extensions;
def->parser2 = findCTags;
def->initialize = initializeCsharpParser;
return def;
}
extern parserDefinition* JavaParser (void)
{
static const char *const extensions [] = { "java", NULL };
parserDefinition* def = parserNew ("Java");
def->kinds = JavaKinds;
def->kindCount = KIND_COUNT (JavaKinds);
def->extensions = extensions;
def->parser2 = findCTags;
def->initialize = initializeJavaParser;
return def;
}
extern parserDefinition* VeraParser (void)
{
static const char *const extensions [] = { "vr", "vri", "vrh", NULL };
parserDefinition* def = parserNew ("Vera");
def->kinds = VeraKinds;
def->kindCount = KIND_COUNT (VeraKinds);
def->extensions = extensions;
def->parser2 = findCTags;
def->initialize = initializeVeraParser;
return def;
}
/* vi:set tabstop=4 shiftwidth=4 noexpandtab: */
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Fri, Jul 4, 10:46 AM (4 w, 1 d ago)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
3452528
Attached To
rPUC universal-ctags debian packaging
Event Timeline
Log In to Comment