xref: /Universal-ctags/parsers/eiffel.c (revision 14781660f5ef320b4f27fe28dbf3a87dc47481d3)
1 /*
2 *   Copyright (c) 1998-2002, Darren Hiebert
3 *
4 *   This source code is released for free distribution under the terms of the
5 *   GNU General Public License version 2 or (at your option) any later version.
6 *
7 *   This module contains functions for generating tags for Eiffel language
8 *   files.
9 */
10 
11 /*
12 *   INCLUDE FILES
13 */
14 #include "general.h"  /* must always come first */
15 
16 #include <string.h>
17 #include <limits.h>
18 #include <ctype.h>  /* to define tolower () */
19 
20 #include "debug.h"
21 #include "keyword.h"
22 #include "routines.h"
23 #include "vstring.h"
24 #include "entry.h"
25 #include "parse.h"
26 #include "read.h"
27 #include "xtag.h"
28 
29 /*
30 *   MACROS
31 */
32 #define isident(c)            (isalnum(c) || (c) == '_')
33 #define isFreeOperatorChar(c) ((c) == '@' || (c) == '#' || \
34                                (c) == '|' || (c) == '&')
35 #define isType(token,t)       (bool) ((token)->type == (t))
36 #define isKeyword(token,k)    (bool) ((token)->keyword == (k))
37 
38 /*
39 *   DATA DECLARATIONS
40 */
41 
42 /*  Used to specify type of keyword.
43  */
44 enum eKeywordId {
45 	KEYWORD_across,
46 	KEYWORD_alias,
47 	KEYWORD_all,
48 	KEYWORD_and,
49 	KEYWORD_as,
50 	KEYWORD_assign,
51 	KEYWORD_attached,
52 	KEYWORD_attribute,
53 	KEYWORD_check,
54 	KEYWORD_class,
55 	KEYWORD_convert,
56 	KEYWORD_create,
57 	KEYWORD_creation,
58 	KEYWORD_Current,
59 	KEYWORD_debug,
60 	KEYWORD_deferred,
61 	KEYWORD_detachable,
62 	KEYWORD_do,
63 	KEYWORD_else,
64 	KEYWORD_elseif,
65 	KEYWORD_end,
66 	KEYWORD_ensure,
67 	KEYWORD_expanded,
68 	KEYWORD_export,
69 	KEYWORD_external,
70 	KEYWORD_false,
71 	KEYWORD_feature,
72 	KEYWORD_from,
73 	KEYWORD_frozen,
74 	KEYWORD_if,
75 	KEYWORD_implies,
76 	KEYWORD_infix,
77 	KEYWORD_inherit,
78 	KEYWORD_inspect,
79 	KEYWORD_invariant,
80 	KEYWORD_is,
81 	KEYWORD_like,
82 	KEYWORD_local,
83 	KEYWORD_loop,
84 	KEYWORD_not,
85 	KEYWORD_note,
86 	KEYWORD_obsolete,
87 	KEYWORD_old,
88 	KEYWORD_once,
89 	KEYWORD_or,
90 	KEYWORD_prefix,
91 	KEYWORD_redefine,
92 	KEYWORD_rename,
93 	KEYWORD_require,
94 	KEYWORD_rescue,
95 	KEYWORD_Result,
96 	KEYWORD_retry,
97 	KEYWORD_select,
98 	KEYWORD_separate,
99 	KEYWORD_strip,
100 	KEYWORD_then,
101 	KEYWORD_true,
102 	KEYWORD_undefine,
103 	KEYWORD_unique,
104 	KEYWORD_until,
105 	KEYWORD_variant,
106 	KEYWORD_when,
107 	KEYWORD_xor
108 };
109 typedef int keywordId; /* to allow KEYWORD_NONE */
110 
111 typedef enum eTokenType {
112 	TOKEN_EOF,
113 	TOKEN_UNDEFINED,
114 	TOKEN_BANG,
115 	TOKEN_CHARACTER,
116 	TOKEN_CLOSE_BRACE,
117 	TOKEN_CLOSE_BRACKET,
118 	TOKEN_CLOSE_PAREN,
119 	TOKEN_COLON,
120 	TOKEN_COMMA,
121 	TOKEN_CONSTRAINT,
122 	TOKEN_DOT,
123 	TOKEN_DOLLAR,
124 	TOKEN_IDENTIFIER,
125 	TOKEN_KEYWORD,
126 	TOKEN_NUMERIC,
127 	TOKEN_OPEN_BRACE,
128 	TOKEN_OPEN_BRACKET,
129 	TOKEN_OPEN_PAREN,
130 	TOKEN_OPERATOR,
131 	TOKEN_QUESTION,
132 	TOKEN_SEMICOLON,
133 	TOKEN_STRING,
134 	TOKEN_TILDE
135 } tokenType;
136 
137 typedef struct sTokenInfo {
138 	tokenType type;
139 	keywordId keyword;
140 	bool   isExported;
141 	vString*  string;
142 	vString*  className;
143 	vString*  featureName;
144 } tokenInfo;
145 
146 /*
147 *   DATA DEFINITIONS
148 */
149 
150 static langType Lang_eiffel;
151 
152 typedef enum {
153 	EKIND_CLASS, EKIND_FEATURE, EKIND_LOCAL, EKIND_QUALIFIED_TAGS
154 } eiffelKind;
155 
156 static kindDefinition EiffelKinds [] = {
157 	{ true,  'c', "class",   "classes"},
158 	{ true,  'f', "feature", "features"},
159 	{ false, 'l', "local",   "local entities"}
160 };
161 
162 static const keywordTable EiffelKeywordTable [] = {
163 	/* keyword          keyword ID */
164 	{ "across",         KEYWORD_across     },
165 	{ "alias",          KEYWORD_alias      },
166 	{ "all",            KEYWORD_all        },
167 	{ "and",            KEYWORD_and        },
168 	{ "as",             KEYWORD_as         },
169 	{ "assign",         KEYWORD_assign     },
170 	{ "attached",       KEYWORD_attached   },
171 	{ "attribute",      KEYWORD_attribute  },
172 	{ "check",          KEYWORD_check      },
173 	{ "class",          KEYWORD_class      },
174 	{ "convert",        KEYWORD_convert    },
175 	{ "create",         KEYWORD_create     },
176 	{ "creation",       KEYWORD_creation   },
177 	{ "current",        KEYWORD_Current    },
178 	{ "debug",          KEYWORD_debug      },
179 	{ "deferred",       KEYWORD_deferred   },
180 	{ "detachable",     KEYWORD_detachable },
181 	{ "do",             KEYWORD_do         },
182 	{ "else",           KEYWORD_else       },
183 	{ "elseif",         KEYWORD_elseif     },
184 	{ "end",            KEYWORD_end        },
185 	{ "ensure",         KEYWORD_ensure     },
186 	{ "expanded",       KEYWORD_expanded   },
187 	{ "export",         KEYWORD_export     },
188 	{ "external",       KEYWORD_external   },
189 	{ "false",          KEYWORD_false      },
190 	{ "feature",        KEYWORD_feature    },
191 	{ "from",           KEYWORD_from       },
192 	{ "frozen",         KEYWORD_frozen     },
193 	{ "if",             KEYWORD_if         },
194 	{ "implies",        KEYWORD_implies    },
195 	{ "indexing",       KEYWORD_note       },
196 	{ "infix",          KEYWORD_infix      },
197 	{ "inherit",        KEYWORD_inherit    },
198 	{ "insert",         KEYWORD_inherit    },
199 	{ "inspect",        KEYWORD_inspect    },
200 	{ "invariant",      KEYWORD_invariant  },
201 	{ "is",             KEYWORD_is         },
202 	{ "like",           KEYWORD_like       },
203 	{ "local",          KEYWORD_local      },
204 	{ "loop",           KEYWORD_loop       },
205 	{ "not",            KEYWORD_not        },
206 	{ "note",           KEYWORD_note       },
207 	{ "obsolete",       KEYWORD_obsolete   },
208 	{ "old",            KEYWORD_old        },
209 	{ "once",           KEYWORD_once       },
210 	{ "or",             KEYWORD_or         },
211 	{ "prefix",         KEYWORD_prefix     },
212 	{ "redefine",       KEYWORD_redefine   },
213 	{ "rename",         KEYWORD_rename     },
214 	{ "require",        KEYWORD_require    },
215 	{ "rescue",         KEYWORD_rescue     },
216 	{ "result",         KEYWORD_Result     },
217 	{ "retry",          KEYWORD_retry      },
218 	{ "select",         KEYWORD_select     },
219 	{ "separate",       KEYWORD_separate   },
220 	{ "strip",          KEYWORD_strip      },
221 	{ "then",           KEYWORD_then       },
222 	{ "true",           KEYWORD_true       },
223 	{ "undefine",       KEYWORD_undefine   },
224 	{ "unique",         KEYWORD_unique     },
225 	{ "until",          KEYWORD_until      },
226 	{ "variant",        KEYWORD_variant    },
227 	{ "when",           KEYWORD_when       },
228 	{ "xor",            KEYWORD_xor        }
229 };
230 
231 /*
232 *   FUNCTION DEFINITIONS
233 */
234 
235 /*
236 *   Tag generation functions
237 */
238 
makeEiffelClassTag(tokenInfo * const token)239 static void makeEiffelClassTag (tokenInfo *const token)
240 {
241 	if (EiffelKinds [EKIND_CLASS].enabled)
242 	{
243 		const char *const name = vStringValue (token->string);
244 		tagEntryInfo e;
245 
246 		initTagEntry (&e, name, EKIND_CLASS);
247 
248 		makeTagEntry (&e);
249 	}
250 	vStringCopy (token->className, token->string);
251 }
252 
makeEiffelFeatureTag(tokenInfo * const token)253 static void makeEiffelFeatureTag (tokenInfo *const token)
254 {
255 	if (EiffelKinds [EKIND_FEATURE].enabled  &&
256 		(token->isExported  ||  isXtagEnabled(XTAG_FILE_SCOPE)))
257 	{
258 		const char *const name = vStringValue (token->string);
259 		tagEntryInfo e;
260 
261 		initTagEntry (&e, name, EKIND_FEATURE);
262 
263 		e.isFileScope = (bool) (! token->isExported);
264 		if (e.isFileScope)
265 			markTagExtraBit (&e, XTAG_FILE_SCOPE);
266 		e.extensionFields.scopeKindIndex = EKIND_CLASS;
267 		e.extensionFields.scopeName = vStringValue (token->className);
268 
269 		makeTagEntry (&e);
270 
271 		if (isXtagEnabled(XTAG_QUALIFIED_TAGS))
272 		{
273 			vString* qualified = vStringNewInit (vStringValue (token->className));
274 			vStringPut (qualified, '.');
275 			vStringCat (qualified, token->string);
276 			e.name = vStringValue (qualified);
277 			markTagExtraBit (&e, XTAG_QUALIFIED_TAGS);
278 			makeTagEntry (&e);
279 			vStringDelete (qualified);
280 		}
281 	}
282 	vStringCopy (token->featureName, token->string);
283 }
284 
makeEiffelLocalTag(tokenInfo * const token)285 static void makeEiffelLocalTag (tokenInfo *const token)
286 {
287 	if (EiffelKinds [EKIND_LOCAL].enabled && isXtagEnabled(XTAG_FILE_SCOPE))
288 	{
289 		const char *const name = vStringValue (token->string);
290 		vString* scope = vStringNew ();
291 		tagEntryInfo e;
292 
293 		initTagEntry (&e, name, EKIND_LOCAL);
294 
295 		e.isFileScope = true;
296 		markTagExtraBit (&e, XTAG_FILE_SCOPE);
297 
298 		vStringCopy (scope, token->className);
299 		vStringPut (scope, '.');
300 		vStringCat (scope, token->featureName);
301 
302 		e.extensionFields.scopeKindIndex = EKIND_FEATURE;
303 		e.extensionFields.scopeName = vStringValue (scope);
304 
305 		makeTagEntry (&e);
306 		vStringDelete (scope);
307 	}
308 }
309 
310 /*
311 *   Parsing functions
312 */
313 
skipToCharacter(const int c)314 static int skipToCharacter (const int c)
315 {
316 	int d;
317 
318 	do
319 	{
320 		d = getcFromInputFile ();
321 	} while (d != EOF  &&  d != c);
322 
323 	return d;
324 }
325 
326 /*  If a numeric is passed in 'c', this is used as the first digit of the
327  *  numeric being parsed.
328  */
parseInteger(int c)329 static vString *parseInteger (int c)
330 {
331 	vString *string = vStringNew ();
332 
333 	if (c == '\0')
334 		c = getcFromInputFile ();
335 	if (c == '-')
336 	{
337 		vStringPut (string, c);
338 		c = getcFromInputFile ();
339 	}
340 	else if (! isdigit (c))
341 		c = getcFromInputFile ();
342 	while (c != EOF  &&  (isdigit (c)  ||  c == '_'))
343 	{
344 		vStringPut (string, c);
345 		c = getcFromInputFile ();
346 	}
347 	ungetcToInputFile (c);
348 
349 	return string;
350 }
351 
parseNumeric(int c)352 static vString *parseNumeric (int c)
353 {
354 	vString *string = vStringNew ();
355 	vString *integer = parseInteger (c);
356 	vStringCopy (string, integer);
357 	vStringDelete (integer);
358 
359 	c = getcFromInputFile ();
360 	if (c == '.')
361 	{
362 		integer = parseInteger ('\0');
363 		vStringPut (string, c);
364 		vStringCat (string, integer);
365 		vStringDelete (integer);
366 		c = getcFromInputFile ();
367 	}
368 	if (tolower (c) == 'e')
369 	{
370 		integer = parseInteger ('\0');
371 		vStringPut (string, c);
372 		vStringCat (string, integer);
373 		vStringDelete (integer);
374 	}
375 	else if (!isspace (c))
376 		ungetcToInputFile (c);
377 
378 	return string;
379 }
380 
parseEscapedCharacter(void)381 static int parseEscapedCharacter (void)
382 {
383 	int d = '\0';
384 	int c = getcFromInputFile ();
385 
386 	switch (c)
387 	{
388 		case 'A':  d = '@';   break;
389 		case 'B':  d = '\b';  break;
390 		case 'C':  d = '^';   break;
391 		case 'D':  d = '$';   break;
392 		case 'F':  d = '\f';  break;
393 		case 'H':  d = '\\';  break;
394 		case 'L':  d = '~';   break;
395 		case 'N':  d = '\n';  break;
396 		case 'Q':  d = '`';   break;
397 		case 'R':  d = '\r';  break;
398 		case 'S':  d = '#';   break;
399 		case 'T':  d = '\t';  break;
400 		case 'U':  d = '\0';  break;
401 		case 'V':  d = '|';   break;
402 		case '%':  d = '%';   break;
403 		case '\'': d = '\'';  break;
404 		case '"':  d = '"';   break;
405 		case '(':  d = '[';   break;
406 		case ')':  d = ']';   break;
407 		case '<':  d = '{';   break;
408 		case '>':  d = '}';   break;
409 
410 		case '\n': skipToCharacter ('%'); break;
411 
412 		case '/':
413 		{
414 			vString *string = parseInteger ('\0');
415 			const char *value = vStringValue (string);
416 			const unsigned long ascii = atol (value);
417 			vStringDelete (string);
418 
419 			c = getcFromInputFile ();
420 			if (c == '/'  &&  ascii < 256)
421 				d = ascii;
422 			break;
423 		}
424 
425 		default: break;
426 	}
427 	return d;
428 }
429 
parseCharacter(void)430 static int parseCharacter (void)
431 {
432 	int c = getcFromInputFile ();
433 	int result = c;
434 
435 	if (c == '%')
436 		result = parseEscapedCharacter ();
437 
438 	c = getcFromInputFile ();
439 	if (c != '\'')
440 		skipToCharacter ('\n');
441 
442 	return result;
443 }
444 
parseString(vString * const string)445 static void parseString (vString *const string)
446 {
447 	bool verbatim = false;
448 	bool align = false;
449 	bool end = false;
450 	vString *verbatimCloser = vStringNew ();
451 	vString *lastLine = vStringNew ();
452 	int prev = '\0';
453 	int c;
454 
455 	while (! end)
456 	{
457 		c = getcFromInputFile ();
458 		if (c == EOF)
459 			end = true;
460 		else if (c == '"')
461 		{
462 			if (! verbatim)
463 				end = true;
464 			else
465 				end = (bool) (strcmp (vStringValue (lastLine),
466 				                         vStringValue (verbatimCloser)) == 0);
467 		}
468 		else if (c == '\n')
469 		{
470 			if (verbatim)
471 				vStringClear (lastLine);
472 			if (prev == '[' /* ||  prev == '{' */)
473 			{
474 				verbatim = true;
475 				vStringClear (verbatimCloser);
476 				vStringClear (lastLine);
477 				if (prev == '{')
478 					vStringPut (verbatimCloser, '}');
479 				else
480 				{
481 					vStringPut (verbatimCloser, ']');
482 					align = true;
483 				}
484 				vStringNCat (verbatimCloser, string, vStringLength (string) - 1);
485 				vStringClear (string);
486 			}
487 			if (verbatim && align)
488 			{
489 				do
490 					c = getcFromInputFile ();
491 				while (isspace (c));
492 			}
493 		}
494 		else if (c == '%')
495 			c = parseEscapedCharacter ();
496 		if (! end)
497 		{
498 			vStringPut (string, c);
499 			if (verbatim)
500 				vStringPut (lastLine, c);
501 			prev = c;
502 		}
503 	}
504 	vStringDelete (lastLine);
505 	vStringDelete (verbatimCloser);
506 }
507 
508 /*  Read a C identifier beginning with "firstChar" and places it into "name".
509  */
parseIdentifier(vString * const string,const int firstChar)510 static void parseIdentifier (vString *const string, const int firstChar)
511 {
512 	int c = firstChar;
513 
514 	do
515 	{
516 		vStringPut (string, c);
517 		c = getcFromInputFile ();
518 	} while (isident (c));
519 
520 	if (!isspace (c))
521 		ungetcToInputFile (c);  /* unget non-identifier character */
522 }
523 
parseFreeOperator(vString * const string,const int firstChar)524 static void parseFreeOperator (vString *const string, const int firstChar)
525 {
526 	int c = firstChar;
527 
528 	do
529 	{
530 		vStringPut (string, c);
531 		c = getcFromInputFile ();
532 	} while (c > ' ');
533 
534 	if (!isspace (c))
535 		ungetcToInputFile (c);  /* unget non-identifier character */
536 }
537 
copyToken(tokenInfo * dst,const tokenInfo * src)538 static void copyToken (tokenInfo* dst, const tokenInfo *src)
539 {
540 	dst->type       = src->type;
541 	dst->keyword    = src->keyword;
542 	dst->isExported = src->isExported;
543 
544 	vStringCopy (dst->string, src->string);
545 	vStringCopy (dst->className, src->className);
546 	vStringCopy (dst->featureName, src->featureName);
547 }
548 
newToken(void)549 static tokenInfo *newToken (void)
550 {
551 	tokenInfo *const token = xMalloc (1, tokenInfo);
552 
553 	token->type			= TOKEN_UNDEFINED;
554 	token->keyword		= KEYWORD_NONE;
555 	token->isExported	= true;
556 
557 	token->string = vStringNew ();
558 	token->className = vStringNew ();
559 	token->featureName = vStringNew ();
560 
561 	return token;
562 }
563 
deleteToken(tokenInfo * const token)564 static void deleteToken (tokenInfo *const token)
565 {
566 	vStringDelete (token->string);
567 	vStringDelete (token->className);
568 	vStringDelete (token->featureName);
569 
570 	eFree (token);
571 }
572 
readToken(tokenInfo * const token)573 static void readToken (tokenInfo *const token)
574 {
575 	int c;
576 
577 	token->type    = TOKEN_UNDEFINED;
578 	token->keyword = KEYWORD_NONE;
579 	vStringClear (token->string);
580 
581 getNextChar:
582 
583 	do
584 		c = getcFromInputFile ();
585 	while (c == '\t'  ||  c == ' '  ||  c == '\n');
586 
587 	switch (c)
588 	{
589 		case EOF:  token->type = TOKEN_EOF;                break;
590 		case ';':  token->type = TOKEN_SEMICOLON;          break;
591 		case '!':  token->type = TOKEN_BANG;               break;
592 		case '}':  token->type = TOKEN_CLOSE_BRACE;        break;
593 		case ']':  token->type = TOKEN_CLOSE_BRACKET;      break;
594 		case ')':  token->type = TOKEN_CLOSE_PAREN;        break;
595 		case ',':  token->type = TOKEN_COMMA;              break;
596 		case '$':  token->type = TOKEN_DOLLAR;             break;
597 		case '.':  token->type = TOKEN_DOT;                break;
598 		case '{':  token->type = TOKEN_OPEN_BRACE;         break;
599 		case '[':  token->type = TOKEN_OPEN_BRACKET;       break;
600 		case '(':  token->type = TOKEN_OPEN_PAREN;         break;
601 		case '~':  token->type = TOKEN_TILDE;              break;
602 
603 
604 		case '+':
605 		case '*':
606 		case '^':
607 		case '=':  token->type = TOKEN_OPERATOR;           break;
608 
609 		case '-':
610 			c = getcFromInputFile ();
611 			if (c == '>')
612 				token->type = TOKEN_CONSTRAINT;
613 			else if (c == '-')  /* is this the start of a comment? */
614 			{
615 				skipToCharacter ('\n');
616 				goto getNextChar;
617 			}
618 			else
619 			{
620 				if (!isspace (c))
621 					ungetcToInputFile (c);
622 				token->type = TOKEN_OPERATOR;
623 			}
624 			break;
625 
626 		case '?':
627 		case ':':
628 		{
629 			int c2 = getcFromInputFile ();
630 			if (c2 == '=')
631 				token->type = TOKEN_OPERATOR;
632 			else
633 			{
634 				if (!isspace (c2))
635 					ungetcToInputFile (c2);
636 				if (c == ':')
637 					token->type = TOKEN_COLON;
638 				else
639 					token->type = TOKEN_QUESTION;
640 			}
641 			break;
642 		}
643 
644 		case '<':
645 			c = getcFromInputFile ();
646 			if (c != '='  &&  c != '>'  &&  !isspace (c))
647 				ungetcToInputFile (c);
648 			token->type = TOKEN_OPERATOR;
649 			break;
650 
651 		case '>':
652 			c = getcFromInputFile ();
653 			if (c != '='  &&  c != '>'  &&  !isspace (c))
654 				ungetcToInputFile (c);
655 			token->type = TOKEN_OPERATOR;
656 			break;
657 
658 		case '/':
659 			c = getcFromInputFile ();
660 			if (c != '/'  &&  c != '='  &&  !isspace (c))
661 				ungetcToInputFile (c);
662 			token->type = TOKEN_OPERATOR;
663 			break;
664 
665 		case '\\':
666 			c = getcFromInputFile ();
667 			if (c != '\\'  &&  !isspace (c))
668 				ungetcToInputFile (c);
669 			token->type = TOKEN_OPERATOR;
670 			break;
671 
672 		case '"':
673 			token->type = TOKEN_STRING;
674 			parseString (token->string);
675 			break;
676 
677 		case '\'':
678 			token->type = TOKEN_CHARACTER;
679 			parseCharacter ();
680 			break;
681 
682 		default:
683 			if (isalpha (c))
684 			{
685 				parseIdentifier (token->string, c);
686 				token->keyword = lookupCaseKeyword (vStringValue (token->string), Lang_eiffel);
687 				if (isKeyword (token, KEYWORD_NONE))
688 					token->type = TOKEN_IDENTIFIER;
689 				else
690 					token->type = TOKEN_KEYWORD;
691 			}
692 			else if (isdigit (c))
693 			{
694 				vString* numeric = parseNumeric (c);
695 				vStringCat (token->string, numeric);
696 				vStringDelete (numeric);
697 				token->type = TOKEN_NUMERIC;
698 			}
699 			else if (isFreeOperatorChar (c))
700 			{
701 				parseFreeOperator (token->string, c);
702 				token->type = TOKEN_OPERATOR;
703 			}
704 			else
705 				token->type = TOKEN_UNDEFINED;
706 			break;
707 	}
708 }
709 
710 /*
711 *   Scanning functions
712 */
713 
isIdentifierMatch(const tokenInfo * const token,const char * const name)714 static bool isIdentifierMatch (
715 		const tokenInfo *const token, const char *const name)
716 {
717 	return (bool) (isType (token, TOKEN_IDENTIFIER)  &&
718 		strcasecmp (vStringValue (token->string), name) == 0);
719 }
720 
findToken(tokenInfo * const token,const tokenType type)721 static bool findToken (tokenInfo *const token, const tokenType type)
722 {
723 	while (! isType (token, type) && ! isType (token, TOKEN_EOF))
724 		readToken (token);
725 	return isType (token, type);
726 }
727 
findKeyword(tokenInfo * const token,const keywordId keyword)728 static bool findKeyword (tokenInfo *const token, const keywordId keyword)
729 {
730 	while (! isKeyword (token, keyword) && ! isType (token, TOKEN_EOF))
731 		readToken (token);
732 	return isKeyword (token, keyword);
733 }
734 
735 static bool parseType (tokenInfo *const token);
736 
parseGeneric(tokenInfo * const token,bool declaration CTAGS_ATTR_UNUSED)737 static void parseGeneric (tokenInfo *const token, bool declaration CTAGS_ATTR_UNUSED)
738 {
739 	unsigned int depth = 0;
740 
741 	Assert (isType (token, TOKEN_OPEN_BRACKET));
742 	do
743 	{
744 		if (isType (token, TOKEN_OPEN_BRACKET))
745 		{
746 			++depth;
747 			readToken (token);
748 		}
749 		else if (isType (token, TOKEN_CLOSE_BRACKET))
750 		{
751 			--depth;
752 			readToken (token);
753 		}
754 		else
755 			parseType (token);
756 	} while (depth > 0 && ! isType (token, TOKEN_EOF));
757 }
758 
parseType(tokenInfo * const token)759 static bool parseType (tokenInfo *const token)
760 {
761 	tokenInfo* const id = newToken ();
762 	copyToken (id, token);
763 	readToken (token);
764 	if (isType (token, TOKEN_COLON))  /* check for "{entity: TYPE}" */
765 	{
766 		readToken (id);
767 		readToken (token);
768 	}
769 	if (isKeyword (id, KEYWORD_like))
770 	{
771 		if (isType (token, TOKEN_IDENTIFIER) ||
772 				isKeyword (token, KEYWORD_Current))
773 			readToken (token);
774 	}
775 	else
776 	{
777 		if (isKeyword (id, KEYWORD_attached) ||
778 		    isKeyword (id, KEYWORD_detachable) ||
779 		    isKeyword (id, KEYWORD_expanded))
780 		{
781 			copyToken (id, token);
782 			readToken (token);
783 		}
784 		if (isType (id, TOKEN_IDENTIFIER))
785 		{
786 			if (isType (token, TOKEN_OPEN_BRACKET))
787 				parseGeneric (token, false);
788 			else if ((strcmp ("BIT", vStringValue (id->string)) == 0))
789 				readToken (token);  /* read token after number of bits */
790 		}
791 	}
792 	deleteToken (id);
793 	return true;
794 }
795 
parseEntityType(tokenInfo * const token)796 static void parseEntityType (tokenInfo *const token)
797 {
798 	Assert (isType (token, TOKEN_COLON));
799 	readToken (token);
800 
801 	if (isType (token, TOKEN_BANG) || isType (token, TOKEN_QUESTION))
802 		readToken (token);  /* skip over '!' or '?' */
803 	parseType (token);
804 }
805 
parseLocal(tokenInfo * const token)806 static void parseLocal (tokenInfo *const token)
807 {
808 	Assert (isKeyword (token, KEYWORD_local));
809 	readToken (token);
810 
811 	/*  Check keyword first in case local clause is empty
812 	 */
813 	while (! isKeyword (token, KEYWORD_do)  &&
814 		   ! isKeyword (token, KEYWORD_once) &&
815 		   ! isKeyword (token, KEYWORD_attribute) &&
816 		   ! isType (token, TOKEN_EOF))
817 	{
818 		if (isType (token, TOKEN_IDENTIFIER))
819 			makeEiffelLocalTag (token);
820 		readToken (token);
821 		if (isType (token, TOKEN_COLON))
822 			parseEntityType (token);
823 	}
824 }
825 
findFeatureEnd(tokenInfo * const token)826 static void findFeatureEnd (tokenInfo *const token)
827 {
828 	bool isFound = isKeyword (token, KEYWORD_is);
829 	if (isFound)
830 		readToken (token);
831 	switch (token->keyword)
832 	{
833 		case KEYWORD_attribute:
834 		case KEYWORD_deferred:
835 		case KEYWORD_do:
836 		case KEYWORD_external:
837 		case KEYWORD_local:
838 		case KEYWORD_note:
839 		case KEYWORD_obsolete:
840 		case KEYWORD_once:
841 		case KEYWORD_require:
842 		{
843 			int depth = 1;
844 
845 			while (depth > 0 && ! isType (token, TOKEN_EOF))
846 			{
847 				switch (token->keyword)
848 				{
849 					case KEYWORD_check:
850 					case KEYWORD_debug:
851 					case KEYWORD_from:
852 					case KEYWORD_across:
853 					case KEYWORD_if:
854 					case KEYWORD_inspect:
855 						++depth;
856 						break;
857 
858 					case KEYWORD_local:
859 						parseLocal (token);
860 						break;
861 
862 					case KEYWORD_end:
863 						--depth;
864 						break;
865 
866 					default:
867 						break;
868 				}
869 				readToken (token);
870 			}
871 			break;
872 		}
873 
874 		default:
875 			/* is this a manifest constant? */
876 			if (isFound || isType (token, TOKEN_OPERATOR)) {
877 				if (isType (token, TOKEN_OPERATOR))
878 					readToken (token);
879 				readToken (token);
880 			}
881 			break;
882 	}
883 }
884 
readFeatureName(tokenInfo * const token)885 static bool readFeatureName (tokenInfo *const token)
886 {
887 	bool isFeatureName = false;
888 
889 	if (isKeyword (token, KEYWORD_frozen))
890 		readToken (token);
891 	if (isType (token, TOKEN_IDENTIFIER))
892 		isFeatureName = true;
893 	else if (isKeyword (token, KEYWORD_assign))  /* legacy code */
894 		isFeatureName = true;
895 	else if (isKeyword (token, KEYWORD_infix)  ||
896 			isKeyword (token, KEYWORD_prefix))
897 	{
898 		readToken (token);
899 		if (isType (token, TOKEN_STRING))
900 			isFeatureName = true;
901 	}
902 	return isFeatureName;
903 }
904 
parseArguments(tokenInfo * const token)905 static void parseArguments (tokenInfo *const token)
906 {
907 	if (findToken (token, TOKEN_CLOSE_PAREN))
908 		readToken (token);
909 }
910 
parseFeature(tokenInfo * const token)911 static bool parseFeature (tokenInfo *const token)
912 {
913 	bool found = false;
914 	while (readFeatureName (token))
915 	{
916 		found = true;
917 		makeEiffelFeatureTag (token);
918 		readToken (token);
919 		if (isType (token, TOKEN_COMMA))
920 			readToken (token);
921 	}
922 	if (found)
923 	{
924 		if (isKeyword (token, KEYWORD_alias)) {
925 			readToken (token);
926 			if (isType (token, TOKEN_STRING))
927 				makeEiffelFeatureTag (token);
928 			readToken (token);
929 		}
930 		if (isType (token, TOKEN_OPEN_PAREN))  /* arguments? */
931 			parseArguments (token);
932 		if (isType (token, TOKEN_COLON))       /* a query? */
933 			parseEntityType (token);
934 		if (isKeyword (token, KEYWORD_assign))
935 		{
936 			readToken (token);
937 			readToken (token);
938 		}
939 		if (isKeyword (token, KEYWORD_obsolete))
940 		{
941 			readToken (token);
942 			if (isType (token, TOKEN_STRING))
943 				readToken (token);
944 		}
945 		findFeatureEnd (token);
946 	}
947 	return found;
948 }
949 
parseExport(tokenInfo * const token)950 static void parseExport (tokenInfo *const token)
951 {
952 	token->isExported = true;
953 	readToken (token);
954 	if (isType (token, TOKEN_OPEN_BRACE))
955 	{
956 		token->isExported = false;
957 		while (! isType (token, TOKEN_CLOSE_BRACE) &&
958 		       ! isType (token, TOKEN_EOF))
959 		{
960 			if (isType (token, TOKEN_IDENTIFIER))
961 				token->isExported |= !isIdentifierMatch (token, "NONE");
962 			readToken (token);
963 		}
964 		readToken (token);
965 	}
966 }
967 
parseFeatureClauses(tokenInfo * const token)968 static void parseFeatureClauses (tokenInfo *const token)
969 {
970 	Assert (isKeyword (token, KEYWORD_feature));
971 	do
972 	{
973 		if (isKeyword (token, KEYWORD_feature))
974 			parseExport (token);
975 		if (! isKeyword (token, KEYWORD_feature) &&
976 			! isKeyword (token, KEYWORD_invariant) &&
977 			! isKeyword (token, KEYWORD_note))
978 		{
979 			if (! parseFeature (token))
980 				readToken (token);
981 		}
982 	} while (! isKeyword (token, KEYWORD_end) &&
983 			 ! isKeyword (token, KEYWORD_invariant) &&
984 			 ! isKeyword (token, KEYWORD_note) &&
985 			 ! isType (token, TOKEN_EOF));
986 }
987 
parseRename(tokenInfo * const token)988 static void parseRename (tokenInfo *const token)
989 {
990 	Assert (isKeyword (token, KEYWORD_rename));
991 	do {
992 		readToken (token);
993 		if (readFeatureName (token))
994 		{
995 			readToken (token);
996 			if (isKeyword (token, KEYWORD_as))
997 			{
998 				readToken (token);
999 				if (readFeatureName (token))
1000 				{
1001 					makeEiffelFeatureTag (token);  /* renamed feature */
1002 					readToken (token);
1003 				}
1004 			}
1005 		}
1006 	} while (isType (token, TOKEN_COMMA));
1007 }
1008 
parseInherit(tokenInfo * const token)1009 static void parseInherit (tokenInfo *const token)
1010 {
1011 	Assert (isKeyword (token, KEYWORD_inherit));
1012 	readToken (token);
1013 	while (isType (token, TOKEN_IDENTIFIER))
1014 	{
1015 		parseType (token);
1016 		if (isType (token, TOKEN_KEYWORD))
1017 		{
1018 			switch (token->keyword)  /* check for feature adaptation */
1019 			{
1020 				case KEYWORD_rename:
1021 					parseRename (token);
1022 				case KEYWORD_export:
1023 				case KEYWORD_undefine:
1024 				case KEYWORD_redefine:
1025 				case KEYWORD_select:
1026 					if (findKeyword (token, KEYWORD_end))
1027 						readToken (token);
1028 					break;
1029 
1030 				case KEYWORD_end:
1031 					readToken (token);
1032 					break;
1033 
1034 				default: break;
1035 			}
1036 		}
1037 		if (isType (token, TOKEN_SEMICOLON))
1038 			readToken (token);
1039 	}
1040 }
1041 
parseConvert(tokenInfo * const token)1042 static void parseConvert (tokenInfo *const token)
1043 {
1044 	Assert (isKeyword (token, KEYWORD_convert));
1045 	do
1046 	{
1047 		readToken (token);
1048 		if (! isType (token, TOKEN_IDENTIFIER))
1049 			break;
1050 		else if (isType (token, TOKEN_OPEN_PAREN))
1051 		{
1052 			while (! isType (token, TOKEN_CLOSE_PAREN) &&
1053 			       ! isType (token, TOKEN_EOF))
1054 				readToken (token);
1055 		}
1056 		else if (isType (token, TOKEN_COLON))
1057 		{
1058 			readToken (token);
1059 			if (! isType (token, TOKEN_OPEN_BRACE))
1060 				break;
1061 			else while (! isType (token, TOKEN_CLOSE_BRACE))
1062 				readToken (token);
1063 		}
1064 	} while (isType (token, TOKEN_COMMA));
1065 }
1066 
parseClass(tokenInfo * const token)1067 static void parseClass (tokenInfo *const token)
1068 {
1069 	Assert (isKeyword (token, KEYWORD_class));
1070 	readToken (token);
1071 	if (isType (token, TOKEN_IDENTIFIER))
1072 	{
1073 		makeEiffelClassTag (token);
1074 		readToken (token);
1075 	}
1076 
1077 	do
1078 	{
1079 		if (isType (token, TOKEN_OPEN_BRACKET))
1080 			parseGeneric (token, true);
1081 		else if (! isType (token, TOKEN_KEYWORD))
1082 			readToken (token);
1083 		else switch (token->keyword)
1084 		{
1085 			case KEYWORD_inherit:  parseInherit (token);        break;
1086 			case KEYWORD_feature:  parseFeatureClauses (token); break;
1087 			case KEYWORD_convert:  parseConvert (token);        break;
1088 			default:               readToken (token);           break;
1089 		}
1090 	} while (! isKeyword (token, KEYWORD_end) &&
1091 	         ! isType (token, TOKEN_EOF));
1092 }
1093 
initialize(const langType language)1094 static void initialize (const langType language)
1095 {
1096 	Lang_eiffel = language;
1097 }
1098 
findEiffelTags(void)1099 static void findEiffelTags (void)
1100 {
1101 	tokenInfo *const token = newToken ();
1102 
1103 	while (findKeyword (token, KEYWORD_class))
1104 		parseClass (token);
1105 	deleteToken (token);
1106 }
1107 
EiffelParser(void)1108 extern parserDefinition* EiffelParser (void)
1109 {
1110 	static const char *const extensions [] = { "e", NULL };
1111 	parserDefinition* def = parserNew ("Eiffel");
1112 	def->kindTable      = EiffelKinds;
1113 	def->kindCount  = ARRAY_SIZE (EiffelKinds);
1114 	def->extensions = extensions;
1115 	def->parser     = findEiffelTags;
1116 	def->initialize = initialize;
1117 	def->keywordTable = EiffelKeywordTable;
1118 	def->keywordCount = ARRAY_SIZE (EiffelKeywordTable);
1119 	return def;
1120 }
1121