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
7 *
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:
14 *
15 * to file source.mak add the line
16 * ada.c \
17 *
18 * after
19 * SOURCES = \
20 *
21 * then add the line
22 * ada.$(OBJECT) \
23 *
24 * after
25 * OBJECTS = \
26 *
27 * to file parsers.h add the line
28 * AdaParser, \
29 *
30 * after
31 * #define PARSER_LIST \
32 *
33 * Then compile and install ctags as normal (usually: './configure', './make',
34 * './make install').
35 *
36 * Changelog:
37 *
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
70 *
71 * Future Changes:
72 * TODO: Add inheritance information?
73 * TODO: Add signature gathering?
74 *
75 * Copyright (C) 2006 A. Aaron Cornelius
76 *
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.
81 *
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.
86 *
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.
91 *
92 */
93
94 #include "general.h" /* always include first */
95
96 #include <string.h> /* to declare strxxx() functions */
97 #include <ctype.h> /* to define isxxx() macros */
98
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"
105
106
107 static bool eof_reached;
108
109 typedef enum eAdaParseMode
110 {
111 ADA_ROOT,
112 ADA_DECLARATIONS,
113 ADA_CODE,
114 ADA_EXCEPTIONS,
115 ADA_GENERIC
116 } adaParseMode;
117
118 typedef enum eAdaKinds
119 {
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;
148
149 typedef enum {
150 ADA_PACKAGE_SUBUNIT,
151 } adaPackageRole;
152
153 static roleDefinition AdaPackageRoles [] = {
154 { true, "subunit",
155 "package name referenced in separate()" },
156 };
157
158 static kindDefinition AdaKinds[] =
159 {
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" },
186 };
187
188 typedef struct sAdaTokenList
189 {
190 int numTokens;
191 struct sAdaTokenInfo *head;
192 struct sAdaTokenInfo *tail;
193 } adaTokenList;
194
195 typedef struct sAdaTokenInfo
196 {
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;
207
208 typedef enum eAdaKeywords
209 {
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;
252
253 static const char *AdaKeywords[] =
254 {
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"
296 };
297
298
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;
306
307 /* utility functions */
308 static void makeSpec (adaKind *kind);
309
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);
319
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);
324
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);
338
339 typedef bool (* skipCompFn) (void *data);
340 static void skipPastLambda (skipCompFn cmpfn, void *data);
341
342 struct cmpKeywordOrWordDataElt
343 {
344 enum eltType
345 {
346 ELT_KEYWORD,
347 ELT_WORD,
348 } type;
349 union
350 {
351 adaKeyword keyword;
352 const char* word;
353 } u;
354 };
355 static struct cmpKeywordOrWordDataElt *skipPastKeywordOrWord (struct cmpKeywordOrWordDataElt * elt,
356 int count);
357
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);
365
366 /* prototypes of the functions used by ctags */
367 static void storeAdaTags (adaTokenInfo *token, const char *parentScope);
368 static void findAdaTags (void);
369
makeSpec(adaKind * kind)370 static void makeSpec (adaKind *kind)
371 {
372 switch (*kind)
373 {
374 case ADA_KIND_PACKAGE:
375 *kind = ADA_KIND_PACKAGE_SPEC;
376 break;
377
378 case ADA_KIND_TYPE:
379 *kind = ADA_KIND_TYPE_SPEC;
380 break;
381
382 case ADA_KIND_SUBTYPE:
383 *kind = ADA_KIND_SUBTYPE_SPEC;
384 break;
385
386 case ADA_KIND_VARIABLE:
387 *kind = ADA_KIND_VARIABLE_SPEC;
388 break;
389
390 case ADA_KIND_SUBPROGRAM:
391 *kind = ADA_KIND_SUBPROGRAM_SPEC;
392 break;
393
394 case ADA_KIND_TASK:
395 *kind = ADA_KIND_TASK_SPEC;
396 break;
397
398 case ADA_KIND_PROTECTED:
399 *kind = ADA_KIND_PROTECTED_SPEC;
400 break;
401
402 case ADA_KIND_ENTRY:
403 *kind = ADA_KIND_ENTRY_SPEC;
404 break;
405
406 default:
407 *kind = ADA_KIND_UNDEFINED;
408 break;
409 }
410 }
411
newAdaTokenFull(const char * name,int len,adaKind kind,int role,bool isSpec,adaTokenInfo * parent)412 static adaTokenInfo *newAdaTokenFull (const char *name, int len, adaKind kind, int role,
413 bool isSpec, adaTokenInfo *parent)
414 {
415 char *tmpName = NULL;
416 adaTokenInfo *token = xMalloc (1, adaTokenInfo);
417
418 token->name = NULL;
419
420 if (name != NULL && len != 0)
421 {
422 tmpName = xMalloc (len + 1, char);
423 strncpy (tmpName, name, len);
424 tmpName[len] = '\0';
425 }
426
427 /* init the tag */
428 initTagEntry (&token->tag, tmpName, ADA_KIND_UNDEFINED);
429
430 token->kind = kind;
431 token->isSpec = isSpec;
432 token->isPrivate = false;
433
434 /* set the token data */
435 token->name = tmpName;
436 token->parent = parent;
437
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)))))
449 {
450 token->tag.isFileScope = false;
451 }
452 else
453 {
454 markTagExtraBit (&token->tag, XTAG_FILE_SCOPE);
455 token->tag.isFileScope = true;
456 }
457
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)
461 {
462 token->tag.kindIndex = kind;
463 if (role != ROLE_DEFINITION_INDEX)
464 assignRole(&token->tag, role);
465 }
466 else
467 {
468 token->tag.kindIndex = KIND_GHOST_INDEX;
469 }
470
471 /* setup the parent and children pointers */
472 initAdaTokenList (&token->children);
473 appendAdaToken (parent, token);
474
475 return token;
476 }
477
newAdaToken(const char * name,int len,adaKind kind,bool isSpec,adaTokenInfo * parent)478 static adaTokenInfo *newAdaToken (const char *name, int len, adaKind kind,
479 bool isSpec, adaTokenInfo *parent)
480 {
481 return newAdaTokenFull (name, len, kind, ROLE_DEFINITION_INDEX, isSpec, parent);
482 }
483
freeAdaToken(adaTokenList * list,adaTokenInfo * token)484 static void freeAdaToken (adaTokenList *list, adaTokenInfo *token)
485 {
486 if (token != NULL)
487 {
488 if (token->name != NULL)
489 {
490 eFree ((void *) token->name);
491 token->name = NULL;
492 }
493
494 /* before we delete this token, clean up it's children */
495 freeAdaTokenList (&token->children);
496
497 /* move the next token in the list to this token's spot */
498 if (token->prev != NULL)
499 {
500 token->prev->next = token->next;
501 }
502 else if (list != NULL)
503 {
504 /* Token to remove is head in list as 'token->prev == NULL' */
505 Assert (token->prev == NULL);
506 list->head = token->next;
507 }
508
509 /* move the previous token in the list to this token's spot */
510 if (token->next != NULL)
511 {
512 token->next->prev = token->prev;
513 }
514 else if (list != NULL)
515 {
516 /* Token to remove is tail of list as 'token->next == NULL') */
517 Assert (token->next == NULL);
518 list->tail = token->prev;
519 }
520
521 /* decrement the list count */
522 if (list != NULL)
523 {
524 list->numTokens--;
525 }
526
527 /* now that this node has had everything hanging off of it rearranged,
528 * delete this node */
529 eFree (token);
530 }
531 }
532
appendAdaToken(adaTokenInfo * parent,adaTokenInfo * token)533 static void appendAdaToken (adaTokenInfo *parent, adaTokenInfo *token)
534 {
535 /* if the parent or newChild is NULL there is nothing to be done */
536 if (parent != NULL && token != NULL)
537 {
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;
543
544 if (parent->children.tail != NULL)
545 {
546 parent->children.tail->next = token;
547 }
548
549 /* the token that was just added always becomes the last token int the
550 * list */
551 parent->children.tail = token;
552
553 if (parent->children.head == NULL)
554 {
555 parent->children.head = token;
556 }
557 }
558 }
559
initAdaTokenList(adaTokenList * list)560 static void initAdaTokenList (adaTokenList *list)
561 {
562 if (list != NULL)
563 {
564 list->numTokens = 0;
565 list->head = NULL;
566 list->tail = NULL;
567 }
568 }
569
freeAdaTokenList(adaTokenList * list)570 static void freeAdaTokenList (adaTokenList *list)
571 {
572 if (list != NULL)
573 {
574 while (list->head != NULL)
575 {
576 freeAdaToken (list, list->head);
577 }
578 }
579 }
580
appendAdaTokenList(adaTokenInfo * parent,adaTokenList * children)581 static void appendAdaTokenList (adaTokenInfo *parent, adaTokenList *children)
582 {
583 adaTokenInfo *tmp = NULL;
584
585 if (parent != NULL && children != NULL)
586 {
587 while (children->head != NULL)
588 {
589 tmp = children->head->next;
590 appendAdaToken (parent, children->head);
591
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;
596 }
597
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);
601 }
602 }
603
readNewLine(void)604 static void readNewLine (void)
605 {
606 while (true)
607 {
608 line = (const char *) readLineFromInputFile ();
609 pos = 0;
610
611 if (line == NULL)
612 {
613 lineLen = 0;
614 eof_reached = true;
615 return;
616 }
617
618 lineLen = strlen (line);
619
620 if (lineLen > 0)
621 {
622 return;
623 }
624 }
625 }
626
movePos(int amount)627 static void movePos (int amount)
628 {
629 pos += amount;
630 if (!eof_reached && pos >= lineLen)
631 {
632 readNewLine ();
633 }
634 }
635
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)] == '\''))
648
649
cmp(const char * buf,int len,const char * match)650 static bool cmp (const char *buf, int len, const char *match)
651 {
652 bool status = false;
653 int matchLen;
654
655 /* if we are trying to match nothing, that is always true */
656 if (match == NULL)
657 {
658 return true;
659 }
660
661 /* first check to see if the buffer is empty, if it is, return false */
662 if (buf == NULL)
663 {
664 return status;
665 }
666
667 matchLen = strlen (match);
668
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] == ';'))))
679 {
680 status = true;
681 }
682
683 return status;
684 }
685
adaCmp(const char * match)686 static bool adaCmp (const char *match)
687 {
688 bool status = false;
689
690 /* first check to see if line is empty */
691 if (line == NULL)
692 {
693 eof_reached = true;
694 return status;
695 }
696
697 status = cmp (&line[pos], lineLen - pos, match);
698
699 /* if we match, increment the position pointer */
700 if (status == true && match != NULL)
701 {
702 matchLineNum = getInputLineNumber ();
703 matchFilePos = getInputFilePosition ();
704
705 movePos ((strlen (match)));
706 }
707
708 return status;
709 }
710
711 /* just a version of adaCmp that is a bit more optimized for keywords */
adaKeywordCmp(adaKeyword keyword)712 static bool adaKeywordCmp (adaKeyword keyword)
713 {
714 bool status = false;
715
716 /* first check to see if line is empty, if it is */
717 if (line == NULL)
718 {
719 eof_reached = true;
720 return status;
721 }
722
723 status = cmp (&line[pos], lineLen - pos, AdaKeywords[keyword]);
724
725 /* if we match, increment the position pointer */
726 if (status == true)
727 {
728 matchLineNum = getInputLineNumber ();
729 matchFilePos = getInputFilePosition ();
730
731 movePos ((strlen (AdaKeywords[keyword])));
732 }
733
734 return status;
735 }
736
skipUntilWhiteSpace(void)737 static void skipUntilWhiteSpace (void)
738 {
739 /* first check for a comment line, because this would cause the isspace
740 * check to be true immediately */
741 skipComments ();
742
743 while (!eof_reached && !isspace (line[pos]))
744 {
745 /* don't use movePos () because if we read in a new line with this function
746 * we need to stop */
747 pos++;
748
749 /* the newline counts as whitespace so read in the newline and return
750 * immediately */
751 if (pos >= lineLen)
752 {
753 line = (const char *) readLineFromInputFile ();
754 pos = 0;
755
756 if (line == NULL)
757 {
758 lineLen = 0;
759 eof_reached = true;
760 return;
761 }
762
763 lineLen = strlen (line);
764
765 return;
766 }
767
768 /* now check for comments here */
769 skipComments ();
770 }
771 }
772
skipWhiteSpace(void)773 static void skipWhiteSpace (void)
774 {
775 /* first check for a comment line, because this would cause the isspace
776 * check to fail immediately */
777 skipComments ();
778
779 while (!eof_reached && isspace (line[pos]))
780 {
781 movePos (1);
782
783 /* now check for comments here */
784 skipComments ();
785 }
786 }
787
skipComments(void)788 static void skipComments (void)
789 {
790 while (!eof_reached && isAdaComment (line, pos, lineLen))
791 {
792 readNewLine ();
793 }
794 }
795
796 /* Return true if skipped over a string literal (or char literal).
797 * Return false if no string literal (nor char literal) is found. */
skipStringLiteral(void)798 static bool skipStringLiteral (void)
799 {
800 if (!eof_reached && isAdaStringLiteral (line, pos, lineLen))
801 {
802 do {
803 movePos (1);
804 } while (!eof_reached && !isAdaStringLiteral (line, pos, lineLen));
805
806 /* Go to the next char of " */
807 movePos (1);
808
809 return true;
810 }
811 else if (!eof_reached && isAdaCharLiteral (line, pos, lineLen))
812 {
813 movePos (3);
814 return true;
815 }
816 return false;
817 }
818
skipCommentsAndStringLiteral(void)819 static void skipCommentsAndStringLiteral (void)
820 {
821 while (true)
822 {
823 skipComments ();
824 if (!skipStringLiteral ())
825 break;
826 }
827 }
828
skipPast(const char * past)829 static void skipPast (const char *past)
830 {
831 /* first check for a comment line, because this would cause the isspace
832 * check to fail immediately */
833 skipCommentsAndStringLiteral ();
834
835 /* now look for the keyword */
836 while (!eof_reached && !adaCmp (past))
837 {
838 movePos (1);
839
840 /* now check for comments here */
841 skipCommentsAndStringLiteral ();
842 }
843 }
844
skipPastKeyword(adaKeyword keyword)845 static void skipPastKeyword (adaKeyword keyword)
846 {
847 /* first check for a comment line, because this would cause the isspace
848 * check to fail immediately */
849 skipComments ();
850
851 /* now look for the keyword */
852 while (!eof_reached && !adaKeywordCmp (keyword))
853 {
854 movePos (1);
855
856 /* now check for comments here */
857 skipComments ();
858 }
859 }
860
skipPastWord(void)861 static void skipPastWord (void)
862 {
863 /* first check for a comment line, because this would cause the isspace
864 * check to fail immediately */
865 skipComments ();
866
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] != ';')
872 {
873 /* don't use movePos () because if we read in a new line with this function
874 * we need to stop */
875 pos++;
876
877 /* the newline counts as whitespace so read in the newline and return
878 * immediately */
879 if (pos >= lineLen)
880 {
881 line = (const char *) readLineFromInputFile ();
882 pos = 0;
883
884 if (line == NULL)
885 {
886 lineLen = 0;
887 eof_reached = true;
888 return;
889 }
890
891 lineLen = strlen (line);
892
893 return;
894 }
895
896 /* now check for comments here */
897 skipComments ();
898 }
899 }
900
skipPastLambda(skipCompFn cmpfn,void * data)901 static void skipPastLambda (skipCompFn cmpfn, void *data)
902 {
903 /* first check for a comment line, because this would cause the isspace
904 * check to fail immediately */
905 skipCommentsAndStringLiteral ();
906
907 /* now call the predicate */
908 while (!eof_reached && !cmpfn (data))
909 {
910 movePos (1);
911
912 /* now check for comments here */
913 skipCommentsAndStringLiteral ();
914 }
915 }
916
917 struct cmpKeywordOrWordData
918 {
919 struct cmpKeywordOrWordDataElt *found;
920 int count;
921 struct cmpKeywordOrWordDataElt *elt;
922 };
923
cmpKeywordOrWord(void * data)924 static bool cmpKeywordOrWord (void *data)
925 {
926 struct cmpKeywordOrWordData *cmdData = data;
927
928 cmdData->found = NULL;
929 for (int i = 0; i < cmdData->count; i++)
930 {
931 if (cmdData->elt[i].type == ELT_KEYWORD)
932 {
933 if (adaKeywordCmp (cmdData->elt[i].u.keyword))
934 {
935 cmdData->found = cmdData->elt + i;
936 return true;
937 }
938 }
939 else if (cmdData->elt[i].type == ELT_WORD)
940 {
941 if (adaCmp (cmdData->elt[i].u.word))
942 {
943 cmdData->found = cmdData->elt + i;
944 return true;
945 }
946 }
947 else
948 AssertNotReached ();
949 }
950 return false;
951 }
952
skipPastKeywordOrWord(struct cmpKeywordOrWordDataElt * elt,int count)953 static struct cmpKeywordOrWordDataElt *skipPastKeywordOrWord (struct cmpKeywordOrWordDataElt * elt,
954 int count)
955 {
956 struct cmpKeywordOrWordData data = {
957 .found = NULL,
958 .count = count,
959 .elt = elt
960 };
961 skipPastLambda (cmpKeywordOrWord, &data);
962 return data.found;
963 }
964
adaParseBlock(adaTokenInfo * parent,adaKind kind)965 static adaTokenInfo *adaParseBlock (adaTokenInfo *parent, adaKind kind)
966 {
967 int i;
968 adaTokenInfo *token;
969 bool isSpec = true;
970
971 skipWhiteSpace ();
972
973 /* if the next word is body, this is not a package spec */
974 if (adaKeywordCmp (ADA_KEYWORD_BODY))
975 {
976 isSpec = false;
977 }
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))
981 {
982 /* if this failed to validate then we should just fail */
983 return NULL;
984 }
985 skipWhiteSpace ();
986
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++);
992
993 /* we have reached the tag of the package, so create the tag */
994 token = newAdaToken (&line[pos], i, kind, isSpec, parent);
995
996 movePos (i);
997 skipWhiteSpace ();
998
999 /* task and protected types are allowed to have discriminants */
1000 if (!eof_reached && line[pos] == '(')
1001 {
1002 while (!eof_reached && line[pos] != ')')
1003 {
1004 movePos (1);
1005 adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
1006 }
1007 movePos (1);
1008 }
1009
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)
1013 {
1014 skipWhiteSpace ();
1015
1016 if (adaKeywordCmp (ADA_KEYWORD_IS))
1017 {
1018 skipWhiteSpace ();
1019
1020 if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
1021 {
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;
1026
1027 /* move past the ";" ending this declaration */
1028 skipPast (";");
1029 }
1030 else if (adaKeywordCmp (ADA_KEYWORD_NEW))
1031 {
1032 struct cmpKeywordOrWordDataElt *elt;
1033
1034 elt = skipPastKeywordOrWord ((struct cmpKeywordOrWordDataElt []) {{
1035 .type = ELT_KEYWORD,
1036 .u.keyword = ADA_KEYWORD_WITH,
1037 }, {
1038 .type = ELT_WORD,
1039 .u.word = ";",
1040 }
1041 }, 2);
1042
1043 if (elt && elt->type == ELT_KEYWORD)
1044 adaParse (ADA_DECLARATIONS, token);
1045 }
1046 else
1047 {
1048 adaParse (ADA_DECLARATIONS, token);
1049 }
1050
1051 break;
1052 }
1053 else if (adaKeywordCmp (ADA_KEYWORD_RENAMES))
1054 {
1055 skipPast (";");
1056 break;
1057 }
1058 else if (adaCmp (";"))
1059 {
1060 token->isSpec = true;
1061 break;
1062 }
1063 else
1064 {
1065 /* nothing found, move to the next word */
1066 skipUntilWhiteSpace ();
1067 }
1068
1069 if (eof_reached)
1070 {
1071 freeAdaToken (&parent->children, token);
1072 token = NULL;
1073 }
1074 }
1075
1076 return token;
1077 }
1078
adaParseSubprogram(adaTokenInfo * parent,adaKind kind)1079 static adaTokenInfo *adaParseSubprogram (adaTokenInfo *parent, adaKind kind)
1080 {
1081 int i;
1082 adaTokenInfo *token;
1083 adaTokenInfo *tmpToken = NULL;
1084
1085 skipWhiteSpace ();
1086
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++);
1093
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);
1098
1099 /* move the line position */
1100 movePos (i);
1101 skipWhiteSpace ();
1102
1103 /* if we find a '(' grab any parameters */
1104 if (!eof_reached && line[pos] == '(' && token != NULL)
1105 {
1106 while (!eof_reached && line[pos] != ')')
1107 {
1108 movePos (1);
1109 tmpToken = adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
1110 }
1111 movePos (1);
1112
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)
1117 {
1118 /* skip any existing whitespace and see if there is a second parenthesis
1119 * pair */
1120 skipWhiteSpace ();
1121
1122 if (!eof_reached && line[pos] == '(')
1123 {
1124 while (!eof_reached && line[pos] != ')')
1125 {
1126 movePos (1);
1127 adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
1128 }
1129 movePos (1);
1130 }
1131 }
1132 }
1133
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)
1138 {
1139 skipWhiteSpace ();
1140
1141 if (adaKeywordCmp (ADA_KEYWORD_IS))
1142 {
1143 skipWhiteSpace ();
1144
1145 if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
1146 {
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;
1151
1152 /* move past the ";" ending this declaration */
1153 skipPast (";");
1154 }
1155 else if (adaKeywordCmp (ADA_KEYWORD_NEW))
1156 {
1157 /* if this is a "new" something then no need to parse */
1158 skipPast (";");
1159 }
1160 else if (line[pos] == '(')
1161 {
1162 /* '(' is the starter of an expression function. */
1163 skipPast (";");
1164 }
1165 else
1166 {
1167 adaParse (ADA_DECLARATIONS, token);
1168 }
1169
1170 break;
1171 }
1172 else if (adaKeywordCmp (ADA_KEYWORD_RENAMES))
1173 {
1174 skipPast (";");
1175 break;
1176 }
1177 else if (adaKeywordCmp (ADA_KEYWORD_DO))
1178 {
1179 /* do is the keyword for the beginning of a task entry */
1180 adaParse (ADA_CODE, token);
1181 break;
1182 }
1183 else if (adaCmp (";"))
1184 {
1185 /* this is just a spec then, so set the flag in the token */
1186 token->isSpec = true;
1187 break;
1188 }
1189 else
1190 {
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 ();
1194 }
1195 }
1196
1197 return token;
1198 }
1199
adaParseType(adaTokenInfo * parent,adaKind kind)1200 static adaTokenInfo *adaParseType (adaTokenInfo *parent, adaKind kind)
1201 {
1202 int i;
1203 adaTokenInfo *token = NULL;
1204
1205 skipWhiteSpace ();
1206
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++);
1210
1211 token = newAdaToken (&line[pos], i, kind, false, parent);
1212
1213 movePos (i);
1214 skipWhiteSpace ();
1215
1216 if (!eof_reached && line[pos] == '(')
1217 {
1218 /* in this case there is a discriminant to this type, gather the
1219 * variables */
1220 while (!eof_reached && line[pos] != ')')
1221 {
1222 movePos (1);
1223 adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
1224 }
1225 movePos (1);
1226 skipWhiteSpace ();
1227 }
1228
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))
1232 {
1233 skipWhiteSpace ();
1234 /* check to see if this may be a record or an enumeration */
1235 if (!eof_reached && line[pos] == '(')
1236 {
1237 movePos (1);
1238 adaParseVariables (token, ADA_KIND_ENUM_LITERAL);
1239 }
1240 else
1241 {
1242 /* Parsing following form here.
1243 *
1244 * A. type foo is record ...;
1245 * B. type foo is new bar with record ...;
1246 * C. type foo is new bar;
1247 */
1248 if (adaKeywordCmp (ADA_KEYWORD_NEW))
1249 {
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 = ";",
1258 }
1259 }, 2);
1260 if (elt && elt->type == ELT_WORD)
1261 {
1262 /* C */
1263 return token;
1264 }
1265
1266 /* B */
1267 skipWhiteSpace ();
1268 }
1269 if (adaKeywordCmp (ADA_KEYWORD_RECORD))
1270 {
1271 /* A and B */
1272 /* until we hit "end record" we need to gather type variables */
1273 while (!eof_reached)
1274 {
1275 skipWhiteSpace ();
1276
1277 if (adaKeywordCmp (ADA_KEYWORD_END))
1278 {
1279 skipWhiteSpace ();
1280 if (adaKeywordCmp (ADA_KEYWORD_RECORD))
1281 {
1282 break;
1283 }
1284 skipPast (";");
1285 }
1286 /* handle variant types */
1287 else if (adaKeywordCmp (ADA_KEYWORD_CASE))
1288 {
1289 skipPastKeyword (ADA_KEYWORD_IS);
1290 }
1291 else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
1292 {
1293 skipPast ("=>");
1294 }
1295 else
1296 {
1297 adaParseVariables (token, ADA_KIND_RECORD_COMPONENT);
1298 skipPast (";");
1299 }
1300 }
1301 }
1302 }
1303 }
1304 else
1305 {
1306 token->isSpec = true;
1307 }
1308
1309 skipPast (";");
1310
1311 return token;
1312 }
1313
adaParseVariables(adaTokenInfo * parent,adaKind kind)1314 static adaTokenInfo *adaParseVariables (adaTokenInfo *parent, adaKind kind)
1315 {
1316 /* variables for keeping track of tags */
1317 int varEndPos = -1;
1318 int tokenStart = -1;
1319 adaTokenInfo *token = NULL;
1320
1321 /* buffer management variables */
1322 int i = 0;
1323 int bufPos = 0;
1324 int bufLen = 0;
1325 char *buf = NULL;
1326
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);
1332
1333 /* skip any preliminary whitespace or comments */
1334 skipWhiteSpace ();
1335 skipComments ();
1336
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 ();
1342
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);
1349
1350 /* don't increase bufLen to include the NULL char so that strlen (buf) and
1351 * bufLen match */
1352 buf[bufLen] = '\0';
1353
1354 while (!eof_reached)
1355 {
1356 /* make sure that we don't count anything in a comment as being valid to
1357 * parse */
1358 if (isAdaComment (buf, bufPos, bufLen))
1359 {
1360 /* move bufPos to the end of this 'line' so a new line of input is
1361 * read */
1362 bufPos = bufLen - 1;
1363
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)
1368 {
1369 tokenStart = -1;
1370 }
1371 }
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] == '(')
1377 {
1378 i++;
1379 }
1380 else if (buf[bufPos] == ')')
1381 {
1382 if (i == 0)
1383 {
1384 break;
1385 }
1386 else
1387 {
1388 i--;
1389 }
1390 }
1391 else if (buf[bufPos] == ';' ||
1392 ((bufPos + 1) < bufLen &&
1393 (strncasecmp (&buf[bufPos], ":=", strlen (":=")) == 0 ||
1394 strncasecmp (&buf[bufPos], "=>", strlen ("=>")) == 0)))
1395 {
1396 break;
1397 }
1398 /* if we found the : keep track of where we found it */
1399 else if (buf[bufPos] == ':' &&
1400 (bufPos + 1 >= bufLen || buf[bufPos + 1] != '='))
1401 {
1402 varEndPos = bufPos;
1403 }
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)
1409 {
1410 tokenStart = bufPos;
1411 }
1412 else if (kind == ADA_KIND_VARIABLE && varEndPos != -1 && tokenStart >= 0 &&
1413 ((bufPos + 1) >= bufLen || isspace (buf[bufPos + 1]) ||
1414 buf[bufPos + 1] == ';'))
1415 {
1416 if (cmp (&buf[tokenStart], bufLen - tokenStart,
1417 AdaKeywords[ADA_KEYWORD_CONSTANT]) == true)
1418 {
1419 kind = ADA_KIND_CONSTANT;
1420 }
1421 else if (cmp (&buf[tokenStart], bufLen - tokenStart,
1422 AdaKeywords[ADA_KEYWORD_EXCEPTION]) == true)
1423 {
1424 kind = ADA_KIND_EXCEPTION;
1425 }
1426
1427 /* set tokenStart to -2 to prevent any more words from being checked */
1428 tokenStart = -2;
1429 }
1430
1431 bufPos++;
1432
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)
1436 {
1437 readNewLine ();
1438
1439 /* store the new file position for the start of this line */
1440 filePosIndex++;
1441 while (filePosIndex >= filePosSize)
1442 {
1443 filePosSize *= 2;
1444 filePos = xRealloc (filePos, filePosSize, MIOPos);
1445 }
1446 filePos[filePosIndex] = getInputFilePosition ();
1447
1448 /* increment bufLen and bufPos now so that they jump past the NULL
1449 * character in the buffer */
1450 bufLen++;
1451 bufPos++;
1452
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';
1458 }
1459 }
1460
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)
1464 {
1465 varEndPos = bufPos;
1466 }
1467
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)
1472 {
1473 /* there should be no whitespace at the beginning, so tokenStart is
1474 * initialized to 0 */
1475 tokenStart = 0;
1476
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;
1480
1481 for (i = 0; i < varEndPos; i++)
1482 {
1483 /* skip comments which are '--' unless we are in a word */
1484 if (isAdaComment (buf, i, varEndPos))
1485 {
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++);
1489 }
1490 else if (tokenStart != -1 && (isspace (buf[i]) || buf[i] == ',' ||
1491 buf[i] == '\0'))
1492 {
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"))
1496 {
1497 token = newAdaToken (&buf[tokenStart], i - tokenStart,
1498 kind, false, parent);
1499
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];
1504 }
1505 tokenStart = -1;
1506 }
1507 else if (tokenStart == -1 && !(isspace (buf[i]) || buf[i] == ',' ||
1508 buf[i] == '\0'))
1509 {
1510 /* only set the tokenStart for non-newline characters */
1511 tokenStart = i;
1512 }
1513
1514 /* after we are finished with this line, move the file position */
1515 if (buf[i] == '\0')
1516 {
1517 filePosIndex++;
1518 }
1519 }
1520
1521 /* if token start was 'started' then we should store the last token */
1522 if (tokenStart != -1)
1523 {
1524 token = newAdaToken (&buf[tokenStart], i - tokenStart,
1525 kind, false, parent);
1526
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];
1531 }
1532 }
1533
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);
1543
1544 return token;
1545 }
1546
adaParseLoopVar(adaTokenInfo * parent)1547 static adaTokenInfo *adaParseLoopVar (adaTokenInfo *parent)
1548 {
1549 int i;
1550 adaTokenInfo *token = NULL;
1551
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);
1557
1558 /* now skip to the end of the loop declaration */
1559 skipPastKeyword (ADA_KEYWORD_LOOP);
1560
1561 return token;
1562 }
1563
adaParse(adaParseMode mode,adaTokenInfo * parent)1564 static adaTokenInfo *adaParse (adaParseMode mode, adaTokenInfo *parent)
1565 {
1566 int i;
1567 adaTokenInfo genericParamsRoot;
1568 adaTokenInfo *token = NULL;
1569
1570 initAdaTokenList (&genericParamsRoot.children);
1571
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)
1575 {
1576 /* find the next place to start */
1577 skipWhiteSpace ();
1578
1579 /* check some universal things to check for first */
1580 if (eof_reached)
1581 {
1582 break;
1583 }
1584 else if (isAdaComment (line, pos, lineLen))
1585 {
1586 readNewLine ();
1587 continue;
1588 }
1589 else if (adaKeywordCmp (ADA_KEYWORD_PRAGMA) ||
1590 ((mode != ADA_GENERIC) && adaKeywordCmp (ADA_KEYWORD_WITH)) ||
1591 adaKeywordCmp (ADA_KEYWORD_USE))
1592 {
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;
1600 }
1601
1602 /* check for tags based on our current mode */
1603 switch (mode)
1604 {
1605 case ADA_ROOT:
1606 if (adaKeywordCmp (ADA_KEYWORD_PACKAGE))
1607 {
1608 token = adaParseBlock (parent, ADA_KIND_PACKAGE);
1609 }
1610 else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
1611 adaKeywordCmp (ADA_KEYWORD_FUNCTION))
1612 {
1613 token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
1614 }
1615 else if (adaKeywordCmp (ADA_KEYWORD_TASK))
1616 {
1617 token = adaParseBlock (parent, ADA_KIND_TASK);
1618 }
1619 else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
1620 {
1621 token = adaParseBlock (parent, ADA_KIND_PROTECTED);
1622 }
1623 else if (adaKeywordCmp (ADA_KEYWORD_GENERIC))
1624 {
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;
1629 }
1630 else if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
1631 {
1632 /* skip any possible whitespace */
1633 skipWhiteSpace ();
1634
1635 /* skip over the "(" until we hit the tag */
1636 if (!eof_reached && line[pos] == '(')
1637 {
1638 movePos (1);
1639 skipWhiteSpace ();
1640
1641 /* get length of tag */
1642 for (i = 1; (pos + i) < lineLen && line[pos + i] != ')' &&
1643 !isspace (line[pos + i]); i++);
1644
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.
1657 */
1658 token = newAdaTokenFull (&line[pos], i, ADA_KIND_PACKAGE, ADA_PACKAGE_SUBUNIT,
1659 false, parent);
1660
1661 /* since this is a false top-level token, set parent to be
1662 * token */
1663 parent = token;
1664 token = NULL;
1665
1666 /* skip past the ')' */
1667 skipPast (")");
1668 }
1669 else
1670 {
1671 /* move to the end of this statement */
1672 skipPast (";");
1673 }
1674 }
1675 else
1676 {
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 (";");
1683 }
1684
1685 /* check to see if we succeeded in creating our token */
1686 if (token != NULL)
1687 {
1688 /* if any generic params have been gathered, attach them to
1689 * token */
1690 appendAdaTokenList (token, &genericParamsRoot.children);
1691 }
1692
1693 break;
1694
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))
1700 {
1701 token = adaParseBlock (parent, ADA_KIND_PACKAGE);
1702
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;
1708 }
1709 else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
1710 adaKeywordCmp (ADA_KEYWORD_FUNCTION))
1711 {
1712 token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
1713
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;
1719 }
1720 else if (adaKeywordCmp (ADA_KEYWORD_TASK))
1721 {
1722 token = adaParseBlock (parent, ADA_KIND_TASK);
1723 }
1724 else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
1725 {
1726 token = adaParseBlock (parent, ADA_KIND_PROTECTED);
1727 }
1728 else if (adaKeywordCmp (ADA_KEYWORD_TYPE))
1729 {
1730 skipWhiteSpace ();
1731
1732 /* get length of tag */
1733 for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
1734 line[pos + i] != '(' && line[pos + i] != ';'; i++);
1735
1736 appendAdaToken (&genericParamsRoot,
1737 newAdaToken (&line[pos], i, ADA_KIND_FORMAL, false,
1738 NULL));
1739
1740 /* skip to the end of this formal type declaration */
1741 skipPast (";");
1742 }
1743 else if (adaKeywordCmp (ADA_KEYWORD_WITH))
1744 {
1745 skipWhiteSpace ();
1746 /* skip over the function/procedure keyword, it doesn't matter for
1747 * now */
1748 skipUntilWhiteSpace ();
1749 skipWhiteSpace ();
1750
1751 /* get length of tag */
1752 for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
1753 line[pos + i] != '(' && line[pos + i] != ';'; i++);
1754
1755 appendAdaToken (&genericParamsRoot,
1756 newAdaToken (&line[pos], i, ADA_KIND_FORMAL, false,
1757 NULL));
1758
1759 /* increment the position */
1760 movePos (i);
1761
1762 /* now gather the parameters to this subprogram */
1763 if (!eof_reached && line[pos] == '(')
1764 {
1765 while (!eof_reached && line[pos] != ')')
1766 {
1767 movePos (1);
1768 adaParseVariables (genericParamsRoot.children.tail,
1769 ADA_KIND_AUTOMATIC_VARIABLE);
1770 }
1771 movePos (1);
1772 }
1773
1774 /* skip to the end of this formal type declaration */
1775 skipPast (";");
1776 }
1777 else
1778 {
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 (";");
1785 }
1786
1787 /* check to see if we succeeded in creating our token */
1788 if (token != NULL)
1789 {
1790 /* if any generic params have been gathered, attach them to
1791 * token. */
1792 appendAdaTokenList (token, &genericParamsRoot.children);
1793 }
1794
1795 break;
1796
1797 case ADA_DECLARATIONS:
1798 if (adaKeywordCmp (ADA_KEYWORD_PACKAGE))
1799 {
1800 token = adaParseBlock (parent, ADA_KIND_PACKAGE);
1801 }
1802 else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
1803 adaKeywordCmp (ADA_KEYWORD_FUNCTION))
1804 {
1805 token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
1806 }
1807 else if (adaKeywordCmp (ADA_KEYWORD_TASK))
1808 {
1809 token = adaParseBlock (parent, ADA_KIND_TASK);
1810 }
1811 else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
1812 {
1813 token = adaParseBlock (parent, ADA_KIND_PROTECTED);
1814 }
1815 else if (adaKeywordCmp (ADA_KEYWORD_GENERIC))
1816 {
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;
1821 }
1822 else if (adaKeywordCmp (ADA_KEYWORD_TYPE))
1823 {
1824 token = adaParseType (parent, ADA_KIND_TYPE);
1825 }
1826 else if (adaKeywordCmp (ADA_KEYWORD_SUBTYPE))
1827 {
1828 token = adaParseType (parent, ADA_KIND_SUBTYPE);
1829 }
1830 else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
1831 {
1832 mode = ADA_CODE;
1833 break;
1834 }
1835 else if (adaKeywordCmp (ADA_KEYWORD_FOR))
1836 {
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 ();
1844
1845 if (adaKeywordCmp (ADA_KEYWORD_RECORD))
1846 {
1847 /* now skip to the next "record" keyword, which should be the end
1848 * of this use statement */
1849 skipPastKeyword (ADA_KEYWORD_RECORD);
1850 }
1851
1852 /* lastly, skip past the end ";" */
1853 skipPast (";");
1854 }
1855 else if (adaKeywordCmp (ADA_KEYWORD_END))
1856 {
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))
1863 {
1864 skipPast (";");
1865
1866 /* return the token */
1867 freeAdaTokenList (&genericParamsRoot.children);
1868 return token;
1869 }
1870 else
1871 {
1872 /* set the token to NULL so we accidentally don't pick up something
1873 * from earlier */
1874 token = NULL;
1875 skipPast (";");
1876 }
1877 }
1878 else if (adaKeywordCmp (ADA_KEYWORD_ENTRY))
1879 {
1880 token = adaParseSubprogram (parent, ADA_KIND_ENTRY);
1881 }
1882 else if (adaKeywordCmp (ADA_KEYWORD_PRIVATE))
1883 {
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)
1888 {
1889 parent->isPrivate = true;
1890 }
1891
1892 skipWhiteSpace ();
1893 }
1894 else if (adaKeywordCmp (ADA_KEYWORD_OVERRIDING)
1895 || adaKeywordCmp (ADA_KEYWORD_NOT))
1896 {
1897 /* Do nothing, just ignore these keywords. */
1898 ;
1899 }
1900 else
1901 {
1902 /* if nothing else matched this is probably a variable, constant
1903 * or exception declaration */
1904 token = adaParseVariables (parent, ADA_KIND_VARIABLE);
1905 skipPast (";");
1906 }
1907
1908 /* check to see if we succeeded in creating our token */
1909 if (token != NULL)
1910 {
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)
1917 {
1918 /* if any generic params have been gathered, attach them to
1919 * token */
1920 appendAdaTokenList (token, &genericParamsRoot.children);
1921 }
1922 }
1923 break;
1924
1925 case ADA_CODE:
1926 if (adaKeywordCmp (ADA_KEYWORD_DECLARE))
1927 {
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);
1932
1933 /* save the correct starting line */
1934 token->tag.lineNumber = matchLineNum;
1935 token->tag.filePosition = matchFilePos;
1936
1937 adaParse (ADA_DECLARATIONS, token);
1938 }
1939 else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
1940 {
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);
1948
1949 /* save the correct starting line */
1950 token->tag.lineNumber = matchLineNum;
1951 token->tag.filePosition = matchFilePos;
1952
1953 adaParse (ADA_CODE, token);
1954 }
1955 else if (adaKeywordCmp (ADA_KEYWORD_EXCEPTION))
1956 {
1957 mode = ADA_EXCEPTIONS;
1958 break;
1959 }
1960 else if (adaKeywordCmp (ADA_KEYWORD_END))
1961 {
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))
1968 {
1969 skipPast (";");
1970
1971 /* return the token */
1972 freeAdaTokenList (&genericParamsRoot.children);
1973 return token;
1974 }
1975 else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
1976 {
1977 /* a loop with an identifier has this syntax:
1978 * "end loop <ident>;" */
1979 skipWhiteSpace ();
1980
1981 /* now check for the parent loop's name */
1982 if (adaCmp (parent->name))
1983 {
1984 skipPast (";");
1985
1986 /* return the token */
1987 freeAdaTokenList (&genericParamsRoot.children);
1988 return token;
1989 }
1990 }
1991 else
1992 {
1993 /* otherwise, nothing was found so just skip until the end of
1994 * this statement */
1995 skipPast (";");
1996 }
1997 }
1998 else if (adaKeywordCmp (ADA_KEYWORD_ACCEPT))
1999 {
2000 adaParseSubprogram (parent, ADA_KIND_ENTRY);
2001 }
2002 else if (adaKeywordCmp (ADA_KEYWORD_FOR))
2003 {
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);
2013 }
2014 else if (adaKeywordCmp (ADA_KEYWORD_WHILE))
2015 {
2016 token = newAdaToken (AdaKeywords[ADA_KEYWORD_LOOP],
2017 strlen (AdaKeywords[ADA_KEYWORD_LOOP]),
2018 ADA_KIND_ANONYMOUS, false, parent);
2019
2020 /* skip past the while loop declaration and parse the loop body */
2021 skipPastKeyword (ADA_KEYWORD_LOOP);
2022 skipWhiteSpace ();
2023 adaParse (ADA_CODE, token);
2024 }
2025 else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
2026 {
2027 token = newAdaToken (AdaKeywords[ADA_KEYWORD_LOOP],
2028 strlen (AdaKeywords[ADA_KEYWORD_LOOP]),
2029 ADA_KIND_ANONYMOUS, false, parent);
2030
2031 /* save the correct starting line */
2032 token->tag.lineNumber = matchLineNum;
2033 token->tag.filePosition = matchFilePos;
2034
2035 /* parse the loop body */
2036 skipWhiteSpace ();
2037 adaParse (ADA_CODE, token);
2038 }
2039 else if (line != NULL &&
2040 strncasecmp (&line[pos], "<<", strlen ("<<")) == 0)
2041 {
2042 movePos (strlen ("<<"));
2043
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++);
2051
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)
2055 {
2056 newAdaToken (&line[pos], i, ADA_KIND_LABEL, false, parent);
2057 skipPast (">>");
2058 }
2059 }
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))
2066 {
2067 skipWhiteSpace ();
2068 }
2069 else if (adaKeywordCmp (ADA_KEYWORD_IF) ||
2070 adaKeywordCmp (ADA_KEYWORD_ELSIF))
2071 {
2072 skipPastKeyword (ADA_KEYWORD_THEN);
2073 }
2074 else if (adaKeywordCmp (ADA_KEYWORD_CASE))
2075 {
2076 skipPastKeyword (ADA_KEYWORD_IS);
2077 }
2078 else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
2079 {
2080 skipPast ("=>");
2081 }
2082 else
2083 {
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;
2088
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. */
2095
2096 /* Skip whitespaces between the identifier and ':' */
2097 for (; (pos + i) < lineLen; i++)
2098 if (!isspace((unsigned char)(line[pos + i])))
2099 break;
2100
2101 if ((pos + i) < lineLen
2102 && (line[pos + i] == ':')
2103 && (
2104 ((pos + i + 1) == lineLen)
2105 || (line[pos + i + 1] != '=')
2106 ))
2107 token = newAdaToken (&line[pos], i_end, ADA_KIND_IDENTIFIER, false,
2108 parent);
2109
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)
2114 {
2115 /* if something was found, reset the position variable and try to
2116 * find the next item */
2117 movePos (i + 1);
2118 skipWhiteSpace ();
2119
2120 if (adaKeywordCmp (ADA_KEYWORD_DECLARE))
2121 {
2122 adaParse (ADA_DECLARATIONS, token);
2123 }
2124 else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
2125 {
2126 adaParse (ADA_CODE, token);
2127 }
2128 else if (adaKeywordCmp (ADA_KEYWORD_FOR))
2129 {
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);
2135 }
2136 else if (adaKeywordCmp (ADA_KEYWORD_WHILE))
2137 {
2138 /* skip to the loop keyword */
2139 skipPastKeyword (ADA_KEYWORD_LOOP);
2140 skipWhiteSpace ();
2141
2142 /* parse the loop (it may have something to tag which will be
2143 * a 'child' of the loop) */
2144 adaParse (ADA_CODE, token);
2145 }
2146 else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
2147 {
2148 skipWhiteSpace ();
2149
2150 /* parse the loop (it may have something to tag which will be
2151 * a 'child' of the loop) */
2152 adaParse (ADA_CODE, token);
2153 }
2154 else
2155 {
2156 /* otherwise, nothing was found so this is not a valid
2157 * identifier, delete it */
2158 freeAdaToken (&parent->children, token);
2159 token = NULL;
2160 }
2161 }
2162 else
2163 {
2164 /* since nothing was found, simply skip to the end of this
2165 * statement */
2166 skipPast (";");
2167 }
2168 }
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 */
2172
2173 break;
2174
2175 case ADA_EXCEPTIONS:
2176 if (adaKeywordCmp (ADA_KEYWORD_PRAGMA))
2177 {
2178 skipPast (";");
2179 }
2180 else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
2181 {
2182 skipWhiteSpace ();
2183 token = adaParseVariables (parent, ADA_KIND_AUTOMATIC_VARIABLE);
2184 }
2185 else if (adaKeywordCmp (ADA_KEYWORD_END))
2186 {
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))
2193 {
2194 skipPast (";");
2195
2196 /* return the token */
2197 freeAdaTokenList (&genericParamsRoot.children);
2198 return token;
2199 }
2200 else
2201 {
2202 /* otherwise, nothing was found so just skip until the end of
2203 * this statement */
2204 skipPast (";");
2205 }
2206 }
2207 else
2208 {
2209 /* otherwise, nothing was found so just skip until the end of
2210 * this statement */
2211 skipPast (";");
2212 }
2213
2214 break;
2215
2216 default:
2217 Assert (0);
2218 }
2219 }
2220
2221 freeAdaTokenList (&genericParamsRoot.children);
2222 return token;
2223 }
2224
storeAdaTags(adaTokenInfo * token,const char * parentScope)2225 static void storeAdaTags (adaTokenInfo *token, const char *parentScope)
2226 {
2227 char *currentScope = NULL;
2228 adaTokenInfo *tmp = NULL;
2229
2230 Assert (token);
2231
2232 /* do a spec transition if necessary */
2233 if (token->isSpec == true)
2234 {
2235 makeSpec (&token->kind);
2236
2237 if (token->kind != ADA_KIND_UNDEFINED)
2238 {
2239 token->tag.kindIndex = token->kind;
2240 }
2241 }
2242
2243 /* fill in the scope data */
2244 if (token->parent != NULL)
2245 {
2246 if (token->parent->kind != ADA_KIND_UNDEFINED)
2247 {
2248 token->tag.extensionFields.scopeKindIndex = token->parent->kind;
2249 token->tag.extensionFields.scopeName = token->parent->name;
2250 }
2251 }
2252
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)
2256 {
2257 token->name = (char *) AdaKeywords[ADA_KEYWORD_DECLARE];
2258 token->tag.name = AdaKeywords[ADA_KEYWORD_DECLARE];
2259 }
2260
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))))
2272 {
2273 makeTagEntry (&token->tag);
2274
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))
2287 {
2288 if (parentScope != NULL)
2289 {
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 (¤tScope[parentScope_len + 1], token->name, name_len);
2298 currentScope[parentScope_len + 1 + name_len] = '\0';
2299
2300 token->tag.name = currentScope;
2301 markTagExtraBit (&token->tag, XTAG_QUALIFIED_TAGS);
2302 makeTagEntry (&token->tag);
2303 }
2304 else
2305 {
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;
2311 }
2312 }
2313 }
2314
2315 /* now make the child tags */
2316 tmp = token->children.head;
2317 while (tmp != NULL)
2318 {
2319 storeAdaTags (tmp, currentScope);
2320 tmp = tmp->next;
2321 }
2322
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)
2329 {
2330 token->name = NULL;
2331 token->tag.name = NULL;
2332 }
2333
2334 if ((currentScope != NULL) && (currentScope != token->name))
2335 {
2336 eFree ((void *) currentScope);
2337 }
2338 }
2339
2340 /* main parse function */
findAdaTags(void)2341 static void findAdaTags (void)
2342 {
2343 adaTokenInfo root;
2344 adaTokenInfo *tmp;
2345
2346 /* init all global data now */
2347 eof_reached = false;
2348 line = NULL;
2349 pos = 0;
2350 matchLineNum = 0;
2351
2352 /* cannot just set matchFilePos to 0 because the fpos_t is not a simple
2353 * integer on all systems. */
2354 matchFilePos = getInputFilePosition ();
2355
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);
2363
2364 /* read in the first line */
2365 readNewLine ();
2366 if (eof_reached)
2367 goto out;
2368
2369 /* tokenize entire file */
2370 while (!eof_reached && adaParse (ADA_ROOT, &root) != NULL);
2371
2372 /* store tags */
2373 tmp = root.children.head;
2374 while (tmp != NULL)
2375 {
2376 storeAdaTags (tmp, NULL);
2377 tmp = tmp->next;
2378 }
2379
2380 out:
2381 /* clean up tokens */
2382 freeAdaTokenList (&root.children);
2383 }
2384
2385 /* parser definition function */
AdaParser(void)2386 extern parserDefinition* AdaParser (void)
2387 {
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;
2395 }
2396