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