Merge pull request #11 from esorton/bugfix/add-constexpr-keyword-to-arduino-ctags
[arduino-ctags.git] / eiffel.c
blobe2f5a5c31450a93301a6f46a749b1dc349aa4650
1 /*
2 * $Id: eiffel.c 748 2009-11-06 02:44:42Z dhiebert $
4 * Copyright (c) 1998-2002, Darren Hiebert
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains functions for generating tags for Eiffel language
10 * files.
14 * INCLUDE FILES
16 #include "general.h" /* must always come first */
18 #ifdef TYPE_REFERENCE_TOOL
19 #include <stdio.h>
20 #endif
21 #include <string.h>
22 #include <limits.h>
23 #include <ctype.h> /* to define tolower () */
24 #include <setjmp.h>
26 #include "debug.h"
27 #include "keyword.h"
28 #include "routines.h"
29 #include "vstring.h"
30 #ifndef TYPE_REFERENCE_TOOL
31 #include "entry.h"
32 #include "options.h"
33 #include "parse.h"
34 #include "read.h"
35 #endif
38 * MACROS
40 #define isident(c) (isalnum(c) || (c) == '_')
41 #define isFreeOperatorChar(c) ((c) == '@' || (c) == '#' || \
42 (c) == '|' || (c) == '&')
43 #define isType(token,t) (boolean) ((token)->type == (t))
44 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
47 * DATA DECLARATIONS
50 typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
52 /* Used to specify type of keyword.
54 typedef enum eKeywordId {
55 KEYWORD_NONE = -1,
56 KEYWORD_alias, KEYWORD_all, KEYWORD_and,
57 KEYWORD_as, KEYWORD_assign, KEYWORD_attached,
58 KEYWORD_check, KEYWORD_class, KEYWORD_convert, KEYWORD_create,
59 KEYWORD_creation, KEYWORD_Current,
60 KEYWORD_debug, KEYWORD_deferred, KEYWORD_detachable, KEYWORD_do,
61 KEYWORD_else, KEYWORD_elseif, KEYWORD_end, KEYWORD_ensure,
62 KEYWORD_expanded, KEYWORD_export, KEYWORD_external,
63 KEYWORD_false, KEYWORD_feature, KEYWORD_from, KEYWORD_frozen,
64 KEYWORD_if, KEYWORD_implies,
65 KEYWORD_indexing, KEYWORD_infix, KEYWORD_inherit, KEYWORD_inspect,
66 KEYWORD_invariant, KEYWORD_is, KEYWORD_like, KEYWORD_local,
67 KEYWORD_loop, KEYWORD_not, KEYWORD_obsolete, KEYWORD_old, KEYWORD_once,
68 KEYWORD_or, KEYWORD_prefix, KEYWORD_redefine, KEYWORD_rename,
69 KEYWORD_require, KEYWORD_rescue, KEYWORD_Result, KEYWORD_retry,
70 KEYWORD_select, KEYWORD_separate, KEYWORD_strip, KEYWORD_then,
71 KEYWORD_true, KEYWORD_undefine, KEYWORD_unique, KEYWORD_until,
72 KEYWORD_variant, KEYWORD_when, KEYWORD_xor
73 } keywordId;
75 /* Used to determine whether keyword is valid for the token language and
76 * what its ID is.
78 typedef struct sKeywordDesc {
79 const char *name;
80 keywordId id;
81 } keywordDesc;
83 typedef enum eTokenType {
84 TOKEN_UNDEFINED,
85 TOKEN_BANG,
86 TOKEN_CHARACTER,
87 TOKEN_CLOSE_BRACE,
88 TOKEN_CLOSE_BRACKET,
89 TOKEN_CLOSE_PAREN,
90 TOKEN_COLON,
91 TOKEN_COMMA,
92 TOKEN_CONSTRAINT,
93 TOKEN_DOT,
94 TOKEN_DOLLAR,
95 TOKEN_IDENTIFIER,
96 TOKEN_KEYWORD,
97 TOKEN_NUMERIC,
98 TOKEN_OPEN_BRACE,
99 TOKEN_OPEN_BRACKET,
100 TOKEN_OPEN_PAREN,
101 TOKEN_OPERATOR,
102 TOKEN_OTHER,
103 TOKEN_QUESTION,
104 TOKEN_SEMICOLON,
105 TOKEN_SEPARATOR,
106 TOKEN_STRING,
107 TOKEN_TILDE
108 } tokenType;
110 typedef struct sTokenInfo {
111 tokenType type;
112 keywordId keyword;
113 boolean isExported;
114 vString* string;
115 vString* className;
116 vString* featureName;
117 } tokenInfo;
120 * DATA DEFINITIONS
123 static langType Lang_eiffel;
125 #ifdef TYPE_REFERENCE_TOOL
127 static const char *FileName;
128 static FILE *File;
129 static int PrintClass;
130 static int PrintReferences;
131 static int SelfReferences;
132 static int Debug;
133 static stringList *GenericNames;
134 static stringList *ReferencedTypes;
136 #else
138 typedef enum {
139 EKIND_CLASS, EKIND_FEATURE, EKIND_LOCAL, EKIND_QUALIFIED_TAGS
140 } eiffelKind;
142 static kindOption EiffelKinds [] = {
143 { TRUE, 'c', "class", "classes"},
144 { TRUE, 'f', "feature", "features"},
145 { FALSE, 'l', "local", "local entities"}
148 #endif
150 static jmp_buf Exception;
152 static const keywordDesc EiffelKeywordTable [] = {
153 /* keyword keyword ID */
154 { "alias", KEYWORD_alias },
155 { "all", KEYWORD_all },
156 { "and", KEYWORD_and },
157 { "as", KEYWORD_as },
158 { "assign", KEYWORD_assign },
159 { "attached", KEYWORD_attached },
160 { "check", KEYWORD_check },
161 { "class", KEYWORD_class },
162 { "convert", KEYWORD_convert },
163 { "create", KEYWORD_create },
164 { "creation", KEYWORD_creation },
165 { "current", KEYWORD_Current },
166 { "debug", KEYWORD_debug },
167 { "deferred", KEYWORD_deferred },
168 { "detachable", KEYWORD_detachable },
169 { "do", KEYWORD_do },
170 { "else", KEYWORD_else },
171 { "elseif", KEYWORD_elseif },
172 { "end", KEYWORD_end },
173 { "ensure", KEYWORD_ensure },
174 { "expanded", KEYWORD_expanded },
175 { "export", KEYWORD_export },
176 { "external", KEYWORD_external },
177 { "false", KEYWORD_false },
178 { "feature", KEYWORD_feature },
179 { "from", KEYWORD_from },
180 { "frozen", KEYWORD_frozen },
181 { "if", KEYWORD_if },
182 { "implies", KEYWORD_implies },
183 { "indexing", KEYWORD_indexing },
184 { "infix", KEYWORD_infix },
185 { "inherit", KEYWORD_inherit },
186 { "inspect", KEYWORD_inspect },
187 { "invariant", KEYWORD_invariant },
188 { "is", KEYWORD_is },
189 { "like", KEYWORD_like },
190 { "local", KEYWORD_local },
191 { "loop", KEYWORD_loop },
192 { "not", KEYWORD_not },
193 { "obsolete", KEYWORD_obsolete },
194 { "old", KEYWORD_old },
195 { "once", KEYWORD_once },
196 { "or", KEYWORD_or },
197 { "prefix", KEYWORD_prefix },
198 { "redefine", KEYWORD_redefine },
199 { "rename", KEYWORD_rename },
200 { "require", KEYWORD_require },
201 { "rescue", KEYWORD_rescue },
202 { "result", KEYWORD_Result },
203 { "retry", KEYWORD_retry },
204 { "select", KEYWORD_select },
205 { "separate", KEYWORD_separate },
206 { "strip", KEYWORD_strip },
207 { "then", KEYWORD_then },
208 { "true", KEYWORD_true },
209 { "undefine", KEYWORD_undefine },
210 { "unique", KEYWORD_unique },
211 { "until", KEYWORD_until },
212 { "variant", KEYWORD_variant },
213 { "when", KEYWORD_when },
214 { "xor", KEYWORD_xor }
218 * FUNCTION DEFINITIONS
221 static void buildEiffelKeywordHash (void)
223 const size_t count = sizeof (EiffelKeywordTable) /
224 sizeof (EiffelKeywordTable [0]);
225 size_t i;
226 for (i = 0 ; i < count ; ++i)
228 const keywordDesc* const p = &EiffelKeywordTable [i];
229 addKeyword (p->name, Lang_eiffel, (int) p->id);
233 #ifdef TYPE_REFERENCE_TOOL
235 static void addGenericName (tokenInfo *const token)
237 vStringUpper (token->string);
238 if (vStringLength (token->string) > 0)
239 stringListAdd (GenericNames, vStringNewCopy (token->string));
242 static boolean isGeneric (tokenInfo *const token)
244 return (boolean) stringListHas (GenericNames, vStringValue (token->string));
247 static void reportType (tokenInfo *const token)
249 vStringUpper (token->string);
250 if (vStringLength (token->string) > 0 && ! isGeneric (token) &&
251 (SelfReferences || strcmp (vStringValue (
252 token->string), vStringValue (token->className)) != 0) &&
253 ! stringListHas (ReferencedTypes, vStringValue (token->string)))
255 printf ("%s\n", vStringValue (token->string));
256 stringListAdd (ReferencedTypes, vStringNewCopy (token->string));
260 static int fileGetc (void)
262 int c = getc (File);
263 if (c == '\r')
265 c = getc (File);
266 if (c != '\n')
268 ungetc (c, File);
269 c = '\n';
272 if (Debug > 0 && c != EOF)
273 putc (c, errout);
274 return c;
277 static int fileUngetc (c)
279 return ungetc (c, File);
282 extern char *readLine (vString *const vLine, FILE *const fp)
284 return NULL;
287 #else
290 * Tag generation functions
293 static void makeEiffelClassTag (tokenInfo *const token)
295 if (EiffelKinds [EKIND_CLASS].enabled)
297 const char *const name = vStringValue (token->string);
298 tagEntryInfo e;
300 initTagEntry (&e, name);
302 e.kindName = EiffelKinds [EKIND_CLASS].name;
303 e.kind = EiffelKinds [EKIND_CLASS].letter;
305 makeTagEntry (&e);
307 vStringCopy (token->className, token->string);
310 static void makeEiffelFeatureTag (tokenInfo *const token)
312 if (EiffelKinds [EKIND_FEATURE].enabled &&
313 (token->isExported || Option.include.fileScope))
315 const char *const name = vStringValue (token->string);
316 tagEntryInfo e;
318 initTagEntry (&e, name);
320 e.isFileScope = (boolean) (! token->isExported);
321 e.kindName = EiffelKinds [EKIND_FEATURE].name;
322 e.kind = EiffelKinds [EKIND_FEATURE].letter;
323 e.extensionFields.scope [0] = EiffelKinds [EKIND_CLASS].name;
324 e.extensionFields.scope [1] = vStringValue (token->className);
326 makeTagEntry (&e);
328 if (Option.include.qualifiedTags)
330 vString* qualified = vStringNewInit (vStringValue (token->className));
331 vStringPut (qualified, '.');
332 vStringCat (qualified, token->string);
333 e.name = vStringValue (qualified);
334 makeTagEntry (&e);
335 vStringDelete (qualified);
338 vStringCopy (token->featureName, token->string);
341 static void makeEiffelLocalTag (tokenInfo *const token)
343 if (EiffelKinds [EKIND_LOCAL].enabled && Option.include.fileScope)
345 const char *const name = vStringValue (token->string);
346 vString* scope = vStringNew ();
347 tagEntryInfo e;
349 initTagEntry (&e, name);
351 e.isFileScope = TRUE;
352 e.kindName = EiffelKinds [EKIND_LOCAL].name;
353 e.kind = EiffelKinds [EKIND_LOCAL].letter;
355 vStringCopy (scope, token->className);
356 vStringPut (scope, '.');
357 vStringCat (scope, token->featureName);
359 e.extensionFields.scope [0] = EiffelKinds [EKIND_FEATURE].name;
360 e.extensionFields.scope [1] = vStringValue (scope);
362 makeTagEntry (&e);
363 vStringDelete (scope);
367 #endif
370 * Parsing functions
373 static int skipToCharacter (const int c)
375 int d;
379 d = fileGetc ();
380 } while (d != EOF && d != c);
382 return d;
385 /* If a numeric is passed in 'c', this is used as the first digit of the
386 * numeric being parsed.
388 static vString *parseInteger (int c)
390 vString *string = vStringNew ();
392 if (c == '\0')
393 c = fileGetc ();
394 if (c == '-')
396 vStringPut (string, c);
397 c = fileGetc ();
399 else if (! isdigit (c))
400 c = fileGetc ();
401 while (c != EOF && (isdigit (c) || c == '_'))
403 vStringPut (string, c);
404 c = fileGetc ();
406 vStringTerminate (string);
407 fileUngetc (c);
409 return string;
412 static vString *parseNumeric (int c)
414 vString *string = vStringNew ();
415 vString *integer = parseInteger (c);
416 vStringCopy (string, integer);
417 vStringDelete (integer);
419 c = fileGetc ();
420 if (c == '.')
422 integer = parseInteger ('\0');
423 vStringPut (string, c);
424 vStringCat (string, integer);
425 vStringDelete (integer);
426 c = fileGetc ();
428 if (tolower (c) == 'e')
430 integer = parseInteger ('\0');
431 vStringPut (string, c);
432 vStringCat (string, integer);
433 vStringDelete (integer);
435 else if (!isspace (c))
436 fileUngetc (c);
438 vStringTerminate (string);
440 return string;
443 static int parseEscapedCharacter (void)
445 int d = '\0';
446 int c = fileGetc ();
448 switch (c)
450 case 'A': d = '@'; break;
451 case 'B': d = '\b'; break;
452 case 'C': d = '^'; break;
453 case 'D': d = '$'; break;
454 case 'F': d = '\f'; break;
455 case 'H': d = '\\'; break;
456 case 'L': d = '~'; break;
457 case 'N': d = '\n'; break;
458 #ifdef QDOS
459 case 'Q': d = 0x9F; break;
460 #else
461 case 'Q': d = '`'; break;
462 #endif
463 case 'R': d = '\r'; break;
464 case 'S': d = '#'; break;
465 case 'T': d = '\t'; break;
466 case 'U': d = '\0'; break;
467 case 'V': d = '|'; break;
468 case '%': d = '%'; break;
469 case '\'': d = '\''; break;
470 case '"': d = '"'; break;
471 case '(': d = '['; break;
472 case ')': d = ']'; break;
473 case '<': d = '{'; break;
474 case '>': d = '}'; break;
476 case '\n': skipToCharacter ('%'); break;
478 case '/':
480 vString *string = parseInteger ('\0');
481 const char *value = vStringValue (string);
482 const unsigned long ascii = atol (value);
483 vStringDelete (string);
485 c = fileGetc ();
486 if (c == '/' && ascii < 256)
487 d = ascii;
488 break;
491 default: break;
493 return d;
496 static int parseCharacter (void)
498 int c = fileGetc ();
499 int result = c;
501 if (c == '%')
502 result = parseEscapedCharacter ();
504 c = fileGetc ();
505 if (c != '\'')
506 skipToCharacter ('\n');
508 return result;
511 static void parseString (vString *const string)
513 boolean verbatim = FALSE;
514 boolean align = FALSE;
515 boolean end = FALSE;
516 vString *verbatimCloser = vStringNew ();
517 vString *lastLine = vStringNew ();
518 int prev = '\0';
519 int c;
521 while (! end)
523 c = fileGetc ();
524 if (c == EOF)
525 end = TRUE;
526 else if (c == '"')
528 if (! verbatim)
529 end = TRUE;
530 else
531 end = (boolean) (strcmp (vStringValue (lastLine),
532 vStringValue (verbatimCloser)) == 0);
534 else if (c == '\n')
536 if (verbatim)
537 vStringClear (lastLine);
538 if (prev == '[' /* || prev == '{' */)
540 verbatim = TRUE;
541 vStringClear (verbatimCloser);
542 vStringClear (lastLine);
543 if (prev == '{')
544 vStringPut (verbatimCloser, '}');
545 else
547 vStringPut (verbatimCloser, ']');
548 align = TRUE;
550 vStringNCat (verbatimCloser, string, vStringLength (string) - 1);
551 vStringClear (string);
553 if (verbatim && align)
556 c = fileGetc ();
557 while (isspace (c));
560 else if (c == '%')
561 c = parseEscapedCharacter ();
562 if (! end)
564 vStringPut (string, c);
565 if (verbatim)
567 vStringPut (lastLine, c);
568 vStringTerminate (lastLine);
570 prev = c;
573 vStringTerminate (string);
574 vStringDelete (lastLine);
575 vStringDelete (verbatimCloser);
578 /* Read a C identifier beginning with "firstChar" and places it into "name".
580 static void parseIdentifier (vString *const string, const int firstChar)
582 int c = firstChar;
586 vStringPut (string, c);
587 c = fileGetc ();
588 } while (isident (c));
590 vStringTerminate (string);
591 if (!isspace (c))
592 fileUngetc (c); /* unget non-identifier character */
595 static void parseFreeOperator (vString *const string, const int firstChar)
597 int c = firstChar;
601 vStringPut (string, c);
602 c = fileGetc ();
603 } while (c > ' ');
605 vStringTerminate (string);
606 if (!isspace (c))
607 fileUngetc (c); /* unget non-identifier character */
610 static void copyToken (tokenInfo* dst, const tokenInfo *src)
612 dst->type = src->type;
613 dst->keyword = src->keyword;
614 dst->isExported = src->isExported;
616 vStringCopy (dst->string, src->string);
617 vStringCopy (dst->className, src->className);
618 vStringCopy (dst->featureName, src->featureName);
621 static tokenInfo *newToken (void)
623 tokenInfo *const token = xMalloc (1, tokenInfo);
625 token->type = TOKEN_UNDEFINED;
626 token->keyword = KEYWORD_NONE;
627 token->isExported = TRUE;
629 token->string = vStringNew ();
630 token->className = vStringNew ();
631 token->featureName = vStringNew ();
633 return token;
636 static void deleteToken (tokenInfo *const token)
638 vStringDelete (token->string);
639 vStringDelete (token->className);
640 vStringDelete (token->featureName);
642 eFree (token);
645 static void readToken (tokenInfo *const token)
647 int c;
649 token->type = TOKEN_UNDEFINED;
650 token->keyword = KEYWORD_NONE;
651 vStringClear (token->string);
653 getNextChar:
656 c = fileGetc ();
657 while (c == '\t' || c == ' ' || c == '\n');
659 switch (c)
661 case EOF: longjmp (Exception, (int)ExceptionEOF); break;
662 case ';': token->type = TOKEN_SEMICOLON; break;
663 case '!': token->type = TOKEN_BANG; break;
664 case '}': token->type = TOKEN_CLOSE_BRACE; break;
665 case ']': token->type = TOKEN_CLOSE_BRACKET; break;
666 case ')': token->type = TOKEN_CLOSE_PAREN; break;
667 case ',': token->type = TOKEN_COMMA; break;
668 case '$': token->type = TOKEN_DOLLAR; break;
669 case '.': token->type = TOKEN_DOT; break;
670 case '{': token->type = TOKEN_OPEN_BRACE; break;
671 case '[': token->type = TOKEN_OPEN_BRACKET; break;
672 case '(': token->type = TOKEN_OPEN_PAREN; break;
673 case '~': token->type = TOKEN_TILDE; break;
676 case '+':
677 case '*':
678 case '^':
679 case '=': token->type = TOKEN_OPERATOR; break;
681 case '-':
682 c = fileGetc ();
683 if (c == '>')
684 token->type = TOKEN_CONSTRAINT;
685 else if (c == '-') /* is this the start of a comment? */
687 skipToCharacter ('\n');
688 goto getNextChar;
690 else
692 if (!isspace (c))
693 fileUngetc (c);
694 token->type = TOKEN_OPERATOR;
696 break;
698 case '?':
699 case ':':
701 int c2 = fileGetc ();
702 if (c2 == '=')
703 token->type = TOKEN_OPERATOR;
704 else
706 if (!isspace (c2))
707 fileUngetc (c2);
708 if (c == ':')
709 token->type = TOKEN_COLON;
710 else
711 token->type = TOKEN_QUESTION;
713 break;
716 case '<':
717 c = fileGetc ();
718 if (c != '=' && c != '>' && !isspace (c))
719 fileUngetc (c);
720 token->type = TOKEN_OPERATOR;
721 break;
723 case '>':
724 c = fileGetc ();
725 if (c != '=' && c != '>' && !isspace (c))
726 fileUngetc (c);
727 token->type = TOKEN_OPERATOR;
728 break;
730 case '/':
731 c = fileGetc ();
732 if (c != '/' && c != '=' && !isspace (c))
733 fileUngetc (c);
734 token->type = TOKEN_OPERATOR;
735 break;
737 case '\\':
738 c = fileGetc ();
739 if (c != '\\' && !isspace (c))
740 fileUngetc (c);
741 token->type = TOKEN_OPERATOR;
742 break;
744 case '"':
745 token->type = TOKEN_STRING;
746 parseString (token->string);
747 break;
749 case '\'':
750 token->type = TOKEN_CHARACTER;
751 parseCharacter ();
752 break;
754 default:
755 if (isalpha (c))
757 parseIdentifier (token->string, c);
758 token->keyword = analyzeToken (token->string, Lang_eiffel);
759 if (isKeyword (token, KEYWORD_NONE))
760 token->type = TOKEN_IDENTIFIER;
761 else
762 token->type = TOKEN_KEYWORD;
764 else if (isdigit (c))
766 vString* numeric = parseNumeric (c);
767 vStringCat (token->string, numeric);
768 vStringDelete (numeric);
769 token->type = TOKEN_NUMERIC;
771 else if (isFreeOperatorChar (c))
773 parseFreeOperator (token->string, c);
774 token->type = TOKEN_OPERATOR;
776 else
778 token->type = TOKEN_UNDEFINED;
779 Assert (! isType (token, TOKEN_UNDEFINED));
781 break;
786 * Scanning functions
789 static boolean isIdentifierMatch (
790 const tokenInfo *const token, const char *const name)
792 return (boolean) (isType (token, TOKEN_IDENTIFIER) &&
793 strcasecmp (vStringValue (token->string), name) == 0);
796 static void findToken (tokenInfo *const token, const tokenType type)
798 while (! isType (token, type))
799 readToken (token);
802 static void findKeyword (tokenInfo *const token, const keywordId keyword)
804 while (! isKeyword (token, keyword))
805 readToken (token);
808 static boolean parseType (tokenInfo *const token);
810 static void parseGeneric (tokenInfo *const token, boolean declaration __unused__)
812 unsigned int depth = 0;
813 #ifdef TYPE_REFERENCE_TOOL
814 boolean constraint = FALSE;
815 #endif
816 Assert (isType (token, TOKEN_OPEN_BRACKET));
819 if (isType (token, TOKEN_OPEN_BRACKET))
821 ++depth;
822 readToken (token);
824 else if (isType (token, TOKEN_CLOSE_BRACKET))
826 --depth;
827 readToken (token);
829 #ifdef TYPE_REFERENCE_TOOL
830 else if (declaration)
832 boolean advanced = FALSE;
833 if (depth == 1)
835 if (isType (token, TOKEN_CONSTRAINT))
836 constraint = TRUE;
837 else if (isKeyword (token, KEYWORD_create))
838 findKeyword (token, KEYWORD_end);
839 else if (isType (token, TOKEN_IDENTIFIER))
841 if (constraint)
842 advanced = parseType (token);
843 else
844 addGenericName (token);
845 constraint = FALSE;
848 else if (isType (token, TOKEN_IDENTIFIER))
849 advanced = parseType (token);
850 if (! advanced)
851 readToken (token);
853 #endif
854 else
855 parseType (token);
856 } while (depth > 0);
859 static boolean parseType (tokenInfo *const token)
861 tokenInfo* const id = newToken ();
862 copyToken (id, token);
863 readToken (token);
864 if (isType (token, TOKEN_COLON)) /* check for "{entity: TYPE}" */
866 readToken (id);
867 readToken (token);
869 if (isKeyword (id, KEYWORD_like))
871 if (isType (token, TOKEN_IDENTIFIER) ||
872 isKeyword (token, KEYWORD_Current))
873 readToken (token);
875 else
877 if (isKeyword (id, KEYWORD_attached) ||
878 isKeyword (id, KEYWORD_detachable) ||
879 isKeyword (id, KEYWORD_expanded))
881 copyToken (id, token);
882 readToken (token);
884 if (isType (id, TOKEN_IDENTIFIER))
886 #ifdef TYPE_REFERENCE_TOOL
887 reportType (id);
888 #endif
889 if (isType (token, TOKEN_OPEN_BRACKET))
890 parseGeneric (token, FALSE);
891 else if ((strcmp ("BIT", vStringValue (id->string)) == 0))
892 readToken (token); /* read token after number of bits */
895 deleteToken (id);
896 return TRUE;
899 static void parseEntityType (tokenInfo *const token)
901 Assert (isType (token, TOKEN_COLON));
902 readToken (token);
904 if (isType (token, TOKEN_BANG) || isType (token, TOKEN_QUESTION))
905 readToken (token); /* skip over '!' or '?' */
906 parseType (token);
909 static void parseLocal (tokenInfo *const token)
911 Assert (isKeyword (token, KEYWORD_local));
912 readToken (token);
914 /* Check keyword first in case local clause is empty
916 while (! isKeyword (token, KEYWORD_do) &&
917 ! isKeyword (token, KEYWORD_once))
919 #ifndef TYPE_REFERENCE_TOOL
920 if (isType (token, TOKEN_IDENTIFIER))
921 makeEiffelLocalTag (token);
922 #endif
923 readToken (token);
924 if (isType (token, TOKEN_COLON))
925 parseEntityType (token);
929 static void findFeatureEnd (tokenInfo *const token)
931 boolean isFound = isKeyword (token, KEYWORD_is);
932 if (isFound)
933 readToken (token);
934 switch (token->keyword)
936 case KEYWORD_deferred:
937 case KEYWORD_do:
938 case KEYWORD_external:
939 case KEYWORD_local:
940 case KEYWORD_obsolete:
941 case KEYWORD_once:
942 case KEYWORD_require:
944 int depth = 1;
946 while (depth > 0)
948 #ifdef TYPE_REFERENCE_TOOL
949 if (isType (token, TOKEN_OPEN_BRACE))
951 readToken (token);
952 if (isType (token, TOKEN_IDENTIFIER))
953 parseType (token);
955 else if (isType (token, TOKEN_BANG))
957 readToken (token);
958 if (isType (token, TOKEN_IDENTIFIER))
959 parseType (token);
960 if (isType (token, TOKEN_BANG))
961 readToken (token);
963 else
964 #endif
965 switch (token->keyword)
967 case KEYWORD_check:
968 case KEYWORD_debug:
969 case KEYWORD_from:
970 case KEYWORD_if:
971 case KEYWORD_inspect:
972 ++depth;
973 break;
975 case KEYWORD_local:
976 parseLocal (token);
977 break;
979 case KEYWORD_end:
980 --depth;
981 break;
983 default:
984 break;
986 readToken (token);
988 break;
991 default:
992 /* is this a manifest constant? */
993 if (isFound || isType (token, TOKEN_OPERATOR)) {
994 if (isType (token, TOKEN_OPERATOR))
995 readToken (token);
996 readToken (token);
998 break;
1002 static boolean readFeatureName (tokenInfo *const token)
1004 boolean isFeatureName = FALSE;
1006 if (isKeyword (token, KEYWORD_frozen))
1007 readToken (token);
1008 if (isType (token, TOKEN_IDENTIFIER))
1009 isFeatureName = TRUE;
1010 else if (isKeyword (token, KEYWORD_assign)) /* legacy code */
1011 isFeatureName = TRUE;
1012 else if (isKeyword (token, KEYWORD_infix) ||
1013 isKeyword (token, KEYWORD_prefix))
1015 readToken (token);
1016 if (isType (token, TOKEN_STRING))
1017 isFeatureName = TRUE;
1019 return isFeatureName;
1022 static void parseArguments (tokenInfo *const token)
1024 #ifndef TYPE_REFERENCE_TOOL
1025 findToken (token, TOKEN_CLOSE_PAREN);
1026 readToken (token);
1027 #else
1028 Assert (isType (token, TOKEN_OPEN_PAREN));
1029 readToken (token);
1032 if (isType (token, TOKEN_COLON))
1033 parseEntityType (token);
1034 else
1035 readToken (token);
1036 } while (! isType (token, TOKEN_CLOSE_PAREN));
1037 readToken (token);
1038 #endif
1041 static boolean parseFeature (tokenInfo *const token)
1043 boolean found = FALSE;
1044 while (readFeatureName (token))
1046 found = TRUE;
1047 #ifndef TYPE_REFERENCE_TOOL
1048 makeEiffelFeatureTag (token);
1049 #endif
1050 readToken (token);
1051 if (isType (token, TOKEN_COMMA))
1052 readToken (token);
1054 if (found)
1056 if (isKeyword (token, KEYWORD_alias)) {
1057 readToken (token);
1058 #ifndef TYPE_REFERENCE_TOOL
1059 if (isType (token, TOKEN_STRING))
1060 makeEiffelFeatureTag (token);
1061 #endif
1062 readToken (token);
1064 if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
1065 parseArguments (token);
1066 if (isType (token, TOKEN_COLON)) /* a query? */
1067 parseEntityType (token);
1068 if (isKeyword (token, KEYWORD_assign))
1070 readToken (token);
1071 readToken (token);
1073 if (isKeyword (token, KEYWORD_obsolete))
1075 readToken (token);
1076 if (isType (token, TOKEN_STRING))
1077 readToken (token);
1079 findFeatureEnd (token);
1081 return found;
1084 static void parseExport (tokenInfo *const token)
1086 token->isExported = TRUE;
1087 readToken (token);
1088 if (isType (token, TOKEN_OPEN_BRACE))
1090 token->isExported = FALSE;
1091 while (! isType (token, TOKEN_CLOSE_BRACE))
1093 if (isType (token, TOKEN_IDENTIFIER))
1094 token->isExported |= !isIdentifierMatch (token, "NONE");
1095 readToken (token);
1097 readToken (token);
1101 static void parseFeatureClauses (tokenInfo *const token)
1103 Assert (isKeyword (token, KEYWORD_feature));
1106 if (isKeyword (token, KEYWORD_feature))
1107 parseExport (token);
1108 if (! isKeyword (token, KEYWORD_feature) &&
1109 ! isKeyword (token, KEYWORD_invariant) &&
1110 ! isKeyword (token, KEYWORD_indexing))
1112 if (! parseFeature (token))
1113 readToken (token);
1115 } while (! isKeyword (token, KEYWORD_end) &&
1116 ! isKeyword (token, KEYWORD_invariant) &&
1117 ! isKeyword (token, KEYWORD_indexing));
1120 static void parseRename (tokenInfo *const token)
1122 Assert (isKeyword (token, KEYWORD_rename));
1123 do {
1124 readToken (token);
1125 if (readFeatureName (token))
1127 readToken (token);
1128 if (isKeyword (token, KEYWORD_as))
1130 readToken (token);
1131 if (readFeatureName (token))
1133 #ifndef TYPE_REFERENCE_TOOL
1134 makeEiffelFeatureTag (token); /* renamed feature */
1135 #endif
1136 readToken (token);
1140 } while (isType (token, TOKEN_COMMA));
1143 static void parseInherit (tokenInfo *const token)
1145 Assert (isKeyword (token, KEYWORD_inherit));
1146 readToken (token);
1147 while (isType (token, TOKEN_IDENTIFIER))
1149 parseType (token);
1150 if (isType (token, TOKEN_KEYWORD))
1152 switch (token->keyword) /* check for feature adaptation */
1154 case KEYWORD_rename:
1155 parseRename (token);
1156 case KEYWORD_export:
1157 case KEYWORD_undefine:
1158 case KEYWORD_redefine:
1159 case KEYWORD_select:
1160 findKeyword (token, KEYWORD_end);
1161 readToken (token);
1162 break;
1164 case KEYWORD_end:
1165 readToken (token);
1166 break;
1168 default: break;
1171 if (isType (token, TOKEN_SEMICOLON))
1172 readToken (token);
1176 static void parseConvert (tokenInfo *const token)
1178 Assert (isKeyword (token, KEYWORD_convert));
1181 readToken (token);
1182 if (! isType (token, TOKEN_IDENTIFIER))
1183 break;
1184 else if (isType (token, TOKEN_OPEN_PAREN))
1186 while (! isType (token, TOKEN_CLOSE_PAREN))
1187 readToken (token);
1189 else if (isType (token, TOKEN_COLON))
1191 readToken (token);
1192 if (! isType (token, TOKEN_OPEN_BRACE))
1193 break;
1194 else while (! isType (token, TOKEN_CLOSE_BRACE))
1195 readToken (token);
1197 } while (isType (token, TOKEN_COMMA));
1200 static void parseClass (tokenInfo *const token)
1202 Assert (isKeyword (token, KEYWORD_class));
1203 readToken (token);
1204 if (isType (token, TOKEN_IDENTIFIER))
1206 #ifndef TYPE_REFERENCE_TOOL
1207 makeEiffelClassTag (token);
1208 readToken (token);
1209 #else
1210 vStringCopy (token->className, token->string);
1211 vStringUpper (token->className);
1212 if (PrintClass)
1213 puts (vStringValue (token->className));
1214 if (! PrintReferences)
1215 exit (0);
1216 readToken (token);
1217 #endif
1222 if (isType (token, TOKEN_OPEN_BRACKET))
1223 parseGeneric (token, TRUE);
1224 else if (! isType (token, TOKEN_KEYWORD))
1225 readToken (token);
1226 else switch (token->keyword)
1228 case KEYWORD_inherit: parseInherit (token); break;
1229 case KEYWORD_feature: parseFeatureClauses (token); break;
1230 case KEYWORD_convert: parseConvert (token); break;
1231 default: readToken (token); break;
1233 } while (! isKeyword (token, KEYWORD_end));
1236 static void initialize (const langType language)
1238 Lang_eiffel = language;
1239 buildEiffelKeywordHash ();
1242 static void findEiffelTags (void)
1244 tokenInfo *const token = newToken ();
1245 exception_t exception;
1247 exception = (exception_t) (setjmp (Exception));
1248 while (exception == ExceptionNone)
1250 findKeyword (token, KEYWORD_class);
1251 parseClass (token);
1253 deleteToken (token);
1256 #ifndef TYPE_REFERENCE_TOOL
1258 extern parserDefinition* EiffelParser (void)
1260 static const char *const extensions [] = { "e", NULL };
1261 parserDefinition* def = parserNew ("Eiffel");
1262 def->kinds = EiffelKinds;
1263 def->kindCount = KIND_COUNT (EiffelKinds);
1264 def->extensions = extensions;
1265 def->parser = findEiffelTags;
1266 def->initialize = initialize;
1267 return def;
1270 #else
1272 static void findReferences (void)
1274 ReferencedTypes = stringListNew ();
1275 GenericNames = stringListNew ();
1276 initialize (0);
1278 findEiffelTags ();
1280 stringListDelete (GenericNames);
1281 GenericNames = NULL;
1282 stringListDelete (ReferencedTypes);
1283 ReferencedTypes = NULL;
1286 static const char *const Usage =
1287 "Prints names of types referenced by an Eiffel language file.\n"
1288 "\n"
1289 "Usage: %s [-cdrs] [file_name | -]\n"
1290 "\n"
1291 "Options:\n"
1292 " -c Print class name of current file (on first line of output).\n"
1293 " -d Enable debug output.\n"
1294 " -r Print types referenced by current file (default unless -c).\n"
1295 " -s Include self-references.\n"
1296 "\n";
1298 extern int main (int argc, char** argv)
1300 int i;
1301 for (i = 1 ; argv [i] != NULL ; ++i)
1303 const char *const arg = argv [i];
1304 if (arg [0] == '-')
1306 int j;
1307 if (arg [1] == '\0')
1309 File = stdin;
1310 FileName = "stdin";
1312 else for (j = 1 ; arg [j] != '\0' ; ++j) switch (arg [j])
1314 case 'c': PrintClass = 1; break;
1315 case 'r': PrintReferences = 1; break;
1316 case 's': SelfReferences = 1; break;
1317 case 'd': Debug = 1; break;
1318 default:
1319 fprintf (errout, "%s: unknown option: %c\n", argv [0], arg [1]);
1320 fprintf (errout, Usage, argv [0]);
1321 exit (1);
1322 break;
1325 else if (File != NULL)
1327 fprintf (errout, Usage, argv [0]);
1328 exit (1);
1330 else
1332 FileName = arg;
1333 File = fopen (FileName, "r");
1334 if (File == NULL)
1336 perror (argv [0]);
1337 exit (1);
1341 if (! PrintClass)
1342 PrintReferences = 1;
1343 if (File == NULL)
1345 fprintf (errout, Usage, argv [0]);
1346 exit (1);
1348 else
1350 findReferences ();
1351 fclose (File);
1353 return 0;
1356 #endif
1358 /* vi:set tabstop=4 shiftwidth=4: */