📄 eiffel.c
字号:
/*
* $Id: eiffel.c,v 1.16 2003/07/17 03:08:23 darren Exp $
*
* 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 "general.h" /* must always come first */
#ifdef TYPE_REFERENCE_TOOL
#include <stdio.h>
#endif
#include <string.h>
#include <limits.h>
#include <ctype.h> /* to define tolower () */
#include <setjmp.h>
#include "debug.h"
#include "keyword.h"
#include "routines.h"
#include "vstring.h"
#ifndef TYPE_REFERENCE_TOOL
#include "entry.h"
#include "options.h"
#include "parse.h"
#include "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_check,
KEYWORD_class, KEYWORD_create, KEYWORD_creation, KEYWORD_Current,
KEYWORD_debug, KEYWORD_deferred, 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_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 langType Lang_eiffel;
static jmp_buf Exception;
static const keywordDesc EiffelKeywordTable [] = {
/* keyword keyword ID */
{ "alias", KEYWORD_alias },
{ "all", KEYWORD_all },
{ "and", KEYWORD_and },
{ "as", KEYWORD_as },
{ "check", KEYWORD_check },
{ "class", KEYWORD_class },
{ "create", KEYWORD_create },
{ "creation", KEYWORD_creation },
{ "current", KEYWORD_Current },
{ "debug", KEYWORD_debug },
{ "deferred", KEYWORD_deferred },
{ "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)
{
if (vStringLength (token->string) > 0)
stringListAdd (GenericNames, vStringNewCopy (token->string));
}
static boolean isGeneric (tokenInfo *const token)
{
return (boolean) stringListHasInsensitive (
GenericNames, vStringValue (token->string));
}
static void reportType (tokenInfo *const token)
{
if (vStringLength (token->string) > 0 && ! isGeneric (token) &&
(SelfReferences || strcasecmp (vStringValue (
token->string), vStringValue (token->className)) != 0) &&
! stringListHasInsensitive (
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)
{
static vString *string = NULL;
if (string == NULL)
string = vStringNew ();
vStringClear (string);
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)
{
static vString *string = NULL;
if (string == NULL)
string = vStringNew ();
vStringCopy (string, parseInteger (c));
c = fileGetc ();
if (c == '.')
{
vStringPut (string, c);
vStringCat (string, parseInteger ('\0'));
c = fileGetc ();
}
if (tolower (c) == 'e')
{
vStringPut (string, c);
vStringCat (string, parseInteger ('\0'));
}
else if (!isspace (c))
fileUngetc (c);
vStringTerminate (string);
return string;
}
static int parseEscapedCharacter (void)
{
int d = '\0';
int c = fileGetc ();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -