eiffel.c

来自「Exuberant Ctags is a multilanguage reimp」· C语言 代码 · 共 1,320 行 · 第 1/2 页

C
1,320
字号
/**   $Id: eiffel.c,v 1.18 2006/05/30 04:37:12 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){	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){	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 + =
减小字号Ctrl + -
显示快捷键?