2 * Description: Enables extended Ada parsing support in Exuberant Ctags
4 * Date: October 26, 2006
5 * Author: A. Aaron Cornelius (ADotAaronDotCorneliusAtgmailDotcom)
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
15 * to file source.mak add the line
27 * to file parsers.h add the line
31 * #define PARSER_LIST \
33 * Then compile and install ctags as normal (usually: './configure', './make',
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
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
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
55 * 09/25/2006 - Change matchFilePos initialization in the findAdaTags
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
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
72 * TODO: Add inheritance information?
73 * TODO: Add signature gathering?
75 * Copyright (C) 2006 A. Aaron Cornelius
77 * This program is free software; you can redistribute it and/or
78 * modify it under the terms of the GNU General Public License
79 * as published by the Free Software Foundation; either version 2
80 * of the License, or (at your option) any later version.
82 * This program is distributed in the hope that it will be useful,
83 * but WITHOUT ANY WARRANTY; without even the implied warranty of
84 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
85 * GNU General Public License for more details.
87 * You should have received a copy of the GNU General Public License
88 * along with this program; if not, write to the Free Software
89 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
94 #include "general.h" /* always include first */
96 #include <string.h> /* to declare strxxx() functions */
97 #include <ctype.h> /* to define isxxx() macros */
99 #include "parse.h" /* always include */
100 #include "read.h" /* to define file readLineFromInputFile() */
101 #include "entry.h" /* for the tag entry manipulation */
102 #include "routines.h" /* for generic malloc/realloc/free routines */
103 #include "debug.h" /* for Assert */
107 static bool eof_reached
;
109 typedef enum eAdaParseMode
118 typedef enum eAdaKinds
120 ADA_KIND_UNDEFINED
= KIND_GHOST_INDEX
, /* for default/initialization values */
121 ADA_KIND_PACKAGE_SPEC
,
125 ADA_KIND_SUBTYPE_SPEC
,
127 ADA_KIND_RECORD_COMPONENT
,
128 ADA_KIND_ENUM_LITERAL
,
129 ADA_KIND_VARIABLE_SPEC
,
134 ADA_KIND_SUBPROGRAM_SPEC
,
138 ADA_KIND_PROTECTED_SPEC
,
144 ADA_KIND_AUTOMATIC_VARIABLE
,
145 ADA_KIND_ANONYMOUS
, /* for non-identified loops and blocks */
146 ADA_KIND_COUNT
/* must be last */
153 static roleDefinition AdaPackageRoles
[] = {
155 "package name referenced in separate()" },
158 static kindDefinition AdaKinds
[] =
160 { true, 'P', "packspec", "package specifications" },
161 { true, 'p', "package", "packages",
162 .referenceOnly
= false, ATTACH_ROLES(AdaPackageRoles
) },
163 { false, 'T', "typespec", "type specifications" },
164 { true, 't', "type", "types" },
165 { false, 'U', "subspec", "subtype specifications" },
166 { true, 'u', "subtype", "subtypes" },
167 { true, 'c', "component", "record type components" },
168 { true, 'l', "literal", "enum type literals" },
169 { false, 'V', "varspec", "variable specifications" },
170 { true, 'v', "variable", "variables" },
171 { true, 'f', "formal", "generic formal parameters" },
172 { true, 'n', "constant", "constants" },
173 { true, 'x', "exception", "user defined exceptions" },
174 { true, 'R', "subprogspec", "subprogram specifications" },
175 { true, 'r', "subprogram", "subprograms" },
176 { true, 'K', "taskspec", "task specifications" },
177 { true, 'k', "task", "tasks" },
178 { true, 'O', "protectspec", "protected data specifications" },
179 { true, 'o', "protected", "protected data" },
180 { false, 'E', "entryspec", "task/protected data entry specifications" },
181 { true, 'e', "entry", "task/protected data entries" },
182 { true, 'b', "label", "labels" },
183 { true, 'i', "identifier", "loop/declare identifiers"},
184 { false, 'a', "autovar", "automatic variables" },
185 { false, 'y', "anon", "loops and blocks with no identifier" },
188 typedef struct sAdaTokenList
191 struct sAdaTokenInfo
*head
;
192 struct sAdaTokenInfo
*tail
;
195 typedef struct sAdaTokenInfo
202 struct sAdaTokenInfo
*parent
;
203 struct sAdaTokenInfo
*prev
;
204 struct sAdaTokenInfo
*next
;
205 adaTokenList children
;
208 typedef enum eAdaKeywords
214 ADA_KEYWORD_CONSTANT
,
221 ADA_KEYWORD_EXCEPTION
,
223 ADA_KEYWORD_FUNCTION
,
232 ADA_KEYWORD_OVERRIDING
, /* Ada 2005 */
236 ADA_KEYWORD_PROCEDURE
,
237 ADA_KEYWORD_PROTECTED
,
241 ADA_KEYWORD_SEPARATE
,
253 static const char *AdaKeywords
[] =
299 /* variables for managing the input string, position as well as input line
300 * number and position */
301 static const char *line
;
304 static unsigned long matchLineNum
;
305 static MIOPos matchFilePos
;
307 /* utility functions */
308 static void makeSpec (adaKind
*kind
);
310 /* prototypes of functions for manipulating the Ada tokens */
311 static adaTokenInfo
*newAdaToken (const char *name
, int len
,
312 adaKind kind
, bool isSpec
,
313 adaTokenInfo
*parent
);
314 static adaTokenInfo
*newAdaTokenFull (const char *name
, int len
,
315 adaKind kind
, int role
, bool isSpec
,
316 adaTokenInfo
*parent
);
317 static void freeAdaToken (adaTokenList
*list
, adaTokenInfo
*token
);
318 static void appendAdaToken (adaTokenInfo
*parent
, adaTokenInfo
*token
);
320 /* token list processing function prototypes */
321 static void initAdaTokenList (adaTokenList
*list
);
322 static void freeAdaTokenList (adaTokenList
*list
);
323 static void appendAdaTokenList (adaTokenInfo
*parent
, adaTokenList
*children
);
325 /* prototypes of functions for moving through the DEFINED text */
326 static void readNewLine (void);
327 static void movePos (int amount
);
328 static bool cmp (const char *buf
, int len
, const char *match
);
329 static bool adaCmp (const char *match
);
330 static bool adaKeywordCmp (adaKeyword keyword
);
331 static void skipUntilWhiteSpace (void);
332 static void skipWhiteSpace (void);
333 static void skipComments (void);
334 static void skipCommentsAndStringLiteral (void);
335 static void skipPast (const char *past
);
336 static void skipPastKeyword (adaKeyword keyword
);
337 static void skipPastWord (void);
339 typedef bool (* skipCompFn
) (void *data
);
340 static void skipPastLambda (skipCompFn cmpfn
, void *data
);
342 struct cmpKeywordOrWordDataElt
355 static struct cmpKeywordOrWordDataElt
*skipPastKeywordOrWord (struct cmpKeywordOrWordDataElt
* elt
,
358 /* prototypes of functions for parsing the high-level Ada constructs */
359 static adaTokenInfo
*adaParseBlock (adaTokenInfo
*parent
, adaKind kind
);
360 static adaTokenInfo
*adaParseSubprogram (adaTokenInfo
*parent
, adaKind kind
);
361 static adaTokenInfo
*adaParseType (adaTokenInfo
*parent
, adaKind kind
);
362 static adaTokenInfo
*adaParseVariables (adaTokenInfo
*parent
, adaKind kind
);
363 static adaTokenInfo
*adaParseLoopVar (adaTokenInfo
*parent
);
364 static adaTokenInfo
*adaParse (adaParseMode mode
, adaTokenInfo
*parent
);
366 /* prototypes of the functions used by ctags */
367 static void storeAdaTags (adaTokenInfo
*token
, const char *parentScope
);
368 static void findAdaTags (void);
370 static void makeSpec (adaKind
*kind
)
374 case ADA_KIND_PACKAGE
:
375 *kind
= ADA_KIND_PACKAGE_SPEC
;
379 *kind
= ADA_KIND_TYPE_SPEC
;
382 case ADA_KIND_SUBTYPE
:
383 *kind
= ADA_KIND_SUBTYPE_SPEC
;
386 case ADA_KIND_VARIABLE
:
387 *kind
= ADA_KIND_VARIABLE_SPEC
;
390 case ADA_KIND_SUBPROGRAM
:
391 *kind
= ADA_KIND_SUBPROGRAM_SPEC
;
395 *kind
= ADA_KIND_TASK_SPEC
;
398 case ADA_KIND_PROTECTED
:
399 *kind
= ADA_KIND_PROTECTED_SPEC
;
403 *kind
= ADA_KIND_ENTRY_SPEC
;
407 *kind
= ADA_KIND_UNDEFINED
;
412 static adaTokenInfo
*newAdaTokenFull (const char *name
, int len
, adaKind kind
, int role
,
413 bool isSpec
, adaTokenInfo
*parent
)
415 char *tmpName
= NULL
;
416 adaTokenInfo
*token
= xMalloc (1, adaTokenInfo
);
420 if (name
!= NULL
&& len
!= 0)
422 tmpName
= xMalloc (len
+ 1, char);
423 strncpy (tmpName
, name
, len
);
428 initTagEntry (&token
->tag
, tmpName
, ADA_KIND_UNDEFINED
);
431 token
->isSpec
= isSpec
;
432 token
->isPrivate
= false;
434 /* set the token data */
435 token
->name
= tmpName
;
436 token
->parent
= parent
;
438 /* Now set the file scope for this tag. A tag has file scope if its direct
439 * parent is a package/subprogram/protected/task spec, or if it it's parent
440 * is UNDEFINED (a 'root' token), and if this is not in a 'private' section
442 if ((parent
!= NULL
) && (parent
->isPrivate
== false) &&
443 ((parent
->kind
== ADA_KIND_UNDEFINED
) ||
444 (parent
->kind
== ADA_KIND_PACKAGE
&& isRoleAssigned(&parent
->tag
, ADA_PACKAGE_SUBUNIT
)) ||
445 ((parent
->isSpec
== true) && ((parent
->kind
== ADA_KIND_PACKAGE
) ||
446 (parent
->kind
== ADA_KIND_SUBPROGRAM
) ||
447 (parent
->kind
== ADA_KIND_PROTECTED
) ||
448 (parent
->kind
== ADA_KIND_TASK
)))))
450 token
->tag
.isFileScope
= false;
454 markTagExtraBit (&token
->tag
, XTAG_FILE_SCOPE
);
455 token
->tag
.isFileScope
= true;
458 /* add the kind info - unless this is a SEPARATE kind, in which case keep
459 * them blank because they get filled in later. */
460 if (kind
> ADA_KIND_UNDEFINED
)
462 token
->tag
.kindIndex
= kind
;
463 if (role
!= ROLE_DEFINITION_INDEX
)
464 assignRole(&token
->tag
, role
);
468 token
->tag
.kindIndex
= KIND_GHOST_INDEX
;
471 /* setup the parent and children pointers */
472 initAdaTokenList (&token
->children
);
473 appendAdaToken (parent
, token
);
478 static adaTokenInfo
*newAdaToken (const char *name
, int len
, adaKind kind
,
479 bool isSpec
, adaTokenInfo
*parent
)
481 return newAdaTokenFull (name
, len
, kind
, ROLE_DEFINITION_INDEX
, isSpec
, parent
);
484 static void freeAdaToken (adaTokenList
*list
, adaTokenInfo
*token
)
488 if (token
->name
!= NULL
)
490 eFree ((void *) token
->name
);
494 /* before we delete this token, clean up it's children */
495 freeAdaTokenList (&token
->children
);
497 /* move the next token in the list to this token's spot */
498 if (token
->prev
!= NULL
)
500 token
->prev
->next
= token
->next
;
502 else if (list
!= NULL
)
504 /* Token to remove is head in list as 'token->prev == NULL' */
505 Assert (token
->prev
== NULL
);
506 list
->head
= token
->next
;
509 /* move the previous token in the list to this token's spot */
510 if (token
->next
!= NULL
)
512 token
->next
->prev
= token
->prev
;
514 else if (list
!= NULL
)
516 /* Token to remove is tail of list as 'token->next == NULL') */
517 Assert (token
->next
== NULL
);
518 list
->tail
= token
->prev
;
521 /* decrement the list count */
527 /* now that this node has had everything hanging off of it rearranged,
528 * delete this node */
533 static void appendAdaToken (adaTokenInfo
*parent
, adaTokenInfo
*token
)
535 /* if the parent or newChild is NULL there is nothing to be done */
536 if (parent
!= NULL
&& token
!= NULL
)
538 /* we just need to add this to the list and set a parent pointer */
539 parent
->children
.numTokens
++;
540 token
->parent
= parent
;
541 token
->prev
= parent
->children
.tail
;
544 if (parent
->children
.tail
!= NULL
)
546 parent
->children
.tail
->next
= token
;
549 /* the token that was just added always becomes the last token int the
551 parent
->children
.tail
= token
;
553 if (parent
->children
.head
== NULL
)
555 parent
->children
.head
= token
;
560 static void initAdaTokenList (adaTokenList
*list
)
570 static void freeAdaTokenList (adaTokenList
*list
)
574 while (list
->head
!= NULL
)
576 freeAdaToken (list
, list
->head
);
581 static void appendAdaTokenList (adaTokenInfo
*parent
, adaTokenList
*children
)
583 adaTokenInfo
*tmp
= NULL
;
585 if (parent
!= NULL
&& children
!= NULL
)
587 while (children
->head
!= NULL
)
589 tmp
= children
->head
->next
;
590 appendAdaToken (parent
, children
->head
);
592 /* we just need to worry about setting the head pointer properly during
593 * the list iteration. The node's pointers will get set properly by the
594 * appendAdaToken () function */
595 children
->head
= tmp
;
598 /* now that we have added all nodes from the children list to the parent
599 * node, zero out the children list */
600 initAdaTokenList (children
);
604 static void readNewLine (void)
608 line
= (const char *) readLineFromInputFile ();
618 lineLen
= strlen (line
);
627 static void movePos (int amount
)
630 if (!eof_reached
&& pos
>= lineLen
)
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] != '_')) && \
642 strncasecmp (&(buf)[(pos)], "--", strlen ("--")) == 0)
643 #define isAdaStringLiteral(buf, pos, len) \
644 (((pos) < (len)) && ((buf)[(pos)] == '"'))
645 #define isAdaCharLiteral(buf, pos, len) \
646 (((pos) < (len - 2)) && ((buf)[(pos)] == '\'') \
647 && ((buf)[(pos + 2)] == '\''))
650 static bool cmp (const char *buf
, int len
, const char *match
)
655 /* if we are trying to match nothing, that is always true */
661 /* first check to see if the buffer is empty, if it is, return false */
667 matchLen
= strlen (match
);
669 /* A match only happens the number of chars in the matching string match,
670 * and whitespace follows... Which means we also must check to see if the
671 * end of the line is after the matching string. Also check for some
672 * separation characters such as (, ), :, or ; */
673 if ((strncasecmp (buf
, match
, matchLen
) == 0) &&
676 (isspace (buf
[matchLen
]) || buf
[matchLen
] == '(' ||
677 buf
[matchLen
] == ')' || buf
[matchLen
] == ':' ||
678 buf
[matchLen
] == ';'))))
686 static bool adaCmp (const char *match
)
690 /* first check to see if line is empty */
697 status
= cmp (&line
[pos
], lineLen
- pos
, match
);
699 /* if we match, increment the position pointer */
700 if (status
== true && match
!= NULL
)
702 matchLineNum
= getInputLineNumber ();
703 matchFilePos
= getInputFilePosition ();
705 movePos ((strlen (match
)));
711 /* just a version of adaCmp that is a bit more optimized for keywords */
712 static bool adaKeywordCmp (adaKeyword keyword
)
716 /* first check to see if line is empty, if it is */
723 status
= cmp (&line
[pos
], lineLen
- pos
, AdaKeywords
[keyword
]);
725 /* if we match, increment the position pointer */
728 matchLineNum
= getInputLineNumber ();
729 matchFilePos
= getInputFilePosition ();
731 movePos ((strlen (AdaKeywords
[keyword
])));
737 static void skipUntilWhiteSpace (void)
739 /* first check for a comment line, because this would cause the isspace
740 * check to be true immediately */
743 while (!eof_reached
&& !isspace (line
[pos
]))
745 /* don't use movePos () because if we read in a new line with this function
749 /* the newline counts as whitespace so read in the newline and return
753 line
= (const char *) readLineFromInputFile ();
763 lineLen
= strlen (line
);
768 /* now check for comments here */
773 static void skipWhiteSpace (void)
775 /* first check for a comment line, because this would cause the isspace
776 * check to fail immediately */
779 while (!eof_reached
&& isspace (line
[pos
]))
783 /* now check for comments here */
788 static void skipComments (void)
790 while (!eof_reached
&& isAdaComment (line
, pos
, lineLen
))
796 /* Return true if skipped over a string literal (or char literal).
797 * Return false if no string literal (nor char literal) is found. */
798 static bool skipStringLiteral (void)
800 if (!eof_reached
&& isAdaStringLiteral (line
, pos
, lineLen
))
804 } while (!eof_reached
&& !isAdaStringLiteral (line
, pos
, lineLen
));
806 /* Go to the next char of " */
811 else if (!eof_reached
&& isAdaCharLiteral (line
, pos
, lineLen
))
819 static void skipCommentsAndStringLiteral (void)
824 if (!skipStringLiteral ())
829 static void skipPast (const char *past
)
831 /* first check for a comment line, because this would cause the isspace
832 * check to fail immediately */
833 skipCommentsAndStringLiteral ();
835 /* now look for the keyword */
836 while (!eof_reached
&& !adaCmp (past
))
840 /* now check for comments here */
841 skipCommentsAndStringLiteral ();
845 static void skipPastKeyword (adaKeyword keyword
)
847 /* first check for a comment line, because this would cause the isspace
848 * check to fail immediately */
851 /* now look for the keyword */
852 while (!eof_reached
&& !adaKeywordCmp (keyword
))
856 /* now check for comments here */
861 static void skipPastWord (void)
863 /* first check for a comment line, because this would cause the isspace
864 * check to fail immediately */
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
] != ':' &&
873 /* don't use movePos () because if we read in a new line with this function
877 /* the newline counts as whitespace so read in the newline and return
881 line
= (const char *) readLineFromInputFile ();
891 lineLen
= strlen (line
);
896 /* now check for comments here */
901 static void skipPastLambda (skipCompFn cmpfn
, void *data
)
903 /* first check for a comment line, because this would cause the isspace
904 * check to fail immediately */
905 skipCommentsAndStringLiteral ();
907 /* now call the predicate */
908 while (!eof_reached
&& !cmpfn (data
))
912 /* now check for comments here */
913 skipCommentsAndStringLiteral ();
917 struct cmpKeywordOrWordData
919 struct cmpKeywordOrWordDataElt
*found
;
921 struct cmpKeywordOrWordDataElt
*elt
;
924 static bool cmpKeywordOrWord (void *data
)
926 struct cmpKeywordOrWordData
*cmdData
= data
;
928 cmdData
->found
= NULL
;
929 for (int i
= 0; i
< cmdData
->count
; i
++)
931 if (cmdData
->elt
[i
].type
== ELT_KEYWORD
)
933 if (adaKeywordCmp (cmdData
->elt
[i
].u
.keyword
))
935 cmdData
->found
= cmdData
->elt
+ i
;
939 else if (cmdData
->elt
[i
].type
== ELT_WORD
)
941 if (adaCmp (cmdData
->elt
[i
].u
.word
))
943 cmdData
->found
= cmdData
->elt
+ i
;
953 static struct cmpKeywordOrWordDataElt
*skipPastKeywordOrWord (struct cmpKeywordOrWordDataElt
* elt
,
956 struct cmpKeywordOrWordData data
= {
961 skipPastLambda (cmpKeywordOrWord
, &data
);
965 static adaTokenInfo
*adaParseBlock (adaTokenInfo
*parent
, adaKind kind
)
973 /* if the next word is body, this is not a package spec */
974 if (adaKeywordCmp (ADA_KEYWORD_BODY
))
978 /* if the next word is "type" then this has to be a task or protected spec */
979 else if (adaKeywordCmp (ADA_KEYWORD_TYPE
) &&
980 (kind
!= ADA_KIND_PROTECTED
&& kind
!= ADA_KIND_TASK
))
982 /* if this failed to validate then we should just fail */
987 /* we are at the start of what should be the tag now... But we have to get
988 * it's length. So loop until we hit whitespace, init the counter to 1
989 * since we know that the current position is not whitespace */
990 for (i
= 1; (pos
+ i
) < lineLen
&& !isspace (line
[pos
+ i
]) &&
991 line
[pos
+ i
] != '(' && line
[pos
+ i
] != ';'; i
++);
993 /* we have reached the tag of the package, so create the tag */
994 token
= newAdaToken (&line
[pos
], i
, kind
, isSpec
, parent
);
999 /* task and protected types are allowed to have discriminants */
1000 if (!eof_reached
&& line
[pos
] == '(')
1002 while (!eof_reached
&& line
[pos
] != ')')
1005 adaParseVariables (token
, ADA_KIND_AUTOMATIC_VARIABLE
);
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
)
1016 if (adaKeywordCmp (ADA_KEYWORD_IS
))
1020 if (adaKeywordCmp (ADA_KEYWORD_SEPARATE
))
1022 /* if the next word is the keyword "separate", don't create the tag
1023 * since it will be defined elsewhere */
1024 freeAdaToken (&parent
->children
, token
);
1027 /* move past the ";" ending this declaration */
1030 else if (adaKeywordCmp (ADA_KEYWORD_NEW
))
1032 struct cmpKeywordOrWordDataElt
*elt
;
1034 elt
= skipPastKeywordOrWord ((struct cmpKeywordOrWordDataElt
[]) {{
1035 .type
= ELT_KEYWORD
,
1036 .u
.keyword
= ADA_KEYWORD_WITH
,
1043 if (elt
&& elt
->type
== ELT_KEYWORD
)
1044 adaParse (ADA_DECLARATIONS
, token
);
1048 adaParse (ADA_DECLARATIONS
, token
);
1053 else if (adaKeywordCmp (ADA_KEYWORD_RENAMES
))
1058 else if (adaCmp (";"))
1060 token
->isSpec
= true;
1065 /* nothing found, move to the next word */
1066 skipUntilWhiteSpace ();
1071 freeAdaToken (&parent
->children
, token
);
1079 static adaTokenInfo
*adaParseSubprogram (adaTokenInfo
*parent
, adaKind kind
)
1082 adaTokenInfo
*token
;
1083 adaTokenInfo
*tmpToken
= NULL
;
1087 /* we are at the start of what should be the tag now... But we have to get
1088 * it's length. So loop until we hit whitespace or the beginning of the
1089 * parameter list. Init the counter to 1 * since we know that the current
1090 * position is not whitespace */
1091 for (i
= 1; (pos
+ i
) < lineLen
&& !isspace (line
[pos
+ i
]) &&
1092 line
[pos
+ i
] != '(' && line
[pos
+ i
] != ';'; i
++);
1094 /* we have reached the tag of the subprogram, so create the tag... Init the
1095 * isSpec flag to false and we will adjust it when we see if there is an
1096 * "is", "do" or a ";" following the tag */
1097 token
= newAdaToken (&line
[pos
], i
, kind
, false, parent
);
1099 /* move the line position */
1103 /* if we find a '(' grab any parameters */
1104 if (!eof_reached
&& line
[pos
] == '(' && token
!= NULL
)
1106 while (!eof_reached
&& line
[pos
] != ')')
1109 tmpToken
= adaParseVariables (token
, ADA_KIND_AUTOMATIC_VARIABLE
);
1113 /* check to see if anything was received... If this is an entry this may
1114 * have a 'discriminant' and not have any parameters in the first
1115 * parenthesis pair, so check again if this was the case*/
1116 if (kind
== ADA_KIND_ENTRY
&& tmpToken
== NULL
)
1118 /* skip any existing whitespace and see if there is a second parenthesis
1122 if (!eof_reached
&& line
[pos
] == '(')
1124 while (!eof_reached
&& line
[pos
] != ')')
1127 adaParseVariables (token
, ADA_KIND_AUTOMATIC_VARIABLE
);
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
)
1141 if (adaKeywordCmp (ADA_KEYWORD_IS
))
1145 if (adaKeywordCmp (ADA_KEYWORD_SEPARATE
))
1147 /* if the next word is the keyword "separate", don't create the tag
1148 * since it will be defined elsewhere */
1149 freeAdaToken (&parent
->children
, token
);
1152 /* move past the ";" ending this declaration */
1155 else if (adaKeywordCmp (ADA_KEYWORD_NEW
))
1157 /* if this is a "new" something then no need to parse */
1160 else if (line
[pos
] == '(')
1162 /* '(' is the starter of an expression function. */
1167 adaParse (ADA_DECLARATIONS
, token
);
1172 else if (adaKeywordCmp (ADA_KEYWORD_RENAMES
))
1177 else if (adaKeywordCmp (ADA_KEYWORD_DO
))
1179 /* do is the keyword for the beginning of a task entry */
1180 adaParse (ADA_CODE
, token
);
1183 else if (adaCmp (";"))
1185 /* this is just a spec then, so set the flag in the token */
1186 token
->isSpec
= true;
1191 /* nothing found, move to the next word */
1192 movePos (1); /* make sure to advance even if we aren't actually on a word */
1200 static adaTokenInfo
*adaParseType (adaTokenInfo
*parent
, adaKind kind
)
1203 adaTokenInfo
*token
= NULL
;
1207 /* get the name of the type */
1208 for (i
= 1; (pos
+ i
) < lineLen
&& !isspace (line
[pos
+ i
]) &&
1209 line
[pos
+ i
] != '(' && line
[pos
+ i
] != ';'; i
++);
1211 token
= newAdaToken (&line
[pos
], i
, kind
, false, parent
);
1216 if (!eof_reached
&& line
[pos
] == '(')
1218 /* in this case there is a discriminant to this type, gather the
1220 while (!eof_reached
&& line
[pos
] != ')')
1223 adaParseVariables (token
, ADA_KIND_AUTOMATIC_VARIABLE
);
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
))
1234 /* check to see if this may be a record or an enumeration */
1235 if (!eof_reached
&& line
[pos
] == '(')
1238 adaParseVariables (token
, ADA_KIND_ENUM_LITERAL
);
1242 /* Parsing following form here.
1244 * A. type foo is record ...;
1245 * B. type foo is new bar with record ...;
1246 * C. type foo is new bar;
1248 if (adaKeywordCmp (ADA_KEYWORD_NEW
))
1251 struct cmpKeywordOrWordDataElt
*elt
;
1252 elt
= skipPastKeywordOrWord ((struct cmpKeywordOrWordDataElt
[]) {{
1253 .type
= ELT_KEYWORD
,
1254 .u
.keyword
= ADA_KEYWORD_WITH
,
1260 if (elt
&& elt
->type
== ELT_WORD
)
1269 if (adaKeywordCmp (ADA_KEYWORD_RECORD
))
1272 /* until we hit "end record" we need to gather type variables */
1273 while (!eof_reached
)
1277 if (adaKeywordCmp (ADA_KEYWORD_END
))
1280 if (adaKeywordCmp (ADA_KEYWORD_RECORD
))
1286 /* handle variant types */
1287 else if (adaKeywordCmp (ADA_KEYWORD_CASE
))
1289 skipPastKeyword (ADA_KEYWORD_IS
);
1291 else if (adaKeywordCmp (ADA_KEYWORD_WHEN
))
1297 adaParseVariables (token
, ADA_KIND_RECORD_COMPONENT
);
1306 token
->isSpec
= true;
1314 static adaTokenInfo
*adaParseVariables (adaTokenInfo
*parent
, adaKind kind
)
1316 /* variables for keeping track of tags */
1318 int tokenStart
= -1;
1319 adaTokenInfo
*token
= NULL
;
1321 /* buffer management variables */
1327 /* file and line position variables */
1328 unsigned long int lineNum
;
1329 int filePosIndex
= 0;
1330 int filePosSize
= 32;
1331 MIOPos
*filePos
= xMalloc (filePosSize
, MIOPos
);
1333 /* skip any preliminary whitespace or comments */
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
1340 lineNum
= getInputLineNumber ();
1341 filePos
[filePosIndex
] = getInputFilePosition ();
1343 /* setup local buffer... Since we may have to read a few lines to verify
1344 * that this is a proper variable declaration, and still make a token for
1345 * each variable, add one to the allocated string to account for a '\0' */
1346 bufLen
= lineLen
- pos
;
1347 buf
= xMalloc (bufLen
+ 1, char);
1348 memcpy ((void *) buf
, (void *) &line
[pos
], bufLen
);
1350 /* don't increase bufLen to include the NULL char so that strlen (buf) and
1354 while (!eof_reached
)
1356 /* make sure that we don't count anything in a comment as being valid to
1358 if (isAdaComment (buf
, bufPos
, bufLen
))
1360 /* move bufPos to the end of this 'line' so a new line of input is
1362 bufPos
= bufLen
- 1;
1364 /* if tokenStart is not -2 then we may be trying to track the type
1365 * of this variable declaration, so set tokenStart to -1 so that the
1366 * tracking can start over */
1367 if (tokenStart
!= -2)
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
] == '(')
1380 else if (buf
[bufPos
] == ')')
1391 else if (buf
[bufPos
] == ';' ||
1392 ((bufPos
+ 1) < bufLen
&&
1393 (strncasecmp (&buf
[bufPos
], ":=", strlen (":=")) == 0 ||
1394 strncasecmp (&buf
[bufPos
], "=>", strlen ("=>")) == 0)))
1398 /* if we found the : keep track of where we found it */
1399 else if (buf
[bufPos
] == ':' &&
1400 (bufPos
+ 1 >= bufLen
|| buf
[bufPos
+ 1] != '='))
1404 /* if we have the position of the ':' find out what the next word is,
1405 * because if it "constant" or "exception" then we must tag this slightly
1406 * differently, But only check this for normal variables */
1407 else if (kind
== ADA_KIND_VARIABLE
&& varEndPos
!= -1 &&
1408 !isspace (buf
[bufPos
]) && tokenStart
== -1)
1410 tokenStart
= bufPos
;
1412 else if (kind
== ADA_KIND_VARIABLE
&& varEndPos
!= -1 && tokenStart
>= 0 &&
1413 ((bufPos
+ 1) >= bufLen
|| isspace (buf
[bufPos
+ 1]) ||
1414 buf
[bufPos
+ 1] == ';'))
1416 if (cmp (&buf
[tokenStart
], bufLen
- tokenStart
,
1417 AdaKeywords
[ADA_KEYWORD_CONSTANT
]) == true)
1419 kind
= ADA_KIND_CONSTANT
;
1421 else if (cmp (&buf
[tokenStart
], bufLen
- tokenStart
,
1422 AdaKeywords
[ADA_KEYWORD_EXCEPTION
]) == true)
1424 kind
= ADA_KIND_EXCEPTION
;
1427 /* set tokenStart to -2 to prevent any more words from being checked */
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
)
1439 /* store the new file position for the start of this line */
1441 while (filePosIndex
>= filePosSize
)
1444 filePos
= xRealloc (filePos
, filePosSize
, MIOPos
);
1446 filePos
[filePosIndex
] = getInputFilePosition ();
1448 /* increment bufLen and bufPos now so that they jump past the NULL
1449 * character in the buffer */
1453 /* allocate space and store this into our buffer */
1455 buf
= xRealloc (buf
, bufLen
+ 1, char);
1456 memcpy ((void *) &buf
[bufPos
], (void *) line
, lineLen
);
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)
1468 /* so we found a : or ;... If it is a : go back through the buffer and
1469 * create a token for each word skipping over all whitespace and commas
1470 * until the : is hit*/
1471 if (varEndPos
!= -1)
1473 /* there should be no whitespace at the beginning, so tokenStart is
1474 * initialized to 0 */
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 */
1481 for (i
= 0; i
< varEndPos
; i
++)
1483 /* skip comments which are '--' unless we are in a word */
1484 if (isAdaComment (buf
, i
, varEndPos
))
1486 /* move i past the '\0' that we put at the end of each line stored in
1488 for ( ; i
< varEndPos
&& buf
[i
] != '\0'; i
++);
1490 else if (tokenStart
!= -1 && (isspace (buf
[i
]) || buf
[i
] == ',' ||
1493 /* only store the word if it is not an in/out keyword */
1494 if (!cmp (&buf
[tokenStart
], varEndPos
, "in") &&
1495 !cmp (&buf
[tokenStart
], varEndPos
, "out"))
1497 token
= newAdaToken (&buf
[tokenStart
], i
- tokenStart
,
1498 kind
, false, parent
);
1500 /* now set the proper line and file position counts for this
1502 token
->tag
.lineNumber
= lineNum
+ filePosIndex
;
1503 token
->tag
.filePosition
= filePos
[filePosIndex
];
1507 else if (tokenStart
== -1 && !(isspace (buf
[i
]) || buf
[i
] == ',' ||
1510 /* only set the tokenStart for non-newline characters */
1514 /* after we are finished with this line, move the file position */
1521 /* if token start was 'started' then we should store the last token */
1522 if (tokenStart
!= -1)
1524 token
= newAdaToken (&buf
[tokenStart
], i
- tokenStart
,
1525 kind
, false, parent
);
1527 /* now set the proper line and file position counts for this
1529 token
->tag
.lineNumber
= lineNum
+ filePosIndex
;
1530 token
->tag
.filePosition
= filePos
[filePosIndex
];
1534 /* now get the pos variable to point to the correct place in line where we
1535 * left off in our temp buf, and free our temporary buffer. This is a
1536 * little different than most buf position moves. It gets the distance from
1537 * the current buf position to the end of the buffer, which is also the
1538 * distance from where pos should be wrt the end of the variable
1540 movePos ((lineLen
- (bufLen
- bufPos
)) - pos
);
1541 eFree ((void *) buf
);
1542 eFree ((void *) filePos
);
1547 static adaTokenInfo
*adaParseLoopVar (adaTokenInfo
*parent
)
1550 adaTokenInfo
*token
= NULL
;
1553 for (i
= 1; (pos
+ i
) < lineLen
&& !isspace (line
[pos
+ i
]); i
++);
1554 token
= newAdaToken (&line
[pos
], i
, ADA_KIND_AUTOMATIC_VARIABLE
, false,
1558 /* now skip to the end of the loop declaration */
1559 skipPastKeyword (ADA_KEYWORD_LOOP
);
1564 static adaTokenInfo
*adaParse (adaParseMode mode
, adaTokenInfo
*parent
)
1567 adaTokenInfo genericParamsRoot
;
1568 adaTokenInfo
*token
= NULL
;
1570 initAdaTokenList (&genericParamsRoot
.children
);
1572 /* if we hit the end of the file, line will be NULL and our skip and match
1573 * functions will hit this jump buffer with eof_reached */
1574 while (!eof_reached
)
1576 /* find the next place to start */
1579 /* check some universal things to check for first */
1584 else if (isAdaComment (line
, pos
, lineLen
))
1589 else if (adaKeywordCmp (ADA_KEYWORD_PRAGMA
) ||
1590 ((mode
!= ADA_GENERIC
) && adaKeywordCmp (ADA_KEYWORD_WITH
)) ||
1591 adaKeywordCmp (ADA_KEYWORD_USE
))
1593 /* set the token to NULL so we accidentally don't pick up something
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) */
1602 /* check for tags based on our current mode */
1606 if (adaKeywordCmp (ADA_KEYWORD_PACKAGE
))
1608 token
= adaParseBlock (parent
, ADA_KIND_PACKAGE
);
1610 else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE
) ||
1611 adaKeywordCmp (ADA_KEYWORD_FUNCTION
))
1613 token
= adaParseSubprogram (parent
, ADA_KIND_SUBPROGRAM
);
1615 else if (adaKeywordCmp (ADA_KEYWORD_TASK
))
1617 token
= adaParseBlock (parent
, ADA_KIND_TASK
);
1619 else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED
))
1621 token
= adaParseBlock (parent
, ADA_KIND_PROTECTED
);
1623 else if (adaKeywordCmp (ADA_KEYWORD_GENERIC
))
1625 /* if we have hit a generic declaration, go to the generic section
1626 * and collect the formal parameters */
1630 else if (adaKeywordCmp (ADA_KEYWORD_SEPARATE
))
1632 /* skip any possible whitespace */
1635 /* skip over the "(" until we hit the tag */
1636 if (!eof_reached
&& line
[pos
] == '(')
1641 /* get length of tag */
1642 for (i
= 1; (pos
+ i
) < lineLen
&& line
[pos
+ i
] != ')' &&
1643 !isspace (line
[pos
+ i
]); i
++);
1645 /* the original comment before we introduced reference tags:
1646 * -----------------------------------------------------------------
1647 * if this is a separate declaration, all it really does is create
1648 * a false high level token for everything in this file to belong
1649 * to... But we don't know what kind it is, so we declare it as
1650 * ADA_KIND_SEPARATE, which will cause it not to be placed in
1651 * the tag file, and the item in this file will be printed as
1652 * separate:<name> instead of package:<name> or whatever the
1653 * parent kind really is (assuming the ctags option will be on
1654 * for printing such info to the tag file)
1655 * -----------------------------------------------------------------
1656 * Now we have reference tags. So we can use ADA_KIND_PACKAGE as kind.
1658 token
= newAdaTokenFull (&line
[pos
], i
, ADA_KIND_PACKAGE
, ADA_PACKAGE_SUBUNIT
,
1661 /* since this is a false top-level token, set parent to be
1666 /* skip past the ')' */
1671 /* move to the end of this statement */
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
1685 /* check to see if we succeeded in creating our token */
1688 /* if any generic params have been gathered, attach them to
1690 appendAdaTokenList (token
, &genericParamsRoot
.children
);
1696 /* if we are processing a generic block, make up some temp children
1697 * which we will later attach to the root of the real
1698 * procedure/package/whatever the formal parameters are for */
1699 if (adaKeywordCmp (ADA_KEYWORD_PACKAGE
))
1701 token
= adaParseBlock (parent
, ADA_KIND_PACKAGE
);
1703 /* The above 'adaParseBlock' has read the end of a 'generic package declaration',
1704 * reset the mode back to the original mode.
1705 * see: ARM 12.1(24) */
1707 mode
= (parent
->parent
)? ADA_DECLARATIONS
: ADA_ROOT
;
1709 else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE
) ||
1710 adaKeywordCmp (ADA_KEYWORD_FUNCTION
))
1712 token
= adaParseSubprogram (parent
, ADA_KIND_SUBPROGRAM
);
1714 /* The above 'adaParseBlock' as read the end of a 'generic function/procedure declaration',
1715 * reset the mode back to the original mode.
1716 * see: ARM 12.1(21/22) */
1718 mode
= (parent
->parent
)? ADA_DECLARATIONS
: ADA_ROOT
;
1720 else if (adaKeywordCmp (ADA_KEYWORD_TASK
))
1722 token
= adaParseBlock (parent
, ADA_KIND_TASK
);
1724 else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED
))
1726 token
= adaParseBlock (parent
, ADA_KIND_PROTECTED
);
1728 else if (adaKeywordCmp (ADA_KEYWORD_TYPE
))
1732 /* get length of tag */
1733 for (i
= 1; (pos
+ i
) < lineLen
&& !isspace (line
[pos
+ i
]) &&
1734 line
[pos
+ i
] != '(' && line
[pos
+ i
] != ';'; i
++);
1736 appendAdaToken (&genericParamsRoot
,
1737 newAdaToken (&line
[pos
], i
, ADA_KIND_FORMAL
, false,
1740 /* skip to the end of this formal type declaration */
1743 else if (adaKeywordCmp (ADA_KEYWORD_WITH
))
1746 /* skip over the function/procedure keyword, it doesn't matter for
1748 skipUntilWhiteSpace ();
1751 /* get length of tag */
1752 for (i
= 1; (pos
+ i
) < lineLen
&& !isspace (line
[pos
+ i
]) &&
1753 line
[pos
+ i
] != '(' && line
[pos
+ i
] != ';'; i
++);
1755 appendAdaToken (&genericParamsRoot
,
1756 newAdaToken (&line
[pos
], i
, ADA_KIND_FORMAL
, false,
1759 /* increment the position */
1762 /* now gather the parameters to this subprogram */
1763 if (!eof_reached
&& line
[pos
] == '(')
1765 while (!eof_reached
&& line
[pos
] != ')')
1768 adaParseVariables (genericParamsRoot
.children
.tail
,
1769 ADA_KIND_AUTOMATIC_VARIABLE
);
1774 /* skip to the end of this formal type declaration */
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
1787 /* check to see if we succeeded in creating our token */
1790 /* if any generic params have been gathered, attach them to
1792 appendAdaTokenList (token
, &genericParamsRoot
.children
);
1797 case ADA_DECLARATIONS
:
1798 if (adaKeywordCmp (ADA_KEYWORD_PACKAGE
))
1800 token
= adaParseBlock (parent
, ADA_KIND_PACKAGE
);
1802 else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE
) ||
1803 adaKeywordCmp (ADA_KEYWORD_FUNCTION
))
1805 token
= adaParseSubprogram (parent
, ADA_KIND_SUBPROGRAM
);
1807 else if (adaKeywordCmp (ADA_KEYWORD_TASK
))
1809 token
= adaParseBlock (parent
, ADA_KIND_TASK
);
1811 else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED
))
1813 token
= adaParseBlock (parent
, ADA_KIND_PROTECTED
);
1815 else if (adaKeywordCmp (ADA_KEYWORD_GENERIC
))
1817 /* if we have hit a generic declaration, go to the generic section
1818 * and collect the formal parameters */
1822 else if (adaKeywordCmp (ADA_KEYWORD_TYPE
))
1824 token
= adaParseType (parent
, ADA_KIND_TYPE
);
1826 else if (adaKeywordCmp (ADA_KEYWORD_SUBTYPE
))
1828 token
= adaParseType (parent
, ADA_KIND_SUBTYPE
);
1830 else if (adaKeywordCmp (ADA_KEYWORD_BEGIN
))
1835 else if (adaKeywordCmp (ADA_KEYWORD_FOR
))
1837 /* if we hit a "for" statement it is defining implementation details
1838 * for a specific type/variable/subprogram/etc... So we should just
1839 * skip it, so skip the tag, then we need to see if there is a
1840 * 'record' keyword... If there is we must skip past the
1841 * 'end record;' statement. First skip past the tag */
1842 skipPastKeyword (ADA_KEYWORD_USE
);
1845 if (adaKeywordCmp (ADA_KEYWORD_RECORD
))
1847 /* now skip to the next "record" keyword, which should be the end
1848 * of this use statement */
1849 skipPastKeyword (ADA_KEYWORD_RECORD
);
1852 /* lastly, skip past the end ";" */
1855 else if (adaKeywordCmp (ADA_KEYWORD_END
))
1857 /* if we have hit an end then we must see if the next word matches
1858 * the parent token's name. If it does we hit the end of whatever
1859 * sort of block construct we were processing and we must
1862 if (adaCmp (parent
->name
))
1866 /* return the token */
1867 freeAdaTokenList (&genericParamsRoot
.children
);
1872 /* set the token to NULL so we accidentally don't pick up something
1878 else if (adaKeywordCmp (ADA_KEYWORD_ENTRY
))
1880 token
= adaParseSubprogram (parent
, ADA_KIND_ENTRY
);
1882 else if (adaKeywordCmp (ADA_KEYWORD_PRIVATE
))
1884 /* if this is a private declaration then we need to set the global
1885 * file spec flag and then skip whitespace to get to the next bit of
1889 parent
->isPrivate
= true;
1894 else if (adaKeywordCmp (ADA_KEYWORD_OVERRIDING
)
1895 || adaKeywordCmp (ADA_KEYWORD_NOT
))
1897 /* Do nothing, just ignore these keywords. */
1902 /* if nothing else matched this is probably a variable, constant
1903 * or exception declaration */
1904 token
= adaParseVariables (parent
, ADA_KIND_VARIABLE
);
1908 /* check to see if we succeeded in creating our token */
1911 /* if this is one of the root-type tokens... Do some extra
1913 if (token
->kind
== ADA_KIND_PACKAGE
||
1914 token
->kind
== ADA_KIND_SUBPROGRAM
||
1915 token
->kind
== ADA_KIND_TASK
||
1916 token
->kind
== ADA_KIND_PROTECTED
)
1918 /* if any generic params have been gathered, attach them to
1920 appendAdaTokenList (token
, &genericParamsRoot
.children
);
1926 if (adaKeywordCmp (ADA_KEYWORD_DECLARE
))
1928 /* if we are starting a declare block here, and not down at the
1929 * identifier definition then make an anonymous token to track the
1930 * data in this block */
1931 token
= newAdaToken (NULL
, 0, ADA_KIND_ANONYMOUS
, false, parent
);
1933 /* save the correct starting line */
1934 token
->tag
.lineNumber
= matchLineNum
;
1935 token
->tag
.filePosition
= matchFilePos
;
1937 adaParse (ADA_DECLARATIONS
, token
);
1939 else if (adaKeywordCmp (ADA_KEYWORD_BEGIN
))
1941 /* if we are starting a code block here, and not down at the
1942 * identifier definition then make an anonymous token to track the
1943 * data in this block, if this was part of a proper LABEL:
1944 * declare/begin/end block then the parent would already be a label
1945 * and this begin statement would have been found while in the
1946 * ADA_DECLARATIONS parsing section */
1947 token
= newAdaToken (NULL
, 0, ADA_KIND_ANONYMOUS
, false, parent
);
1949 /* save the correct starting line */
1950 token
->tag
.lineNumber
= matchLineNum
;
1951 token
->tag
.filePosition
= matchFilePos
;
1953 adaParse (ADA_CODE
, token
);
1955 else if (adaKeywordCmp (ADA_KEYWORD_EXCEPTION
))
1957 mode
= ADA_EXCEPTIONS
;
1960 else if (adaKeywordCmp (ADA_KEYWORD_END
))
1962 /* if we have hit an end then we must see if the next word matches
1963 * the parent token's name. If it does we hit the end of whatever
1964 * sort of block construct we were processing and we must
1967 if (adaCmp (parent
->name
))
1971 /* return the token */
1972 freeAdaTokenList (&genericParamsRoot
.children
);
1975 else if (adaKeywordCmp (ADA_KEYWORD_LOOP
))
1977 /* a loop with an identifier has this syntax:
1978 * "end loop <ident>;" */
1981 /* now check for the parent loop's name */
1982 if (adaCmp (parent
->name
))
1986 /* return the token */
1987 freeAdaTokenList (&genericParamsRoot
.children
);
1993 /* otherwise, nothing was found so just skip until the end of
1998 else if (adaKeywordCmp (ADA_KEYWORD_ACCEPT
))
2000 adaParseSubprogram (parent
, ADA_KIND_ENTRY
);
2002 else if (adaKeywordCmp (ADA_KEYWORD_FOR
))
2004 /* if this is a for loop, then we may need to pick up the
2005 * automatic loop iterator, But... The loop variable is only
2006 * available within the loop itself so make an anonymous label
2007 * parent for this loop var to be parsed in */
2008 token
= newAdaToken (AdaKeywords
[ADA_KEYWORD_LOOP
],
2009 strlen (AdaKeywords
[ADA_KEYWORD_LOOP
]),
2010 ADA_KIND_ANONYMOUS
, false, parent
);
2011 adaParseLoopVar (token
);
2012 adaParse (ADA_CODE
, token
);
2014 else if (adaKeywordCmp (ADA_KEYWORD_WHILE
))
2016 token
= newAdaToken (AdaKeywords
[ADA_KEYWORD_LOOP
],
2017 strlen (AdaKeywords
[ADA_KEYWORD_LOOP
]),
2018 ADA_KIND_ANONYMOUS
, false, parent
);
2020 /* skip past the while loop declaration and parse the loop body */
2021 skipPastKeyword (ADA_KEYWORD_LOOP
);
2023 adaParse (ADA_CODE
, token
);
2025 else if (adaKeywordCmp (ADA_KEYWORD_LOOP
))
2027 token
= newAdaToken (AdaKeywords
[ADA_KEYWORD_LOOP
],
2028 strlen (AdaKeywords
[ADA_KEYWORD_LOOP
]),
2029 ADA_KIND_ANONYMOUS
, false, parent
);
2031 /* save the correct starting line */
2032 token
->tag
.lineNumber
= matchLineNum
;
2033 token
->tag
.filePosition
= matchFilePos
;
2035 /* parse the loop body */
2037 adaParse (ADA_CODE
, token
);
2039 else if (line
!= NULL
&&
2040 strncasecmp (&line
[pos
], "<<", strlen ("<<")) == 0)
2042 movePos (strlen ("<<"));
2044 /* if the first chars are <<, find the ending >> and if we do that
2045 * then store the label tag, start i at strlen of "<<" plus 1
2046 * because we don't want to move the real pos until we know for
2047 * sure this is a label */
2048 for (i
= 1; (pos
+ i
) < lineLen
&&
2049 strncasecmp (&line
[pos
+ i
], ">>", strlen (">>")) != 0;
2052 /* if we didn't increment to the end of the line, a match was
2053 * found, if we didn't just fall through */
2054 if ((pos
+ i
) < lineLen
)
2056 newAdaToken (&line
[pos
], i
, ADA_KIND_LABEL
, false, parent
);
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
))
2069 else if (adaKeywordCmp (ADA_KEYWORD_IF
) ||
2070 adaKeywordCmp (ADA_KEYWORD_ELSIF
))
2072 skipPastKeyword (ADA_KEYWORD_THEN
);
2074 else if (adaKeywordCmp (ADA_KEYWORD_CASE
))
2076 skipPastKeyword (ADA_KEYWORD_IS
);
2078 else if (adaKeywordCmp (ADA_KEYWORD_WHEN
))
2085 /* set token to NULL so we don't accidentally not find an identifier,
2086 * But then fall through to the != NULL check */
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
] == '_'))
2094 i_end
= i
; /* Records the end of identifier. */
2096 /* Skip whitespaces between the identifier and ':' */
2097 for (; (pos
+ i
) < lineLen
; i
++)
2098 if (!isspace((unsigned char)(line
[pos
+ i
])))
2101 if ((pos
+ i
) < lineLen
2102 && (line
[pos
+ i
] == ':')
2104 ((pos
+ i
+ 1) == lineLen
)
2105 || (line
[pos
+ i
+ 1] != '=')
2107 token
= newAdaToken (&line
[pos
], i_end
, ADA_KIND_IDENTIFIER
, false,
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 */
2115 /* if something was found, reset the position variable and try to
2116 * find the next item */
2120 if (adaKeywordCmp (ADA_KEYWORD_DECLARE
))
2122 adaParse (ADA_DECLARATIONS
, token
);
2124 else if (adaKeywordCmp (ADA_KEYWORD_BEGIN
))
2126 adaParse (ADA_CODE
, token
);
2128 else if (adaKeywordCmp (ADA_KEYWORD_FOR
))
2130 /* just grab the automatic loop variable, and then parse the
2131 * loop (it may have something to tag which will be a 'child'
2133 adaParseLoopVar (token
);
2134 adaParse (ADA_CODE
, token
);
2136 else if (adaKeywordCmp (ADA_KEYWORD_WHILE
))
2138 /* skip to the loop keyword */
2139 skipPastKeyword (ADA_KEYWORD_LOOP
);
2142 /* parse the loop (it may have something to tag which will be
2143 * a 'child' of the loop) */
2144 adaParse (ADA_CODE
, token
);
2146 else if (adaKeywordCmp (ADA_KEYWORD_LOOP
))
2150 /* parse the loop (it may have something to tag which will be
2151 * a 'child' of the loop) */
2152 adaParse (ADA_CODE
, token
);
2156 /* otherwise, nothing was found so this is not a valid
2157 * identifier, delete it */
2158 freeAdaToken (&parent
->children
, token
);
2164 /* since nothing was found, simply skip to the end of this
2169 /* else... No keyword tag fields found, look for others such as
2170 * loop and declare identifiers labels or just skip over this
2175 case ADA_EXCEPTIONS
:
2176 if (adaKeywordCmp (ADA_KEYWORD_PRAGMA
))
2180 else if (adaKeywordCmp (ADA_KEYWORD_WHEN
))
2183 token
= adaParseVariables (parent
, ADA_KIND_AUTOMATIC_VARIABLE
);
2185 else if (adaKeywordCmp (ADA_KEYWORD_END
))
2187 /* if we have hit an end then we must see if the next word matches
2188 * the parent token's name. If it does we hit the end of whatever
2189 * sort of block construct we were processing and we must
2192 if (adaCmp (parent
->name
))
2196 /* return the token */
2197 freeAdaTokenList (&genericParamsRoot
.children
);
2202 /* otherwise, nothing was found so just skip until the end of
2209 /* otherwise, nothing was found so just skip until the end of
2221 freeAdaTokenList (&genericParamsRoot
.children
);
2225 static void storeAdaTags (adaTokenInfo
*token
, const char *parentScope
)
2227 char *currentScope
= NULL
;
2228 adaTokenInfo
*tmp
= NULL
;
2232 /* do a spec transition if necessary */
2233 if (token
->isSpec
== true)
2235 makeSpec (&token
->kind
);
2237 if (token
->kind
!= ADA_KIND_UNDEFINED
)
2239 token
->tag
.kindIndex
= token
->kind
;
2243 /* fill in the scope data */
2244 if (token
->parent
!= NULL
)
2246 if (token
->parent
->kind
!= ADA_KIND_UNDEFINED
)
2248 token
->tag
.extensionFields
.scopeKindIndex
= token
->parent
->kind
;
2249 token
->tag
.extensionFields
.scopeName
= token
->parent
->name
;
2253 /* one check before we try to make a tag... If this is an anonymous
2254 * declare block then it's name is empty. Give it one */
2255 if (token
->kind
== ADA_KIND_ANONYMOUS
&& token
->name
== NULL
)
2257 token
->name
= (char *) AdaKeywords
[ADA_KEYWORD_DECLARE
];
2258 token
->tag
.name
= AdaKeywords
[ADA_KEYWORD_DECLARE
];
2261 /* Now 'make' tags that have their options set, But only make anonymous
2262 * tags if they have children tags. Also, don't make this tag if the file
2263 * scope flag is not set and this tag is a file scope tag. */
2264 if ((token
->kind
> ADA_KIND_UNDEFINED
) && (token
->kind
< ADA_KIND_COUNT
) &&
2265 (AdaKinds
[token
->kind
].enabled
== true) &&
2266 (token
->name
!= NULL
) &&
2267 ((token
->kind
== ADA_KIND_ANONYMOUS
&& token
->children
.head
!= NULL
) ||
2268 token
->kind
!= ADA_KIND_ANONYMOUS
) &&
2269 ((isXtagEnabled (XTAG_FILE_SCOPE
) == true) ||
2270 ((isXtagEnabled (XTAG_FILE_SCOPE
) == false) &&
2271 (token
->tag
.isFileScope
== false))))
2273 makeTagEntry (&token
->tag
);
2275 /* before making the tag, if the --extra=+q flag is set we should create
2276 * an extra entry which is the full parent.tag name. But only do this if
2277 * the parentScope flag is not NULL, and this token is not of a limited
2278 * scope type such as a record component, enum literal, label, etc. */
2279 if ((isXtagEnabled (XTAG_QUALIFIED_TAGS
) == true) &&
2280 (token
->kind
!= ADA_KIND_RECORD_COMPONENT
) &&
2281 (token
->kind
!= ADA_KIND_ENUM_LITERAL
) &&
2282 (token
->kind
!= ADA_KIND_FORMAL
) &&
2283 (token
->kind
!= ADA_KIND_LABEL
) &&
2284 (token
->kind
!= ADA_KIND_IDENTIFIER
) &&
2285 (token
->kind
!= ADA_KIND_AUTOMATIC_VARIABLE
) &&
2286 (token
->kind
!= ADA_KIND_ANONYMOUS
))
2288 if (parentScope
!= NULL
)
2290 /* first create our new scope which is the parent scope + '.' + the
2291 * current tag name. */
2292 size_t parentScope_len
= strlen (parentScope
);
2293 size_t name_len
= strlen (token
->name
);
2294 currentScope
= xMalloc (parentScope_len
+ name_len
+ 2, char);
2295 memcpy (currentScope
, parentScope
, parentScope_len
);
2296 currentScope
[parentScope_len
] = '.';
2297 memcpy (¤tScope
[parentScope_len
+ 1], token
->name
, name_len
);
2298 currentScope
[parentScope_len
+ 1 + name_len
] = '\0';
2300 token
->tag
.name
= currentScope
;
2301 markTagExtraBit (&token
->tag
, XTAG_QUALIFIED_TAGS
);
2302 makeTagEntry (&token
->tag
);
2306 /* if the parent scope is null then the current token does not have
2307 * a parent tag to prepend onto the current scope. Therefore, just
2308 * setup the current scope as a copy of the current tag name, but make
2309 * no extra entry. */
2310 currentScope
= token
->name
;
2315 /* now make the child tags */
2316 tmp
= token
->children
.head
;
2319 storeAdaTags (tmp
, currentScope
);
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
2326 if (token
->kind
== ADA_KIND_ANONYMOUS
&&
2327 strncasecmp (token
->name
, AdaKeywords
[ADA_KEYWORD_DECLARE
],
2328 strlen (AdaKeywords
[ADA_KEYWORD_DECLARE
])) == 0)
2331 token
->tag
.name
= NULL
;
2334 if ((currentScope
!= NULL
) && (currentScope
!= token
->name
))
2336 eFree ((void *) currentScope
);
2340 /* main parse function */
2341 static void findAdaTags (void)
2346 /* init all global data now */
2347 eof_reached
= false;
2352 /* cannot just set matchFilePos to 0 because the fpos_t is not a simple
2353 * integer on all systems. */
2354 matchFilePos
= getInputFilePosition ();
2356 /* init the root tag */
2357 root
.kind
= ADA_KIND_UNDEFINED
;
2358 root
.isSpec
= false;
2361 root
.isPrivate
= false;
2362 initAdaTokenList (&root
.children
);
2364 /* read in the first line */
2369 /* tokenize entire file */
2370 while (!eof_reached
&& adaParse (ADA_ROOT
, &root
) != NULL
);
2373 tmp
= root
.children
.head
;
2376 storeAdaTags (tmp
, NULL
);
2381 /* clean up tokens */
2382 freeAdaTokenList (&root
.children
);
2385 /* parser definition function */
2386 extern parserDefinition
* AdaParser (void)
2388 static const char *const extensions
[] = { "adb", "ads", "Ada", "ada", NULL
};
2389 parserDefinition
* def
= parserNew ("Ada");
2390 def
->kindTable
= AdaKinds
;
2391 def
->kindCount
= ADA_KIND_COUNT
;
2392 def
->extensions
= extensions
;
2393 def
->parser
= findAdaTags
;