cosmopolitan/third_party/ctags/eiffel.c
2022-11-13 13:26:28 -08:00

1374 lines
31 KiB
C

// clang-format off
/*
* $Id: eiffel.c 748 2009-11-06 02:44:42Z dhiebert $
*
* Copyright (c) 1998-2002, Darren Hiebert
*
* This source code is released for free distribution under the terms of the
* GNU General Public License.
*
* This module contains functions for generating tags for Eiffel language
* files.
*/
/*
* INCLUDE FILES
*/
#include "libc/fmt/conv.h"
#include "third_party/ctags/general.h" /* must always come first */
#ifdef TYPE_REFERENCE_TOOL
#include "libc/calls/calls.h"
#include "libc/calls/dprintf.h"
#include "libc/calls/weirdtypes.h"
#include "libc/fmt/fmt.h"
#include "libc/mem/fmt.h"
#include "libc/stdio/stdio.h"
#include "libc/stdio/temp.h"
#include "third_party/musl/tempnam.h"
#endif
#include "libc/mem/alg.h"
#include "libc/str/str.h"
#include "libc/limits.h"
#include "libc/sysv/consts/_posix.h"
#include "libc/sysv/consts/iov.h"
#include "libc/sysv/consts/limits.h"
#include "libc/sysv/consts/xopen.h"
#include "libc/thread/thread.h"
#include "libc/str/str.h" /* to define tolower () */
#include "libc/runtime/runtime.h"
#include "third_party/ctags/debug.h"
#include "third_party/ctags/keyword.h"
#include "third_party/ctags/routines.h"
#include "third_party/ctags/vstring.h"
#ifndef TYPE_REFERENCE_TOOL
#include "third_party/ctags/entry.h"
#include "third_party/ctags/options.h"
#include "third_party/ctags/parse.h"
#include "third_party/ctags/read.h"
#endif
/*
* MACROS
*/
#define isident(c) (isalnum(c) || (c) == '_')
#define isFreeOperatorChar(c) ((c) == '@' || (c) == '#' || \
(c) == '|' || (c) == '&')
#define isType(token,t) (boolean) ((token)->type == (t))
#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
/*
* DATA DECLARATIONS
*/
typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
/* Used to specify type of keyword.
*/
typedef enum eKeywordId {
KEYWORD_NONE = -1,
KEYWORD_alias, KEYWORD_all, KEYWORD_and,
KEYWORD_as, KEYWORD_assign, KEYWORD_attached,
KEYWORD_check, KEYWORD_class, KEYWORD_convert, KEYWORD_create,
KEYWORD_creation, KEYWORD_Current,
KEYWORD_debug, KEYWORD_deferred, KEYWORD_detachable, KEYWORD_do,
KEYWORD_else, KEYWORD_elseif, KEYWORD_end, KEYWORD_ensure,
KEYWORD_expanded, KEYWORD_export, KEYWORD_external,
KEYWORD_false, KEYWORD_feature, KEYWORD_from, KEYWORD_frozen,
KEYWORD_if, KEYWORD_implies,
KEYWORD_indexing, KEYWORD_infix, KEYWORD_inherit, KEYWORD_inspect,
KEYWORD_invariant, KEYWORD_is, KEYWORD_like, KEYWORD_local,
KEYWORD_loop, KEYWORD_not, KEYWORD_obsolete, KEYWORD_old, KEYWORD_once,
KEYWORD_or, KEYWORD_prefix, KEYWORD_redefine, KEYWORD_rename,
KEYWORD_require, KEYWORD_rescue, KEYWORD_Result, KEYWORD_retry,
KEYWORD_select, KEYWORD_separate, KEYWORD_strip, KEYWORD_then,
KEYWORD_true, KEYWORD_undefine, KEYWORD_unique, KEYWORD_until,
KEYWORD_variant, KEYWORD_when, KEYWORD_xor
} keywordId;
/* Used to determine whether keyword is valid for the token language and
* what its ID is.
*/
typedef struct sKeywordDesc {
const char *name;
keywordId id;
} keywordDesc;
typedef enum eTokenType {
TOKEN_UNDEFINED,
TOKEN_BANG,
TOKEN_CHARACTER,
TOKEN_CLOSE_BRACE,
TOKEN_CLOSE_BRACKET,
TOKEN_CLOSE_PAREN,
TOKEN_COLON,
TOKEN_COMMA,
TOKEN_CONSTRAINT,
TOKEN_DOT,
TOKEN_DOLLAR,
TOKEN_IDENTIFIER,
TOKEN_KEYWORD,
TOKEN_NUMERIC,
TOKEN_OPEN_BRACE,
TOKEN_OPEN_BRACKET,
TOKEN_OPEN_PAREN,
TOKEN_OPERATOR,
TOKEN_OTHER,
TOKEN_QUESTION,
TOKEN_SEMICOLON,
TOKEN_SEPARATOR,
TOKEN_STRING,
TOKEN_TILDE
} tokenType;
typedef struct sTokenInfo {
tokenType type;
keywordId keyword;
boolean isExported;
vString* string;
vString* className;
vString* featureName;
} tokenInfo;
/*
* DATA DEFINITIONS
*/
static langType Lang_eiffel;
#ifdef TYPE_REFERENCE_TOOL
static const char *FileName;
static FILE *File;
static int PrintClass;
static int PrintReferences;
static int SelfReferences;
static int Debug;
static stringList *GenericNames;
static stringList *ReferencedTypes;
#else
typedef enum {
EKIND_CLASS, EKIND_FEATURE, EKIND_LOCAL, EKIND_QUALIFIED_TAGS
} eiffelKind;
static kindOption EiffelKinds [] = {
{ TRUE, 'c', "class", "classes"},
{ TRUE, 'f', "feature", "features"},
{ FALSE, 'l', "local", "local entities"}
};
#endif
static jmp_buf Exception;
static const keywordDesc EiffelKeywordTable [] = {
/* keyword keyword ID */
{ "alias", KEYWORD_alias },
{ "all", KEYWORD_all },
{ "and", KEYWORD_and },
{ "as", KEYWORD_as },
{ "assign", KEYWORD_assign },
{ "attached", KEYWORD_attached },
{ "check", KEYWORD_check },
{ "class", KEYWORD_class },
{ "convert", KEYWORD_convert },
{ "create", KEYWORD_create },
{ "creation", KEYWORD_creation },
{ "current", KEYWORD_Current },
{ "debug", KEYWORD_debug },
{ "deferred", KEYWORD_deferred },
{ "detachable", KEYWORD_detachable },
{ "do", KEYWORD_do },
{ "else", KEYWORD_else },
{ "elseif", KEYWORD_elseif },
{ "end", KEYWORD_end },
{ "ensure", KEYWORD_ensure },
{ "expanded", KEYWORD_expanded },
{ "export", KEYWORD_export },
{ "external", KEYWORD_external },
{ "false", KEYWORD_false },
{ "feature", KEYWORD_feature },
{ "from", KEYWORD_from },
{ "frozen", KEYWORD_frozen },
{ "if", KEYWORD_if },
{ "implies", KEYWORD_implies },
{ "indexing", KEYWORD_indexing },
{ "infix", KEYWORD_infix },
{ "inherit", KEYWORD_inherit },
{ "inspect", KEYWORD_inspect },
{ "invariant", KEYWORD_invariant },
{ "is", KEYWORD_is },
{ "like", KEYWORD_like },
{ "local", KEYWORD_local },
{ "loop", KEYWORD_loop },
{ "not", KEYWORD_not },
{ "obsolete", KEYWORD_obsolete },
{ "old", KEYWORD_old },
{ "once", KEYWORD_once },
{ "or", KEYWORD_or },
{ "prefix", KEYWORD_prefix },
{ "redefine", KEYWORD_redefine },
{ "rename", KEYWORD_rename },
{ "require", KEYWORD_require },
{ "rescue", KEYWORD_rescue },
{ "result", KEYWORD_Result },
{ "retry", KEYWORD_retry },
{ "select", KEYWORD_select },
{ "separate", KEYWORD_separate },
{ "strip", KEYWORD_strip },
{ "then", KEYWORD_then },
{ "true", KEYWORD_true },
{ "undefine", KEYWORD_undefine },
{ "unique", KEYWORD_unique },
{ "until", KEYWORD_until },
{ "variant", KEYWORD_variant },
{ "when", KEYWORD_when },
{ "xor", KEYWORD_xor }
};
/*
* FUNCTION DEFINITIONS
*/
static void buildEiffelKeywordHash (void)
{
const size_t count = sizeof (EiffelKeywordTable) /
sizeof (EiffelKeywordTable [0]);
size_t i;
for (i = 0 ; i < count ; ++i)
{
const keywordDesc* const p = &EiffelKeywordTable [i];
addKeyword (p->name, Lang_eiffel, (int) p->id);
}
}
#ifdef TYPE_REFERENCE_TOOL
static void addGenericName (tokenInfo *const token)
{
vStringUpper (token->string);
if (vStringLength (token->string) > 0)
stringListAdd (GenericNames, vStringNewCopy (token->string));
}
static boolean isGeneric (tokenInfo *const token)
{
return (boolean) stringListHas (GenericNames, vStringValue (token->string));
}
static void reportType (tokenInfo *const token)
{
vStringUpper (token->string);
if (vStringLength (token->string) > 0 && ! isGeneric (token) &&
(SelfReferences || strcmp (vStringValue (
token->string), vStringValue (token->className)) != 0) &&
! stringListHas (ReferencedTypes, vStringValue (token->string)))
{
printf ("%s\n", vStringValue (token->string));
stringListAdd (ReferencedTypes, vStringNewCopy (token->string));
}
}
static int fileGetc (void)
{
int c = getc (File);
if (c == '\r')
{
c = getc (File);
if (c != '\n')
{
ungetc (c, File);
c = '\n';
}
}
if (Debug > 0 && c != EOF)
putc (c, errout);
return c;
}
static int fileUngetc (c)
{
return ungetc (c, File);
}
extern char *readLine (vString *const vLine, FILE *const fp)
{
return NULL;
}
#else
/*
* Tag generation functions
*/
static void makeEiffelClassTag (tokenInfo *const token)
{
if (EiffelKinds [EKIND_CLASS].enabled)
{
const char *const name = vStringValue (token->string);
tagEntryInfo e;
initTagEntry (&e, name);
e.kindName = EiffelKinds [EKIND_CLASS].name;
e.kind = EiffelKinds [EKIND_CLASS].letter;
makeTagEntry (&e);
}
vStringCopy (token->className, token->string);
}
static void makeEiffelFeatureTag (tokenInfo *const token)
{
if (EiffelKinds [EKIND_FEATURE].enabled &&
(token->isExported || Option.include.fileScope))
{
const char *const name = vStringValue (token->string);
tagEntryInfo e;
initTagEntry (&e, name);
e.isFileScope = (boolean) (! token->isExported);
e.kindName = EiffelKinds [EKIND_FEATURE].name;
e.kind = EiffelKinds [EKIND_FEATURE].letter;
e.extensionFields.scope [0] = EiffelKinds [EKIND_CLASS].name;
e.extensionFields.scope [1] = vStringValue (token->className);
makeTagEntry (&e);
if (Option.include.qualifiedTags)
{
vString* qualified = vStringNewInit (vStringValue (token->className));
vStringPut (qualified, '.');
vStringCat (qualified, token->string);
e.name = vStringValue (qualified);
makeTagEntry (&e);
vStringDelete (qualified);
}
}
vStringCopy (token->featureName, token->string);
}
static void makeEiffelLocalTag (tokenInfo *const token)
{
if (EiffelKinds [EKIND_LOCAL].enabled && Option.include.fileScope)
{
const char *const name = vStringValue (token->string);
vString* scope = vStringNew ();
tagEntryInfo e;
initTagEntry (&e, name);
e.isFileScope = TRUE;
e.kindName = EiffelKinds [EKIND_LOCAL].name;
e.kind = EiffelKinds [EKIND_LOCAL].letter;
vStringCopy (scope, token->className);
vStringPut (scope, '.');
vStringCat (scope, token->featureName);
e.extensionFields.scope [0] = EiffelKinds [EKIND_FEATURE].name;
e.extensionFields.scope [1] = vStringValue (scope);
makeTagEntry (&e);
vStringDelete (scope);
}
}
#endif
/*
* Parsing functions
*/
static int skipToCharacter (const int c)
{
int d;
do
{
d = fileGetc ();
} while (d != EOF && d != c);
return d;
}
/* If a numeric is passed in 'c', this is used as the first digit of the
* numeric being parsed.
*/
static vString *parseInteger (int c)
{
vString *string = vStringNew ();
if (c == '\0')
c = fileGetc ();
if (c == '-')
{
vStringPut (string, c);
c = fileGetc ();
}
else if (! isdigit (c))
c = fileGetc ();
while (c != EOF && (isdigit (c) || c == '_'))
{
vStringPut (string, c);
c = fileGetc ();
}
vStringTerminate (string);
fileUngetc (c);
return string;
}
static vString *parseNumeric (int c)
{
vString *string = vStringNew ();
vString *integer = parseInteger (c);
vStringCopy (string, integer);
vStringDelete (integer);
c = fileGetc ();
if (c == '.')
{
integer = parseInteger ('\0');
vStringPut (string, c);
vStringCat (string, integer);
vStringDelete (integer);
c = fileGetc ();
}
if (tolower (c) == 'e')
{
integer = parseInteger ('\0');
vStringPut (string, c);
vStringCat (string, integer);
vStringDelete (integer);
}
else if (!isspace (c))
fileUngetc (c);
vStringTerminate (string);
return string;
}
static int parseEscapedCharacter (void)
{
int d = '\0';
int c = fileGetc ();
switch (c)
{
case 'A': d = '@'; break;
case 'B': d = '\b'; break;
case 'C': d = '^'; break;
case 'D': d = '$'; break;
case 'F': d = '\f'; break;
case 'H': d = '\\'; break;
case 'L': d = '~'; break;
case 'N': d = '\n'; break;
#ifdef QDOS
case 'Q': d = 0x9F; break;
#else
case 'Q': d = '`'; break;
#endif
case 'R': d = '\r'; break;
case 'S': d = '#'; break;
case 'T': d = '\t'; break;
case 'U': d = '\0'; break;
case 'V': d = '|'; break;
case '%': d = '%'; break;
case '\'': d = '\''; break;
case '"': d = '"'; break;
case '(': d = '['; break;
case ')': d = ']'; break;
case '<': d = '{'; break;
case '>': d = '}'; break;
case '\n': skipToCharacter ('%'); break;
case '/':
{
vString *string = parseInteger ('\0');
const char *value = vStringValue (string);
const unsigned long ascii = atol (value);
vStringDelete (string);
c = fileGetc ();
if (c == '/' && ascii < 256)
d = ascii;
break;
}
default: break;
}
return d;
}
static int parseCharacter (void)
{
int c = fileGetc ();
int result = c;
if (c == '%')
result = parseEscapedCharacter ();
c = fileGetc ();
if (c != '\'')
skipToCharacter ('\n');
return result;
}
static void parseString (vString *const string)
{
boolean verbatim = FALSE;
boolean align = FALSE;
boolean end = FALSE;
vString *verbatimCloser = vStringNew ();
vString *lastLine = vStringNew ();
int prev = '\0';
int c;
while (! end)
{
c = fileGetc ();
if (c == EOF)
end = TRUE;
else if (c == '"')
{
if (! verbatim)
end = TRUE;
else
end = (boolean) (strcmp (vStringValue (lastLine),
vStringValue (verbatimCloser)) == 0);
}
else if (c == '\n')
{
if (verbatim)
vStringClear (lastLine);
if (prev == '[' /* || prev == '{' */)
{
verbatim = TRUE;
vStringClear (verbatimCloser);
vStringClear (lastLine);
if (prev == '{')
vStringPut (verbatimCloser, '}');
else
{
vStringPut (verbatimCloser, ']');
align = TRUE;
}
vStringNCat (verbatimCloser, string, vStringLength (string) - 1);
vStringClear (string);
}
if (verbatim && align)
{
do
c = fileGetc ();
while (isspace (c));
}
}
else if (c == '%')
c = parseEscapedCharacter ();
if (! end)
{
vStringPut (string, c);
if (verbatim)
{
vStringPut (lastLine, c);
vStringTerminate (lastLine);
}
prev = c;
}
}
vStringTerminate (string);
vStringDelete (lastLine);
vStringDelete (verbatimCloser);
}
/* Read a C identifier beginning with "firstChar" and places it into "name".
*/
static void parseIdentifier (vString *const string, const int firstChar)
{
int c = firstChar;
do
{
vStringPut (string, c);
c = fileGetc ();
} while (isident (c));
vStringTerminate (string);
if (!isspace (c))
fileUngetc (c); /* unget non-identifier character */
}
static void parseFreeOperator (vString *const string, const int firstChar)
{
int c = firstChar;
do
{
vStringPut (string, c);
c = fileGetc ();
} while (c > ' ');
vStringTerminate (string);
if (!isspace (c))
fileUngetc (c); /* unget non-identifier character */
}
static void copyToken (tokenInfo* dst, const tokenInfo *src)
{
dst->type = src->type;
dst->keyword = src->keyword;
dst->isExported = src->isExported;
vStringCopy (dst->string, src->string);
vStringCopy (dst->className, src->className);
vStringCopy (dst->featureName, src->featureName);
}
static tokenInfo *newToken (void)
{
tokenInfo *const token = xMalloc (1, tokenInfo);
token->type = TOKEN_UNDEFINED;
token->keyword = KEYWORD_NONE;
token->isExported = TRUE;
token->string = vStringNew ();
token->className = vStringNew ();
token->featureName = vStringNew ();
return token;
}
static void deleteToken (tokenInfo *const token)
{
vStringDelete (token->string);
vStringDelete (token->className);
vStringDelete (token->featureName);
eFree (token);
}
static void readToken (tokenInfo *const token)
{
int c;
token->type = TOKEN_UNDEFINED;
token->keyword = KEYWORD_NONE;
vStringClear (token->string);
getNextChar:
do
c = fileGetc ();
while (c == '\t' || c == ' ' || c == '\n');
switch (c)
{
case EOF: longjmp (Exception, (int)ExceptionEOF); break;
case ';': token->type = TOKEN_SEMICOLON; break;
case '!': token->type = TOKEN_BANG; break;
case '}': token->type = TOKEN_CLOSE_BRACE; break;
case ']': token->type = TOKEN_CLOSE_BRACKET; break;
case ')': token->type = TOKEN_CLOSE_PAREN; break;
case ',': token->type = TOKEN_COMMA; break;
case '$': token->type = TOKEN_DOLLAR; break;
case '.': token->type = TOKEN_DOT; break;
case '{': token->type = TOKEN_OPEN_BRACE; break;
case '[': token->type = TOKEN_OPEN_BRACKET; break;
case '(': token->type = TOKEN_OPEN_PAREN; break;
case '~': token->type = TOKEN_TILDE; break;
case '+':
case '*':
case '^':
case '=': token->type = TOKEN_OPERATOR; break;
case '-':
c = fileGetc ();
if (c == '>')
token->type = TOKEN_CONSTRAINT;
else if (c == '-') /* is this the start of a comment? */
{
skipToCharacter ('\n');
goto getNextChar;
}
else
{
if (!isspace (c))
fileUngetc (c);
token->type = TOKEN_OPERATOR;
}
break;
case '?':
case ':':
{
int c2 = fileGetc ();
if (c2 == '=')
token->type = TOKEN_OPERATOR;
else
{
if (!isspace (c2))
fileUngetc (c2);
if (c == ':')
token->type = TOKEN_COLON;
else
token->type = TOKEN_QUESTION;
}
break;
}
case '<':
c = fileGetc ();
if (c != '=' && c != '>' && !isspace (c))
fileUngetc (c);
token->type = TOKEN_OPERATOR;
break;
case '>':
c = fileGetc ();
if (c != '=' && c != '>' && !isspace (c))
fileUngetc (c);
token->type = TOKEN_OPERATOR;
break;
case '/':
c = fileGetc ();
if (c != '/' && c != '=' && !isspace (c))
fileUngetc (c);
token->type = TOKEN_OPERATOR;
break;
case '\\':
c = fileGetc ();
if (c != '\\' && !isspace (c))
fileUngetc (c);
token->type = TOKEN_OPERATOR;
break;
case '"':
token->type = TOKEN_STRING;
parseString (token->string);
break;
case '\'':
token->type = TOKEN_CHARACTER;
parseCharacter ();
break;
default:
if (isalpha (c))
{
parseIdentifier (token->string, c);
token->keyword = analyzeToken (token->string, Lang_eiffel);
if (isKeyword (token, KEYWORD_NONE))
token->type = TOKEN_IDENTIFIER;
else
token->type = TOKEN_KEYWORD;
}
else if (isdigit (c))
{
vString* numeric = parseNumeric (c);
vStringCat (token->string, numeric);
vStringDelete (numeric);
token->type = TOKEN_NUMERIC;
}
else if (isFreeOperatorChar (c))
{
parseFreeOperator (token->string, c);
token->type = TOKEN_OPERATOR;
}
else
{
token->type = TOKEN_UNDEFINED;
Assert (! isType (token, TOKEN_UNDEFINED));
}
break;
}
}
/*
* Scanning functions
*/
static boolean isIdentifierMatch (
const tokenInfo *const token, const char *const name)
{
return (boolean) (isType (token, TOKEN_IDENTIFIER) &&
strcasecmp (vStringValue (token->string), name) == 0);
}
static void findToken (tokenInfo *const token, const tokenType type)
{
while (! isType (token, type))
readToken (token);
}
static void findKeyword (tokenInfo *const token, const keywordId keyword)
{
while (! isKeyword (token, keyword))
readToken (token);
}
static boolean parseType (tokenInfo *const token);
static void parseGeneric (tokenInfo *const token, boolean declaration __unused)
{
unsigned int depth = 0;
#ifdef TYPE_REFERENCE_TOOL
boolean constraint = FALSE;
#endif
Assert (isType (token, TOKEN_OPEN_BRACKET));
do
{
if (isType (token, TOKEN_OPEN_BRACKET))
{
++depth;
readToken (token);
}
else if (isType (token, TOKEN_CLOSE_BRACKET))
{
--depth;
readToken (token);
}
#ifdef TYPE_REFERENCE_TOOL
else if (declaration)
{
boolean advanced = FALSE;
if (depth == 1)
{
if (isType (token, TOKEN_CONSTRAINT))
constraint = TRUE;
else if (isKeyword (token, KEYWORD_create))
findKeyword (token, KEYWORD_end);
else if (isType (token, TOKEN_IDENTIFIER))
{
if (constraint)
advanced = parseType (token);
else
addGenericName (token);
constraint = FALSE;
}
}
else if (isType (token, TOKEN_IDENTIFIER))
advanced = parseType (token);
if (! advanced)
readToken (token);
}
#endif
else
parseType (token);
} while (depth > 0);
}
static boolean parseType (tokenInfo *const token)
{
tokenInfo* const id = newToken ();
copyToken (id, token);
readToken (token);
if (isType (token, TOKEN_COLON)) /* check for "{entity: TYPE}" */
{
readToken (id);
readToken (token);
}
if (isKeyword (id, KEYWORD_like))
{
if (isType (token, TOKEN_IDENTIFIER) ||
isKeyword (token, KEYWORD_Current))
readToken (token);
}
else
{
if (isKeyword (id, KEYWORD_attached) ||
isKeyword (id, KEYWORD_detachable) ||
isKeyword (id, KEYWORD_expanded))
{
copyToken (id, token);
readToken (token);
}
if (isType (id, TOKEN_IDENTIFIER))
{
#ifdef TYPE_REFERENCE_TOOL
reportType (id);
#endif
if (isType (token, TOKEN_OPEN_BRACKET))
parseGeneric (token, FALSE);
else if ((strcmp ("BIT", vStringValue (id->string)) == 0))
readToken (token); /* read token after number of bits */
}
}
deleteToken (id);
return TRUE;
}
static void parseEntityType (tokenInfo *const token)
{
Assert (isType (token, TOKEN_COLON));
readToken (token);
if (isType (token, TOKEN_BANG) || isType (token, TOKEN_QUESTION))
readToken (token); /* skip over '!' or '?' */
parseType (token);
}
static void parseLocal (tokenInfo *const token)
{
Assert (isKeyword (token, KEYWORD_local));
readToken (token);
/* Check keyword first in case local clause is empty
*/
while (! isKeyword (token, KEYWORD_do) &&
! isKeyword (token, KEYWORD_once))
{
#ifndef TYPE_REFERENCE_TOOL
if (isType (token, TOKEN_IDENTIFIER))
makeEiffelLocalTag (token);
#endif
readToken (token);
if (isType (token, TOKEN_COLON))
parseEntityType (token);
}
}
static void findFeatureEnd (tokenInfo *const token)
{
boolean isFound = isKeyword (token, KEYWORD_is);
if (isFound)
readToken (token);
switch (token->keyword)
{
case KEYWORD_deferred:
case KEYWORD_do:
case KEYWORD_external:
case KEYWORD_local:
case KEYWORD_obsolete:
case KEYWORD_once:
case KEYWORD_require:
{
int depth = 1;
while (depth > 0)
{
#ifdef TYPE_REFERENCE_TOOL
if (isType (token, TOKEN_OPEN_BRACE))
{
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
parseType (token);
}
else if (isType (token, TOKEN_BANG))
{
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
parseType (token);
if (isType (token, TOKEN_BANG))
readToken (token);
}
else
#endif
switch (token->keyword)
{
case KEYWORD_check:
case KEYWORD_debug:
case KEYWORD_from:
case KEYWORD_if:
case KEYWORD_inspect:
++depth;
break;
case KEYWORD_local:
parseLocal (token);
break;
case KEYWORD_end:
--depth;
break;
default:
break;
}
readToken (token);
}
break;
}
default:
/* is this a manifest constant? */
if (isFound || isType (token, TOKEN_OPERATOR)) {
if (isType (token, TOKEN_OPERATOR))
readToken (token);
readToken (token);
}
break;
}
}
static boolean readFeatureName (tokenInfo *const token)
{
boolean isFeatureName = FALSE;
if (isKeyword (token, KEYWORD_frozen))
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
isFeatureName = TRUE;
else if (isKeyword (token, KEYWORD_assign)) /* legacy code */
isFeatureName = TRUE;
else if (isKeyword (token, KEYWORD_infix) ||
isKeyword (token, KEYWORD_prefix))
{
readToken (token);
if (isType (token, TOKEN_STRING))
isFeatureName = TRUE;
}
return isFeatureName;
}
static void parseArguments (tokenInfo *const token)
{
#ifndef TYPE_REFERENCE_TOOL
findToken (token, TOKEN_CLOSE_PAREN);
readToken (token);
#else
Assert (isType (token, TOKEN_OPEN_PAREN));
readToken (token);
do
{
if (isType (token, TOKEN_COLON))
parseEntityType (token);
else
readToken (token);
} while (! isType (token, TOKEN_CLOSE_PAREN));
readToken (token);
#endif
}
static boolean parseFeature (tokenInfo *const token)
{
boolean found = FALSE;
while (readFeatureName (token))
{
found = TRUE;
#ifndef TYPE_REFERENCE_TOOL
makeEiffelFeatureTag (token);
#endif
readToken (token);
if (isType (token, TOKEN_COMMA))
readToken (token);
}
if (found)
{
if (isKeyword (token, KEYWORD_alias)) {
readToken (token);
#ifndef TYPE_REFERENCE_TOOL
if (isType (token, TOKEN_STRING))
makeEiffelFeatureTag (token);
#endif
readToken (token);
}
if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
parseArguments (token);
if (isType (token, TOKEN_COLON)) /* a query? */
parseEntityType (token);
if (isKeyword (token, KEYWORD_assign))
{
readToken (token);
readToken (token);
}
if (isKeyword (token, KEYWORD_obsolete))
{
readToken (token);
if (isType (token, TOKEN_STRING))
readToken (token);
}
findFeatureEnd (token);
}
return found;
}
static void parseExport (tokenInfo *const token)
{
token->isExported = TRUE;
readToken (token);
if (isType (token, TOKEN_OPEN_BRACE))
{
token->isExported = FALSE;
while (! isType (token, TOKEN_CLOSE_BRACE))
{
if (isType (token, TOKEN_IDENTIFIER))
token->isExported |= !isIdentifierMatch (token, "NONE");
readToken (token);
}
readToken (token);
}
}
static void parseFeatureClauses (tokenInfo *const token)
{
Assert (isKeyword (token, KEYWORD_feature));
do
{
if (isKeyword (token, KEYWORD_feature))
parseExport (token);
if (! isKeyword (token, KEYWORD_feature) &&
! isKeyword (token, KEYWORD_invariant) &&
! isKeyword (token, KEYWORD_indexing))
{
if (! parseFeature (token))
readToken (token);
}
} while (! isKeyword (token, KEYWORD_end) &&
! isKeyword (token, KEYWORD_invariant) &&
! isKeyword (token, KEYWORD_indexing));
}
static void parseRename (tokenInfo *const token)
{
Assert (isKeyword (token, KEYWORD_rename));
do {
readToken (token);
if (readFeatureName (token))
{
readToken (token);
if (isKeyword (token, KEYWORD_as))
{
readToken (token);
if (readFeatureName (token))
{
#ifndef TYPE_REFERENCE_TOOL
makeEiffelFeatureTag (token); /* renamed feature */
#endif
readToken (token);
}
}
}
} while (isType (token, TOKEN_COMMA));
}
static void parseInherit (tokenInfo *const token)
{
Assert (isKeyword (token, KEYWORD_inherit));
readToken (token);
while (isType (token, TOKEN_IDENTIFIER))
{
parseType (token);
if (isType (token, TOKEN_KEYWORD))
{
switch (token->keyword) /* check for feature adaptation */
{
case KEYWORD_rename:
parseRename (token);
case KEYWORD_export:
case KEYWORD_undefine:
case KEYWORD_redefine:
case KEYWORD_select:
findKeyword (token, KEYWORD_end);
readToken (token);
break;
case KEYWORD_end:
readToken (token);
break;
default: break;
}
}
if (isType (token, TOKEN_SEMICOLON))
readToken (token);
}
}
static void parseConvert (tokenInfo *const token)
{
Assert (isKeyword (token, KEYWORD_convert));
do
{
readToken (token);
if (! isType (token, TOKEN_IDENTIFIER))
break;
else if (isType (token, TOKEN_OPEN_PAREN))
{
while (! isType (token, TOKEN_CLOSE_PAREN))
readToken (token);
}
else if (isType (token, TOKEN_COLON))
{
readToken (token);
if (! isType (token, TOKEN_OPEN_BRACE))
break;
else while (! isType (token, TOKEN_CLOSE_BRACE))
readToken (token);
}
} while (isType (token, TOKEN_COMMA));
}
static void parseClass (tokenInfo *const token)
{
Assert (isKeyword (token, KEYWORD_class));
readToken (token);
if (isType (token, TOKEN_IDENTIFIER))
{
#ifndef TYPE_REFERENCE_TOOL
makeEiffelClassTag (token);
readToken (token);
#else
vStringCopy (token->className, token->string);
vStringUpper (token->className);
if (PrintClass)
puts (vStringValue (token->className));
if (! PrintReferences)
exit (0);
readToken (token);
#endif
}
do
{
if (isType (token, TOKEN_OPEN_BRACKET))
parseGeneric (token, TRUE);
else if (! isType (token, TOKEN_KEYWORD))
readToken (token);
else switch (token->keyword)
{
case KEYWORD_inherit: parseInherit (token); break;
case KEYWORD_feature: parseFeatureClauses (token); break;
case KEYWORD_convert: parseConvert (token); break;
default: readToken (token); break;
}
} while (! isKeyword (token, KEYWORD_end));
}
static void initialize (const langType language)
{
Lang_eiffel = language;
buildEiffelKeywordHash ();
}
static void findEiffelTags (void)
{
tokenInfo *const token = newToken ();
exception_t exception;
exception = (exception_t) (setjmp (Exception));
while (exception == ExceptionNone)
{
findKeyword (token, KEYWORD_class);
parseClass (token);
}
deleteToken (token);
}
#ifndef TYPE_REFERENCE_TOOL
extern parserDefinition* EiffelParser (void)
{
static const char *const extensions [] = { "e", NULL };
parserDefinition* def = parserNew ("Eiffel");
def->kinds = EiffelKinds;
def->kindCount = KIND_COUNT (EiffelKinds);
def->extensions = extensions;
def->parser = findEiffelTags;
def->initialize = initialize;
return def;
}
#else
static void findReferences (void)
{
ReferencedTypes = stringListNew ();
GenericNames = stringListNew ();
initialize (0);
findEiffelTags ();
stringListDelete (GenericNames);
GenericNames = NULL;
stringListDelete (ReferencedTypes);
ReferencedTypes = NULL;
}
static const char *const Usage =
"Prints names of types referenced by an Eiffel language file.\n"
"\n"
"Usage: %s [-cdrs] [file_name | -]\n"
"\n"
"Options:\n"
" -c Print class name of current file (on first line of output).\n"
" -d Enable debug output.\n"
" -r Print types referenced by current file (default unless -c).\n"
" -s Include self-references.\n"
"\n";
extern int main (int argc, char** argv)
{
int i;
for (i = 1 ; argv [i] != NULL ; ++i)
{
const char *const arg = argv [i];
if (arg [0] == '-')
{
int j;
if (arg [1] == '\0')
{
File = stdin;
FileName = "stdin";
}
else for (j = 1 ; arg [j] != '\0' ; ++j) switch (arg [j])
{
case 'c': PrintClass = 1; break;
case 'r': PrintReferences = 1; break;
case 's': SelfReferences = 1; break;
case 'd': Debug = 1; break;
default:
fprintf (errout, "%s: unknown option: %c\n", argv [0], arg [1]);
fprintf (errout, Usage, argv [0]);
exit (1);
break;
}
}
else if (File != NULL)
{
fprintf (errout, Usage, argv [0]);
exit (1);
}
else
{
FileName = arg;
File = fopen (FileName, "r");
if (File == NULL)
{
perror (argv [0]);
exit (1);
}
}
}
if (! PrintClass)
PrintReferences = 1;
if (File == NULL)
{
fprintf (errout, Usage, argv [0]);
exit (1);
}
else
{
findReferences ();
fclose (File);
}
return 0;
}
#endif
/* vi:set tabstop=4 shiftwidth=4: */