📄 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_TOOLstatic const char *FileName;static FILE *File;static int PrintClass;static int PrintReferences;static int SelfReferences;static int Debug;static stringList *GenericNames;static stringList *ReferencedTypes;#elsetypedef 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"}};#endifstatic 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_TOOLstatic 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 (); 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); 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 = NULL; vString *lastLine = NULL; 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; verbatimCloser = vStringNew (); lastLine = vStringNew (); 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);}/* 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 keywordId analyzeToken (vString *const name){ static vString *keyword = NULL; keywordId id; if (keyword == NULL) keyword = vStringNew (); vStringCopyToLower (keyword, name); id = (keywordId) lookupKeyword (vStringValue (keyword), Lang_eiffel); return id;}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_BANG; break; case '$': token->type = TOKEN_DOLLAR; break; case '(': token->type = TOKEN_OPEN_PAREN; break; case ')': token->type = TOKEN_CLOSE_PAREN; break; case ',': token->type = TOKEN_COMMA; break; case '.': token->type = TOKEN_DOT; break; case ';': goto getNextChar; case '[': token->type = TOKEN_OPEN_BRACKET; break; case ']': token->type = TOKEN_CLOSE_BRACKET; break; case '{': token->type = TOKEN_OPEN_BRACE; break; case '}': token->type = TOKEN_CLOSE_BRACE; 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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -