xref: /Universal-ctags/parsers/ada.c (revision 2598053f205449bda0eb6cc7112975b85e2502e8)
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 (&currentScope[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