📄 parse.c
字号:
/*****************************************************************************
* $Id: parse.c,v 6.6 1998/08/20 04:50:36 darren Exp $
*
* Copyright (c) 1996-1998, 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 of a source file.
*****************************************************************************/
/*============================================================================
= Include files
============================================================================*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "ctags.h"
/*============================================================================
= Macros
============================================================================*/
#define hashIndex(c) ((c) - '_')
#define activeTag(st) ((st)->tag[(int)(st)->buf1])
#define activeName(st) (activeTag(st).name)
#define swapNameBuffers(st) ((st)->buf1 = (boolean)!(st)->buf1)
#define isExternCDecl(st,c) ((c) == STRING_SYMBOL && !(st)->gotName && \
(st)->scope == SCOPE_EXTERN)
/*============================================================================
= Data declarations
============================================================================*/
/* Used to specify type of keyword.
*/
typedef enum _keywordId {
KEYWORD_UNKNOWN,
KEYWORD_ABSTRACT, KEYWORD_ATTRIBUTE,
KEYWORD_BOOLEAN, KEYWORD_BYTE,
KEYWORD_CHAR, KEYWORD_CLASS, KEYWORD_CONST,
KEYWORD_DOUBLE,
KEYWORD_ENUM, KEYWORD_EXPLICIT, KEYWORD_EXTERN, KEYWORD_EXTENDS,
KEYWORD_FINAL, KEYWORD_FLOAT, KEYWORD_FRIEND,
KEYWORD_IMPLEMENTS, KEYWORD_IMPORT, KEYWORD_INLINE, KEYWORD_INT,
KEYWORD_INTERFACE,
KEYWORD_LONG,
KEYWORD_MUTABLE,
KEYWORD_NAMESPACE, KEYWORD_NEW, KEYWORD_NATIVE,
KEYWORD_OPERATOR, KEYWORD_OVERLOAD,
KEYWORD_PACKAGE, KEYWORD_PRIVATE, KEYWORD_PROTECTED, KEYWORD_PUBLIC,
KEYWORD_REGISTER,
KEYWORD_SHORT, KEYWORD_SIGNED, KEYWORD_STATIC, KEYWORD_STRUCT,
KEYWORD_SYNCHRONIZED,
KEYWORD_TEMPLATE, KEYWORD_THROW, KEYWORD_THROWS, KEYWORD_TRANSIENT,
KEYWORD_TYPEDEF, KEYWORD_TYPENAME,
KEYWORD_UNION, KEYWORD_UNSIGNED, KEYWORD_USING,
KEYWORD_VIRTUAL, KEYWORD_VOID, KEYWORD_VOLATILE,
KEYWORD_WCHAR_T
} keywordId;
/* Used to determine whether keyword is valid for the current language and
* what its ID is.
*/
typedef struct _keywordDesc {
const char *name;
keywordId id;
short isValid[LANG_COUNT]; /* indicates languages for which kw is valid */
} keywordDesc;
/* Used for reporting the type of object parsed by nextToken().
*/
typedef enum _tokenType {
TOK_NONE, /* none */
TOK_ARGS, /* a parenthetical pair and its contents */
TOK_BODY, /* a brace enclosed block */
TOK_COMMA, /* the comma character */
TOK_IGNORE, /* a sequence not to be seen by createTags() */
TOK_ENUM_BODY_END, /* the beginning of a list of enumeration values */
TOK_EOF, /* end of file */
TOK_NAME, /* an unknown name */
TOK_SEMICOLON, /* the semicolon character */
TOK_SPEC /* a storage class specifier, qualifier, type, etc. */
} tokenType;
/* Describes the statement currently undergoing analysis.
*/
typedef struct _statementInfo {
tagScope scope;
enum _declaration {
DECL_BASE, /* base type (default) */
DECL_CLASS, /* C++ class */
DECL_ENUM, /* enumeration */
DECL_IGNORE, /* non-taggable "declaration" */
DECL_INTERFACE, /* interface */
DECL_NAMESPACE, /* namespace */
DECL_STRUCT, /* structure */
DECL_UNION, /* union */
DECL_NOMANGLE /* C++ name demangling block */
} declaration; /* describes specifier associated with TOK_SPEC */
tokenType token; /* the most recent type of token */
tokenType prev[2]; /* the previous tokens */
boolean gotName; /* was a name parsed yet? */
boolean isFuncPtr; /* is 'name' a pointer? */
boolean inEnumBody; /* currently within enumeration value list? */
boolean buf1; /* is tag[1] the primary buffer? */
tagInfo tag[2]; /* information regarding last 2 tag candidates */
tagInfo class; /* class declaration name info */
memberInfo member; /* information regarding parent class/struct */
} statementInfo;
/* Information about an identifier within parentheses.
*/
typedef struct _parenInfo {
char name[MaxNameLength];
boolean gotName;
long location;
long lineNumber;
} parenInfo;
/*============================================================================
= Data definitions
============================================================================*/
enum { HashSize = ('z' - '_' + 1) }; /* '_' through 'z' */
static short KeywordHash[(int)HashSize];
static const keywordDesc KeywordTable[] = {
/* C++ */
/* ANSI C | Java */
/* keyword keyword ID \ | / */
{ "__attribute__", KEYWORD_ATTRIBUTE, { 1, 1, 0 } },
{ "abstract", KEYWORD_ABSTRACT, { 0, 0, 1 } },
{ "boolean", KEYWORD_BOOLEAN, { 0, 0, 1 } },
{ "byte", KEYWORD_BYTE, { 0, 0, 1 } },
{ "char", KEYWORD_CHAR, { 1, 1, 1 } },
{ "class", KEYWORD_CLASS, { 0, 1, 1 } },
{ "const", KEYWORD_CONST, { 1, 1, 1 } },
{ "double", KEYWORD_DOUBLE, { 1, 1, 1 } },
{ "enum", KEYWORD_ENUM, { 1, 1, 0 } },
{ "explicit", KEYWORD_EXPLICIT, { 0, 1, 0 } },
{ "extends", KEYWORD_EXTENDS, { 0, 0, 1 } },
{ "extern", KEYWORD_EXTERN, { 1, 1, 0 } },
{ "final", KEYWORD_FINAL, { 0, 0, 1 } },
{ "float", KEYWORD_FLOAT, { 1, 1, 1 } },
{ "friend", KEYWORD_FRIEND, { 0, 1, 0 } },
{ "implements", KEYWORD_IMPLEMENTS, { 0, 0, 1 } },
{ "import", KEYWORD_IMPORT, { 0, 0, 1 } },
{ "inline", KEYWORD_INLINE, { 0, 1, 0 } },
{ "int", KEYWORD_INT, { 1, 1, 1 } },
{ "interface", KEYWORD_INTERFACE, { 0, 0, 1 } },
{ "long", KEYWORD_LONG, { 1, 1, 1 } },
{ "mutable", KEYWORD_MUTABLE, { 0, 1, 0 } },
{ "namespace", KEYWORD_NAMESPACE, { 0, 1, 0 } },
{ "native", KEYWORD_NATIVE, { 0, 0, 1 } },
{ "new", KEYWORD_NEW, { 0, 1, 1 } },
{ "operator", KEYWORD_OPERATOR, { 0, 1, 0 } },
{ "overload", KEYWORD_OVERLOAD, { 0, 1, 0 } },
{ "package", KEYWORD_PACKAGE, { 0, 0, 1 } },
{ "private", KEYWORD_PRIVATE, { 0, 1, 1 } },
{ "protected", KEYWORD_PROTECTED, { 0, 1, 1 } },
{ "public", KEYWORD_PUBLIC, { 0, 1, 1 } },
{ "register", KEYWORD_REGISTER, { 1, 1, 0 } },
{ "short", KEYWORD_SHORT, { 1, 1, 1 } },
{ "signed", KEYWORD_SIGNED, { 1, 1, 0 } },
{ "static", KEYWORD_STATIC, { 1, 1, 1 } },
{ "struct", KEYWORD_STRUCT, { 1, 1, 0 } },
{ "synchronized", KEYWORD_SYNCHRONIZED, { 0, 0, 1 } },
{ "template", KEYWORD_TEMPLATE, { 0, 1, 0 } },
{ "throw", KEYWORD_THROW, { 0, 1, 1 } },
{ "throws", KEYWORD_THROWS, { 0, 0, 1 } },
{ "transient", KEYWORD_TRANSIENT, { 0, 0, 1 } },
{ "typedef", KEYWORD_TYPEDEF, { 1, 1, 0 } },
{ "typename", KEYWORD_TYPENAME, { 0, 1, 0 } },
{ "union", KEYWORD_UNION, { 1, 1, 0 } },
{ "unsigned", KEYWORD_UNSIGNED, { 1, 1, 0 } },
{ "using", KEYWORD_USING, { 0, 1, 0 } },
{ "virtual", KEYWORD_VIRTUAL, { 0, 1, 0 } },
{ "void", KEYWORD_VOID, { 1, 1, 1 } },
{ "volatile", KEYWORD_VOLATILE, { 1, 1, 1 } },
{ "wchar_t", KEYWORD_WCHAR_T, { 1, 1, 0 } }
};
static const size_t KeywordTableSize =
sizeof(KeywordTable)/sizeof(KeywordTable[0]);
/*============================================================================
= Function prototypes
============================================================================*/
static void initTag __ARGS((tagInfo *const tag));
static void initMemberInfo __ARGS((memberInfo *const pMember));
static void reinitStatement __ARGS((statementInfo *const st));
static void initStatement __ARGS((statementInfo *const st, const statementInfo *const parent));
/* Tag generation functions.
*/
static void qualifyBlockTag __ARGS((const statementInfo *const st, const tagInfo *const tag, const tagScope declScope));
static void qualifyEnumeratorTag __ARGS((const statementInfo *const st, const tagInfo *const tag, const tagScope declScope));
static void qualifyFunctionTag __ARGS((statementInfo *const st, const tagInfo *const tag));
static void qualifyVariableTag __ARGS((const statementInfo *const st, const tagInfo *const tag));
static void qualifyFunctionDeclTag __ARGS((const statementInfo *const st, const tagInfo *const tag));
/* Parsing functions.
*/
static int skipToNonWhite __ARGS((void));
static void skipToFormattedBraceMatch __ARGS((void));
static boolean skipToMatch __ARGS((const char *const pair));
static void readIdentifier __ARGS((const int firstChar, char *const name));
static void readOperator __ARGS((const int firstChar, char *const name));
static keywordId analyzeKeyword __ARGS((const char *const name));
static void processKeyword __ARGS((statementInfo *const st, keywordId keyword));
static int skipPostArgumentStuff __ARGS((int c, statementInfo *const st, const boolean emptyArgList));
static boolean analyzePostParens __ARGS((statementInfo *const st, const parenInfo *const paren, const boolean emptyArgList));
static void initParenInfo __ARGS((parenInfo *const paren));
static boolean saveParenInfo __ARGS((parenInfo *const paren, int c));
static boolean doubleParens __ARGS((statementInfo *const st));
static boolean analyzeParens __ARGS((statementInfo *const st));
static void analyzeIdentifier __ARGS((statementInfo *const st));
static boolean beginBlock __ARGS((statementInfo *const st, const unsigned int nesting));
static boolean endBlock __ARGS((statementInfo *const st, const unsigned int nesting));
static void processColon __ARGS((statementInfo *const st));
static int skipInitializer __ARGS((const boolean inEnumBody));
static boolean processInitializer __ARGS((statementInfo *const st));
static boolean processArray __ARGS((statementInfo *const st));
static boolean processTemplate __ARGS((statementInfo *const st));
static void processIdentifier __ARGS((statementInfo *const st, const int c));
static boolean nextToken __ARGS((statementInfo *const st, const unsigned int nesting));
/*============================================================================
= Function definitions
============================================================================*/
static void initTag( tag )
tagInfo *const tag;
{
tag->location = 0;
tag->lineNumber = 0;
tag->name[0] = '\0';
DebugStatement( clearString(tag->name, MaxNameLength); )
}
static void initMemberInfo( pMember )
memberInfo *const pMember;
{
pMember->type = MEMBER_NONE;
pMember->visibility = VIS_UNDEFINED;
pMember->parent[0] = '\0';
DebugStatement( clearString(pMember->parent, MaxNameLength); )
}
static void reinitStatement( st )
statementInfo *const st;
{
int i;
st->scope = SCOPE_GLOBAL;
st->declaration = DECL_BASE;
st->token = TOK_NONE;
st->prev[0] = TOK_NONE;
st->prev[1] = TOK_NONE;
st->gotName = FALSE;
st->isFuncPtr = FALSE;
st->buf1 = FALSE;
for (i = 0 ; i < 2 ; ++i)
initTag(&st->tag[i]);
initTag(&st->class);
if (st->member.type != MEMBER_NONE && ! st->member.persistent)
initMemberInfo(&st->member);
}
static void initStatement( st, parent )
statementInfo *const st;
const statementInfo *const parent;
{
/* Set the member information. If there is a parent statement, inherit
* the parent member information from it.
*/
if (parent == NULL)
{
initMemberInfo(&st->member);
st->inEnumBody = FALSE;
}
else
{
st->inEnumBody = (boolean)(parent->declaration == DECL_ENUM);
st->member.visibility = VIS_UNDEFINED;
switch (parent->declaration)
{
case DECL_ENUM: st->member.type = MEMBER_ENUM; break;
case DECL_CLASS: st->member.type = MEMBER_CLASS;
st->member.visibility = VIS_PRIVATE; break;
case DECL_INTERFACE:st->member.type = MEMBER_INTERFACE; break;
case DECL_NAMESPACE:st->member.type = MEMBER_NAMESPACE; break;
case DECL_STRUCT: st->member.type = MEMBER_STRUCT;
st->member.visibility = VIS_PUBLIC; break;
case DECL_UNION: st->member.type = MEMBER_UNION; break;
default: st->member.type = MEMBER_NONE; break;
}
DebugStatement( clearString(st->member.parent, MaxNameLength); )
if (st->member.type != MEMBER_NONE)
{
st->member.persistent = TRUE;
if (parent->declaration == DECL_CLASS)
strcpy(st->member.parent, parent->class.name);
else
strcpy(st->member.parent,
(parent->prev[0] == TOK_NAME) ? activeName(parent) : "");
}
}
reinitStatement(st);
}
/*----------------------------------------------------------------------------
*- Tag generation functions
----------------------------------------------------------------------------*/
static void qualifyBlockTag( st, tag, declScope )
const statementInfo *const st;
const tagInfo *const tag;
const tagScope declScope;
{
if (st->prev[0] == TOK_NAME)
{
boolean ok = TRUE;
tagType type = TAG_NUMTYPES; /* assignment to avoid warning */
switch (st->declaration)
{
case DECL_CLASS: type = TAG_CLASS; break;
case DECL_ENUM: type = TAG_ENUM; break;
case DECL_INTERFACE:type = TAG_INTERFACE; break;
case DECL_NAMESPACE:type = TAG_NAMESPACE; break;
case DECL_STRUCT: type = TAG_STRUCT; break;
case DECL_UNION: type = TAG_UNION; break;
default: ok = FALSE; break;
}
if (ok)
makeTag(tag, &st->member, declScope, type);
}
}
static void qualifyEnumeratorTag( st, tag, declScope )
const statementInfo *const st;
const tagInfo *const tag;
const tagScope declScope;
{
if (st->token == TOK_NAME)
makeTag(tag, &st->member, declScope, TAG_ENUMERATOR);
}
static void qualifyFunctionTag( st, tag )
statementInfo *const st;
const tagInfo *const tag;
{
if (st->scope == SCOPE_EXTERN) /* allowed for func. def. */
st->scope = SCOPE_GLOBAL;
makeTag(tag, &st->member, st->scope, TAG_FUNCTION);
}
static void qualifyVariableTag( st, tag )
const statementInfo *const st;
const tagInfo *const tag;
{
/* 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
* declaration will be either class, enum, struct or union, and prev[1]
* will be empty.
*/
if (st->declaration == DECL_IGNORE)
;
else if (st->declaration == DECL_BASE || st->prev[1] != TOK_SPEC)
{
if (st->member.type == MEMBER_NONE)
{
if (st->scope == SCOPE_EXTERN)
makeTag(tag, &st->member, st->scope, TAG_EXTERN_VAR);
else
makeTag(tag, &st->member, st->scope, TAG_VARIABLE);
}
else
{
if (st->scope == SCOPE_GLOBAL)
makeTag(tag, &st->member, st->scope, TAG_MEMBER);
else if (st->scope == SCOPE_STATIC)
makeTag(tag, &st->member, SCOPE_EXTERN, TAG_MEMBER);
}
}
}
static void qualifyFunctionDeclTag( st, tag )
const statementInfo *const st;
const tagInfo *const tag;
{
if (! File.isHeader)
makeTag(tag, &st->member, SCOPE_STATIC, TAG_FUNCDECL);
else if (st->scope == SCOPE_GLOBAL || st->scope == SCOPE_EXTERN)
makeTag(tag, &st->member, SCOPE_GLOBAL, TAG_FUNCDECL);
}
/*----------------------------------------------------------------------------
*- Parsing functions
----------------------------------------------------------------------------*/
/* Skip to the next non-white character.
*/
static int skipToNonWhite()
{
int c;
do
{
c = cppGetc();
} while (c != EOF && isspace(c));
return c;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -