Merge commit 'git-svn'
[anjuta-git-plugin.git] / tagmanager / eiffel.c
blobf8b90dc320bbaf7d536c683d6c2114558bb40392
1 /*
2 * $Id$
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, KEYWORD_as, KEYWORD_check,
57 KEYWORD_class, KEYWORD_create, KEYWORD_creation, KEYWORD_Current,
58 KEYWORD_debug, KEYWORD_deferred, KEYWORD_do, KEYWORD_else,
59 KEYWORD_elseif, KEYWORD_end, KEYWORD_ensure, KEYWORD_expanded,
60 KEYWORD_export, KEYWORD_external, KEYWORD_false, KEYWORD_feature,
61 KEYWORD_from, KEYWORD_frozen, KEYWORD_if, KEYWORD_implies,
62 KEYWORD_indexing, KEYWORD_infix, KEYWORD_inherit, KEYWORD_inspect,
63 KEYWORD_invariant, KEYWORD_is, KEYWORD_like, KEYWORD_local,
64 KEYWORD_loop, KEYWORD_not, KEYWORD_obsolete, KEYWORD_old, KEYWORD_once,
65 KEYWORD_or, KEYWORD_prefix, KEYWORD_redefine, KEYWORD_rename,
66 KEYWORD_require, KEYWORD_rescue, KEYWORD_Result, KEYWORD_retry,
67 KEYWORD_select, KEYWORD_separate, KEYWORD_strip, KEYWORD_then,
68 KEYWORD_true, KEYWORD_undefine, KEYWORD_unique, KEYWORD_until,
69 KEYWORD_variant, KEYWORD_when, KEYWORD_xor
70 } keywordId;
72 /* Used to determine whether keyword is valid for the token language and
73 * what its ID is.
75 typedef struct sKeywordDesc {
76 const char *name;
77 keywordId id;
78 } keywordDesc;
80 typedef enum eTokenType {
81 TOKEN_UNDEFINED,
82 TOKEN_BANG,
83 TOKEN_CHARACTER,
84 TOKEN_CLOSE_BRACE,
85 TOKEN_CLOSE_BRACKET,
86 TOKEN_CLOSE_PAREN,
87 TOKEN_COLON,
88 TOKEN_COMMA,
89 TOKEN_CONSTRAINT,
90 TOKEN_DOT,
91 TOKEN_DOLLAR,
92 TOKEN_IDENTIFIER,
93 TOKEN_KEYWORD,
94 TOKEN_NUMERIC,
95 TOKEN_OPEN_BRACE,
96 TOKEN_OPEN_BRACKET,
97 TOKEN_OPEN_PAREN,
98 TOKEN_OPERATOR,
99 TOKEN_OTHER,
100 TOKEN_SEPARATOR,
101 TOKEN_STRING,
102 TOKEN_TILDE
103 } tokenType;
105 typedef struct sTokenInfo {
106 tokenType type;
107 keywordId keyword;
108 boolean isExported;
109 vString* string;
110 vString* className;
111 vString* featureName;
112 } tokenInfo;
115 * DATA DEFINITIONS
118 static langType Lang_eiffel;
120 #ifdef TYPE_REFERENCE_TOOL
122 static const char *FileName;
123 static FILE *File;
124 static int PrintClass;
125 static int PrintReferences;
126 static int SelfReferences;
127 static int Debug;
128 static stringList *GenericNames;
129 static stringList *ReferencedTypes;
131 #else
133 typedef enum {
134 EKIND_CLASS, EKIND_FEATURE, EKIND_LOCAL, EKIND_QUALIFIED_TAGS
135 } eiffelKind;
137 static kindOption EiffelKinds [] = {
138 { TRUE, 'c', "class", "classes"},
139 { TRUE, 'f', "feature", "features"},
140 { FALSE, 'l', "local", "local entities"}
143 #endif
145 static langType Lang_eiffel;
147 static jmp_buf Exception;
149 static const keywordDesc EiffelKeywordTable [] = {
150 /* keyword keyword ID */
151 { "alias", KEYWORD_alias },
152 { "all", KEYWORD_all },
153 { "and", KEYWORD_and },
154 { "as", KEYWORD_as },
155 { "check", KEYWORD_check },
156 { "class", KEYWORD_class },
157 { "create", KEYWORD_create },
158 { "creation", KEYWORD_creation },
159 { "current", KEYWORD_Current },
160 { "debug", KEYWORD_debug },
161 { "deferred", KEYWORD_deferred },
162 { "do", KEYWORD_do },
163 { "else", KEYWORD_else },
164 { "elseif", KEYWORD_elseif },
165 { "end", KEYWORD_end },
166 { "ensure", KEYWORD_ensure },
167 { "expanded", KEYWORD_expanded },
168 { "export", KEYWORD_export },
169 { "external", KEYWORD_external },
170 { "false", KEYWORD_false },
171 { "feature", KEYWORD_feature },
172 { "from", KEYWORD_from },
173 { "frozen", KEYWORD_frozen },
174 { "if", KEYWORD_if },
175 { "implies", KEYWORD_implies },
176 { "indexing", KEYWORD_indexing },
177 { "infix", KEYWORD_infix },
178 { "inherit", KEYWORD_inherit },
179 { "inspect", KEYWORD_inspect },
180 { "invariant", KEYWORD_invariant },
181 { "is", KEYWORD_is },
182 { "like", KEYWORD_like },
183 { "local", KEYWORD_local },
184 { "loop", KEYWORD_loop },
185 { "not", KEYWORD_not },
186 { "obsolete", KEYWORD_obsolete },
187 { "old", KEYWORD_old },
188 { "once", KEYWORD_once },
189 { "or", KEYWORD_or },
190 { "prefix", KEYWORD_prefix },
191 { "redefine", KEYWORD_redefine },
192 { "rename", KEYWORD_rename },
193 { "require", KEYWORD_require },
194 { "rescue", KEYWORD_rescue },
195 { "result", KEYWORD_Result },
196 { "retry", KEYWORD_retry },
197 { "select", KEYWORD_select },
198 { "separate", KEYWORD_separate },
199 { "strip", KEYWORD_strip },
200 { "then", KEYWORD_then },
201 { "true", KEYWORD_true },
202 { "undefine", KEYWORD_undefine },
203 { "unique", KEYWORD_unique },
204 { "until", KEYWORD_until },
205 { "variant", KEYWORD_variant },
206 { "when", KEYWORD_when },
207 { "xor", KEYWORD_xor }
211 * FUNCTION DEFINITIONS
214 static void buildEiffelKeywordHash (void)
216 const size_t count = sizeof (EiffelKeywordTable) /
217 sizeof (EiffelKeywordTable [0]);
218 size_t i;
219 for (i = 0 ; i < count ; ++i)
221 const keywordDesc* const p = &EiffelKeywordTable [i];
222 addKeyword (p->name, Lang_eiffel, (int) p->id);
226 #ifdef TYPE_REFERENCE_TOOL
228 static void addGenericName (tokenInfo *const token)
230 vStringUpper (token->string);
231 if (vStringLength (token->string) > 0)
232 stringListAdd (GenericNames, vStringNewCopy (token->string));
235 static boolean isGeneric (tokenInfo *const token)
237 return (boolean) stringListHas (GenericNames, vStringValue (token->string));
240 static void reportType (tokenInfo *const token)
242 vStringUpper (token->string);
243 if (vStringLength (token->string) > 0 && ! isGeneric (token) &&
244 (SelfReferences || strcmp (vStringValue (
245 token->string), vStringValue (token->className)) != 0) &&
246 ! stringListHas (ReferencedTypes, vStringValue (token->string)))
248 printf ("%s\n", vStringValue (token->string));
249 stringListAdd (ReferencedTypes, vStringNewCopy (token->string));
253 static int fileGetc (void)
255 int c = getc (File);
256 if (c == '\r')
258 c = getc (File);
259 if (c != '\n')
261 ungetc (c, File);
262 c = '\n';
265 if (Debug > 0 && c != EOF)
266 putc (c, errout);
267 return c;
270 static int fileUngetc (c)
272 return ungetc (c, File);
275 extern char *readLine (vString *const vLine, FILE *const fp)
277 return NULL;
280 #else
283 * Tag generation functions
286 static void makeEiffelClassTag (tokenInfo *const token)
288 if (EiffelKinds [EKIND_CLASS].enabled)
290 const char *const name = vStringValue (token->string);
291 tagEntryInfo e;
293 initTagEntry (&e, name);
295 e.kindName = EiffelKinds [EKIND_CLASS].name;
296 e.kind = EiffelKinds [EKIND_CLASS].letter;
298 makeTagEntry (&e);
300 vStringCopy (token->className, token->string);
303 static void makeEiffelFeatureTag (tokenInfo *const token)
305 if (EiffelKinds [EKIND_FEATURE].enabled &&
306 (token->isExported || Option.include.fileScope))
308 const char *const name = vStringValue (token->string);
309 tagEntryInfo e;
311 initTagEntry (&e, name);
313 e.isFileScope = (boolean) (! token->isExported);
314 e.kindName = EiffelKinds [EKIND_FEATURE].name;
315 e.kind = EiffelKinds [EKIND_FEATURE].letter;
316 e.extensionFields.scope [0] = EiffelKinds [EKIND_CLASS].name;
317 e.extensionFields.scope [1] = vStringValue (token->className);
319 makeTagEntry (&e);
321 if (Option.include.qualifiedTags)
323 vString* qualified = vStringNewInit (vStringValue (token->className));
324 vStringPut (qualified, '.');
325 vStringCat (qualified, token->string);
326 e.name = vStringValue (qualified);
327 makeTagEntry (&e);
328 vStringDelete (qualified);
331 vStringCopy (token->featureName, token->string);
334 static void makeEiffelLocalTag (tokenInfo *const token)
336 if (EiffelKinds [EKIND_LOCAL].enabled && Option.include.fileScope)
338 const char *const name = vStringValue (token->string);
339 vString* scope = vStringNew ();
340 tagEntryInfo e;
342 initTagEntry (&e, name);
344 e.isFileScope = TRUE;
345 e.kindName = EiffelKinds [EKIND_LOCAL].name;
346 e.kind = EiffelKinds [EKIND_LOCAL].letter;
348 vStringCopy (scope, token->className);
349 vStringPut (scope, '.');
350 vStringCat (scope, token->featureName);
352 e.extensionFields.scope [0] = EiffelKinds [EKIND_FEATURE].name;
353 e.extensionFields.scope [1] = vStringValue (scope);
355 makeTagEntry (&e);
356 vStringDelete (scope);
360 #endif
363 * Parsing functions
366 static int skipToCharacter (const int c)
368 int d;
372 d = fileGetc ();
373 } while (d != EOF && d != c);
375 return d;
378 /* If a numeric is passed in 'c', this is used as the first digit of the
379 * numeric being parsed.
381 static vString *parseInteger (int c)
383 static vString *string = NULL;
385 if (string == NULL)
386 string = vStringNew ();
387 vStringClear (string);
389 if (c == '\0')
390 c = fileGetc ();
391 if (c == '-')
393 vStringPut (string, c);
394 c = fileGetc ();
396 else if (! isdigit (c))
397 c = fileGetc ();
398 while (c != EOF && (isdigit (c) || c == '_'))
400 vStringPut (string, c);
401 c = fileGetc ();
403 vStringTerminate (string);
404 fileUngetc (c);
406 return string;
409 static vString *parseNumeric (int c)
411 static vString *string = NULL;
413 if (string == NULL)
414 string = vStringNew ();
415 vStringCopy (string, parseInteger (c));
417 c = fileGetc ();
418 if (c == '.')
420 vStringPut (string, c);
421 vStringCat (string, parseInteger ('\0'));
422 c = fileGetc ();
424 if (tolower (c) == 'e')
426 vStringPut (string, c);
427 vStringCat (string, parseInteger ('\0'));
429 else if (!isspace (c))
430 fileUngetc (c);
432 vStringTerminate (string);
434 return string;
437 static int parseEscapedCharacter (void)
439 int d = '\0';
440 int c = fileGetc ();
442 switch (c)
444 case 'A': d = '@'; break;
445 case 'B': d = '\b'; break;
446 case 'C': d = '^'; break;
447 case 'D': d = '$'; break;
448 case 'F': d = '\f'; break;
449 case 'H': d = '\\'; break;
450 case 'L': d = '~'; break;
451 case 'N': d = '\n'; break;
452 #ifdef QDOS
453 case 'Q': d = 0x9F; break;
454 #else
455 case 'Q': d = '`'; break;
456 #endif
457 case 'R': d = '\r'; break;
458 case 'S': d = '#'; break;
459 case 'T': d = '\t'; break;
460 case 'U': d = '\0'; break;
461 case 'V': d = '|'; break;
462 case '%': d = '%'; break;
463 case '\'': d = '\''; break;
464 case '"': d = '"'; break;
465 case '(': d = '['; break;
466 case ')': d = ']'; break;
467 case '<': d = '{'; break;
468 case '>': d = '}'; break;
470 case '\n': skipToCharacter ('%'); break;
472 case '/':
474 vString *string = parseInteger ('\0');
475 const char *value = vStringValue (string);
476 const unsigned long ascii = atol (value);
478 c = fileGetc ();
479 if (c == '/' && ascii < 256)
480 d = ascii;
481 break;
484 default: break;
486 return d;
489 static int parseCharacter (void)
491 int c = fileGetc ();
492 int result = c;
494 if (c == '%')
495 result = parseEscapedCharacter ();
497 c = fileGetc ();
498 if (c != '\'')
499 skipToCharacter ('\n');
501 return result;
504 static void parseString (vString *const string)
506 boolean verbatim = FALSE;
507 boolean align = FALSE;
508 boolean end = FALSE;
509 vString *verbatimCloser = NULL;
510 vString *lastLine = NULL;
511 int prev = '\0';
512 int c;
514 while (! end)
516 c = fileGetc ();
517 if (c == EOF)
518 end = TRUE;
519 else if (c == '"')
521 if (! verbatim)
522 end = TRUE;
523 else
524 end = (boolean) (strcmp (vStringValue (lastLine),
525 vStringValue (verbatimCloser)) == 0);
527 else if (c == '\n')
529 if (verbatim)
530 vStringClear (lastLine);
531 if (prev == '[' /* || prev == '{' */)
533 verbatim = TRUE;
534 verbatimCloser = vStringNew ();
535 lastLine = vStringNew ();
536 if (prev == '{')
537 vStringPut (verbatimCloser, '}');
538 else
540 vStringPut (verbatimCloser, ']');
541 align = TRUE;
543 vStringNCat (verbatimCloser, string, vStringLength (string) - 1);
544 vStringClear (string);
546 if (verbatim && align)
549 c = fileGetc ();
550 while (isspace (c));
553 else if (c == '%')
554 c = parseEscapedCharacter ();
555 if (! end)
557 vStringPut (string, c);
558 if (verbatim)
560 vStringPut (lastLine, c);
561 vStringTerminate (lastLine);
563 prev = c;
566 vStringTerminate (string);
569 /* Read a C identifier beginning with "firstChar" and places it into "name".
571 static void parseIdentifier (vString *const string, const int firstChar)
573 int c = firstChar;
577 vStringPut (string, c);
578 c = fileGetc ();
579 } while (isident (c));
581 vStringTerminate (string);
582 if (!isspace (c))
583 fileUngetc (c); /* unget non-identifier character */
586 static void parseFreeOperator (vString *const string, const int firstChar)
588 int c = firstChar;
592 vStringPut (string, c);
593 c = fileGetc ();
594 } while (c > ' ');
596 vStringTerminate (string);
597 if (!isspace (c))
598 fileUngetc (c); /* unget non-identifier character */
601 static keywordId analyzeToken (vString *const name)
603 static vString *keyword = NULL;
604 keywordId id;
606 if (keyword == NULL)
607 keyword = vStringNew ();
608 vStringCopyToLower (keyword, name);
609 id = (keywordId) lookupKeyword (vStringValue (keyword), Lang_eiffel);
611 return id;
614 static void readToken (tokenInfo *const token)
616 int c;
618 token->type = TOKEN_UNDEFINED;
619 token->keyword = KEYWORD_NONE;
620 vStringClear (token->string);
622 getNextChar:
625 c = fileGetc ();
626 while (c == '\t' || c == ' ' || c == '\n');
628 switch (c)
630 case EOF: longjmp (Exception, (int)ExceptionEOF); break;
631 case '!': token->type = TOKEN_BANG; break;
632 case '$': token->type = TOKEN_DOLLAR; break;
633 case '(': token->type = TOKEN_OPEN_PAREN; break;
634 case ')': token->type = TOKEN_CLOSE_PAREN; break;
635 case ',': token->type = TOKEN_COMMA; break;
636 case '.': token->type = TOKEN_DOT; break;
637 case ';': goto getNextChar;
638 case '[': token->type = TOKEN_OPEN_BRACKET; break;
639 case ']': token->type = TOKEN_CLOSE_BRACKET; break;
640 case '{': token->type = TOKEN_OPEN_BRACE; break;
641 case '}': token->type = TOKEN_CLOSE_BRACE; break;
642 case '~': token->type = TOKEN_TILDE; break;
645 case '+':
646 case '*':
647 case '^':
648 case '=': token->type = TOKEN_OPERATOR; break;
650 case '-':
651 c = fileGetc ();
652 if (c == '>')
653 token->type = TOKEN_CONSTRAINT;
654 else if (c == '-') /* is this the start of a comment? */
656 skipToCharacter ('\n');
657 goto getNextChar;
659 else
661 if (!isspace (c))
662 fileUngetc (c);
663 token->type = TOKEN_OPERATOR;
665 break;
667 case '?':
668 case ':':
669 c = fileGetc ();
670 if (c == '=')
671 token->type = TOKEN_OPERATOR;
672 else
674 token->type = TOKEN_COLON;
675 if (!isspace (c))
676 fileUngetc (c);
678 break;
680 case '<':
681 c = fileGetc ();
682 if (c != '=' && c != '>' && !isspace (c))
683 fileUngetc (c);
684 token->type = TOKEN_OPERATOR;
685 break;
687 case '>':
688 c = fileGetc ();
689 if (c != '=' && c != '>' && !isspace (c))
690 fileUngetc (c);
691 token->type = TOKEN_OPERATOR;
692 break;
694 case '/':
695 c = fileGetc ();
696 if (c != '/' && c != '=' && !isspace (c))
697 fileUngetc (c);
698 token->type = TOKEN_OPERATOR;
699 break;
701 case '\\':
702 c = fileGetc ();
703 if (c != '\\' && !isspace (c))
704 fileUngetc (c);
705 token->type = TOKEN_OPERATOR;
706 break;
708 case '"':
709 token->type = TOKEN_STRING;
710 parseString (token->string);
711 break;
713 case '\'':
714 token->type = TOKEN_CHARACTER;
715 parseCharacter ();
716 break;
718 default:
719 if (isalpha (c))
721 parseIdentifier (token->string, c);
722 token->keyword = analyzeToken (token->string);
723 if (isKeyword (token, KEYWORD_NONE))
724 token->type = TOKEN_IDENTIFIER;
725 else
726 token->type = TOKEN_KEYWORD;
728 else if (isdigit (c))
730 vStringCat (token->string, parseNumeric (c));
731 token->type = TOKEN_NUMERIC;
733 else if (isFreeOperatorChar (c))
735 parseFreeOperator (token->string, c);
736 token->type = TOKEN_OPERATOR;
738 else
740 token->type = TOKEN_UNDEFINED;
741 Assert (! isType (token, TOKEN_UNDEFINED));
743 break;
748 * Scanning functions
751 static boolean isIdentifierMatch (
752 const tokenInfo *const token, const char *const name)
754 return (boolean) (isType (token, TOKEN_IDENTIFIER) &&
755 strcasecmp (vStringValue (token->string), name) == 0);
758 static void findToken (tokenInfo *const token, const tokenType type)
760 while (! isType (token, type))
761 readToken (token);
764 static void findKeyword (tokenInfo *const token, const keywordId keyword)
766 while (! isKeyword (token, keyword))
767 readToken (token);
770 static void parseGeneric (tokenInfo *const token, boolean declaration __unused__)
772 unsigned int depth = 0;
773 #ifdef TYPE_REFERENCE_TOOL
774 boolean constraint = FALSE;
775 #endif
776 Assert (isType (token, TOKEN_OPEN_BRACKET));
779 if (isType (token, TOKEN_OPEN_BRACKET))
780 ++depth;
781 else if (isType (token, TOKEN_CLOSE_BRACKET))
782 --depth;
783 #ifdef TYPE_REFERENCE_TOOL
784 else if (declaration)
786 if (depth == 1)
788 if (isType (token, TOKEN_CONSTRAINT))
789 constraint = TRUE;
790 else if (isKeyword (token, KEYWORD_create))
791 findKeyword (token, KEYWORD_end);
792 else if (isType (token, TOKEN_IDENTIFIER))
794 if (constraint)
795 reportType (token);
796 else
797 addGenericName (token);
798 constraint = FALSE;
801 else if (isKeyword (token, KEYWORD_like))
802 readToken (token);
803 else if (isType (token, TOKEN_IDENTIFIER))
804 reportType (token);
806 else
808 if (isType (token, TOKEN_OPEN_BRACKET))
809 ++depth;
810 else if (isType (token, TOKEN_IDENTIFIER))
811 reportType (token);
812 else if (isKeyword (token, KEYWORD_like))
813 readToken (token);
815 #endif
816 readToken (token);
817 } while (depth > 0);
820 static void parseType (tokenInfo *const token)
822 boolean bitType;
823 Assert (isType (token, TOKEN_IDENTIFIER));
824 #ifdef TYPE_REFERENCE_TOOL
825 reportType (token);
826 #endif
827 bitType = (boolean)(strcmp ("BIT", vStringValue (token->string)) == 0);
828 readToken (token);
829 if (bitType && isType (token, TOKEN_NUMERIC))
830 readToken (token);
831 else if (isType (token, TOKEN_OPEN_BRACKET))
832 parseGeneric (token, FALSE);
835 static void parseEntityType (tokenInfo *const token)
837 Assert (isType (token, TOKEN_COLON));
838 readToken (token);
840 if (isKeyword (token, KEYWORD_expanded))
841 readToken (token);
843 /* Skip over the type name, with possible generic parameters.
845 if (isType (token, TOKEN_IDENTIFIER))
846 parseType (token);
847 else if (isKeyword (token, KEYWORD_like))
849 readToken (token);
850 if (isType (token, TOKEN_IDENTIFIER) ||
851 isKeyword (token, KEYWORD_Current))
852 readToken (token);
857 static void parseLocal (tokenInfo *const token)
859 Assert (isKeyword (token, KEYWORD_local));
860 readToken (token);
862 /* Check keyword first in case local clause is empty
864 while (! isKeyword (token, KEYWORD_do) &&
865 ! isKeyword (token, KEYWORD_once))
867 #ifndef TYPE_REFERENCE_TOOL
868 if (isType (token, TOKEN_IDENTIFIER))
869 makeEiffelLocalTag (token);
870 #endif
871 readToken (token);
872 if (isType (token, TOKEN_COLON))
874 readToken (token);
875 if (isType (token, TOKEN_IDENTIFIER))
876 parseType (token);
881 static void findFeatureEnd (tokenInfo *const token)
883 readToken (token);
885 switch (token->keyword)
887 default:
888 if (isType (token, TOKEN_OPERATOR)) /* sign of manifest constant */
889 readToken (token);
890 readToken (token); /* skip to next token after constant */
891 break;
893 case KEYWORD_deferred:
894 case KEYWORD_do:
895 case KEYWORD_external:
896 case KEYWORD_local:
897 case KEYWORD_obsolete:
898 case KEYWORD_once:
899 case KEYWORD_require:
901 int depth = 1;
903 while (depth > 0)
905 #ifdef TYPE_REFERENCE_TOOL
906 if (isType (token, TOKEN_OPEN_BRACE))
908 readToken (token);
909 if (isType (token, TOKEN_IDENTIFIER))
910 parseType (token);
912 else if (isType (token, TOKEN_BANG))
914 readToken (token);
915 if (isType (token, TOKEN_IDENTIFIER))
916 parseType (token);
917 if (isType (token, TOKEN_BANG))
918 readToken (token);
920 else
921 #endif
922 switch (token->keyword)
924 case KEYWORD_check:
925 case KEYWORD_debug:
926 case KEYWORD_from:
927 case KEYWORD_if:
928 case KEYWORD_inspect:
929 ++depth;
930 break;
932 case KEYWORD_local:
933 parseLocal (token);
934 break;
936 case KEYWORD_end:
937 --depth;
938 break;
940 default:
941 break;
943 readToken (token);
945 break;
950 static boolean readFeatureName (tokenInfo *const token)
952 boolean isFeatureName = FALSE;
954 if (isKeyword (token, KEYWORD_frozen))
955 readToken (token);
956 if (isType (token, TOKEN_IDENTIFIER))
957 isFeatureName = TRUE;
958 else if (isKeyword (token, KEYWORD_infix) ||
959 isKeyword (token, KEYWORD_prefix))
961 readToken (token);
962 if (isType (token, TOKEN_STRING))
963 isFeatureName = TRUE;
965 return isFeatureName;
968 static void parseArguments (tokenInfo *const token)
970 #ifndef TYPE_REFERENCE_TOOL
971 findToken (token, TOKEN_CLOSE_PAREN);
972 readToken (token);
973 #else
974 Assert (isType (token, TOKEN_OPEN_PAREN));
975 readToken (token);
978 if (! isType (token, TOKEN_COLON))
979 readToken (token);
980 else
982 readToken (token);
983 if (isType (token, TOKEN_IDENTIFIER))
984 parseType (token);
986 } while (! isType (token, TOKEN_CLOSE_PAREN));
987 readToken (token);
988 #endif
991 static boolean parseFeature (tokenInfo *const token)
993 boolean found = FALSE;
994 while (readFeatureName (token))
996 found = TRUE;
997 #ifndef TYPE_REFERENCE_TOOL
998 makeEiffelFeatureTag (token);
999 #endif
1000 readToken (token);
1001 if (isType (token, TOKEN_COMMA))
1002 readToken (token);
1004 if (found)
1006 if (isType (token, TOKEN_OPEN_PAREN)) /* arguments? */
1007 parseArguments (token);
1008 if (isType (token, TOKEN_COLON)) /* a query? */
1009 parseEntityType (token);
1010 if (isKeyword (token, KEYWORD_obsolete))
1012 readToken (token);
1013 if (isType (token, TOKEN_STRING))
1014 readToken (token);
1016 if (isKeyword (token, KEYWORD_is))
1017 findFeatureEnd (token);
1019 return found;
1022 static void parseExport (tokenInfo *const token)
1024 token->isExported = TRUE;
1025 readToken (token);
1026 if (isType (token, TOKEN_OPEN_BRACE))
1028 token->isExported = FALSE;
1029 while (! isType (token, TOKEN_CLOSE_BRACE))
1031 if (isType (token, TOKEN_IDENTIFIER))
1032 token->isExported |= !isIdentifierMatch (token, "NONE");
1033 readToken (token);
1035 readToken (token);
1039 static void parseFeatureClauses (tokenInfo *const token)
1041 Assert (isKeyword (token, KEYWORD_feature));
1044 if (isKeyword (token, KEYWORD_feature))
1045 parseExport (token);
1046 if (! isKeyword (token, KEYWORD_feature) &&
1047 ! isKeyword (token, KEYWORD_invariant) &&
1048 ! isKeyword (token, KEYWORD_indexing))
1050 if (! parseFeature (token))
1051 readToken (token);
1053 } while (! isKeyword (token, KEYWORD_end) &&
1054 ! isKeyword (token, KEYWORD_invariant) &&
1055 ! isKeyword (token, KEYWORD_indexing));
1058 static void parseRename (tokenInfo *const token)
1060 do {
1061 readToken (token);
1062 if (readFeatureName (token))
1064 readToken (token);
1065 if (isKeyword (token, KEYWORD_as))
1067 readToken (token);
1068 if (readFeatureName (token))
1070 #ifndef TYPE_REFERENCE_TOOL
1071 makeEiffelFeatureTag (token); /* renamed feature */
1072 #endif
1073 readToken (token);
1077 } while (isType (token, TOKEN_COMMA));
1079 findKeyword (token, KEYWORD_end);
1080 readToken (token);
1084 static void parseInherit (tokenInfo *const token)
1086 Assert (isKeyword (token, KEYWORD_inherit));
1087 #ifdef TYPE_REFERENCE_TOOL
1088 readToken (token);
1089 while (isType (token, TOKEN_IDENTIFIER))
1091 parseType (token);
1092 if (isType (token, TOKEN_KEYWORD))
1094 switch (token->keyword) /* check for feature adaptation */
1096 case KEYWORD_rename:
1097 case KEYWORD_export:
1098 case KEYWORD_undefine:
1099 case KEYWORD_redefine:
1100 case KEYWORD_select:
1101 findKeyword (token, KEYWORD_end);
1102 readToken (token);
1103 default: break;
1107 #else
1108 readToken (token);
1109 while (isType (token, TOKEN_IDENTIFIER))
1111 parseType (token);
1112 switch (token->keyword) /* check for feature adaptation */
1114 case KEYWORD_rename:
1115 parseRename (token);
1116 if (isKeyword (token, KEYWORD_end))
1117 readToken (token);
1118 break;
1120 case KEYWORD_export:
1121 case KEYWORD_undefine:
1122 case KEYWORD_redefine:
1123 case KEYWORD_select:
1124 findKeyword (token, KEYWORD_end);
1125 readToken (token);
1126 break;
1128 case KEYWORD_end:
1129 readToken (token);
1130 break;
1132 default: break;
1135 #endif
1138 static void parseClass (tokenInfo *const token)
1140 Assert (isKeyword (token, KEYWORD_class));
1141 readToken (token);
1142 if (isType (token, TOKEN_IDENTIFIER))
1144 #ifndef TYPE_REFERENCE_TOOL
1145 makeEiffelClassTag (token);
1146 readToken (token);
1147 #else
1148 vStringCopy (token->className, token->string);
1149 vStringUpper (token->className);
1150 if (PrintClass)
1151 puts (vStringValue (token->className));
1152 if (! PrintReferences)
1153 exit (0);
1154 readToken (token);
1155 #endif
1160 if (isType (token, TOKEN_OPEN_BRACKET))
1161 parseGeneric (token, TRUE);
1162 else if (! isType (token, TOKEN_KEYWORD))
1163 readToken (token);
1164 else switch (token->keyword)
1166 case KEYWORD_inherit: parseInherit (token); break;
1167 case KEYWORD_feature: parseFeatureClauses (token); break;
1168 default: readToken (token); break;
1170 } while (! isKeyword (token, KEYWORD_end));
1173 static tokenInfo *newToken (void)
1175 tokenInfo *const token = xMalloc (1, tokenInfo);
1177 token->type = TOKEN_UNDEFINED;
1178 token->keyword = KEYWORD_NONE;
1179 token->isExported = TRUE;
1181 token->string = vStringNew ();
1182 token->className = vStringNew ();
1183 token->featureName = vStringNew ();
1185 return token;
1188 static void deleteToken (tokenInfo *const token)
1190 vStringDelete (token->string);
1191 vStringDelete (token->className);
1192 vStringDelete (token->featureName);
1194 eFree (token);
1197 static void initialize (const langType language)
1199 Lang_eiffel = language;
1200 buildEiffelKeywordHash ();
1203 static void findEiffelTags (void)
1205 tokenInfo *const token = newToken ();
1206 exception_t exception;
1208 exception = (exception_t) (setjmp (Exception));
1209 while (exception == ExceptionNone)
1211 findKeyword (token, KEYWORD_class);
1212 parseClass (token);
1214 deleteToken (token);
1217 #ifndef TYPE_REFERENCE_TOOL
1219 extern parserDefinition* EiffelParser (void)
1221 static const char *const extensions [] = { "e", NULL };
1222 parserDefinition* def = parserNew ("Eiffel");
1223 def->kinds = EiffelKinds;
1224 def->kindCount = KIND_COUNT (EiffelKinds);
1225 def->extensions = extensions;
1226 def->parser = findEiffelTags;
1227 def->initialize = initialize;
1228 return def;
1231 #else
1233 static void findReferences (void)
1235 ReferencedTypes = stringListNew ();
1236 GenericNames = stringListNew ();
1237 initialize (0);
1239 findEiffelTags ();
1241 stringListDelete (GenericNames);
1242 GenericNames = NULL;
1243 stringListDelete (ReferencedTypes);
1244 ReferencedTypes = NULL;
1247 static const char *const Usage =
1248 "Prints names of types referenced by an Eiffel language file.\n"
1249 "\n"
1250 "Usage: %s [-cdrs] [file_name | -]\n"
1251 "\n"
1252 "Options:\n"
1253 " -c Print class name of current file (on first line of output).\n"
1254 " -d Enable debug output.\n"
1255 " -r Print types referenced by current file (default unless -c).\n"
1256 " -s Include self-references.\n"
1257 "\n";
1259 extern int main (int argc, char** argv)
1261 int i;
1262 for (i = 1 ; argv [i] != NULL ; ++i)
1264 const char *const arg = argv [i];
1265 if (arg [0] == '-')
1267 int j;
1268 if (arg [1] == '\0')
1270 File = stdin;
1271 FileName = "stdin";
1273 else for (j = 1 ; arg [j] != '\0' ; ++j) switch (arg [j])
1275 case 'c': PrintClass = 1; break;
1276 case 'r': PrintReferences = 1; break;
1277 case 's': SelfReferences = 1; break;
1278 case 'd': Debug = 1; break;
1279 default:
1280 fprintf (errout, "%s: unknown option: %c\n", argv [0], arg [1]);
1281 fprintf (errout, Usage, argv [0]);
1282 exit (1);
1283 break;
1286 else if (File != NULL)
1288 fprintf (errout, Usage, argv [0]);
1289 exit (1);
1291 else
1293 FileName = arg;
1294 File = fopen (FileName, "r");
1295 if (File == NULL)
1297 perror (argv [0]);
1298 exit (1);
1302 if (! PrintClass)
1303 PrintReferences = 1;
1304 if (File == NULL)
1306 fprintf (errout, Usage, argv [0]);
1307 exit (1);
1309 else
1311 findReferences ();
1312 fclose (File);
1314 return 0;
1317 #endif
1319 /* vi:set tabstop=4 shiftwidth=4: */