Small update of German translation
[geany-mirror.git] / ctags / parsers / ada.c
blob3b837bfb9693792e8f80f85eb0cde2c35ad6d31f
1 /* File: Ada.c
2 * Description: Enables extended Ada parsing support in Exuberant Ctags
3 * Version: 0.6
4 * Date: October 26, 2006
5 * Author: A. Aaron Cornelius (ADotAaronDotCorneliusAtgmailDotcom)
6 * License: GPL-2
8 * Installation:
9 * You must have the Exuberant Ctags source to install this parser. Once you
10 * have the source, place this file into the directory with the rest of the
11 * ctags source. After ada.c is in the correct directory you need to make the
12 * following changes so that the ada parser is included when you compile and
13 * install ctags:
15 * to file source.mak add the line
16 * ada.c \
18 * after
19 * SOURCES = \
21 * then add the line
22 * ada.$(OBJECT) \
24 * after
25 * OBJECTS = \
27 * to file parsers.h add the line
28 * AdaParser, \
30 * after
31 * #define PARSER_LIST \
33 * Then compile and install ctags as normal (usually: './configure', './make',
34 * './make install').
36 * Changelog:
38 * 11/02/2006 - Completed implementation of file scope info and qualified tags
39 * information gathering.
40 * 11/02/2006 - Added recognition of private flag in a token for file scope
41 * checking purposes.
42 * 10/27/2006 - Added full package scope name when --extra=+q is set.
43 * 10/27/2006 - Fixed file scope setting, and added check to verify that tags
44 * with file scope should be included in the tag file.
45 * 10/26/2006 - Fixed error which caused infinite loop when parsing some
46 * files.
47 * 0.5 - Bugfixes
48 * 10/20/2006 - Cleaned up freeAdaTokenList.
49 * 10/20/2006 - Fixed error in freeAdaToken that caused the child token lists
50 * to become corrupted when "separate" tokens were deleted.
51 * 0.4 - Third Revision - 09/25/2006
52 * 09/25/2006 - Fixed error in newAdaToken which could cause an error on some
53 * systems when a separate token (which is temporary) gets
54 * created.
55 * 09/25/2006 - Change matchFilePos initialization in the findAdaTags
56 * function.
57 * 0.3 - Second Revision
58 * 06/02/2006 - Added missing EOF checks to prevent infinite loops in the case
59 * of an incomplete Ada (or non-Ada) file being parsed.
60 * 06/02/2006 - Added Copyright notice.
61 * 0.2 - First Revision
62 * 05/26/2006 - Fixed an error where tagging the proper scope of something
63 * declared in an anonymous block or anonymous loop was not
64 * working properly.
65 * 05/26/2006 - Fixed an error capturing the name of a 'separate' tag.
66 * 05/26/2006 - Fixed the cmp() function so that it finds matches correctly.
67 * 05/26/2006 - Fixed some spelling errors.
68 * 05/26/2006 - Added explicit skipping of use and with clauses.
69 * 0.1 - Initial Release
71 * Future Changes:
72 * TODO: Add inheritance information?
73 * TODO: Add signature gathering?
75 * Copyright (C) 2006 A. Aaron Cornelius
77 * This program is free software; you can redistribute it and/or
78 * modify it under the terms of the GNU General Public License
79 * as published by the Free Software Foundation; either version 2
80 * of the License, or (at your option) any later version.
82 * This program is distributed in the hope that it will be useful,
83 * but WITHOUT ANY WARRANTY; without even the implied warranty of
84 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
85 * GNU General Public License for more details.
87 * You should have received a copy of the GNU General Public License
88 * along with this program; if not, write to the Free Software
89 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
90 * USA.
94 #include "general.h" /* always include first */
96 #include <string.h> /* to declare strxxx() functions */
97 #include <ctype.h> /* to define isxxx() macros */
99 #include "parse.h" /* always include */
100 #include "read.h" /* to define file readLineFromInputFile() */
101 #include "entry.h" /* for the tag entry manipulation */
102 #include "routines.h" /* for generic malloc/realloc/free routines */
103 #include "debug.h" /* for Assert */
104 #include "xtag.h"
107 static bool eof_reached;
109 typedef enum eAdaParseMode
111 ADA_ROOT,
112 ADA_DECLARATIONS,
113 ADA_CODE,
114 ADA_EXCEPTIONS,
115 ADA_GENERIC
116 } adaParseMode;
118 typedef enum eAdaKinds
120 ADA_KIND_UNDEFINED = KIND_GHOST_INDEX, /* for default/initialization values */
121 ADA_KIND_PACKAGE_SPEC,
122 ADA_KIND_PACKAGE,
123 ADA_KIND_TYPE_SPEC,
124 ADA_KIND_TYPE,
125 ADA_KIND_SUBTYPE_SPEC,
126 ADA_KIND_SUBTYPE,
127 ADA_KIND_RECORD_COMPONENT,
128 ADA_KIND_ENUM_LITERAL,
129 ADA_KIND_VARIABLE_SPEC,
130 ADA_KIND_VARIABLE,
131 ADA_KIND_FORMAL,
132 ADA_KIND_CONSTANT,
133 ADA_KIND_EXCEPTION,
134 ADA_KIND_SUBPROGRAM_SPEC,
135 ADA_KIND_SUBPROGRAM,
136 ADA_KIND_TASK_SPEC,
137 ADA_KIND_TASK,
138 ADA_KIND_PROTECTED_SPEC,
139 ADA_KIND_PROTECTED,
140 ADA_KIND_ENTRY_SPEC,
141 ADA_KIND_ENTRY,
142 ADA_KIND_LABEL,
143 ADA_KIND_IDENTIFIER,
144 ADA_KIND_AUTOMATIC_VARIABLE,
145 ADA_KIND_ANONYMOUS, /* for non-identified loops and blocks */
146 ADA_KIND_COUNT /* must be last */
147 } adaKind;
149 typedef enum {
150 ADA_PACKAGE_SUBUNIT,
151 } adaPackageRole;
153 static roleDefinition AdaPackageRoles [] = {
154 { true, "subunit",
155 "package name referenced in separate()" },
158 static kindDefinition AdaKinds[] =
160 { true, 'P', "packspec", "package specifications" },
161 { true, 'p', "package", "packages",
162 .referenceOnly = false, ATTACH_ROLES(AdaPackageRoles) },
163 { false, 'T', "typespec", "type specifications" },
164 { true, 't', "type", "types" },
165 { false, 'U', "subspec", "subtype specifications" },
166 { true, 'u', "subtype", "subtypes" },
167 { true, 'c', "component", "record type components" },
168 { true, 'l', "literal", "enum type literals" },
169 { false, 'V', "varspec", "variable specifications" },
170 { true, 'v', "variable", "variables" },
171 { true, 'f', "formal", "generic formal parameters" },
172 { true, 'n', "constant", "constants" },
173 { true, 'x', "exception", "user defined exceptions" },
174 { true, 'R', "subprogspec", "subprogram specifications" },
175 { true, 'r', "subprogram", "subprograms" },
176 { true, 'K', "taskspec", "task specifications" },
177 { true, 'k', "task", "tasks" },
178 { true, 'O', "protectspec", "protected data specifications" },
179 { true, 'o', "protected", "protected data" },
180 { false, 'E', "entryspec", "task/protected data entry specifications" },
181 { true, 'e', "entry", "task/protected data entries" },
182 { true, 'b', "label", "labels" },
183 { true, 'i', "identifier", "loop/declare identifiers"},
184 { false, 'a', "autovar", "automatic variables" },
185 { false, 'y', "anon", "loops and blocks with no identifier" },
188 typedef struct sAdaTokenList
190 int numTokens;
191 struct sAdaTokenInfo *head;
192 struct sAdaTokenInfo *tail;
193 } adaTokenList;
195 typedef struct sAdaTokenInfo
197 adaKind kind;
198 bool isSpec;
199 bool isPrivate;
200 char *name;
201 tagEntryInfo tag;
202 struct sAdaTokenInfo *parent;
203 struct sAdaTokenInfo *prev;
204 struct sAdaTokenInfo *next;
205 adaTokenList children;
206 } adaTokenInfo;
208 typedef enum eAdaKeywords
210 ADA_KEYWORD_ACCEPT,
211 ADA_KEYWORD_BEGIN,
212 ADA_KEYWORD_BODY,
213 ADA_KEYWORD_CASE,
214 ADA_KEYWORD_CONSTANT,
215 ADA_KEYWORD_DECLARE,
216 ADA_KEYWORD_DO,
217 ADA_KEYWORD_ELSE,
218 ADA_KEYWORD_ELSIF,
219 ADA_KEYWORD_END,
220 ADA_KEYWORD_ENTRY,
221 ADA_KEYWORD_EXCEPTION,
222 ADA_KEYWORD_FOR,
223 ADA_KEYWORD_FUNCTION,
224 ADA_KEYWORD_GENERIC,
225 ADA_KEYWORD_IF,
226 ADA_KEYWORD_IN,
227 ADA_KEYWORD_IS,
228 ADA_KEYWORD_LOOP,
229 ADA_KEYWORD_NEW,
230 ADA_KEYWORD_NOT,
231 ADA_KEYWORD_OR,
232 ADA_KEYWORD_OVERRIDING, /* Ada 2005 */
233 ADA_KEYWORD_PACKAGE,
234 ADA_KEYWORD_PRAGMA,
235 ADA_KEYWORD_PRIVATE,
236 ADA_KEYWORD_PROCEDURE,
237 ADA_KEYWORD_PROTECTED,
238 ADA_KEYWORD_RECORD,
239 ADA_KEYWORD_RENAMES,
240 ADA_KEYWORD_SELECT,
241 ADA_KEYWORD_SEPARATE,
242 ADA_KEYWORD_SUBTYPE,
243 ADA_KEYWORD_TASK,
244 ADA_KEYWORD_THEN,
245 ADA_KEYWORD_TYPE,
246 ADA_KEYWORD_UNTIL,
247 ADA_KEYWORD_USE,
248 ADA_KEYWORD_WHEN,
249 ADA_KEYWORD_WHILE,
250 ADA_KEYWORD_WITH
251 } adaKeyword;
253 static const char *AdaKeywords[] =
255 "accept",
256 "begin",
257 "body",
258 "case",
259 "constant",
260 "declare",
261 "do",
262 "else",
263 "elsif",
264 "end",
265 "entry",
266 "exception",
267 "for",
268 "function",
269 "generic",
270 "if",
271 "in",
272 "is",
273 "loop",
274 "new",
275 "not",
276 "or",
277 "overriding",
278 "package",
279 "pragma",
280 "private",
281 "procedure",
282 "protected",
283 "record",
284 "renames",
285 "select",
286 "separate",
287 "subtype",
288 "task",
289 "then",
290 "type",
291 "until",
292 "use",
293 "when",
294 "while",
295 "with"
299 /* variables for managing the input string, position as well as input line
300 * number and position */
301 static const char *line;
302 static int lineLen;
303 static int pos;
304 static unsigned long matchLineNum;
305 static MIOPos matchFilePos;
307 /* utility functions */
308 static void makeSpec (adaKind *kind);
310 /* prototypes of functions for manipulating the Ada tokens */
311 static adaTokenInfo *newAdaToken (const char *name, int len,
312 adaKind kind, bool isSpec,
313 adaTokenInfo *parent);
314 static adaTokenInfo *newAdaTokenFull (const char *name, int len,
315 adaKind kind, int role, bool isSpec,
316 adaTokenInfo *parent);
317 static void freeAdaToken (adaTokenList *list, adaTokenInfo *token);
318 static void appendAdaToken (adaTokenInfo *parent, adaTokenInfo *token);
320 /* token list processing function prototypes */
321 static void initAdaTokenList (adaTokenList *list);
322 static void freeAdaTokenList (adaTokenList *list);
323 static void appendAdaTokenList (adaTokenInfo *parent, adaTokenList *children);
325 /* prototypes of functions for moving through the DEFINED text */
326 static void readNewLine (void);
327 static void movePos (int amount);
328 static bool cmp (const char *buf, int len, const char *match);
329 static bool adaCmp (const char *match);
330 static bool adaKeywordCmp (adaKeyword keyword);
331 static void skipUntilWhiteSpace (void);
332 static void skipWhiteSpace (void);
333 static void skipComments (void);
334 static void skipCommentsAndStringLiteral (void);
335 static void skipPast (const char *past);
336 static void skipPastKeyword (adaKeyword keyword);
337 static void skipPastWord (void);
339 typedef bool (* skipCompFn) (void *data);
340 static void skipPastLambda (skipCompFn cmpfn, void *data);
342 struct cmpKeywordOrWordDataElt
344 enum eltType
346 ELT_KEYWORD,
347 ELT_WORD,
348 } type;
349 union
351 adaKeyword keyword;
352 const char* word;
353 } u;
355 static struct cmpKeywordOrWordDataElt *skipPastKeywordOrWord (struct cmpKeywordOrWordDataElt * elt,
356 int count);
358 /* prototypes of functions for parsing the high-level Ada constructs */
359 static adaTokenInfo *adaParseBlock (adaTokenInfo *parent, adaKind kind);
360 static adaTokenInfo *adaParseSubprogram (adaTokenInfo *parent, adaKind kind);
361 static adaTokenInfo *adaParseType (adaTokenInfo *parent, adaKind kind);
362 static adaTokenInfo *adaParseVariables (adaTokenInfo *parent, adaKind kind);
363 static adaTokenInfo *adaParseLoopVar (adaTokenInfo *parent);
364 static adaTokenInfo *adaParse (adaParseMode mode, adaTokenInfo *parent);
366 /* prototypes of the functions used by ctags */
367 static void storeAdaTags (adaTokenInfo *token, const char *parentScope);
368 static void findAdaTags (void);
370 static void makeSpec (adaKind *kind)
372 switch (*kind)
374 case ADA_KIND_PACKAGE:
375 *kind = ADA_KIND_PACKAGE_SPEC;
376 break;
378 case ADA_KIND_TYPE:
379 *kind = ADA_KIND_TYPE_SPEC;
380 break;
382 case ADA_KIND_SUBTYPE:
383 *kind = ADA_KIND_SUBTYPE_SPEC;
384 break;
386 case ADA_KIND_VARIABLE:
387 *kind = ADA_KIND_VARIABLE_SPEC;
388 break;
390 case ADA_KIND_SUBPROGRAM:
391 *kind = ADA_KIND_SUBPROGRAM_SPEC;
392 break;
394 case ADA_KIND_TASK:
395 *kind = ADA_KIND_TASK_SPEC;
396 break;
398 case ADA_KIND_PROTECTED:
399 *kind = ADA_KIND_PROTECTED_SPEC;
400 break;
402 case ADA_KIND_ENTRY:
403 *kind = ADA_KIND_ENTRY_SPEC;
404 break;
406 default:
407 *kind = ADA_KIND_UNDEFINED;
408 break;
412 static adaTokenInfo *newAdaTokenFull (const char *name, int len, adaKind kind, int role,
413 bool isSpec, adaTokenInfo *parent)
415 char *tmpName = NULL;
416 adaTokenInfo *token = xMalloc (1, adaTokenInfo);
418 token->name = NULL;
420 if (name != NULL && len != 0)
422 tmpName = xMalloc (len + 1, char);
423 strncpy (tmpName, name, len);
424 tmpName[len] = '\0';
427 /* init the tag */
428 initTagEntry (&token->tag, tmpName, ADA_KIND_UNDEFINED);
430 token->kind = kind;
431 token->isSpec = isSpec;
432 token->isPrivate = false;
434 /* set the token data */
435 token->name = tmpName;
436 token->parent = parent;
438 /* Now set the file scope for this tag. A tag has file scope if its direct
439 * parent is a package/subprogram/protected/task spec, or if it it's parent
440 * is UNDEFINED (a 'root' token), and if this is not in a 'private' section
441 * of that spec. */
442 if ((parent != NULL) && (parent->isPrivate == false) &&
443 ((parent->kind == ADA_KIND_UNDEFINED) ||
444 (parent->kind == ADA_KIND_PACKAGE && isRoleAssigned(&parent->tag, ADA_PACKAGE_SUBUNIT)) ||
445 ((parent->isSpec == true) && ((parent->kind == ADA_KIND_PACKAGE) ||
446 (parent->kind == ADA_KIND_SUBPROGRAM) ||
447 (parent->kind == ADA_KIND_PROTECTED) ||
448 (parent->kind == ADA_KIND_TASK)))))
450 token->tag.isFileScope = false;
452 else
454 markTagExtraBit (&token->tag, XTAG_FILE_SCOPE);
455 token->tag.isFileScope = true;
458 /* add the kind info - unless this is a SEPARATE kind, in which case keep
459 * them blank because they get filled in later. */
460 if (kind > ADA_KIND_UNDEFINED)
462 token->tag.kindIndex = kind;
463 if (role != ROLE_DEFINITION_INDEX)
464 assignRole(&token->tag, role);
466 else
468 token->tag.kindIndex = KIND_GHOST_INDEX;
471 /* setup the parent and children pointers */
472 initAdaTokenList (&token->children);
473 appendAdaToken (parent, token);
475 return token;
478 static adaTokenInfo *newAdaToken (const char *name, int len, adaKind kind,
479 bool isSpec, adaTokenInfo *parent)
481 return newAdaTokenFull (name, len, kind, ROLE_DEFINITION_INDEX, isSpec, parent);
484 static void freeAdaToken (adaTokenList *list, adaTokenInfo *token)
486 if (token != NULL)
488 if (token->name != NULL)
490 eFree ((void *) token->name);
491 token->name = NULL;
494 /* before we delete this token, clean up it's children */
495 freeAdaTokenList (&token->children);
497 /* move the next token in the list to this token's spot */
498 if (token->prev != NULL)
500 token->prev->next = token->next;
502 else if (list != NULL)
504 /* Token to remove is head in list as 'token->prev == NULL' */
505 Assert (token->prev == NULL);
506 list->head = token->next;
509 /* move the previous token in the list to this token's spot */
510 if (token->next != NULL)
512 token->next->prev = token->prev;
514 else if (list != NULL)
516 /* Token to remove is tail of list as 'token->next == NULL') */
517 Assert (token->next == NULL);
518 list->tail = token->prev;
521 /* decrement the list count */
522 if (list != NULL)
524 list->numTokens--;
527 /* now that this node has had everything hanging off of it rearranged,
528 * delete this node */
529 eFree (token);
533 static void appendAdaToken (adaTokenInfo *parent, adaTokenInfo *token)
535 /* if the parent or newChild is NULL there is nothing to be done */
536 if (parent != NULL && token != NULL)
538 /* we just need to add this to the list and set a parent pointer */
539 parent->children.numTokens++;
540 token->parent = parent;
541 token->prev = parent->children.tail;
542 token->next = NULL;
544 if (parent->children.tail != NULL)
546 parent->children.tail->next = token;
549 /* the token that was just added always becomes the last token int the
550 * list */
551 parent->children.tail = token;
553 if (parent->children.head == NULL)
555 parent->children.head = token;
560 static void initAdaTokenList (adaTokenList *list)
562 if (list != NULL)
564 list->numTokens = 0;
565 list->head = NULL;
566 list->tail = NULL;
570 static void freeAdaTokenList (adaTokenList *list)
572 if (list != NULL)
574 while (list->head != NULL)
576 freeAdaToken (list, list->head);
581 static void appendAdaTokenList (adaTokenInfo *parent, adaTokenList *children)
583 adaTokenInfo *tmp = NULL;
585 if (parent != NULL && children != NULL)
587 while (children->head != NULL)
589 tmp = children->head->next;
590 appendAdaToken (parent, children->head);
592 /* we just need to worry about setting the head pointer properly during
593 * the list iteration. The node's pointers will get set properly by the
594 * appendAdaToken () function */
595 children->head = tmp;
598 /* now that we have added all nodes from the children list to the parent
599 * node, zero out the children list */
600 initAdaTokenList (children);
604 static void readNewLine (void)
606 while (true)
608 line = (const char *) readLineFromInputFile ();
609 pos = 0;
611 if (line == NULL)
613 lineLen = 0;
614 eof_reached = true;
615 return;
618 lineLen = strlen (line);
620 if (lineLen > 0)
622 return;
627 static void movePos (int amount)
629 pos += amount;
630 if (!eof_reached && pos >= lineLen)
632 readNewLine ();
636 /* a macro for checking for comments... This isn't the same as the check in
637 * cmp () because comments don't have to have whitespace or separation-type
638 * characters following the "--" */
639 #define isAdaComment(buf, pos, len) \
640 (((pos) == 0 || (!isalnum ((buf)[(pos) - 1]) && (buf)[(pos) - 1] != '_')) && \
641 (pos) < (len) && \
642 strncasecmp (&(buf)[(pos)], "--", strlen ("--")) == 0)
643 #define isAdaStringLiteral(buf, pos, len) \
644 (((pos) < (len)) && ((buf)[(pos)] == '"'))
645 #define isAdaCharLiteral(buf, pos, len) \
646 (((pos) < (len - 2)) && ((buf)[(pos)] == '\'') \
647 && ((buf)[(pos + 2)] == '\''))
650 static bool cmp (const char *buf, int len, const char *match)
652 bool status = false;
653 int matchLen;
655 /* if we are trying to match nothing, that is always true */
656 if (match == NULL)
658 return true;
661 /* first check to see if the buffer is empty, if it is, return false */
662 if (buf == NULL)
664 return status;
667 matchLen = strlen (match);
669 /* A match only happens the number of chars in the matching string match,
670 * and whitespace follows... Which means we also must check to see if the
671 * end of the line is after the matching string. Also check for some
672 * separation characters such as (, ), :, or ; */
673 if ((strncasecmp (buf, match, matchLen) == 0) &&
674 (matchLen == len ||
675 (matchLen < len &&
676 (isspace (buf[matchLen]) || buf[matchLen] == '(' ||
677 buf[matchLen] == ')' || buf[matchLen] == ':' ||
678 buf[matchLen] == ';'))))
680 status = true;
683 return status;
686 static bool adaCmp (const char *match)
688 bool status = false;
690 /* first check to see if line is empty */
691 if (line == NULL)
693 eof_reached = true;
694 return status;
697 status = cmp (&line[pos], lineLen - pos, match);
699 /* if we match, increment the position pointer */
700 if (status == true && match != NULL)
702 matchLineNum = getInputLineNumber ();
703 matchFilePos = getInputFilePosition ();
705 movePos ((strlen (match)));
708 return status;
711 /* just a version of adaCmp that is a bit more optimized for keywords */
712 static bool adaKeywordCmp (adaKeyword keyword)
714 bool status = false;
716 /* first check to see if line is empty, if it is */
717 if (line == NULL)
719 eof_reached = true;
720 return status;
723 status = cmp (&line[pos], lineLen - pos, AdaKeywords[keyword]);
725 /* if we match, increment the position pointer */
726 if (status == true)
728 matchLineNum = getInputLineNumber ();
729 matchFilePos = getInputFilePosition ();
731 movePos ((strlen (AdaKeywords[keyword])));
734 return status;
737 static void skipUntilWhiteSpace (void)
739 /* first check for a comment line, because this would cause the isspace
740 * check to be true immediately */
741 skipComments ();
743 while (!eof_reached && !isspace (line[pos]))
745 /* don't use movePos () because if we read in a new line with this function
746 * we need to stop */
747 pos++;
749 /* the newline counts as whitespace so read in the newline and return
750 * immediately */
751 if (pos >= lineLen)
753 line = (const char *) readLineFromInputFile ();
754 pos = 0;
756 if (line == NULL)
758 lineLen = 0;
759 eof_reached = true;
760 return;
763 lineLen = strlen (line);
765 return;
768 /* now check for comments here */
769 skipComments ();
773 static void skipWhiteSpace (void)
775 /* first check for a comment line, because this would cause the isspace
776 * check to fail immediately */
777 skipComments ();
779 while (!eof_reached && isspace (line[pos]))
781 movePos (1);
783 /* now check for comments here */
784 skipComments ();
788 static void skipComments (void)
790 while (!eof_reached && isAdaComment (line, pos, lineLen))
792 readNewLine ();
796 /* Return true if skipped over a string literal (or char literal).
797 * Return false if no string literal (nor char literal) is found. */
798 static bool skipStringLiteral (void)
800 if (!eof_reached && isAdaStringLiteral (line, pos, lineLen))
802 do {
803 movePos (1);
804 } while (!eof_reached && !isAdaStringLiteral (line, pos, lineLen));
806 /* Go to the next char of " */
807 movePos (1);
809 return true;
811 else if (!eof_reached && isAdaCharLiteral (line, pos, lineLen))
813 movePos (3);
814 return true;
816 return false;
819 static void skipCommentsAndStringLiteral (void)
821 while (true)
823 skipComments ();
824 if (!skipStringLiteral ())
825 break;
829 static void skipPast (const char *past)
831 /* first check for a comment line, because this would cause the isspace
832 * check to fail immediately */
833 skipCommentsAndStringLiteral ();
835 /* now look for the keyword */
836 while (!eof_reached && !adaCmp (past))
838 movePos (1);
840 /* now check for comments here */
841 skipCommentsAndStringLiteral ();
845 static void skipPastKeyword (adaKeyword keyword)
847 /* first check for a comment line, because this would cause the isspace
848 * check to fail immediately */
849 skipComments ();
851 /* now look for the keyword */
852 while (!eof_reached && !adaKeywordCmp (keyword))
854 movePos (1);
856 /* now check for comments here */
857 skipComments ();
861 static void skipPastWord (void)
863 /* first check for a comment line, because this would cause the isspace
864 * check to fail immediately */
865 skipComments ();
867 /* now increment until we hit a non-word character... Specifically,
868 * whitespace, '(', ')', ':', and ';' */
869 while (!eof_reached && !isspace (line[pos]) &&
870 line[pos] != '(' && line[pos] != ')' && line[pos] != ':' &&
871 line[pos] != ';')
873 /* don't use movePos () because if we read in a new line with this function
874 * we need to stop */
875 pos++;
877 /* the newline counts as whitespace so read in the newline and return
878 * immediately */
879 if (pos >= lineLen)
881 line = (const char *) readLineFromInputFile ();
882 pos = 0;
884 if (line == NULL)
886 lineLen = 0;
887 eof_reached = true;
888 return;
891 lineLen = strlen (line);
893 return;
896 /* now check for comments here */
897 skipComments ();
901 static void skipPastLambda (skipCompFn cmpfn, void *data)
903 /* first check for a comment line, because this would cause the isspace
904 * check to fail immediately */
905 skipCommentsAndStringLiteral ();
907 /* now call the predicate */
908 while (!eof_reached && !cmpfn (data))
910 movePos (1);
912 /* now check for comments here */
913 skipCommentsAndStringLiteral ();
917 struct cmpKeywordOrWordData
919 struct cmpKeywordOrWordDataElt *found;
920 int count;
921 struct cmpKeywordOrWordDataElt *elt;
924 static bool cmpKeywordOrWord (void *data)
926 struct cmpKeywordOrWordData *cmdData = data;
928 cmdData->found = NULL;
929 for (int i = 0; i < cmdData->count; i++)
931 if (cmdData->elt[i].type == ELT_KEYWORD)
933 if (adaKeywordCmp (cmdData->elt[i].u.keyword))
935 cmdData->found = cmdData->elt + i;
936 return true;
939 else if (cmdData->elt[i].type == ELT_WORD)
941 if (adaCmp (cmdData->elt[i].u.word))
943 cmdData->found = cmdData->elt + i;
944 return true;
947 else
948 AssertNotReached ();
950 return false;
953 static struct cmpKeywordOrWordDataElt *skipPastKeywordOrWord (struct cmpKeywordOrWordDataElt * elt,
954 int count)
956 struct cmpKeywordOrWordData data = {
957 .found = NULL,
958 .count = count,
959 .elt = elt
961 skipPastLambda (cmpKeywordOrWord, &data);
962 return data.found;
965 static adaTokenInfo *adaParseBlock (adaTokenInfo *parent, adaKind kind)
967 int i;
968 adaTokenInfo *token;
969 bool isSpec = true;
971 skipWhiteSpace ();
973 /* if the next word is body, this is not a package spec */
974 if (adaKeywordCmp (ADA_KEYWORD_BODY))
976 isSpec = false;
978 /* if the next word is "type" then this has to be a task or protected spec */
979 else if (adaKeywordCmp (ADA_KEYWORD_TYPE) &&
980 (kind != ADA_KIND_PROTECTED && kind != ADA_KIND_TASK))
982 /* if this failed to validate then we should just fail */
983 return NULL;
985 skipWhiteSpace ();
987 /* we are at the start of what should be the tag now... But we have to get
988 * it's length. So loop until we hit whitespace, init the counter to 1
989 * since we know that the current position is not whitespace */
990 for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
991 line[pos + i] != '(' && line[pos + i] != ';'; i++);
993 /* we have reached the tag of the package, so create the tag */
994 token = newAdaToken (&line[pos], i, kind, isSpec, parent);
996 movePos (i);
997 skipWhiteSpace ();
999 /* task and protected types are allowed to have discriminants */
1000 if (!eof_reached && line[pos] == '(')
1002 while (!eof_reached && line[pos] != ')')
1004 movePos (1);
1005 adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
1007 movePos (1);
1010 /* we must parse until we hit the "is" string to reach the end of
1011 * this package declaration, or a "renames" keyword */
1012 while (token != NULL)
1014 skipWhiteSpace ();
1016 if (adaKeywordCmp (ADA_KEYWORD_IS))
1018 skipWhiteSpace ();
1020 if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
1022 /* if the next word is the keyword "separate", don't create the tag
1023 * since it will be defined elsewhere */
1024 freeAdaToken (&parent->children, token);
1025 token = NULL;
1027 /* move past the ";" ending this declaration */
1028 skipPast (";");
1030 else if (adaKeywordCmp (ADA_KEYWORD_NEW))
1032 struct cmpKeywordOrWordDataElt *elt;
1034 elt = skipPastKeywordOrWord ((struct cmpKeywordOrWordDataElt []) {{
1035 .type = ELT_KEYWORD,
1036 .u.keyword = ADA_KEYWORD_WITH,
1037 }, {
1038 .type = ELT_WORD,
1039 .u.word = ";",
1041 }, 2);
1043 if (elt && elt->type == ELT_KEYWORD)
1044 adaParse (ADA_DECLARATIONS, token);
1046 else
1048 adaParse (ADA_DECLARATIONS, token);
1051 break;
1053 else if (adaKeywordCmp (ADA_KEYWORD_RENAMES))
1055 skipPast (";");
1056 break;
1058 else if (adaCmp (";"))
1060 token->isSpec = true;
1061 break;
1063 else
1065 /* nothing found, move to the next word */
1066 skipUntilWhiteSpace ();
1069 if (eof_reached)
1071 freeAdaToken (&parent->children, token);
1072 token = NULL;
1076 return token;
1079 static adaTokenInfo *adaParseSubprogram (adaTokenInfo *parent, adaKind kind)
1081 int i;
1082 adaTokenInfo *token;
1083 adaTokenInfo *tmpToken = NULL;
1085 skipWhiteSpace ();
1087 /* we are at the start of what should be the tag now... But we have to get
1088 * it's length. So loop until we hit whitespace or the beginning of the
1089 * parameter list. Init the counter to 1 * since we know that the current
1090 * position is not whitespace */
1091 for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
1092 line[pos + i] != '(' && line[pos + i] != ';'; i++);
1094 /* we have reached the tag of the subprogram, so create the tag... Init the
1095 * isSpec flag to false and we will adjust it when we see if there is an
1096 * "is", "do" or a ";" following the tag */
1097 token = newAdaToken (&line[pos], i, kind, false, parent);
1099 /* move the line position */
1100 movePos (i);
1101 skipWhiteSpace ();
1103 /* if we find a '(' grab any parameters */
1104 if (!eof_reached && line[pos] == '(' && token != NULL)
1106 while (!eof_reached && line[pos] != ')')
1108 movePos (1);
1109 tmpToken = adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
1111 movePos (1);
1113 /* check to see if anything was received... If this is an entry this may
1114 * have a 'discriminant' and not have any parameters in the first
1115 * parenthesis pair, so check again if this was the case*/
1116 if (kind == ADA_KIND_ENTRY && tmpToken == NULL)
1118 /* skip any existing whitespace and see if there is a second parenthesis
1119 * pair */
1120 skipWhiteSpace ();
1122 if (!eof_reached && line[pos] == '(')
1124 while (!eof_reached && line[pos] != ')')
1126 movePos (1);
1127 adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
1129 movePos (1);
1134 /* loop infinitely until we hit a "is", "do" or ";", this will skip over
1135 * the returns keyword, returned-type for functions as well as any one of a
1136 * myriad of keyword qualifiers */
1137 while (!eof_reached && token != NULL)
1139 skipWhiteSpace ();
1141 if (adaKeywordCmp (ADA_KEYWORD_IS))
1143 skipWhiteSpace ();
1145 if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
1147 /* if the next word is the keyword "separate", don't create the tag
1148 * since it will be defined elsewhere */
1149 freeAdaToken (&parent->children, token);
1150 token = NULL;
1152 /* move past the ";" ending this declaration */
1153 skipPast (";");
1155 else if (adaKeywordCmp (ADA_KEYWORD_NEW))
1157 /* if this is a "new" something then no need to parse */
1158 skipPast (";");
1160 else if (line[pos] == '(')
1162 /* '(' is the starter of an expression function. */
1163 skipPast (";");
1165 else
1167 adaParse (ADA_DECLARATIONS, token);
1170 break;
1172 else if (adaKeywordCmp (ADA_KEYWORD_RENAMES))
1174 skipPast (";");
1175 break;
1177 else if (adaKeywordCmp (ADA_KEYWORD_DO))
1179 /* do is the keyword for the beginning of a task entry */
1180 adaParse (ADA_CODE, token);
1181 break;
1183 else if (adaCmp (";"))
1185 /* this is just a spec then, so set the flag in the token */
1186 token->isSpec = true;
1187 break;
1189 else
1191 /* nothing found, move to the next word */
1192 movePos (1); /* make sure to advance even if we aren't actually on a word */
1193 skipPastWord ();
1197 return token;
1200 static adaTokenInfo *adaParseType (adaTokenInfo *parent, adaKind kind)
1202 int i;
1203 adaTokenInfo *token = NULL;
1205 skipWhiteSpace ();
1207 /* get the name of the type */
1208 for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
1209 line[pos + i] != '(' && line[pos + i] != ';'; i++);
1211 token = newAdaToken (&line[pos], i, kind, false, parent);
1213 movePos (i);
1214 skipWhiteSpace ();
1216 if (!eof_reached && line[pos] == '(')
1218 /* in this case there is a discriminant to this type, gather the
1219 * variables */
1220 while (!eof_reached && line[pos] != ')')
1222 movePos (1);
1223 adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
1225 movePos (1);
1226 skipWhiteSpace ();
1229 /* check to see what is next, if it is not "is" then just skip to the end of
1230 * the statement and register this as a 'spec' */
1231 if (adaKeywordCmp (ADA_KEYWORD_IS))
1233 skipWhiteSpace ();
1234 /* check to see if this may be a record or an enumeration */
1235 if (!eof_reached && line[pos] == '(')
1237 movePos (1);
1238 adaParseVariables (token, ADA_KIND_ENUM_LITERAL);
1240 else
1242 /* Parsing following form here.
1244 * A. type foo is record ...;
1245 * B. type foo is new bar with record ...;
1246 * C. type foo is new bar;
1248 if (adaKeywordCmp (ADA_KEYWORD_NEW))
1250 /* B and C */
1251 struct cmpKeywordOrWordDataElt *elt;
1252 elt = skipPastKeywordOrWord ((struct cmpKeywordOrWordDataElt []) {{
1253 .type = ELT_KEYWORD,
1254 .u.keyword = ADA_KEYWORD_WITH,
1255 }, {
1256 .type = ELT_WORD,
1257 .u.word = ";",
1259 }, 2);
1260 if (elt && elt->type == ELT_WORD)
1262 /* C */
1263 return token;
1266 /* B */
1267 skipWhiteSpace ();
1269 if (adaKeywordCmp (ADA_KEYWORD_RECORD))
1271 /* A and B */
1272 /* until we hit "end record" we need to gather type variables */
1273 while (!eof_reached)
1275 skipWhiteSpace ();
1277 if (adaKeywordCmp (ADA_KEYWORD_END))
1279 skipWhiteSpace ();
1280 if (adaKeywordCmp (ADA_KEYWORD_RECORD))
1282 break;
1284 skipPast (";");
1286 /* handle variant types */
1287 else if (adaKeywordCmp (ADA_KEYWORD_CASE))
1289 skipPastKeyword (ADA_KEYWORD_IS);
1291 else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
1293 skipPast ("=>");
1295 else
1297 adaParseVariables (token, ADA_KIND_RECORD_COMPONENT);
1298 skipPast (";");
1304 else
1306 token->isSpec = true;
1309 skipPast (";");
1311 return token;
1314 static adaTokenInfo *adaParseVariables (adaTokenInfo *parent, adaKind kind)
1316 /* variables for keeping track of tags */
1317 int varEndPos = -1;
1318 int tokenStart = -1;
1319 adaTokenInfo *token = NULL;
1321 /* buffer management variables */
1322 int i = 0;
1323 int bufPos = 0;
1324 int bufLen = 0;
1325 char *buf = NULL;
1327 /* file and line position variables */
1328 unsigned long int lineNum;
1329 int filePosIndex = 0;
1330 int filePosSize = 32;
1331 MIOPos *filePos = xMalloc (filePosSize, MIOPos);
1333 /* skip any preliminary whitespace or comments */
1334 skipWhiteSpace ();
1335 skipComments ();
1337 /* before we start reading input save the current line number and file
1338 * position, so we can reconstruct the correct line & file position for any
1339 * tags we create */
1340 lineNum = getInputLineNumber ();
1341 filePos[filePosIndex] = getInputFilePosition ();
1343 /* setup local buffer... Since we may have to read a few lines to verify
1344 * that this is a proper variable declaration, and still make a token for
1345 * each variable, add one to the allocated string to account for a '\0' */
1346 bufLen = lineLen - pos;
1347 buf = xMalloc (bufLen + 1, char);
1348 memcpy ((void *) buf, (void *) &line[pos], bufLen);
1350 /* don't increase bufLen to include the NULL char so that strlen (buf) and
1351 * bufLen match */
1352 buf[bufLen] = '\0';
1354 while (!eof_reached)
1356 /* make sure that we don't count anything in a comment as being valid to
1357 * parse */
1358 if (isAdaComment (buf, bufPos, bufLen))
1360 /* move bufPos to the end of this 'line' so a new line of input is
1361 * read */
1362 bufPos = bufLen - 1;
1364 /* if tokenStart is not -2 then we may be trying to track the type
1365 * of this variable declaration, so set tokenStart to -1 so that the
1366 * tracking can start over */
1367 if (tokenStart != -2)
1369 tokenStart = -1;
1372 /* we have to keep track of any () pairs that may be in the variable
1373 * declarations. And then quit if we hit a ';' the real end ')', or also
1374 * a variable initialization... Once we hit := then we have hit the end of
1375 * the variable declaration */
1376 else if (buf[bufPos] == '(')
1378 i++;
1380 else if (buf[bufPos] == ')')
1382 if (i == 0)
1384 break;
1386 else
1388 i--;
1391 else if (buf[bufPos] == ';' ||
1392 ((bufPos + 1) < bufLen &&
1393 (strncasecmp (&buf[bufPos], ":=", strlen (":=")) == 0 ||
1394 strncasecmp (&buf[bufPos], "=>", strlen ("=>")) == 0)))
1396 break;
1398 /* if we found the : keep track of where we found it */
1399 else if (buf[bufPos] == ':' &&
1400 (bufPos + 1 >= bufLen || buf[bufPos + 1] != '='))
1402 varEndPos = bufPos;
1404 /* if we have the position of the ':' find out what the next word is,
1405 * because if it "constant" or "exception" then we must tag this slightly
1406 * differently, But only check this for normal variables */
1407 else if (kind == ADA_KIND_VARIABLE && varEndPos != -1 &&
1408 !isspace (buf[bufPos]) && tokenStart == -1)
1410 tokenStart = bufPos;
1412 else if (kind == ADA_KIND_VARIABLE && varEndPos != -1 && tokenStart >= 0 &&
1413 ((bufPos + 1) >= bufLen || isspace (buf[bufPos + 1]) ||
1414 buf[bufPos + 1] == ';'))
1416 if (cmp (&buf[tokenStart], bufLen - tokenStart,
1417 AdaKeywords[ADA_KEYWORD_CONSTANT]) == true)
1419 kind = ADA_KIND_CONSTANT;
1421 else if (cmp (&buf[tokenStart], bufLen - tokenStart,
1422 AdaKeywords[ADA_KEYWORD_EXCEPTION]) == true)
1424 kind = ADA_KIND_EXCEPTION;
1427 /* set tokenStart to -2 to prevent any more words from being checked */
1428 tokenStart = -2;
1431 bufPos++;
1433 /* if we just incremented beyond the length of the current buffer, we need
1434 * to read in a new line */
1435 if (!eof_reached && bufPos >= bufLen)
1437 readNewLine ();
1439 /* store the new file position for the start of this line */
1440 filePosIndex++;
1441 while (filePosIndex >= filePosSize)
1443 filePosSize *= 2;
1444 filePos = xRealloc (filePos, filePosSize, MIOPos);
1446 filePos[filePosIndex] = getInputFilePosition ();
1448 /* increment bufLen and bufPos now so that they jump past the NULL
1449 * character in the buffer */
1450 bufLen++;
1451 bufPos++;
1453 /* allocate space and store this into our buffer */
1454 bufLen += lineLen;
1455 buf = xRealloc (buf, bufLen + 1, char);
1456 memcpy ((void *) &buf[bufPos], (void *) line, lineLen);
1457 buf[bufLen] = '\0';
1461 /* There is a special case if we are gathering enumeration values and we hit
1462 * a ')', that is allowed so we need to move varEndPos to where the ')' is */
1463 if (kind == ADA_KIND_ENUM_LITERAL && buf[bufPos] == ')' && varEndPos == -1)
1465 varEndPos = bufPos;
1468 /* so we found a : or ;... If it is a : go back through the buffer and
1469 * create a token for each word skipping over all whitespace and commas
1470 * until the : is hit*/
1471 if (varEndPos != -1)
1473 /* there should be no whitespace at the beginning, so tokenStart is
1474 * initialized to 0 */
1475 tokenStart = 0;
1477 /* before we start set the filePosIndex back to 0 so we can go through the
1478 * file position table as the read line number increases */
1479 filePosIndex = 0;
1481 for (i = 0; i < varEndPos; i++)
1483 /* skip comments which are '--' unless we are in a word */
1484 if (isAdaComment (buf, i, varEndPos))
1486 /* move i past the '\0' that we put at the end of each line stored in
1487 * buf */
1488 for ( ; i < varEndPos && buf[i] != '\0'; i++);
1490 else if (tokenStart != -1 && (isspace (buf[i]) || buf[i] == ',' ||
1491 buf[i] == '\0'))
1493 /* only store the word if it is not an in/out keyword */
1494 if (!cmp (&buf[tokenStart], varEndPos, "in") &&
1495 !cmp (&buf[tokenStart], varEndPos, "out"))
1497 token = newAdaToken (&buf[tokenStart], i - tokenStart,
1498 kind, false, parent);
1500 /* now set the proper line and file position counts for this
1501 * new token */
1502 token->tag.lineNumber = lineNum + filePosIndex;
1503 token->tag.filePosition = filePos[filePosIndex];
1505 tokenStart = -1;
1507 else if (tokenStart == -1 && !(isspace (buf[i]) || buf[i] == ',' ||
1508 buf[i] == '\0'))
1510 /* only set the tokenStart for non-newline characters */
1511 tokenStart = i;
1514 /* after we are finished with this line, move the file position */
1515 if (buf[i] == '\0')
1517 filePosIndex++;
1521 /* if token start was 'started' then we should store the last token */
1522 if (tokenStart != -1)
1524 token = newAdaToken (&buf[tokenStart], i - tokenStart,
1525 kind, false, parent);
1527 /* now set the proper line and file position counts for this
1528 * new token */
1529 token->tag.lineNumber = lineNum + filePosIndex;
1530 token->tag.filePosition = filePos[filePosIndex];
1534 /* now get the pos variable to point to the correct place in line where we
1535 * left off in our temp buf, and free our temporary buffer. This is a
1536 * little different than most buf position moves. It gets the distance from
1537 * the current buf position to the end of the buffer, which is also the
1538 * distance from where pos should be wrt the end of the variable
1539 * definition */
1540 movePos ((lineLen - (bufLen - bufPos)) - pos);
1541 eFree ((void *) buf);
1542 eFree ((void *) filePos);
1544 return token;
1547 static adaTokenInfo *adaParseLoopVar (adaTokenInfo *parent)
1549 int i;
1550 adaTokenInfo *token = NULL;
1552 skipWhiteSpace ();
1553 for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]); i++);
1554 token = newAdaToken (&line[pos], i, ADA_KIND_AUTOMATIC_VARIABLE, false,
1555 parent);
1556 movePos (i);
1558 /* now skip to the end of the loop declaration */
1559 skipPastKeyword (ADA_KEYWORD_LOOP);
1561 return token;
1564 static adaTokenInfo *adaParse (adaParseMode mode, adaTokenInfo *parent)
1566 int i;
1567 adaTokenInfo genericParamsRoot;
1568 adaTokenInfo *token = NULL;
1570 initAdaTokenList (&genericParamsRoot.children);
1572 /* if we hit the end of the file, line will be NULL and our skip and match
1573 * functions will hit this jump buffer with eof_reached */
1574 while (!eof_reached)
1576 /* find the next place to start */
1577 skipWhiteSpace ();
1579 /* check some universal things to check for first */
1580 if (eof_reached)
1582 break;
1584 else if (isAdaComment (line, pos, lineLen))
1586 readNewLine ();
1587 continue;
1589 else if (adaKeywordCmp (ADA_KEYWORD_PRAGMA) ||
1590 ((mode != ADA_GENERIC) && adaKeywordCmp (ADA_KEYWORD_WITH)) ||
1591 adaKeywordCmp (ADA_KEYWORD_USE))
1593 /* set the token to NULL so we accidentally don't pick up something
1594 * from earlier
1595 * Do not skip lines having 'with' when 'mode == ADA_GENERIC'
1596 * this to intercept 'formal subprograms' of a generic declaration.
1597 * see: ARM 12.1(22) */
1598 skipPast (";");
1599 continue;
1602 /* check for tags based on our current mode */
1603 switch (mode)
1605 case ADA_ROOT:
1606 if (adaKeywordCmp (ADA_KEYWORD_PACKAGE))
1608 token = adaParseBlock (parent, ADA_KIND_PACKAGE);
1610 else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
1611 adaKeywordCmp (ADA_KEYWORD_FUNCTION))
1613 token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
1615 else if (adaKeywordCmp (ADA_KEYWORD_TASK))
1617 token = adaParseBlock (parent, ADA_KIND_TASK);
1619 else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
1621 token = adaParseBlock (parent, ADA_KIND_PROTECTED);
1623 else if (adaKeywordCmp (ADA_KEYWORD_GENERIC))
1625 /* if we have hit a generic declaration, go to the generic section
1626 * and collect the formal parameters */
1627 mode = ADA_GENERIC;
1628 break;
1630 else if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
1632 /* skip any possible whitespace */
1633 skipWhiteSpace ();
1635 /* skip over the "(" until we hit the tag */
1636 if (!eof_reached && line[pos] == '(')
1638 movePos (1);
1639 skipWhiteSpace ();
1641 /* get length of tag */
1642 for (i = 1; (pos + i) < lineLen && line[pos + i] != ')' &&
1643 !isspace (line[pos + i]); i++);
1645 /* the original comment before we introduced reference tags:
1646 * -----------------------------------------------------------------
1647 * if this is a separate declaration, all it really does is create
1648 * a false high level token for everything in this file to belong
1649 * to... But we don't know what kind it is, so we declare it as
1650 * ADA_KIND_SEPARATE, which will cause it not to be placed in
1651 * the tag file, and the item in this file will be printed as
1652 * separate:<name> instead of package:<name> or whatever the
1653 * parent kind really is (assuming the ctags option will be on
1654 * for printing such info to the tag file)
1655 * -----------------------------------------------------------------
1656 * Now we have reference tags. So we can use ADA_KIND_PACKAGE as kind.
1658 token = newAdaTokenFull (&line[pos], i, ADA_KIND_PACKAGE, ADA_PACKAGE_SUBUNIT,
1659 false, parent);
1661 /* since this is a false top-level token, set parent to be
1662 * token */
1663 parent = token;
1664 token = NULL;
1666 /* skip past the ')' */
1667 skipPast (")");
1669 else
1671 /* move to the end of this statement */
1672 skipPast (";");
1675 else
1677 /* otherwise, nothing was found so just skip until the end of this
1678 * unknown statement... It's most likely just a use or with
1679 * clause. Also set token to NULL so we don't attempt anything
1680 * incorrect */
1681 token = NULL;
1682 skipPast (";");
1685 /* check to see if we succeeded in creating our token */
1686 if (token != NULL)
1688 /* if any generic params have been gathered, attach them to
1689 * token */
1690 appendAdaTokenList (token, &genericParamsRoot.children);
1693 break;
1695 case ADA_GENERIC:
1696 /* if we are processing a generic block, make up some temp children
1697 * which we will later attach to the root of the real
1698 * procedure/package/whatever the formal parameters are for */
1699 if (adaKeywordCmp (ADA_KEYWORD_PACKAGE))
1701 token = adaParseBlock (parent, ADA_KIND_PACKAGE);
1703 /* The above 'adaParseBlock' has read the end of a 'generic package declaration',
1704 * reset the mode back to the original mode.
1705 * see: ARM 12.1(24) */
1706 Assert (parent);
1707 mode = (parent->parent)? ADA_DECLARATIONS: ADA_ROOT;
1709 else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
1710 adaKeywordCmp (ADA_KEYWORD_FUNCTION))
1712 token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
1714 /* The above 'adaParseBlock' as read the end of a 'generic function/procedure declaration',
1715 * reset the mode back to the original mode.
1716 * see: ARM 12.1(21/22) */
1717 Assert (parent);
1718 mode = (parent->parent)? ADA_DECLARATIONS: ADA_ROOT;
1720 else if (adaKeywordCmp (ADA_KEYWORD_TASK))
1722 token = adaParseBlock (parent, ADA_KIND_TASK);
1724 else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
1726 token = adaParseBlock (parent, ADA_KIND_PROTECTED);
1728 else if (adaKeywordCmp (ADA_KEYWORD_TYPE))
1730 skipWhiteSpace ();
1732 /* get length of tag */
1733 for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
1734 line[pos + i] != '(' && line[pos + i] != ';'; i++);
1736 appendAdaToken (&genericParamsRoot,
1737 newAdaToken (&line[pos], i, ADA_KIND_FORMAL, false,
1738 NULL));
1740 /* skip to the end of this formal type declaration */
1741 skipPast (";");
1743 else if (adaKeywordCmp (ADA_KEYWORD_WITH))
1745 skipWhiteSpace ();
1746 /* skip over the function/procedure keyword, it doesn't matter for
1747 * now */
1748 skipUntilWhiteSpace ();
1749 skipWhiteSpace ();
1751 /* get length of tag */
1752 for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
1753 line[pos + i] != '(' && line[pos + i] != ';'; i++);
1755 appendAdaToken (&genericParamsRoot,
1756 newAdaToken (&line[pos], i, ADA_KIND_FORMAL, false,
1757 NULL));
1759 /* increment the position */
1760 movePos (i);
1762 /* now gather the parameters to this subprogram */
1763 if (!eof_reached && line[pos] == '(')
1765 while (!eof_reached && line[pos] != ')')
1767 movePos (1);
1768 adaParseVariables (genericParamsRoot.children.tail,
1769 ADA_KIND_AUTOMATIC_VARIABLE);
1771 movePos (1);
1774 /* skip to the end of this formal type declaration */
1775 skipPast (";");
1777 else
1779 /* otherwise, nothing was found so just skip until the end of this
1780 * unknown statement... It's most likely just a use or with
1781 * clause. Also set token to NULL so we don't attempt anything
1782 * incorrect */
1783 token = NULL;
1784 skipPast (";");
1787 /* check to see if we succeeded in creating our token */
1788 if (token != NULL)
1790 /* if any generic params have been gathered, attach them to
1791 * token. */
1792 appendAdaTokenList (token, &genericParamsRoot.children);
1795 break;
1797 case ADA_DECLARATIONS:
1798 if (adaKeywordCmp (ADA_KEYWORD_PACKAGE))
1800 token = adaParseBlock (parent, ADA_KIND_PACKAGE);
1802 else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
1803 adaKeywordCmp (ADA_KEYWORD_FUNCTION))
1805 token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
1807 else if (adaKeywordCmp (ADA_KEYWORD_TASK))
1809 token = adaParseBlock (parent, ADA_KIND_TASK);
1811 else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
1813 token = adaParseBlock (parent, ADA_KIND_PROTECTED);
1815 else if (adaKeywordCmp (ADA_KEYWORD_GENERIC))
1817 /* if we have hit a generic declaration, go to the generic section
1818 * and collect the formal parameters */
1819 mode = ADA_GENERIC;
1820 break;
1822 else if (adaKeywordCmp (ADA_KEYWORD_TYPE))
1824 token = adaParseType (parent, ADA_KIND_TYPE);
1826 else if (adaKeywordCmp (ADA_KEYWORD_SUBTYPE))
1828 token = adaParseType (parent, ADA_KIND_SUBTYPE);
1830 else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
1832 mode = ADA_CODE;
1833 break;
1835 else if (adaKeywordCmp (ADA_KEYWORD_FOR))
1837 /* if we hit a "for" statement it is defining implementation details
1838 * for a specific type/variable/subprogram/etc... So we should just
1839 * skip it, so skip the tag, then we need to see if there is a
1840 * 'record' keyword... If there is we must skip past the
1841 * 'end record;' statement. First skip past the tag */
1842 skipPastKeyword (ADA_KEYWORD_USE);
1843 skipWhiteSpace ();
1845 if (adaKeywordCmp (ADA_KEYWORD_RECORD))
1847 /* now skip to the next "record" keyword, which should be the end
1848 * of this use statement */
1849 skipPastKeyword (ADA_KEYWORD_RECORD);
1852 /* lastly, skip past the end ";" */
1853 skipPast (";");
1855 else if (adaKeywordCmp (ADA_KEYWORD_END))
1857 /* if we have hit an end then we must see if the next word matches
1858 * the parent token's name. If it does we hit the end of whatever
1859 * sort of block construct we were processing and we must
1860 * return */
1861 skipWhiteSpace ();
1862 if (adaCmp (parent->name))
1864 skipPast (";");
1866 /* return the token */
1867 freeAdaTokenList (&genericParamsRoot.children);
1868 return token;
1870 else
1872 /* set the token to NULL so we accidentally don't pick up something
1873 * from earlier */
1874 token = NULL;
1875 skipPast (";");
1878 else if (adaKeywordCmp (ADA_KEYWORD_ENTRY))
1880 token = adaParseSubprogram (parent, ADA_KIND_ENTRY);
1882 else if (adaKeywordCmp (ADA_KEYWORD_PRIVATE))
1884 /* if this is a private declaration then we need to set the global
1885 * file spec flag and then skip whitespace to get to the next bit of
1886 * code to parse. */
1887 if (parent != NULL)
1889 parent->isPrivate = true;
1892 skipWhiteSpace ();
1894 else if (adaKeywordCmp (ADA_KEYWORD_OVERRIDING)
1895 || adaKeywordCmp (ADA_KEYWORD_NOT))
1897 /* Do nothing, just ignore these keywords. */
1900 else
1902 /* if nothing else matched this is probably a variable, constant
1903 * or exception declaration */
1904 token = adaParseVariables (parent, ADA_KIND_VARIABLE);
1905 skipPast (";");
1908 /* check to see if we succeeded in creating our token */
1909 if (token != NULL)
1911 /* if this is one of the root-type tokens... Do some extra
1912 * processing */
1913 if (token->kind == ADA_KIND_PACKAGE ||
1914 token->kind == ADA_KIND_SUBPROGRAM ||
1915 token->kind == ADA_KIND_TASK ||
1916 token->kind == ADA_KIND_PROTECTED)
1918 /* if any generic params have been gathered, attach them to
1919 * token */
1920 appendAdaTokenList (token, &genericParamsRoot.children);
1923 break;
1925 case ADA_CODE:
1926 if (adaKeywordCmp (ADA_KEYWORD_DECLARE))
1928 /* if we are starting a declare block here, and not down at the
1929 * identifier definition then make an anonymous token to track the
1930 * data in this block */
1931 token = newAdaToken (NULL, 0, ADA_KIND_ANONYMOUS, false, parent);
1933 /* save the correct starting line */
1934 token->tag.lineNumber = matchLineNum;
1935 token->tag.filePosition = matchFilePos;
1937 adaParse (ADA_DECLARATIONS, token);
1939 else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
1941 /* if we are starting a code block here, and not down at the
1942 * identifier definition then make an anonymous token to track the
1943 * data in this block, if this was part of a proper LABEL:
1944 * declare/begin/end block then the parent would already be a label
1945 * and this begin statement would have been found while in the
1946 * ADA_DECLARATIONS parsing section */
1947 token = newAdaToken (NULL, 0, ADA_KIND_ANONYMOUS, false, parent);
1949 /* save the correct starting line */
1950 token->tag.lineNumber = matchLineNum;
1951 token->tag.filePosition = matchFilePos;
1953 adaParse (ADA_CODE, token);
1955 else if (adaKeywordCmp (ADA_KEYWORD_EXCEPTION))
1957 mode = ADA_EXCEPTIONS;
1958 break;
1960 else if (adaKeywordCmp (ADA_KEYWORD_END))
1962 /* if we have hit an end then we must see if the next word matches
1963 * the parent token's name. If it does we hit the end of whatever
1964 * sort of block construct we were processing and we must
1965 * return */
1966 skipWhiteSpace ();
1967 if (adaCmp (parent->name))
1969 skipPast (";");
1971 /* return the token */
1972 freeAdaTokenList (&genericParamsRoot.children);
1973 return token;
1975 else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
1977 /* a loop with an identifier has this syntax:
1978 * "end loop <ident>;" */
1979 skipWhiteSpace ();
1981 /* now check for the parent loop's name */
1982 if (adaCmp (parent->name))
1984 skipPast (";");
1986 /* return the token */
1987 freeAdaTokenList (&genericParamsRoot.children);
1988 return token;
1991 else
1993 /* otherwise, nothing was found so just skip until the end of
1994 * this statement */
1995 skipPast (";");
1998 else if (adaKeywordCmp (ADA_KEYWORD_ACCEPT))
2000 adaParseSubprogram (parent, ADA_KIND_ENTRY);
2002 else if (adaKeywordCmp (ADA_KEYWORD_FOR))
2004 /* if this is a for loop, then we may need to pick up the
2005 * automatic loop iterator, But... The loop variable is only
2006 * available within the loop itself so make an anonymous label
2007 * parent for this loop var to be parsed in */
2008 token = newAdaToken (AdaKeywords[ADA_KEYWORD_LOOP],
2009 strlen (AdaKeywords[ADA_KEYWORD_LOOP]),
2010 ADA_KIND_ANONYMOUS, false, parent);
2011 adaParseLoopVar (token);
2012 adaParse (ADA_CODE, token);
2014 else if (adaKeywordCmp (ADA_KEYWORD_WHILE))
2016 token = newAdaToken (AdaKeywords[ADA_KEYWORD_LOOP],
2017 strlen (AdaKeywords[ADA_KEYWORD_LOOP]),
2018 ADA_KIND_ANONYMOUS, false, parent);
2020 /* skip past the while loop declaration and parse the loop body */
2021 skipPastKeyword (ADA_KEYWORD_LOOP);
2022 skipWhiteSpace ();
2023 adaParse (ADA_CODE, token);
2025 else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
2027 token = newAdaToken (AdaKeywords[ADA_KEYWORD_LOOP],
2028 strlen (AdaKeywords[ADA_KEYWORD_LOOP]),
2029 ADA_KIND_ANONYMOUS, false, parent);
2031 /* save the correct starting line */
2032 token->tag.lineNumber = matchLineNum;
2033 token->tag.filePosition = matchFilePos;
2035 /* parse the loop body */
2036 skipWhiteSpace ();
2037 adaParse (ADA_CODE, token);
2039 else if (line != NULL &&
2040 strncasecmp (&line[pos], "<<", strlen ("<<")) == 0)
2042 movePos (strlen ("<<"));
2044 /* if the first chars are <<, find the ending >> and if we do that
2045 * then store the label tag, start i at strlen of "<<" plus 1
2046 * because we don't want to move the real pos until we know for
2047 * sure this is a label */
2048 for (i = 1; (pos + i) < lineLen &&
2049 strncasecmp (&line[pos + i], ">>", strlen (">>")) != 0;
2050 i++);
2052 /* if we didn't increment to the end of the line, a match was
2053 * found, if we didn't just fall through */
2054 if ((pos + i) < lineLen)
2056 newAdaToken (&line[pos], i, ADA_KIND_LABEL, false, parent);
2057 skipPast (">>");
2060 /* we need to check for a few special case keywords that might cause
2061 * the simple ; ending statement checks to fail, first the simple
2062 * one word keywords and then the start <stuff> end statements */
2063 else if (adaKeywordCmp (ADA_KEYWORD_SELECT) ||
2064 adaKeywordCmp (ADA_KEYWORD_OR) ||
2065 adaKeywordCmp (ADA_KEYWORD_ELSE))
2067 skipWhiteSpace ();
2069 else if (adaKeywordCmp (ADA_KEYWORD_IF) ||
2070 adaKeywordCmp (ADA_KEYWORD_ELSIF))
2072 skipPastKeyword (ADA_KEYWORD_THEN);
2074 else if (adaKeywordCmp (ADA_KEYWORD_CASE))
2076 skipPastKeyword (ADA_KEYWORD_IS);
2078 else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
2080 skipPast ("=>");
2082 else
2084 int i_end;
2085 /* set token to NULL so we don't accidentally not find an identifier,
2086 * But then fall through to the != NULL check */
2087 token = NULL;
2089 /* there is a possibility that this may be a loop or block
2090 * identifier, so check for a <random_word>[ ]?: statement */
2091 for (i = 1; (pos + i) < lineLen; i++)
2092 if(!(isalnum (line[pos + i]) || line[pos + i] == '_'))
2093 break;
2094 i_end = i; /* Records the end of identifier. */
2096 /* Skip whitespaces between the identifier and ':' */
2097 for (; (pos + i) < lineLen; i++)
2098 if (!isspace((unsigned char)(line[pos + i])))
2099 break;
2101 if ((pos + i) < lineLen
2102 && (line[pos + i] == ':')
2103 && (
2104 ((pos + i + 1) == lineLen)
2105 || (line[pos + i + 1] != '=')
2107 token = newAdaToken (&line[pos], i_end, ADA_KIND_IDENTIFIER, false,
2108 parent);
2110 /* if we created a token, we found an identifier. Now check for a
2111 * declare or begin statement to see if we need to start parsing
2112 * the following code like a root-style token would */
2113 if (token != NULL)
2115 /* if something was found, reset the position variable and try to
2116 * find the next item */
2117 movePos (i + 1);
2118 skipWhiteSpace ();
2120 if (adaKeywordCmp (ADA_KEYWORD_DECLARE))
2122 adaParse (ADA_DECLARATIONS, token);
2124 else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
2126 adaParse (ADA_CODE, token);
2128 else if (adaKeywordCmp (ADA_KEYWORD_FOR))
2130 /* just grab the automatic loop variable, and then parse the
2131 * loop (it may have something to tag which will be a 'child'
2132 * of the loop) */
2133 adaParseLoopVar (token);
2134 adaParse (ADA_CODE, token);
2136 else if (adaKeywordCmp (ADA_KEYWORD_WHILE))
2138 /* skip to the loop keyword */
2139 skipPastKeyword (ADA_KEYWORD_LOOP);
2140 skipWhiteSpace ();
2142 /* parse the loop (it may have something to tag which will be
2143 * a 'child' of the loop) */
2144 adaParse (ADA_CODE, token);
2146 else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
2148 skipWhiteSpace ();
2150 /* parse the loop (it may have something to tag which will be
2151 * a 'child' of the loop) */
2152 adaParse (ADA_CODE, token);
2154 else
2156 /* otherwise, nothing was found so this is not a valid
2157 * identifier, delete it */
2158 freeAdaToken (&parent->children, token);
2159 token = NULL;
2162 else
2164 /* since nothing was found, simply skip to the end of this
2165 * statement */
2166 skipPast (";");
2169 /* else... No keyword tag fields found, look for others such as
2170 * loop and declare identifiers labels or just skip over this
2171 * line */
2173 break;
2175 case ADA_EXCEPTIONS:
2176 if (adaKeywordCmp (ADA_KEYWORD_PRAGMA))
2178 skipPast (";");
2180 else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
2182 skipWhiteSpace ();
2183 token = adaParseVariables (parent, ADA_KIND_AUTOMATIC_VARIABLE);
2185 else if (adaKeywordCmp (ADA_KEYWORD_END))
2187 /* if we have hit an end then we must see if the next word matches
2188 * the parent token's name. If it does we hit the end of whatever
2189 * sort of block construct we were processing and we must
2190 * return */
2191 skipWhiteSpace ();
2192 if (adaCmp (parent->name))
2194 skipPast (";");
2196 /* return the token */
2197 freeAdaTokenList (&genericParamsRoot.children);
2198 return token;
2200 else
2202 /* otherwise, nothing was found so just skip until the end of
2203 * this statement */
2204 skipPast (";");
2207 else
2209 /* otherwise, nothing was found so just skip until the end of
2210 * this statement */
2211 skipPast (";");
2214 break;
2216 default:
2217 Assert (0);
2221 freeAdaTokenList (&genericParamsRoot.children);
2222 return token;
2225 static void storeAdaTags (adaTokenInfo *token, const char *parentScope)
2227 char *currentScope = NULL;
2228 adaTokenInfo *tmp = NULL;
2230 Assert (token);
2232 /* do a spec transition if necessary */
2233 if (token->isSpec == true)
2235 makeSpec (&token->kind);
2237 if (token->kind != ADA_KIND_UNDEFINED)
2239 token->tag.kindIndex = token->kind;
2243 /* fill in the scope data */
2244 if (token->parent != NULL)
2246 if (token->parent->kind != ADA_KIND_UNDEFINED)
2248 token->tag.extensionFields.scopeKindIndex = token->parent->kind;
2249 token->tag.extensionFields.scopeName = token->parent->name;
2253 /* one check before we try to make a tag... If this is an anonymous
2254 * declare block then it's name is empty. Give it one */
2255 if (token->kind == ADA_KIND_ANONYMOUS && token->name == NULL)
2257 token->name = (char *) AdaKeywords[ADA_KEYWORD_DECLARE];
2258 token->tag.name = AdaKeywords[ADA_KEYWORD_DECLARE];
2261 /* Now 'make' tags that have their options set, But only make anonymous
2262 * tags if they have children tags. Also, don't make this tag if the file
2263 * scope flag is not set and this tag is a file scope tag. */
2264 if ((token->kind > ADA_KIND_UNDEFINED) && (token->kind < ADA_KIND_COUNT) &&
2265 (AdaKinds[token->kind].enabled == true) &&
2266 (token->name != NULL) &&
2267 ((token->kind == ADA_KIND_ANONYMOUS && token->children.head != NULL) ||
2268 token->kind != ADA_KIND_ANONYMOUS) &&
2269 ((isXtagEnabled (XTAG_FILE_SCOPE) == true) ||
2270 ((isXtagEnabled (XTAG_FILE_SCOPE) == false) &&
2271 (token->tag.isFileScope == false))))
2273 makeTagEntry (&token->tag);
2275 /* before making the tag, if the --extra=+q flag is set we should create
2276 * an extra entry which is the full parent.tag name. But only do this if
2277 * the parentScope flag is not NULL, and this token is not of a limited
2278 * scope type such as a record component, enum literal, label, etc. */
2279 if ((isXtagEnabled (XTAG_QUALIFIED_TAGS) == true) &&
2280 (token->kind != ADA_KIND_RECORD_COMPONENT) &&
2281 (token->kind != ADA_KIND_ENUM_LITERAL) &&
2282 (token->kind != ADA_KIND_FORMAL) &&
2283 (token->kind != ADA_KIND_LABEL) &&
2284 (token->kind != ADA_KIND_IDENTIFIER) &&
2285 (token->kind != ADA_KIND_AUTOMATIC_VARIABLE) &&
2286 (token->kind != ADA_KIND_ANONYMOUS))
2288 if (parentScope != NULL)
2290 /* first create our new scope which is the parent scope + '.' + the
2291 * current tag name. */
2292 size_t parentScope_len = strlen (parentScope);
2293 size_t name_len = strlen (token->name);
2294 currentScope = xMalloc (parentScope_len + name_len + 2, char);
2295 memcpy (currentScope, parentScope, parentScope_len);
2296 currentScope[parentScope_len] = '.';
2297 memcpy (&currentScope[parentScope_len + 1], token->name, name_len);
2298 currentScope[parentScope_len + 1 + name_len] = '\0';
2300 token->tag.name = currentScope;
2301 markTagExtraBit (&token->tag, XTAG_QUALIFIED_TAGS);
2302 makeTagEntry (&token->tag);
2304 else
2306 /* if the parent scope is null then the current token does not have
2307 * a parent tag to prepend onto the current scope. Therefore, just
2308 * setup the current scope as a copy of the current tag name, but make
2309 * no extra entry. */
2310 currentScope = token->name;
2315 /* now make the child tags */
2316 tmp = token->children.head;
2317 while (tmp != NULL)
2319 storeAdaTags (tmp, currentScope);
2320 tmp = tmp->next;
2323 /* we have to clear out the declare name here or else it may cause issues
2324 * when we try to process it's children, and when we try to free the token
2325 * data */
2326 if (token->kind == ADA_KIND_ANONYMOUS &&
2327 strncasecmp (token->name, AdaKeywords[ADA_KEYWORD_DECLARE],
2328 strlen (AdaKeywords[ADA_KEYWORD_DECLARE])) == 0)
2330 token->name = NULL;
2331 token->tag.name = NULL;
2334 if ((currentScope != NULL) && (currentScope != token->name))
2336 eFree ((void *) currentScope);
2340 /* main parse function */
2341 static void findAdaTags (void)
2343 adaTokenInfo root;
2344 adaTokenInfo *tmp;
2346 /* init all global data now */
2347 eof_reached = false;
2348 line = NULL;
2349 pos = 0;
2350 matchLineNum = 0;
2352 /* cannot just set matchFilePos to 0 because the fpos_t is not a simple
2353 * integer on all systems. */
2354 matchFilePos = getInputFilePosition ();
2356 /* init the root tag */
2357 root.kind = ADA_KIND_UNDEFINED;
2358 root.isSpec = false;
2359 root.name = NULL;
2360 root.parent = NULL;
2361 root.isPrivate = false;
2362 initAdaTokenList (&root.children);
2364 /* read in the first line */
2365 readNewLine ();
2366 if (eof_reached)
2367 goto out;
2369 /* tokenize entire file */
2370 while (!eof_reached && adaParse (ADA_ROOT, &root) != NULL);
2372 /* store tags */
2373 tmp = root.children.head;
2374 while (tmp != NULL)
2376 storeAdaTags (tmp, NULL);
2377 tmp = tmp->next;
2380 out:
2381 /* clean up tokens */
2382 freeAdaTokenList (&root.children);
2385 /* parser definition function */
2386 extern parserDefinition* AdaParser (void)
2388 static const char *const extensions[] = { "adb", "ads", "Ada", "ada", NULL };
2389 parserDefinition* def = parserNew ("Ada");
2390 def->kindTable = AdaKinds;
2391 def->kindCount = ADA_KIND_COUNT;
2392 def->extensions = extensions;
2393 def->parser = findAdaTags;
2394 return def;