www.pudn.com > vim53src.zip > 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#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; } #if 0 /* Skip to the next occurance of the specified character. */ static int skipToCharacter( findchar ) const int findchar; { int c; do c = cppGetc(); while (c != EOF && c != findchar); return c; } #endif /* Skips to the next brace in column 1. This is intended for cases where * preprocessor constructs result in unbalanced braces. */ static void skipToFormattedBraceMatch() { 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 boolean skipToMatch( pair ) const char *const pair; { const int begin = pair[0], end = pair[1]; const unsigned int initialLevel = Cpp.directive.nestLevel; const boolean braceFormatting = (boolean)(Option.braceFormat && strcmp("{}", pair) == 0); boolean ok = TRUE; int matchLevel = 1; int c = '\0'; while (matchLevel > 0 && (c = cppGetc()) != EOF) { if (c == begin) { ++matchLevel; if (braceFormatting && Cpp.directive.nestLevel != initialLevel) { skipToFormattedBraceMatch(); break; } } else if (c == end) { --matchLevel; if (braceFormatting && Cpp.directive.nestLevel != initialLevel) { skipToFormattedBraceMatch(); break; } } } if (c == EOF) ok = FALSE; return ok; } /* Read a C identifier beginning with "firstChar" and places it into "name". */ static void readIdentifier( firstChar, name ) const int firstChar; char *const name; { int c, i; name[0] = firstChar; for (i = 1, c = cppGetc() ; i < (int)MaxNameLength - 1 && isident(c) ; i++, c = cppGetc()) { name[i] = c; } name[i] = '\0'; /* null terminate name */ cppUngetc(c); /* unget non-identifier character */ } /* Read a C++ operator and appends to "name" (which should contain "operator"). */ static void readOperator( firstChar, name ) const int firstChar; char *const name; { int c, i; for (c = firstChar, i = strlen(name) ; i < (int)MaxNameLength - 1 && ! isspace(c) && c != '(' ; i++, c = cppGetc()) { name[i] = c; } if (i > 0) name[i] = '\0'; /* null terminate operator */ cppUngetc(c); /* unget non-operator character */ } /* Analyzes the identifier contained in a statement described by the * statement structure and adjusts the structure according the significance * of the identifier. */ static keywordId analyzeKeyword( name ) const char *const name; { keywordId id = KEYWORD_UNKNOWN; if (name[0] == '_' || islower(name[0])) { const short hash = KeywordHash[hashIndex(name[0])]; if (hash >= 0) { unsigned int i; for (i = hash ; i < KeywordTableSize ; ++i) { const keywordDesc *pKw = &KeywordTable[i]; if (pKw->name[0] != name[0]) break; if (pKw->isValid[File.language] && strcmp(pKw->name, name) == 0) { id = pKw->id; break; } } } } return id; } static void processKeyword( st, keyword ) statementInfo *const st; keywordId keyword; { st->token = TOK_SPEC; /* default unless otherwise */ switch (keyword) /* is it a reserved word? */ { default: st->token = TOK_IGNORE; break; case KEYWORD_CHAR: st->declaration = DECL_BASE; break; case KEYWORD_CLASS: st->declaration = DECL_CLASS; break; case KEYWORD_DOUBLE: st->declaration = DECL_BASE; break; case KEYWORD_ENUM: st->declaration = DECL_ENUM; break; case KEYWORD_EXTERN: st->scope = SCOPE_EXTERN; break; case KEYWORD_FLOAT: st->declaration = DECL_BASE; break; case KEYWORD_FRIEND: st->scope = SCOPE_FRIEND; break; case KEYWORD_IMPORT: st->declaration = DECL_IGNORE; break; case KEYWORD_INT: st->declaration = DECL_BASE; break; case KEYWORD_INTERFACE: st->declaration = DECL_INTERFACE; break; case KEYWORD_LONG: st->declaration = DECL_BASE; break; case KEYWORD_NAMESPACE: st->declaration = DECL_NAMESPACE; break; case KEYWORD_PACKAGE: st->declaration = DECL_IGNORE; break; case KEYWORD_PRIVATE: st->member.visibility = VIS_PRIVATE; break; case KEYWORD_PROTECTED: st->member.visibility = VIS_PROTECTED; break; case KEYWORD_PUBLIC: st->member.visibility = VIS_PUBLIC; break; case KEYWORD_SHORT: st->declaration = DECL_BASE; break; case KEYWORD_SIGNED: st->declaration = DECL_BASE; break; case KEYWORD_STRUCT: st->declaration = DECL_STRUCT; break; case KEYWORD_TYPEDEF: st->scope = SCOPE_TYPEDEF; break; case KEYWORD_UNION: st->declaration = DECL_UNION; break; case KEYWORD_UNSIGNED: st->declaration = DECL_BASE; break; case KEYWORD_VOID: st->declaration = DECL_BASE; break; case KEYWORD_ATTRIBUTE: { const int c = skipToNonWhite(); if (c == '(') skipToMatch("()"); else cppUngetc(c); st->token = TOK_IGNORE; break; } case KEYWORD_EXTENDS: case KEYWORD_IMPLEMENTS: case KEYWORD_THROWS: { char *const name = activeName(st); int c = skipToNonWhite(); /* Read and discard interface or class type-list (ident[, ident]). */ while (isident1(c)) { readIdentifier(c, name); c = skipToNonWhite(); if (c == '.' || c == ',') c = skipToNonWhite(); } cppUngetc(c); st->token = TOK_IGNORE; break; } case KEYWORD_STATIC: if (File.language != LANG_JAVA) st->scope = SCOPE_STATIC; break; case KEYWORD_OPERATOR: { char *const name = activeName(st); const int c = skipToNonWhite(); if (isident1(c)) readIdentifier(c, name); else readOperator(c, name); } /* fall through to unknown keyword case */ case KEYWORD_UNKNOWN: { tagInfo *const tag = &activeTag(st); st->token = TOK_NAME; st->gotName = TRUE; tag->location = File.seek; tag->lineNumber = File.lineNumber; if (st->declaration == DECL_CLASS && st->class.name[0] == '\0') { strcpy(st->class.name, tag->name); st->class.location = tag->location; st->class.lineNumber = tag->lineNumber; } break; } } } /* Skips over characters following the parameter list. This might be non-ANSI * style function declarations, a C++ exception specification, or another C++ * construct that I don't yet understand (e.g. "void Class::foo(int a): * attr(v1), attr(v2)"). */ static int skipPostArgumentStuff( c, st, emptyArgList ) int c; statementInfo *const st; const boolean emptyArgList; { boolean end = FALSE, firstSemicolon = TRUE; boolean skipCPlusStuff = (boolean)(c == ':'); unsigned int tokenCount = 0; while (c != EOF && ! end) { if (skipCPlusStuff) { if (c == '{' || c == ';') break; } else if (isident1(c)) { char name[MaxNameLength]; keywordId keyword; readIdentifier(c, name); ++tokenCount; keyword = analyzeKeyword(name); switch (keyword) { /* These words are explicitly allowed following the closing * parenthesis in C++. */ case KEYWORD_CONST: break; case KEYWORD_THROW: skipCPlusStuff = TRUE; break; /* These words are never allowed within parameter declarations. */ case KEYWORD_CLASS: case KEYWORD_EXTERN: case KEYWORD_STATIC: case KEYWORD_TYPEDEF: DebugStatement( if (debug(DEBUG_PARSE)) printf(" "); ) reinitStatement(st); processKeyword(st, keyword); c = skipToNonWhite(); end = TRUE; continue; /* skip read of next character */ case KEYWORD_ATTRIBUTE: { c = skipToNonWhite(); if (c == '(' && ! skipToMatch("()")) c = EOF; } break; default: /* 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. */ if (emptyArgList) { DebugStatement( if (debug(DEBUG_PARSE)) printf(" "); ) reinitStatement(st); processKeyword(st, keyword); c = skipToNonWhite(); end = TRUE; continue; /* skip read of next character */ } break; } } else switch (c) { default: break; /* ignore */ case ';': /* A lone word is most likely a preprocessor qualifier. */ if (firstSemicolon && tokenCount == 1) { end = TRUE; continue; } else { tokenCount = 0; firstSemicolon = FALSE; break; } case '(': ++tokenCount; if (! skipToMatch("()")) c = EOF; break; case '[': if (! skipToMatch("[]")) c = EOF; break; case '{': case '}': end = TRUE; continue; /* skip read of next character */ } if (c != EOF) c = cppGetc(); } return c; } static boolean analyzePostParens( st, paren, emptyArgList ) statementInfo *const st; const parenInfo *const paren; const boolean emptyArgList; { boolean ok = TRUE; int c; /* At this point we should be at the character following the * closing parenthesis. */ c = skipToNonWhite(); if (st->gotName) { if (strchr("{;,", c) != NULL) { st->token = TOK_ARGS; /* parameter list to a func. */ st->declaration = DECL_BASE; /* clear any other decl. */ } else if (isident1(c) || c == ':') { st->token = TOK_ARGS; /* parameter list to a func. */ st->declaration = DECL_BASE; /* clear any other decl. */ if (File.language != LANG_JAVA) c = skipPostArgumentStuff(c, st, emptyArgList); } else st->token = TOK_IGNORE; } /* The name inside the parentheses must have been a function or * variable name. */ else if (paren->gotName) { tagInfo *const tag = &activeTag(st); st->gotName = TRUE; st->token = TOK_NAME; tag->location = paren->location; tag->lineNumber = paren->lineNumber; strcpy(tag->name, paren->name); } else st->token = TOK_IGNORE; if (c == EOF) ok = FALSE; else cppUngetc(c); return ok; } static void initParenInfo( paren ) parenInfo *const paren; { DebugStatement( clearString(paren->name, MaxNameLength); ) paren->gotName = FALSE; paren->location = 0; paren->lineNumber = 0; } static boolean saveParenInfo( paren, c ) parenInfo *const paren; int c; { boolean ok = TRUE; readIdentifier(c, paren->name); c = skipToNonWhite(); if (c == ')') /* saved if only identifier in parentheses */ { paren->gotName = TRUE; paren->location = File.seek; paren->lineNumber = File.lineNumber; } else { if (c == '(') cppUngetc(c); ok = skipToMatch("()"); } return ok; } static boolean doubleParens( st ) statementInfo *const st; { /* A double parenthesis almost certainly means one of those conditional * prototype macro thingies (e.g. __ARGS((void)) ). If found, we will use * the previous name, if it is not empty. */ if (st->gotName && *st->tag[!st->buf1].name != '\0') swapNameBuffers(st); cppUngetc('('); /* put back for skipToMatch() */ return skipToMatch("()"); } /* Analyzes the context and contents of parentheses. */ static boolean analyzeParens( st ) statementInfo *const st; { boolean ok = TRUE; int c; c = skipToNonWhite(); if (c == '*') /* this is a function pointer */ { st->gotName = FALSE; /* invalidate previous name */ st->isFuncPtr = TRUE; st->token = TOK_IGNORE; } if (! st->gotName) { st->token = TOK_IGNORE; cppUngetc(c); } else { boolean terminate = FALSE, emptyArgList = FALSE; parenInfo paren; initParenInfo(&paren); if (isident1(c)) ok = saveParenInfo(&paren, c); else if (c == '(') ok = doubleParens(st); else if (c == ')') /* empty parentheses... */ emptyArgList = TRUE; else { /* This is an invalid character to be inside a paren in this * context. This must be a macro call. After we read to the * end of the parenthesis seqence, force a termination of the * current statement, */ st->token = TOK_NONE; st->gotName = FALSE; terminate = TRUE; ok = skipToMatch("()"); } if (ok && ! terminate) ok = analyzePostParens(st, &paren, emptyArgList); } return ok; } /* Analyzes the identifier contained in a statement described by the * statement structure and adjusts the structure according the significance * of the identifier. */ static void analyzeIdentifier( st ) statementInfo *const st; { tagInfo *const tag = &activeTag(st); char *const name = tag->name; if (isIgnoreToken(name)) st->token = TOK_IGNORE; else processKeyword(st, analyzeKeyword(name)); if (st->token == TOK_IGNORE) name[0] = '\0'; } static void processIdentifier( st, c ) statementInfo *const st; const int c; { if (st->gotName) swapNameBuffers(st); readIdentifier(c, activeName(st)); analyzeIdentifier(st); if (st->gotName && st->token == TOK_IGNORE) swapNameBuffers(st); } static void processColon( st ) statementInfo *const st; { if (st->declaration != DECL_CLASS) { const int c = skipToNonWhite(); if (c == ':') /* this is a method or static member definition */ { st->member.type = MEMBER_CLASS; strcpy(st->member.parent, activeName(st)); st->member.persistent = FALSE; } else { cppUngetc(c); st->token = TOK_IGNORE; } } } /* Skips over any initializing value which may follow a '=' character in a * variable definition. */ static int skipInitializer( enumInitializer ) const boolean enumInitializer; { boolean done = FALSE; int c; do { c = cppGetc(); if (c != EOF) switch (c) { case ',': case ';': done = TRUE; break; case '[': if (! skipToMatch("[]")) c = EOF; break; case '(': if (! skipToMatch("()")) c = EOF; break; case '{': if (! skipToMatch("{}")) c = EOF; break; case '}': if (enumInitializer) { cppUngetc(c); done = TRUE; } else if (! Option.braceFormat) c = EOF; break; default: break; } } while (! done && c != EOF); return c; } static boolean processInitializer( st ) statementInfo *const st; { boolean ok = TRUE; const int c = skipInitializer(st->inEnumBody); if (c == EOF) ok = FALSE; else if (c == ';') st->token = TOK_SEMICOLON; else if (c == ',') st->token = TOK_COMMA; else if ('}' && st->inEnumBody) st->token = TOK_COMMA; if (st->scope == SCOPE_EXTERN) st->scope = SCOPE_GLOBAL; return ok; } static boolean processArray( st ) statementInfo *const st; { st->token = TOK_IGNORE; return skipToMatch("[]"); } static boolean processTemplate( st ) statementInfo *const st; { st->token = TOK_IGNORE; return skipToMatch("<>"); } static boolean beginBlock( st, nesting ) statementInfo *const st; const unsigned int nesting; { const tagScope declScope = (File.isHeader || File.language == LANG_JAVA) ? SCOPE_GLOBAL : SCOPE_STATIC; tagInfo *const tag = &activeTag(st); boolean ok; switch (st->declaration) { case DECL_CLASS: qualifyBlockTag(st, &st->class, declScope); ok = createTags(nesting + 1, st); break; case DECL_ENUM: case DECL_INTERFACE: case DECL_NAMESPACE: case DECL_STRUCT: case DECL_UNION: qualifyBlockTag(st, tag, declScope); ok = createTags(nesting + 1, st); break; case DECL_NOMANGLE: ok = createTags(nesting + 1, st); break; default: ok = skipToMatch("{}"); } st->token = TOK_BODY; return ok; } static boolean endBlock( st, nesting ) statementInfo *const st; const unsigned int nesting; { boolean ok = TRUE; if (nesting > 0) st->token = TOK_EOF; /* fake out */ else { st->token = TOK_IGNORE; ok = FALSE; } return ok; } /* Reads characters from the pre-processor and assembles tokens, setting * the current statement state. */ static boolean nextToken( st, nesting ) statementInfo *const st; const unsigned int nesting; { int c; boolean ok = TRUE; do { c = cppGetc(); switch (c) { case EOF: st->token = TOK_EOF; break; case '(': ok = analyzeParens(st); break; case '*': st->gotName = FALSE; break; case ',': st->token = TOK_COMMA; break; case ':': processColon(st); break; case ';': st->token = TOK_SEMICOLON; break; case '=': ok = processInitializer(st); break; case '[': ok = processArray(st); break; case '{': ok = beginBlock(st, nesting); break; case '}': ok = endBlock(st, nesting); break; case '<': ok = processTemplate(st); break; default: if (isident1(c)) processIdentifier(st, c); else if (isExternCDecl(st, c)) { st->declaration = DECL_NOMANGLE; st->scope = SCOPE_GLOBAL; } else st->token = TOK_IGNORE; } } while (ok && st->token == TOK_IGNORE); return ok; } /*---------------------------------------------------------------------------- *- Scanning functions ----------------------------------------------------------------------------*/ /* Parses the current file and decides whether to write out and tags that * are discovered. */ extern boolean createTags( nesting, parent ) const unsigned int nesting; const void *const parent; { const tagScope declScope = File.isHeader ? SCOPE_GLOBAL : SCOPE_STATIC; statementInfo *st; boolean ok; DebugStatement( if (nesting > 0) debugParseNest(TRUE, nesting); ) st = (statementInfo *)malloc(sizeof(statementInfo)); if (st == NULL) error(FATAL | PERROR, "cannot allocate statement info"); initStatement(st, (const statementInfo *)parent); while ((ok = nextToken(st, nesting))) { tagInfo *const tag = &activeTag(st); if (st->token == TOK_EOF) break; else if (! st->gotName) ; else if (st->inEnumBody) qualifyEnumeratorTag(st, tag, declScope); else if (st->token == TOK_BODY && st->prev[0] == TOK_ARGS) qualifyFunctionTag(st, tag); else if (st->token == TOK_SEMICOLON || st->token == TOK_COMMA) { if (st->scope == SCOPE_TYPEDEF) makeTag(tag, &st->member, declScope, TAG_TYPEDEF); else if (st->prev[0] == TOK_NAME || st->isFuncPtr) qualifyVariableTag(st, tag); else if (st->prev[0] == TOK_ARGS) qualifyFunctionDeclTag(st, tag); } /* Reset after a semicolon, or BODY preceeded by ARGS (a function), * a namspace definition, or an "extern" block. */ if (st->token == TOK_SEMICOLON || (st->token == TOK_BODY && (st->declaration == DECL_NAMESPACE || st->declaration == DECL_NOMANGLE || st->prev[0] == TOK_ARGS))) { DebugStatement( if (debug(DEBUG_PARSE)) printf(" "); ) reinitStatement(st); Cpp.resolveRequired = FALSE; /* end of statement */ } else Cpp.resolveRequired = TRUE; /* in middle of statement */ st->prev[1] = st->prev[0]; st->prev[0] = st->token; } DebugStatement( if (nesting > 0) debugParseNest(FALSE, nesting - 1); ) free(st); return ok; } extern void buildKeywordHash() { int lastInitialChar = '\0'; size_t i; /* Clear all hash entries. */ for (i = 0 ; i < sizeof(KeywordHash)/sizeof(KeywordHash[0]) ; ++i) KeywordHash[i] = -1; /* Set those hash entries corresponding to keywords. */ for (i = 0 ; i < KeywordTableSize ; ++i) { const unsigned char initialChar = KeywordTable[i].name[0]; if (initialChar != lastInitialChar) { KeywordHash[hashIndex(initialChar)] = i; lastInitialChar = initialChar; } } } /* vi:set tabstop=8 shiftwidth=4: */