[geany/geany] 2ed895: Add ADA ctags parser

Jiří Techet git-noreply at geany.org
Fri May 13 00:39:12 UTC 2022


Branch:      refs/heads/master
Author:      Jiří Techet <techet at gmail.com>
Committer:   Jiří Techet <techet at gmail.com>
Date:        Fri, 13 May 2022 00:39:12 UTC
Commit:      2ed8955886b142a44e6bb69dc30f75d4bd02063b
             https://github.com/geany/geany/commit/2ed8955886b142a44e6bb69dc30f75d4bd02063b

Log Message:
-----------
Add ADA ctags parser


Modified Paths:
--------------
    ctags/Makefile.am
    ctags/parsers/ada.c
    meson.build
    src/filetypes.c
    src/tagmanager/tm_parser.c
    src/tagmanager/tm_parser.h
    src/tagmanager/tm_parsers.h
    tests/ctags/Makefile.am
    tests/ctags/ada-adb.adb
    tests/ctags/ada-adb.adb.tags
    tests/ctags/ada-ads.ads
    tests/ctags/ada-ads.ads.tags
    tests/ctags/ada-expression-function-generic.adb
    tests/ctags/ada-expression-function-generic.adb.tags
    tests/ctags/ada-expression-function.adb
    tests/ctags/ada-expression-function.adb.tags
    tests/ctags/ada-overriding.ads
    tests/ctags/ada-overriding.ads.tags
    tests/ctags/ada-package-generic.ads
    tests/ctags/ada-package-generic.ads.tags
    tests/ctags/ada-task.adb
    tests/ctags/ada-task.adb.tags
    tests/meson.build

Modified: ctags/Makefile.am
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -45,6 +45,7 @@ parsers = \
 	parsers/cpreprocessor.h \
 	parsers/abaqus.c \
 	parsers/abc.c \
+	parsers/ada.c \
 	parsers/asciidoc.c \
 	parsers/asm.c \
 	parsers/basic.c \


Modified: ctags/parsers/ada.c
2395 lines changed, 2395 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,2395 @@
+/* File:          Ada.c
+ * Description:   Enables extended Ada parsing support in Exuberant Ctags
+ * Version:       0.6
+ * Date:          October 26, 2006
+ * Author:        A. Aaron Cornelius (ADotAaronDotCorneliusAtgmailDotcom)
+ * License:       GPL-2
+ *
+ * Installation:
+ * You must have the Exuberant Ctags source to install this parser.  Once you
+ * have the source, place this file into the directory with the rest of the
+ * ctags source.  After ada.c is in the correct directory you need to make the
+ * following changes so that the ada parser is included when you compile and
+ * install ctags:
+ *
+ * to file source.mak add the line
+ *   ada.c \
+ *
+ * after
+ * SOURCES = \
+ *
+ * then add the line
+ *   ada.$(OBJECT) \
+ *
+ * after
+ * OBJECTS = \
+ *
+ * to file parsers.h add the line
+ *     AdaParser, \
+ *
+ * after
+ * #define PARSER_LIST \
+ *
+ * Then compile and install ctags as normal (usually: './configure', './make',
+ * './make install').
+ *
+ * Changelog:
+ *
+ * 11/02/2006 - Completed implementation of file scope info and qualified tags
+ *              information gathering.
+ * 11/02/2006 - Added recognition of private flag in a token for file scope
+ *              checking purposes.
+ * 10/27/2006 - Added full package scope name when --extra=+q is set.
+ * 10/27/2006 - Fixed file scope setting, and added check to verify that tags
+ *              with file scope should be included in the tag file.
+ * 10/26/2006 - Fixed error which caused infinite loop when parsing some
+ *              files.
+ * 0.5 - Bugfixes
+ * 10/20/2006 - Cleaned up freeAdaTokenList.
+ * 10/20/2006 - Fixed error in freeAdaToken that caused the child token lists
+ *              to become corrupted when "separate" tokens were deleted.
+ * 0.4 - Third Revision - 09/25/2006
+ * 09/25/2006 - Fixed error in newAdaToken which could cause an error on some
+ *              systems when a separate token (which is temporary) gets
+ *              created.
+ * 09/25/2006 - Change matchFilePos initialization in the findAdaTags
+ *              function.
+ * 0.3 - Second Revision
+ * 06/02/2006 - Added missing EOF checks to prevent infinite loops in the case
+ *              of an incomplete Ada (or non-Ada) file being parsed.
+ * 06/02/2006 - Added Copyright notice.
+ * 0.2 - First Revision
+ * 05/26/2006 - Fixed an error where tagging the proper scope of something
+ *              declared in an anonymous block or anonymous loop was not
+ *              working properly.
+ * 05/26/2006 - Fixed an error capturing the name of a 'separate' tag.
+ * 05/26/2006 - Fixed the cmp() function so that it finds matches correctly.
+ * 05/26/2006 - Fixed some spelling errors.
+ * 05/26/2006 - Added explicit skipping of use and with clauses.
+ * 0.1 - Initial Release
+ *
+ * Future Changes:
+ * TODO: Add inheritance information?
+ * TODO: Add signature gathering?
+ *
+ * Copyright (C) 2006 A. Aaron Cornelius
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ */
+
+#include "general.h"    /* always include first */
+
+#include <string.h>     /* to declare strxxx() functions */
+#include <ctype.h>      /* to define isxxx() macros */
+
+#include "parse.h"      /* always include */
+#include "read.h"       /* to define file readLineFromInputFile() */
+#include "entry.h"      /* for the tag entry manipulation */
+#include "routines.h"   /* for generic malloc/realloc/free routines */
+#include "debug.h"      /* for Assert */
+#include "xtag.h"
+
+
+static bool eof_reached;
+
+typedef enum eAdaParseMode
+{
+	ADA_ROOT,
+	ADA_DECLARATIONS,
+	ADA_CODE,
+	ADA_EXCEPTIONS,
+	ADA_GENERIC
+} adaParseMode;
+
+typedef enum eAdaKinds
+{
+	ADA_KIND_UNDEFINED = KIND_GHOST_INDEX,  /* for default/initialization values */
+	ADA_KIND_PACKAGE_SPEC,
+	ADA_KIND_PACKAGE,
+	ADA_KIND_TYPE_SPEC,
+	ADA_KIND_TYPE,
+	ADA_KIND_SUBTYPE_SPEC,
+	ADA_KIND_SUBTYPE,
+	ADA_KIND_RECORD_COMPONENT,
+	ADA_KIND_ENUM_LITERAL,
+	ADA_KIND_VARIABLE_SPEC,
+	ADA_KIND_VARIABLE,
+	ADA_KIND_FORMAL,
+	ADA_KIND_CONSTANT,
+	ADA_KIND_EXCEPTION,
+	ADA_KIND_SUBPROGRAM_SPEC,
+	ADA_KIND_SUBPROGRAM,
+	ADA_KIND_TASK_SPEC,
+	ADA_KIND_TASK,
+	ADA_KIND_PROTECTED_SPEC,
+	ADA_KIND_PROTECTED,
+	ADA_KIND_ENTRY_SPEC,
+	ADA_KIND_ENTRY,
+	ADA_KIND_LABEL,
+	ADA_KIND_IDENTIFIER,
+	ADA_KIND_AUTOMATIC_VARIABLE,
+	ADA_KIND_ANONYMOUS,      /* for non-identified loops and blocks */
+	ADA_KIND_COUNT            /* must be last */
+} adaKind;
+
+typedef enum {
+	ADA_PACKAGE_SUBUNIT,
+} adaPackageRole;
+
+static roleDefinition AdaPackageRoles [] = {
+	{ true, "subunit",
+	  "package name referenced in separate()" },
+};
+
+static kindDefinition AdaKinds[] =
+{
+	{ true,   'P', "packspec",    "package specifications" },
+	{ true,   'p', "package",     "packages",
+	  .referenceOnly = false, ATTACH_ROLES(AdaPackageRoles) },
+	{ false,  'T', "typespec",    "type specifications" },
+	{ true,   't', "type",        "types" },
+	{ false,  'U', "subspec",     "subtype specifications" },
+	{ true,   'u', "subtype",     "subtypes" },
+	{ true,   'c', "component",   "record type components" },
+	{ true,   'l', "literal",     "enum type literals" },
+	{ false,  'V', "varspec",     "variable specifications" },
+	{ true,   'v', "variable",    "variables" },
+	{ true,   'f', "formal",      "generic formal parameters" },
+	{ true,   'n', "constant",    "constants" },
+	{ true,   'x', "exception",   "user defined exceptions" },
+	{ true,   'R', "subprogspec", "subprogram specifications" },
+	{ true,   'r', "subprogram",  "subprograms" },
+	{ true,   'K', "taskspec",    "task specifications" },
+	{ true,   'k', "task",        "tasks" },
+	{ true,   'O', "protectspec", "protected data specifications" },
+	{ true,   'o', "protected",   "protected data" },
+	{ false,  'E', "entryspec",   "task/protected data entry specifications" },
+	{ true,   'e', "entry",       "task/protected data entries" },
+	{ true,   'b', "label",       "labels" },
+	{ true,   'i', "identifier",  "loop/declare identifiers"},
+	{ false,  'a', "autovar",     "automatic variables" },
+	{ false,  'y', "anon",        "loops and blocks with no identifier" },
+};
+
+typedef struct sAdaTokenList
+{
+	int numTokens;
+	struct sAdaTokenInfo *head;
+	struct sAdaTokenInfo *tail;
+} adaTokenList;
+
+typedef struct sAdaTokenInfo
+{
+	adaKind kind;
+	bool isSpec;
+	bool isPrivate;
+	char *name;
+	tagEntryInfo tag;
+	struct sAdaTokenInfo *parent;
+	struct sAdaTokenInfo *prev;
+	struct sAdaTokenInfo *next;
+	adaTokenList children;
+} adaTokenInfo;
+
+typedef enum eAdaKeywords
+{
+	ADA_KEYWORD_ACCEPT,
+	ADA_KEYWORD_BEGIN,
+	ADA_KEYWORD_BODY,
+	ADA_KEYWORD_CASE,
+	ADA_KEYWORD_CONSTANT,
+	ADA_KEYWORD_DECLARE,
+	ADA_KEYWORD_DO,
+	ADA_KEYWORD_ELSE,
+	ADA_KEYWORD_ELSIF,
+	ADA_KEYWORD_END,
+	ADA_KEYWORD_ENTRY,
+	ADA_KEYWORD_EXCEPTION,
+	ADA_KEYWORD_FOR,
+	ADA_KEYWORD_FUNCTION,
+	ADA_KEYWORD_GENERIC,
+	ADA_KEYWORD_IF,
+	ADA_KEYWORD_IN,
+	ADA_KEYWORD_IS,
+	ADA_KEYWORD_LOOP,
+	ADA_KEYWORD_NEW,
+	ADA_KEYWORD_NOT,
+	ADA_KEYWORD_OR,
+	ADA_KEYWORD_OVERRIDING,		/* Ada 2005 */
+	ADA_KEYWORD_PACKAGE,
+	ADA_KEYWORD_PRAGMA,
+	ADA_KEYWORD_PRIVATE,
+	ADA_KEYWORD_PROCEDURE,
+	ADA_KEYWORD_PROTECTED,
+	ADA_KEYWORD_RECORD,
+	ADA_KEYWORD_RENAMES,
+	ADA_KEYWORD_SELECT,
+	ADA_KEYWORD_SEPARATE,
+	ADA_KEYWORD_SUBTYPE,
+	ADA_KEYWORD_TASK,
+	ADA_KEYWORD_THEN,
+	ADA_KEYWORD_TYPE,
+	ADA_KEYWORD_UNTIL,
+	ADA_KEYWORD_USE,
+	ADA_KEYWORD_WHEN,
+	ADA_KEYWORD_WHILE,
+	ADA_KEYWORD_WITH
+} adaKeyword;
+
+static const char *AdaKeywords[] =
+{
+	"accept",
+	"begin",
+	"body",
+	"case",
+	"constant",
+	"declare",
+	"do",
+	"else",
+	"elsif",
+	"end",
+	"entry",
+	"exception",
+	"for",
+	"function",
+	"generic",
+	"if",
+	"in",
+	"is",
+	"loop",
+	"new",
+	"not",
+	"or",
+	"overriding",
+	"package",
+	"pragma",
+	"private",
+	"procedure",
+	"protected",
+	"record",
+	"renames",
+	"select",
+	"separate",
+	"subtype",
+	"task",
+	"then",
+	"type",
+	"until",
+	"use",
+	"when",
+	"while",
+	"with"
+};
+
+
+/* variables for managing the input string, position as well as input line
+ * number and position */
+static const char *line;
+static int lineLen;
+static int pos;
+static unsigned long matchLineNum;
+static MIOPos matchFilePos;
+
+/* utility functions */
+static void makeSpec (adaKind *kind);
+
+/* prototypes of functions for manipulating the Ada tokens */
+static adaTokenInfo *newAdaToken (const char *name, int len,
+								  adaKind kind, bool isSpec,
+								  adaTokenInfo *parent);
+static adaTokenInfo *newAdaTokenFull (const char *name, int len,
+									  adaKind kind, int role, bool isSpec,
+									  adaTokenInfo *parent);
+static void freeAdaToken (adaTokenList *list, adaTokenInfo *token);
+static void appendAdaToken (adaTokenInfo *parent, adaTokenInfo *token);
+
+/* token list processing function prototypes */
+static void initAdaTokenList (adaTokenList *list);
+static void freeAdaTokenList (adaTokenList *list);
+static void appendAdaTokenList (adaTokenInfo *parent, adaTokenList *children);
+
+/* prototypes of functions for moving through the DEFINED text */
+static void readNewLine (void);
+static void movePos (int amount);
+static bool cmp (const char *buf, int len, const char *match);
+static bool adaCmp (const char *match);
+static bool adaKeywordCmp (adaKeyword keyword);
+static void skipUntilWhiteSpace (void);
+static void skipWhiteSpace (void);
+static void skipComments (void);
+static void skipCommentsAndStringLiteral (void);
+static void skipPast (const char *past);
+static void skipPastKeyword (adaKeyword keyword);
+static void skipPastWord (void);
+
+typedef bool (* skipCompFn) (void *data);
+static void skipPastLambda (skipCompFn cmpfn, void *data);
+
+struct cmpKeywordOrWordDataElt
+{
+	enum eltType
+	{
+		ELT_KEYWORD,
+		ELT_WORD,
+	} type;
+	union
+	{
+		adaKeyword keyword;
+		const char* word;
+	} u;
+};
+static struct cmpKeywordOrWordDataElt *skipPastKeywordOrWord (struct cmpKeywordOrWordDataElt * elt,
+															  int count);
+
+/* prototypes of functions for parsing the high-level Ada constructs */
+static adaTokenInfo *adaParseBlock (adaTokenInfo *parent, adaKind kind);
+static adaTokenInfo *adaParseSubprogram (adaTokenInfo *parent, adaKind kind);
+static adaTokenInfo *adaParseType (adaTokenInfo *parent, adaKind kind);
+static adaTokenInfo *adaParseVariables (adaTokenInfo *parent, adaKind kind);
+static adaTokenInfo *adaParseLoopVar (adaTokenInfo *parent);
+static adaTokenInfo *adaParse (adaParseMode mode, adaTokenInfo *parent);
+
+/* prototypes of the functions used by ctags */
+static void storeAdaTags (adaTokenInfo *token, const char *parentScope);
+static void findAdaTags (void);
+
+static void makeSpec (adaKind *kind)
+{
+	switch (*kind)
+	{
+	case ADA_KIND_PACKAGE:
+		*kind = ADA_KIND_PACKAGE_SPEC;
+		break;
+
+	case ADA_KIND_TYPE:
+		*kind = ADA_KIND_TYPE_SPEC;
+		break;
+
+	case ADA_KIND_SUBTYPE:
+		*kind = ADA_KIND_SUBTYPE_SPEC;
+		break;
+
+	case ADA_KIND_VARIABLE:
+		*kind = ADA_KIND_VARIABLE_SPEC;
+		break;
+
+	case ADA_KIND_SUBPROGRAM:
+		*kind = ADA_KIND_SUBPROGRAM_SPEC;
+		break;
+
+	case ADA_KIND_TASK:
+		*kind = ADA_KIND_TASK_SPEC;
+		break;
+
+	case ADA_KIND_PROTECTED:
+		*kind = ADA_KIND_PROTECTED_SPEC;
+		break;
+
+	case ADA_KIND_ENTRY:
+		*kind = ADA_KIND_ENTRY_SPEC;
+		break;
+
+	default:
+		*kind = ADA_KIND_UNDEFINED;
+		break;
+	}
+}
+
+static adaTokenInfo *newAdaTokenFull (const char *name, int len, adaKind kind, int role,
+									  bool isSpec, adaTokenInfo *parent)
+{
+	char *tmpName = NULL;
+	adaTokenInfo *token = xMalloc (1, adaTokenInfo);
+
+	token->name = NULL;
+
+	if (name != NULL && len != 0)
+	{
+		tmpName = xMalloc (len + 1, char);
+		strncpy (tmpName, name, len);
+		tmpName[len] = '\0';
+	}
+
+	/* init the tag */
+	initTagEntry (&token->tag, tmpName, ADA_KIND_UNDEFINED);
+
+	token->kind = kind;
+	token->isSpec = isSpec;
+	token->isPrivate = false;
+
+	/* set the token data */
+	token->name = tmpName;
+	token->parent = parent;
+
+	/* Now set the file scope for this tag.  A tag has file scope if its direct
+	 * parent is a package/subprogram/protected/task spec, or if it it's parent
+	 * is UNDEFINED (a 'root' token), and if this is not in a 'private' section
+	 * of that spec. */
+	if ((parent != NULL) && (parent->isPrivate == false) &&
+		((parent->kind == ADA_KIND_UNDEFINED) ||
+		 (parent->kind == ADA_KIND_PACKAGE && isRoleAssigned(&parent->tag, ADA_PACKAGE_SUBUNIT)) ||
+		 ((parent->isSpec == true) && ((parent->kind == ADA_KIND_PACKAGE) ||
+									   (parent->kind == ADA_KIND_SUBPROGRAM) ||
+									   (parent->kind == ADA_KIND_PROTECTED) ||
+									   (parent->kind == ADA_KIND_TASK)))))
+	{
+		token->tag.isFileScope = false;
+	}
+	else
+	{
+		markTagExtraBit (&token->tag, XTAG_FILE_SCOPE);
+		token->tag.isFileScope = true;
+	}
+
+	/* add the kind info - unless this is a SEPARATE kind, in which case keep
+	 * them blank because they get filled in later. */
+	if (kind > ADA_KIND_UNDEFINED)
+	{
+		token->tag.kindIndex = kind;
+		if (role != ROLE_DEFINITION_INDEX)
+			assignRole(&token->tag, role);
+	}
+	else
+	{
+		token->tag.kindIndex = KIND_GHOST_INDEX;
+	}
+
+	/* setup the parent and children pointers */
+	initAdaTokenList (&token->children);
+	appendAdaToken (parent, token);
+
+	return token;
+}
+
+static adaTokenInfo *newAdaToken (const char *name, int len, adaKind kind,
+								  bool isSpec, adaTokenInfo *parent)
+{
+	return newAdaTokenFull (name, len, kind, ROLE_DEFINITION_INDEX, isSpec, parent);
+}
+
+static void freeAdaToken (adaTokenList *list, adaTokenInfo *token)
+{
+	if (token != NULL)
+	{
+		if (token->name != NULL)
+		{
+			eFree ((void *) token->name);
+			token->name = NULL;
+		}
+
+		/* before we delete this token, clean up it's children */
+		freeAdaTokenList (&token->children);
+
+		/* move the next token in the list to this token's spot */
+		if (token->prev != NULL)
+		{
+			token->prev->next = token->next;
+		}
+		else if (list != NULL)
+		{
+			/* Token to remove is head in list as 'token->prev == NULL' */
+			Assert (token->prev == NULL);
+			list->head = token->next;
+		}
+
+		/* move the previous token in the list to this token's spot */
+		if (token->next != NULL)
+		{
+			token->next->prev = token->prev;
+		}
+		else if (list != NULL)
+		{
+			/* Token to remove is tail of list as 'token->next == NULL') */
+			Assert (token->next == NULL);
+			list->tail = token->prev;
+		}
+
+		/* decrement the list count */
+		if (list != NULL)
+		{
+			list->numTokens--;
+		}
+
+		/* now that this node has had everything hanging off of it rearranged,
+		 * delete this node */
+		eFree (token);
+	}
+}
+
+static void appendAdaToken (adaTokenInfo *parent, adaTokenInfo *token)
+{
+	/* if the parent or newChild is NULL there is nothing to be done */
+	if (parent != NULL && token != NULL)
+	{
+		/* we just need to add this to the list and set a parent pointer */
+		parent->children.numTokens++;
+		token->parent = parent;
+		token->prev = parent->children.tail;
+		token->next = NULL;
+
+		if (parent->children.tail != NULL)
+		{
+			parent->children.tail->next = token;
+		}
+
+		/* the token that was just added always becomes the last token int the
+		 * list */
+		parent->children.tail = token;
+
+		if (parent->children.head == NULL)
+		{
+			parent->children.head = token;
+		}
+	}
+}
+
+static void initAdaTokenList (adaTokenList *list)
+{
+	if (list != NULL)
+	{
+		list->numTokens = 0;
+		list->head = NULL;
+		list->tail = NULL;
+	}
+}
+
+static void freeAdaTokenList (adaTokenList *list)
+{
+	if (list != NULL)
+	{
+		while (list->head != NULL)
+		{
+			freeAdaToken (list, list->head);
+		}
+	}
+}
+
+static void appendAdaTokenList (adaTokenInfo *parent, adaTokenList *children)
+{
+	adaTokenInfo *tmp = NULL;
+
+	if (parent != NULL && children != NULL)
+	{
+		while (children->head != NULL)
+		{
+			tmp = children->head->next;
+			appendAdaToken (parent, children->head);
+
+			/* we just need to worry about setting the head pointer properly during
+			 * the list iteration.  The node's pointers will get set properly by the
+			 * appendAdaToken () function */
+			children->head = tmp;
+		}
+
+		/* now that we have added all nodes from the children list to the parent
+		 * node, zero out the children list */
+		initAdaTokenList (children);
+	}
+}
+
+static void readNewLine (void)
+{
+	while (true)
+	{
+		line = (const char *) readLineFromInputFile ();
+		pos = 0;
+
+		if (line == NULL)
+		{
+			lineLen = 0;
+			eof_reached = true;
+			return;
+		}
+
+		lineLen = strlen (line);
+
+		if (lineLen > 0)
+		{
+			return;
+		}
+	}
+}
+
+static void movePos (int amount)
+{
+	pos += amount;
+	if (!eof_reached && pos >= lineLen)
+	{
+		readNewLine ();
+	}
+}
+
+/* a macro for checking for comments... This isn't the same as the check in
+ * cmp () because comments don't have to have whitespace or separation-type
+ * characters following the "--" */
+#define isAdaComment(buf, pos, len)										\
+	(((pos) == 0 || (!isalnum ((buf)[(pos) - 1]) && (buf)[(pos) - 1] != '_')) && \
+	 (pos) < (len) &&													\
+	 strncasecmp (&(buf)[(pos)], "--", strlen ("--")) == 0)
+#define isAdaStringLiteral(buf, pos, len)		\
+	(((pos) < (len)) && ((buf)[(pos)] == '"'))
+#define isAdaCharLiteral(buf, pos, len)				\
+	(((pos) < (len - 2)) && ((buf)[(pos)] == '\'')	\
+	 && ((buf)[(pos + 2)] == '\''))
+
+
+static bool cmp (const char *buf, int len, const char *match)
+{
+	bool status = false;
+	int matchLen;
+
+	/* if we are trying to match nothing, that is always true */
+	if (match == NULL)
+	{
+		return true;
+	}
+
+	/* first check to see if the buffer is empty, if it is, return false */
+	if (buf == NULL)
+	{
+		return status;
+	}
+
+	matchLen = strlen (match);
+
+	/* A match only happens the number of chars in the matching string match,
+	 * and whitespace follows... Which means we also must check to see if the
+	 * end of the line is after the matching string.  Also check for some
+	 * separation characters such as (, ), :, or ; */
+	if ((strncasecmp (buf, match, matchLen) == 0) &&
+		(matchLen == len ||
+		 (matchLen < len &&
+		  (isspace (buf[matchLen]) || buf[matchLen] == '(' ||
+		   buf[matchLen] == ')' || buf[matchLen] == ':' ||
+		   buf[matchLen] == ';'))))
+	{
+		status = true;
+	}
+
+	return status;
+}
+
+static bool adaCmp (const char *match)
+{
+	bool status = false;
+
+	/* first check to see if line is empty */
+	if (line == NULL)
+	{
+		eof_reached = true;
+		return status;
+	}
+
+	status = cmp (&line[pos], lineLen - pos, match);
+
+	/* if we match, increment the position pointer */
+	if (status == true && match != NULL)
+	{
+		matchLineNum = getInputLineNumber ();
+		matchFilePos = getInputFilePosition ();
+
+		movePos ((strlen (match)));
+	}
+
+	return status;
+}
+
+/* just a version of adaCmp that is a bit more optimized for keywords */
+static bool adaKeywordCmp (adaKeyword keyword)
+{
+	bool status = false;
+
+	/* first check to see if line is empty, if it is */
+	if (line == NULL)
+	{
+		eof_reached = true;
+		return status;
+	}
+
+	status = cmp (&line[pos], lineLen - pos, AdaKeywords[keyword]);
+
+	/* if we match, increment the position pointer */
+	if (status == true)
+	{
+		matchLineNum = getInputLineNumber ();
+		matchFilePos = getInputFilePosition ();
+
+		movePos ((strlen (AdaKeywords[keyword])));
+	}
+
+	return status;
+}
+
+static void skipUntilWhiteSpace (void)
+{
+	/* first check for a comment line, because this would cause the isspace
+	 * check to be true immediately */
+	skipComments ();
+
+	while (!eof_reached && !isspace (line[pos]))
+	{
+		/* don't use movePos () because if we read in a new line with this function
+		 * we need to stop */
+		pos++;
+
+		/* the newline counts as whitespace so read in the newline and return
+		 * immediately */
+		if (pos >= lineLen)
+		{
+			line = (const char *) readLineFromInputFile ();
+			pos = 0;
+
+			if (line == NULL)
+			{
+				lineLen = 0;
+				eof_reached = true;
+				return;
+			}
+
+			lineLen = strlen (line);
+
+			return;
+		}
+
+		/* now check for comments here */
+		skipComments ();
+	}
+}
+
+static void skipWhiteSpace (void)
+{
+	/* first check for a comment line, because this would cause the isspace
+	 * check to fail immediately */
+	skipComments ();
+
+	while (!eof_reached && isspace (line[pos]))
+	{
+		movePos (1);
+
+		/* now check for comments here */
+		skipComments ();
+	}
+}
+
+static void skipComments (void)
+{
+	while (!eof_reached && isAdaComment (line, pos, lineLen))
+	{
+		readNewLine ();
+	}
+}
+
+/* Return true if skipped over a string literal (or char literal).
+ * Return false if no string literal (nor char literal) is found. */
+static bool skipStringLiteral (void)
+{
+	if (!eof_reached && isAdaStringLiteral (line, pos, lineLen))
+	{
+		do {
+			movePos (1);
+		} while (!eof_reached && !isAdaStringLiteral (line, pos, lineLen));
+
+		/* Go to the next char of " */
+		movePos (1);
+
+		return true;
+	}
+	else if (!eof_reached && isAdaCharLiteral (line, pos, lineLen))
+	{
+		movePos (3);
+		return true;
+	}
+	return false;
+}
+
+static void skipCommentsAndStringLiteral (void)
+{
+	while (true)
+	{
+		skipComments ();
+		if (!skipStringLiteral ())
+			break;
+	}
+}
+
+static void skipPast (const char *past)
+{
+	/* first check for a comment line, because this would cause the isspace
+	 * check to fail immediately */
+	skipCommentsAndStringLiteral ();
+
+	/* now look for the keyword */
+	while (!eof_reached && !adaCmp (past))
+	{
+		movePos (1);
+
+		/* now check for comments here */
+		skipCommentsAndStringLiteral ();
+	}
+}
+
+static void skipPastKeyword (adaKeyword keyword)
+{
+	/* first check for a comment line, because this would cause the isspace
+	 * check to fail immediately */
+	skipComments ();
+
+	/* now look for the keyword */
+	while (!eof_reached && !adaKeywordCmp (keyword))
+	{
+		movePos (1);
+
+		/* now check for comments here */
+		skipComments ();
+	}
+}
+
+static void skipPastWord (void)
+{
+	/* first check for a comment line, because this would cause the isspace
+	 * check to fail immediately */
+	skipComments ();
+
+	/* now increment until we hit a non-word character... Specifically,
+	 * whitespace, '(', ')', ':', and ';' */
+	while (!eof_reached && !isspace (line[pos]) &&
+		   line[pos] != '(' && line[pos] != ')' && line[pos] != ':' &&
+		   line[pos] != ';')
+	{
+		/* don't use movePos () because if we read in a new line with this function
+		 * we need to stop */
+		pos++;
+
+		/* the newline counts as whitespace so read in the newline and return
+		 * immediately */
+		if (pos >= lineLen)
+		{
+			line = (const char *) readLineFromInputFile ();
+			pos = 0;
+
+			if (line == NULL)
+			{
+				lineLen = 0;
+				eof_reached = true;
+				return;
+			}
+
+			lineLen = strlen (line);
+
+			return;
+		}
+
+		/* now check for comments here */
+		skipComments ();
+	}
+}
+
+static void skipPastLambda (skipCompFn cmpfn, void *data)
+{
+	/* first check for a comment line, because this would cause the isspace
+	 * check to fail immediately */
+	skipCommentsAndStringLiteral ();
+
+	/* now call the predicate */
+	while (!eof_reached && !cmpfn (data))
+	{
+		movePos (1);
+
+		/* now check for comments here */
+		skipCommentsAndStringLiteral ();
+	}
+}
+
+struct cmpKeywordOrWordData
+{
+	struct cmpKeywordOrWordDataElt *found;
+	int count;
+	struct cmpKeywordOrWordDataElt *elt;
+};
+
+static bool cmpKeywordOrWord (void *data)
+{
+	struct cmpKeywordOrWordData *cmdData = data;
+
+	cmdData->found = NULL;
+	for (int i = 0; i < cmdData->count; i++)
+	{
+		if (cmdData->elt[i].type == ELT_KEYWORD)
+		{
+			if (adaKeywordCmp (cmdData->elt[i].u.keyword))
+			{
+				cmdData->found = cmdData->elt + i;
+				return true;
+			}
+		}
+		else if (cmdData->elt[i].type == ELT_WORD)
+		{
+			if (adaCmp (cmdData->elt[i].u.word))
+			{
+				cmdData->found = cmdData->elt + i;
+				return true;
+			}
+		}
+		else
+			AssertNotReached ();
+	}
+	return false;
+}
+
+static struct cmpKeywordOrWordDataElt *skipPastKeywordOrWord (struct cmpKeywordOrWordDataElt * elt,
+															  int count)
+{
+	struct cmpKeywordOrWordData data = {
+		.found = NULL,
+		.count = count,
+		.elt = elt
+	};
+	skipPastLambda (cmpKeywordOrWord, &data);
+	return data.found;
+}
+
+static adaTokenInfo *adaParseBlock (adaTokenInfo *parent, adaKind kind)
+{
+	int i;
+	adaTokenInfo *token;
+	bool isSpec = true;
+
+	skipWhiteSpace ();
+
+	/* if the next word is body, this is not a package spec */
+	if (adaKeywordCmp (ADA_KEYWORD_BODY))
+	{
+		isSpec = false;
+	}
+	/* if the next word is "type" then this has to be a task or protected spec */
+	else if (adaKeywordCmp (ADA_KEYWORD_TYPE) &&
+			 (kind != ADA_KIND_PROTECTED && kind != ADA_KIND_TASK))
+	{
+		/* if this failed to validate then we should just fail */
+		return NULL;
+	}
+	skipWhiteSpace ();
+
+	/* we are at the start of what should be the tag now... But we have to get
+	 * it's length.  So loop until we hit whitespace, init the counter to 1
+	 * since we know that the current position is not whitespace */
+	for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
+			 line[pos + i] != '(' && line[pos + i] != ';'; i++);
+
+	/* we have reached the tag of the package, so create the tag */
+	token = newAdaToken (&line[pos], i, kind, isSpec, parent);
+
+	movePos (i);
+	skipWhiteSpace ();
+
+	/* task and protected types are allowed to have discriminants */
+	if (!eof_reached && line[pos] == '(')
+	{
+		while (!eof_reached && line[pos] != ')')
+		{
+			movePos (1);
+			adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
+		}
+		movePos (1);
+	}
+
+	/* we must parse until we hit the "is" string to reach the end of
+	 * this package declaration, or a "renames" keyword */
+	while (token != NULL)
+	{
+		skipWhiteSpace ();
+
+		if (adaKeywordCmp (ADA_KEYWORD_IS))
+		{
+			skipWhiteSpace ();
+
+			if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
+			{
+				/* if the next word is the keyword "separate", don't create the tag
+				 * since it will be defined elsewhere */
+				freeAdaToken (&parent->children, token);
+				token = NULL;
+
+				/* move past the ";" ending this declaration */
+				skipPast (";");
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_NEW))
+			{
+				struct cmpKeywordOrWordDataElt *elt;
+
+				elt = skipPastKeywordOrWord ((struct cmpKeywordOrWordDataElt []) {{
+							.type = ELT_KEYWORD,
+							.u.keyword = ADA_KEYWORD_WITH,
+						}, {
+							.type = ELT_WORD,
+							.u.word = ";",
+						}
+					}, 2);
+
+				if (elt && elt->type == ELT_KEYWORD)
+					adaParse (ADA_DECLARATIONS, token);
+			}
+			else
+			{
+				adaParse (ADA_DECLARATIONS, token);
+			}
+
+			break;
+		}
+		else if (adaKeywordCmp (ADA_KEYWORD_RENAMES))
+		{
+			skipPast (";");
+			break;
+		}
+		else if (adaCmp (";"))
+		{
+			token->isSpec = true;
+			break;
+		}
+		else
+		{
+			/* nothing found, move to the next word */
+			skipUntilWhiteSpace ();
+		}
+
+		if (eof_reached)
+		{
+			freeAdaToken (&parent->children, token);
+			token = NULL;
+		}
+	}
+
+	return token;
+}
+
+static adaTokenInfo *adaParseSubprogram (adaTokenInfo *parent, adaKind kind)
+{
+	int i;
+	adaTokenInfo *token;
+	adaTokenInfo *tmpToken = NULL;
+
+	skipWhiteSpace ();
+
+	/* we are at the start of what should be the tag now... But we have to get
+	 * it's length.  So loop until we hit whitespace or the beginning of the
+	 * parameter list.  Init the counter to 1 * since we know that the current
+	 * position is not whitespace */
+	for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
+			 line[pos + i] != '(' && line[pos + i] != ';'; i++);
+
+	/* we have reached the tag of the subprogram, so create the tag... Init the
+	 * isSpec flag to false and we will adjust it when we see if there is an
+	 * "is", "do" or a ";" following the tag */
+	token = newAdaToken (&line[pos], i, kind, false, parent);
+
+	/* move the line position */
+	movePos (i);
+	skipWhiteSpace ();
+
+	/* if we find a '(' grab any parameters */
+	if (!eof_reached && line[pos] == '(' && token != NULL)
+	{
+		while (!eof_reached && line[pos] != ')')
+		{
+			movePos (1);
+			tmpToken = adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
+		}
+		movePos (1);
+
+		/* check to see if anything was received... If this is an entry this may
+		 * have a 'discriminant' and not have any parameters in the first
+		 * parenthesis pair, so check again if this was the case*/
+		if (kind == ADA_KIND_ENTRY && tmpToken == NULL)
+		{
+			/* skip any existing whitespace and see if there is a second parenthesis
+			 * pair */
+			skipWhiteSpace ();
+
+			if (!eof_reached && line[pos] == '(')
+			{
+				while (!eof_reached && line[pos] != ')')
+				{
+					movePos (1);
+					adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
+				}
+				movePos (1);
+			}
+		}
+	}
+
+	/* loop infinitely until we hit a "is", "do" or ";", this will skip over
+	 * the returns keyword, returned-type for functions as well as any one of a
+	 * myriad of keyword qualifiers */
+	while (!eof_reached && token != NULL)
+	{
+		skipWhiteSpace ();
+
+		if (adaKeywordCmp (ADA_KEYWORD_IS))
+		{
+			skipWhiteSpace ();
+
+			if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
+			{
+				/* if the next word is the keyword "separate", don't create the tag
+				 * since it will be defined elsewhere */
+				freeAdaToken (&parent->children, token);
+				token = NULL;
+
+				/* move past the ";" ending this declaration */
+				skipPast (";");
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_NEW))
+			{
+				/* if this is a "new" something then no need to parse */
+				skipPast (";");
+			}
+			else if (line[pos] == '(')
+			{
+				/* '(' is the starter of an expression function. */
+				skipPast (";");
+			}
+			else
+			{
+				adaParse (ADA_DECLARATIONS, token);
+			}
+
+			break;
+		}
+		else if (adaKeywordCmp (ADA_KEYWORD_RENAMES))
+		{
+			skipPast (";");
+			break;
+		}
+		else if (adaKeywordCmp (ADA_KEYWORD_DO))
+		{
+			/* do is the keyword for the beginning of a task entry */
+			adaParse (ADA_CODE, token);
+			break;
+		}
+		else if (adaCmp (";"))
+		{
+			/* this is just a spec then, so set the flag in the token */
+			token->isSpec = true;
+			break;
+		}
+		else
+		{
+			/* nothing found, move to the next word */
+			movePos (1); /* make sure to advance even if we aren't actually on a word */
+			skipPastWord ();
+		}
+	}
+
+	return token;
+}
+
+static adaTokenInfo *adaParseType (adaTokenInfo *parent, adaKind kind)
+{
+	int i;
+	adaTokenInfo *token = NULL;
+
+	skipWhiteSpace ();
+
+	/* get the name of the type */
+	for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
+			 line[pos + i] != '(' && line[pos + i] != ';'; i++);
+
+	token = newAdaToken (&line[pos], i, kind, false, parent);
+
+	movePos (i);
+	skipWhiteSpace ();
+
+	if (!eof_reached && line[pos] == '(')
+	{
+		/* in this case there is a discriminant to this type, gather the
+		 * variables */
+		while (!eof_reached && line[pos] != ')')
+		{
+			movePos (1);
+			adaParseVariables (token, ADA_KIND_AUTOMATIC_VARIABLE);
+		}
+		movePos (1);
+		skipWhiteSpace ();
+	}
+
+	/* check to see what is next, if it is not "is" then just skip to the end of
+	 * the statement and register this as a 'spec' */
+	if (adaKeywordCmp (ADA_KEYWORD_IS))
+	{
+		skipWhiteSpace ();
+		/* check to see if this may be a record or an enumeration */
+		if (!eof_reached && line[pos] == '(')
+		{
+			movePos (1);
+			adaParseVariables (token, ADA_KIND_ENUM_LITERAL);
+		}
+		else
+		{
+			/* Parsing following form here.
+			 *
+			 * A. type foo is record ...;
+			 * B. type foo is new bar with record ...;
+			 * C. type foo is new bar;
+			 */
+			if (adaKeywordCmp (ADA_KEYWORD_NEW))
+			{
+				/* B and C */
+				struct cmpKeywordOrWordDataElt *elt;
+				elt = skipPastKeywordOrWord ((struct cmpKeywordOrWordDataElt []) {{
+							.type = ELT_KEYWORD,
+							.u.keyword = ADA_KEYWORD_WITH,
+						}, {
+							.type = ELT_WORD,
+							.u.word = ";",
+						}
+					}, 2);
+				if (elt && elt->type == ELT_WORD)
+				{
+					/* C */
+					return token;
+				}
+
+				/* B */
+				skipWhiteSpace ();
+			}
+			if (adaKeywordCmp (ADA_KEYWORD_RECORD))
+			{
+				/* A and B */
+				/* until we hit "end record" we need to gather type variables */
+				while (!eof_reached)
+				{
+					skipWhiteSpace ();
+
+					if (adaKeywordCmp (ADA_KEYWORD_END))
+					{
+						skipWhiteSpace ();
+						if (adaKeywordCmp (ADA_KEYWORD_RECORD))
+						{
+							break;
+						}
+						skipPast (";");
+					}
+					/* handle variant types */
+					else if (adaKeywordCmp (ADA_KEYWORD_CASE))
+					{
+						skipPastKeyword (ADA_KEYWORD_IS);
+					}
+					else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
+					{
+						skipPast ("=>");
+					}
+					else
+					{
+						adaParseVariables (token, ADA_KIND_RECORD_COMPONENT);
+						skipPast (";");
+					}
+				}
+			}
+		}
+	}
+	else
+	{
+		token->isSpec = true;
+	}
+
+	skipPast (";");
+
+	return token;
+}
+
+static adaTokenInfo *adaParseVariables (adaTokenInfo *parent, adaKind kind)
+{
+	/* variables for keeping track of tags */
+	int varEndPos = -1;
+	int tokenStart = -1;
+	adaTokenInfo *token = NULL;
+
+	/* buffer management variables */
+	int i = 0;
+	int bufPos = 0;
+	int bufLen = 0;
+	char *buf = NULL;
+
+	/* file and line position variables */
+	unsigned long int lineNum;
+	int filePosIndex = 0;
+	int filePosSize = 32;
+	MIOPos *filePos = xMalloc (filePosSize, MIOPos);
+
+	/* skip any preliminary whitespace or comments */
+	skipWhiteSpace ();
+	skipComments ();
+
+	/* before we start reading input save the current line number and file
+	 * position, so we can reconstruct the correct line & file position for any
+	 * tags we create */
+	lineNum = getInputLineNumber ();
+	filePos[filePosIndex] = getInputFilePosition ();
+
+	/* setup local buffer... Since we may have to read a few lines to verify
+	 * that this is a proper variable declaration, and still make a token for
+	 * each variable, add one to the allocated string to account for a '\0' */
+	bufLen = lineLen - pos;
+	buf = xMalloc (bufLen + 1, char);
+	memcpy ((void *) buf, (void *) &line[pos], bufLen);
+
+	/* don't increase bufLen to include the NULL char so that strlen (buf) and
+	 * bufLen match */
+	buf[bufLen] = '\0';
+
+	while (!eof_reached)
+	{
+		/* make sure that we don't count anything in a comment as being valid to
+		 * parse */
+		if (isAdaComment (buf, bufPos, bufLen))
+		{
+			/* move bufPos to the end of this 'line' so a new line of input is
+			 * read */
+			bufPos = bufLen - 1;
+
+			/* if tokenStart is not -2 then we may be trying to track the type
+			 * of this variable declaration, so set tokenStart to -1 so that the
+			 * tracking can start over */
+			if (tokenStart != -2)
+			{
+				tokenStart = -1;
+			}
+		}
+		/* we have to keep track of any () pairs that may be in the variable
+		 * declarations.  And then quit if we hit a ';' the real end ')', or also
+		 * a variable initialization... Once we hit := then we have hit the end of
+		 * the variable declaration */
+		else if (buf[bufPos] == '(')
+		{
+			i++;
+		}
+		else if (buf[bufPos] == ')')
+		{
+			if (i == 0)
+			{
+				break;
+			}
+			else
+			{
+				i--;
+			}
+		}
+		else if (buf[bufPos] == ';' ||
+				 ((bufPos + 1) < bufLen &&
+				  (strncasecmp (&buf[bufPos], ":=", strlen (":=")) == 0 ||
+				   strncasecmp (&buf[bufPos], "=>", strlen ("=>")) == 0)))
+		{
+			break;
+		}
+		/* if we found the : keep track of where we found it */
+		else if (buf[bufPos] == ':' &&
+				 (bufPos + 1 >= bufLen || buf[bufPos + 1] != '='))
+		{
+			varEndPos = bufPos;
+		}
+		/* if we have the position of the ':' find out what the next word is,
+		 * because if it "constant" or "exception" then we must tag this slightly
+		 * differently, But only check this for normal variables */
+		else if (kind == ADA_KIND_VARIABLE && varEndPos != -1 &&
+				 !isspace (buf[bufPos]) && tokenStart == -1)
+		{
+			tokenStart = bufPos;
+		}
+		else if (kind == ADA_KIND_VARIABLE && varEndPos != -1 && tokenStart >= 0 &&
+				 ((bufPos + 1) >= bufLen || isspace (buf[bufPos + 1]) ||
+				  buf[bufPos + 1] == ';'))
+		{
+			if (cmp (&buf[tokenStart], bufLen - tokenStart,
+					 AdaKeywords[ADA_KEYWORD_CONSTANT]) == true)
+			{
+				kind = ADA_KIND_CONSTANT;
+			}
+			else if (cmp (&buf[tokenStart], bufLen - tokenStart,
+						  AdaKeywords[ADA_KEYWORD_EXCEPTION]) == true)
+			{
+				kind = ADA_KIND_EXCEPTION;
+			}
+
+			/* set tokenStart to -2 to prevent any more words from being checked */
+			tokenStart = -2;
+		}
+
+		bufPos++;
+
+		/* if we just incremented beyond the length of the current buffer, we need
+		 * to read in a new line */
+		if (!eof_reached && bufPos >= bufLen)
+		{
+			readNewLine ();
+
+			/* store the new file position for the start of this line */
+			filePosIndex++;
+			while (filePosIndex >= filePosSize)
+			{
+				filePosSize *= 2;
+				filePos = xRealloc (filePos, filePosSize, MIOPos);
+			}
+			filePos[filePosIndex] = getInputFilePosition ();
+
+			/* increment bufLen and bufPos now so that they jump past the NULL
+			 * character in the buffer */
+			bufLen++;
+			bufPos++;
+
+			/* allocate space and store this into our buffer */
+			bufLen += lineLen;
+			buf = xRealloc (buf, bufLen + 1, char);
+			memcpy ((void *) &buf[bufPos], (void *) line, lineLen);
+			buf[bufLen] = '\0';
+		}
+	}
+
+	/* There is a special case if we are gathering enumeration values and we hit
+	 * a ')', that is allowed so we need to move varEndPos to where the ')' is */
+	if (kind == ADA_KIND_ENUM_LITERAL && buf[bufPos] == ')' && varEndPos == -1)
+	{
+		varEndPos = bufPos;
+	}
+
+	/* so we found a : or ;... If it is a : go back through the buffer and
+	 * create a token for each word skipping over all whitespace and commas
+	 * until the : is hit*/
+	if (varEndPos != -1)
+	{
+		/* there should be no whitespace at the beginning, so tokenStart is
+		 * initialized to 0 */
+		tokenStart = 0;
+
+		/* before we start set the filePosIndex back to 0 so we can go through the
+		 * file position table as the read line number increases */
+		filePosIndex = 0;
+
+		for (i = 0; i < varEndPos; i++)
+		{
+			/* skip comments which are '--' unless we are in a word */
+			if (isAdaComment (buf, i, varEndPos))
+			{
+				/* move i past the '\0' that we put at the end of each line stored in
+				 * buf */
+				for ( ; i < varEndPos && buf[i] != '\0'; i++);
+			}
+			else if (tokenStart != -1 && (isspace (buf[i]) || buf[i] == ',' ||
+										  buf[i] == '\0'))
+			{
+				/* only store the word if it is not an in/out keyword */
+				if (!cmp (&buf[tokenStart], varEndPos, "in") &&
+					!cmp (&buf[tokenStart], varEndPos, "out"))
+				{
+					token = newAdaToken (&buf[tokenStart], i - tokenStart,
+										 kind, false, parent);
+
+					/* now set the proper line and file position counts for this
+					 * new token */
+					token->tag.lineNumber = lineNum + filePosIndex;
+					token->tag.filePosition = filePos[filePosIndex];
+				}
+				tokenStart = -1;
+			}
+			else if (tokenStart == -1 && !(isspace (buf[i]) || buf[i] == ',' ||
+										   buf[i] == '\0'))
+			{
+				/* only set the tokenStart for non-newline characters */
+				tokenStart = i;
+			}
+
+			/* after we are finished with this line, move the file position */
+			if (buf[i] == '\0')
+			{
+				filePosIndex++;
+			}
+		}
+
+		/* if token start was 'started' then we should store the last token */
+		if (tokenStart != -1)
+		{
+			token = newAdaToken (&buf[tokenStart], i - tokenStart,
+								 kind, false, parent);
+
+			/* now set the proper line and file position counts for this
+			 * new token */
+			token->tag.lineNumber = lineNum + filePosIndex;
+			token->tag.filePosition = filePos[filePosIndex];
+		}
+	}
+
+	/* now get the pos variable to point to the correct place in line where we
+	 * left off in our temp buf, and free our temporary buffer.  This is a
+	 * little different than most buf position moves.  It gets the distance from
+	 * the current buf position to the end of the buffer, which is also the
+	 * distance from where pos should be wrt the end of the variable
+	 * definition */
+	movePos ((lineLen - (bufLen - bufPos)) - pos);
+	eFree ((void *) buf);
+	eFree ((void *) filePos);
+
+	return token;
+}
+
+static adaTokenInfo *adaParseLoopVar (adaTokenInfo *parent)
+{
+	int i;
+	adaTokenInfo *token = NULL;
+
+	skipWhiteSpace ();
+	for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]); i++);
+	token = newAdaToken (&line[pos], i, ADA_KIND_AUTOMATIC_VARIABLE, false,
+						 parent);
+	movePos (i);
+
+	/* now skip to the end of the loop declaration */
+	skipPastKeyword (ADA_KEYWORD_LOOP);
+
+	return token;
+}
+
+static adaTokenInfo *adaParse (adaParseMode mode, adaTokenInfo *parent)
+{
+	int i;
+	adaTokenInfo genericParamsRoot;
+	adaTokenInfo *token = NULL;
+
+	initAdaTokenList (&genericParamsRoot.children);
+
+	/* if we hit the end of the file, line will be NULL and our skip and match
+	 * functions will hit this jump buffer with eof_reached */
+	while (!eof_reached)
+	{
+		/* find the next place to start */
+		skipWhiteSpace ();
+
+		/* check some universal things to check for first */
+		if (eof_reached)
+		{
+			break;
+		}
+		else if (isAdaComment (line, pos, lineLen))
+		{
+			readNewLine ();
+			continue;
+		}
+		else if (adaKeywordCmp (ADA_KEYWORD_PRAGMA) ||
+				 ((mode != ADA_GENERIC) && adaKeywordCmp (ADA_KEYWORD_WITH)) ||
+				 adaKeywordCmp (ADA_KEYWORD_USE))
+		{
+			/* set the token to NULL so we accidentally don't pick up something
+			 * from earlier
+			 * Do not skip lines having 'with' when 'mode == ADA_GENERIC'
+			 * this to intercept 'formal subprograms' of a generic declaration.
+			 * see: ARM 12.1(22) */
+			skipPast (";");
+			continue;
+		}
+
+		/* check for tags based on our current mode */
+		switch (mode)
+		{
+		case ADA_ROOT:
+			if (adaKeywordCmp (ADA_KEYWORD_PACKAGE))
+			{
+				token = adaParseBlock (parent, ADA_KIND_PACKAGE);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
+					 adaKeywordCmp (ADA_KEYWORD_FUNCTION))
+			{
+				token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_TASK))
+			{
+				token = adaParseBlock (parent, ADA_KIND_TASK);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
+			{
+				token = adaParseBlock (parent, ADA_KIND_PROTECTED);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_GENERIC))
+			{
+				/* if we have hit a generic declaration, go to the generic section
+				 * and collect the formal parameters */
+				mode = ADA_GENERIC;
+				break;
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_SEPARATE))
+			{
+				/* skip any possible whitespace */
+				skipWhiteSpace ();
+
+				/* skip over the "(" until we hit the tag */
+				if (!eof_reached && line[pos] == '(')
+				{
+					movePos (1);
+					skipWhiteSpace ();
+
+					/* get length of tag */
+					for (i = 1; (pos + i) < lineLen && line[pos + i] != ')' &&
+							 !isspace (line[pos + i]); i++);
+
+					/* the original comment before we introduced reference tags:
+					 * -----------------------------------------------------------------
+					 * if this is a separate declaration, all it really does is create
+					 * a false high level token for everything in this file to belong
+					 * to... But we don't know what kind it is, so we declare it as
+					 * ADA_KIND_SEPARATE, which will cause it not to be placed in
+					 * the tag file, and the item in this file will be printed as
+					 * separate:<name> instead of package:<name> or whatever the
+					 * parent kind really is (assuming the ctags option will be on
+					 * for printing such info to the tag file)
+					 * -----------------------------------------------------------------
+					 * Now we have reference tags. So we can use ADA_KIND_PACKAGE as kind.
+					 */
+					token = newAdaTokenFull (&line[pos], i, ADA_KIND_PACKAGE, ADA_PACKAGE_SUBUNIT,
+											 false, parent);
+
+					/* since this is a false top-level token, set parent to be
+					 * token */
+					parent = token;
+					token = NULL;
+
+					/* skip past the ')' */
+					skipPast (")");
+				}
+				else
+				{
+					/* move to the end of this statement */
+					skipPast (";");
+				}
+			}
+			else
+			{
+				/* otherwise, nothing was found so just skip until the end of this
+				 * unknown statement... It's most likely just a use or with
+				 * clause.  Also set token to NULL so we don't attempt anything
+				 * incorrect */
+				token = NULL;
+				skipPast (";");
+			}
+
+			/* check to see if we succeeded in creating our token */
+			if (token != NULL)
+			{
+				/* if any generic params have been gathered, attach them to
+				 * token */
+				appendAdaTokenList (token, &genericParamsRoot.children);
+			}
+
+			break;
+
+		case ADA_GENERIC:
+			/* if we are processing a generic block, make up some temp children
+			 * which we will later attach to the root of the real
+			 * procedure/package/whatever the formal parameters are for */
+			if (adaKeywordCmp (ADA_KEYWORD_PACKAGE))
+			{
+				token = adaParseBlock (parent, ADA_KIND_PACKAGE);
+
+				/* The above 'adaParseBlock' has read the end of a 'generic package declaration',
+				 * reset the mode back to the original mode.
+				 * see: ARM 12.1(24) */
+				Assert (parent);
+				mode = (parent->parent)? ADA_DECLARATIONS: ADA_ROOT;
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
+					 adaKeywordCmp (ADA_KEYWORD_FUNCTION))
+			{
+				token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
+
+				/* The above 'adaParseBlock' as read the end of a 'generic function/procedure declaration',
+				 * reset the mode back to the original mode.
+				 * see: ARM 12.1(21/22) */
+				Assert (parent);
+				mode = (parent->parent)? ADA_DECLARATIONS: ADA_ROOT;
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_TASK))
+			{
+				token = adaParseBlock (parent, ADA_KIND_TASK);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
+			{
+				token = adaParseBlock (parent, ADA_KIND_PROTECTED);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_TYPE))
+			{
+				skipWhiteSpace ();
+
+				/* get length of tag */
+				for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
+						 line[pos + i] != '(' && line[pos + i] != ';'; i++);
+
+				appendAdaToken (&genericParamsRoot,
+								newAdaToken (&line[pos], i, ADA_KIND_FORMAL, false,
+											 NULL));
+
+				/* skip to the end of this formal type declaration */
+				skipPast (";");
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_WITH))
+			{
+				skipWhiteSpace ();
+				/* skip over the function/procedure keyword, it doesn't matter for
+				 * now */
+				skipUntilWhiteSpace ();
+				skipWhiteSpace ();
+
+				/* get length of tag */
+				for (i = 1; (pos + i) < lineLen && !isspace (line[pos + i]) &&
+						 line[pos + i] != '(' && line[pos + i] != ';'; i++);
+
+				appendAdaToken (&genericParamsRoot,
+								newAdaToken (&line[pos], i, ADA_KIND_FORMAL, false,
+											 NULL));
+
+				/* increment the position */
+				movePos (i);
+
+				/* now gather the parameters to this subprogram */
+				if (!eof_reached && line[pos] == '(')
+				{
+					while (!eof_reached && line[pos] != ')')
+					{
+						movePos (1);
+						adaParseVariables (genericParamsRoot.children.tail,
+										   ADA_KIND_AUTOMATIC_VARIABLE);
+					}
+					movePos (1);
+				}
+
+				/* skip to the end of this formal type declaration */
+				skipPast (";");
+			}
+			else
+			{
+				/* otherwise, nothing was found so just skip until the end of this
+				 * unknown statement... It's most likely just a use or with
+				 * clause.  Also set token to NULL so we don't attempt anything
+				 * incorrect */
+				token = NULL;
+				skipPast (";");
+			}
+
+			/* check to see if we succeeded in creating our token */
+			if (token != NULL)
+			{
+				/* if any generic params have been gathered, attach them to
+				 * token. */
+				appendAdaTokenList (token, &genericParamsRoot.children);
+			}
+
+			break;
+
+		case ADA_DECLARATIONS:
+			if (adaKeywordCmp (ADA_KEYWORD_PACKAGE))
+			{
+				token = adaParseBlock (parent, ADA_KIND_PACKAGE);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_PROCEDURE) ||
+					 adaKeywordCmp (ADA_KEYWORD_FUNCTION))
+			{
+				token = adaParseSubprogram (parent, ADA_KIND_SUBPROGRAM);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_TASK))
+			{
+				token = adaParseBlock (parent, ADA_KIND_TASK);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_PROTECTED))
+			{
+				token = adaParseBlock (parent, ADA_KIND_PROTECTED);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_GENERIC))
+			{
+				/* if we have hit a generic declaration, go to the generic section
+				 * and collect the formal parameters */
+				mode = ADA_GENERIC;
+				break;
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_TYPE))
+			{
+				token = adaParseType (parent, ADA_KIND_TYPE);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_SUBTYPE))
+			{
+				token = adaParseType (parent, ADA_KIND_SUBTYPE);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
+			{
+				mode = ADA_CODE;
+				break;
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_FOR))
+			{
+				/* if we hit a "for" statement it is defining implementation details
+				 * for a specific type/variable/subprogram/etc...  So we should just
+				 * skip it, so skip the tag, then we need to see if there is a
+				 * 'record' keyword... If there is we must skip past the
+				 * 'end record;' statement.  First skip past the tag */
+				skipPastKeyword (ADA_KEYWORD_USE);
+				skipWhiteSpace ();
+
+				if (adaKeywordCmp (ADA_KEYWORD_RECORD))
+				{
+					/* now skip to the next "record" keyword, which should be the end
+					 * of this use statement */
+					skipPastKeyword (ADA_KEYWORD_RECORD);
+				}
+
+				/* lastly, skip past the end ";" */
+				skipPast (";");
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_END))
+			{
+				/* if we have hit an end then we must see if the next word matches
+				 * the parent token's name.  If it does we hit the end of whatever
+				 * sort of block construct we were processing and we must
+				 * return */
+				skipWhiteSpace ();
+				if (adaCmp (parent->name))
+				{
+					skipPast (";");
+
+					/* return the token */
+					freeAdaTokenList (&genericParamsRoot.children);
+					return token;
+				}
+				else
+				{
+					/* set the token to NULL so we accidentally don't pick up something
+					 * from earlier */
+					token = NULL;
+					skipPast (";");
+				}
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_ENTRY))
+			{
+				token = adaParseSubprogram (parent, ADA_KIND_ENTRY);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_PRIVATE))
+			{
+				/* if this is a private declaration then we need to set the global
+				 * file spec flag and then skip whitespace to get to the next bit of
+				 * code to parse. */
+				if (parent != NULL)
+				{
+					parent->isPrivate = true;
+				}
+
+				skipWhiteSpace ();
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_OVERRIDING)
+					 || adaKeywordCmp (ADA_KEYWORD_NOT))
+			{
+				/* Do nothing, just ignore these keywords. */
+				;
+			}
+			else
+			{
+				/* if nothing else matched this is probably a variable, constant
+				 * or exception declaration */
+				token = adaParseVariables (parent, ADA_KIND_VARIABLE);
+				skipPast (";");
+			}
+
+			/* check to see if we succeeded in creating our token */
+			if (token != NULL)
+			{
+				/* if this is one of the root-type tokens... Do some extra
+				 * processing */
+				if (token->kind == ADA_KIND_PACKAGE ||
+					token->kind == ADA_KIND_SUBPROGRAM ||
+					token->kind == ADA_KIND_TASK ||
+					token->kind == ADA_KIND_PROTECTED)
+				{
+					/* if any generic params have been gathered, attach them to
+					 * token */
+					appendAdaTokenList (token, &genericParamsRoot.children);
+				}
+			}
+			break;
+
+		case ADA_CODE:
+			if (adaKeywordCmp (ADA_KEYWORD_DECLARE))
+			{
+				/* if we are starting a declare block here, and not down at the
+				 * identifier definition then make an anonymous token to track the
+				 * data in this block */
+				token = newAdaToken (NULL, 0, ADA_KIND_ANONYMOUS, false, parent);
+
+				/* save the correct starting line */
+				token->tag.lineNumber = matchLineNum;
+				token->tag.filePosition = matchFilePos;
+
+				adaParse (ADA_DECLARATIONS, token);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
+			{
+				/* if we are starting a code block here, and not down at the
+				 * identifier definition then make an anonymous token to track the
+				 * data in this block, if this was part of a proper LABEL:
+				 * declare/begin/end block then the parent would already be a label
+				 * and this begin statement would have been found while in the
+				 * ADA_DECLARATIONS parsing section  */
+				token = newAdaToken (NULL, 0, ADA_KIND_ANONYMOUS, false, parent);
+
+				/* save the correct starting line */
+				token->tag.lineNumber = matchLineNum;
+				token->tag.filePosition = matchFilePos;
+
+				adaParse (ADA_CODE, token);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_EXCEPTION))
+			{
+				mode = ADA_EXCEPTIONS;
+				break;
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_END))
+			{
+				/* if we have hit an end then we must see if the next word matches
+				 * the parent token's name.  If it does we hit the end of whatever
+				 * sort of block construct we were processing and we must
+				 * return */
+				skipWhiteSpace ();
+				if (adaCmp (parent->name))
+				{
+					skipPast (";");
+
+					/* return the token */
+					freeAdaTokenList (&genericParamsRoot.children);
+					return token;
+				}
+				else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
+				{
+					/* a loop with an identifier has this syntax:
+					 * "end loop <ident>;" */
+					skipWhiteSpace ();
+
+					/* now check for the parent loop's name */
+					if (adaCmp (parent->name))
+					{
+						skipPast (";");
+
+						/* return the token */
+						freeAdaTokenList (&genericParamsRoot.children);
+						return token;
+					}
+				}
+				else
+				{
+					/* otherwise, nothing was found so just skip until the end of
+					 * this statement */
+					skipPast (";");
+				}
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_ACCEPT))
+			{
+				adaParseSubprogram (parent, ADA_KIND_ENTRY);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_FOR))
+			{
+				/* if this is a for loop, then we may need to pick up the
+				 * automatic loop iterator, But... The loop variable is only
+				 * available within the loop itself so make an anonymous label
+				 * parent for this loop var to be parsed in */
+				token = newAdaToken (AdaKeywords[ADA_KEYWORD_LOOP],
+									 strlen (AdaKeywords[ADA_KEYWORD_LOOP]),
+									 ADA_KIND_ANONYMOUS, false, parent);
+				adaParseLoopVar (token);
+				adaParse (ADA_CODE, token);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_WHILE))
+			{
+				token = newAdaToken (AdaKeywords[ADA_KEYWORD_LOOP],
+									 strlen (AdaKeywords[ADA_KEYWORD_LOOP]),
+									 ADA_KIND_ANONYMOUS, false, parent);
+
+				/* skip past the while loop declaration and parse the loop body */
+				skipPastKeyword (ADA_KEYWORD_LOOP);
+				skipWhiteSpace ();
+				adaParse (ADA_CODE, token);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
+			{
+				token = newAdaToken (AdaKeywords[ADA_KEYWORD_LOOP],
+									 strlen (AdaKeywords[ADA_KEYWORD_LOOP]),
+									 ADA_KIND_ANONYMOUS, false, parent);
+
+				/* save the correct starting line */
+				token->tag.lineNumber = matchLineNum;
+				token->tag.filePosition = matchFilePos;
+
+				/* parse the loop body */
+				skipWhiteSpace ();
+				adaParse (ADA_CODE, token);
+			}
+			else if (line != NULL &&
+					 strncasecmp (&line[pos], "<<", strlen ("<<")) == 0)
+			{
+				movePos (strlen ("<<"));
+
+				/* if the first chars are <<, find the ending >> and if we do that
+				 * then store the label tag, start i at strlen of "<<" plus 1
+				 * because we don't want to move the real pos until we know for
+				 * sure this is a label */
+				for (i = 1; (pos + i) < lineLen &&
+						 strncasecmp (&line[pos + i], ">>", strlen (">>")) != 0;
+					 i++);
+
+				/* if we didn't increment to the end of the line, a match was
+				 * found, if we didn't just fall through */
+				if ((pos + i) < lineLen)
+				{
+					newAdaToken (&line[pos], i, ADA_KIND_LABEL, false, parent);
+					skipPast (">>");
+				}
+			}
+			/* we need to check for a few special case keywords that might cause
+			 * the simple ; ending statement checks to fail, first the simple
+			 * one word keywords and then the start <stuff> end statements */
+			else if (adaKeywordCmp (ADA_KEYWORD_SELECT) ||
+					 adaKeywordCmp (ADA_KEYWORD_OR) ||
+					 adaKeywordCmp (ADA_KEYWORD_ELSE))
+			{
+				skipWhiteSpace ();
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_IF) ||
+					 adaKeywordCmp (ADA_KEYWORD_ELSIF))
+			{
+				skipPastKeyword (ADA_KEYWORD_THEN);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_CASE))
+			{
+				skipPastKeyword (ADA_KEYWORD_IS);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
+			{
+				skipPast ("=>");
+			}
+			else
+			{
+				int i_end;
+				/* set token to NULL so we don't accidentally not find an identifier,
+				 * But then fall through to the != NULL check */
+				token = NULL;
+
+				/* there is a possibility that this may be a loop or block
+				 * identifier, so check for a <random_word>[ ]?: statement */
+				for (i = 1; (pos + i) < lineLen; i++)
+					if(!(isalnum (line[pos + i]) || line[pos + i] == '_'))
+						break;
+				i_end = i;		/* Records the end of identifier. */
+
+				/* Skip whitespaces between the identifier and ':' */
+				for (; (pos + i) < lineLen; i++)
+					if (!isspace((unsigned char)(line[pos + i])))
+						break;
+
+				if ((pos + i) < lineLen
+					&& (line[pos + i] == ':')
+					&& (
+						((pos + i + 1) == lineLen)
+						|| (line[pos + i + 1] != '=')
+						))
+					token = newAdaToken (&line[pos], i_end, ADA_KIND_IDENTIFIER, false,
+										 parent);
+
+				/* if we created a token, we found an identifier.  Now check for a
+				 * declare or begin statement to see if we need to start parsing
+				 * the following code like a root-style token would */
+				if (token != NULL)
+				{
+					/* if something was found, reset the position variable and try to
+					 * find the next item */
+					movePos (i + 1);
+					skipWhiteSpace ();
+
+					if (adaKeywordCmp (ADA_KEYWORD_DECLARE))
+					{
+						adaParse (ADA_DECLARATIONS, token);
+					}
+					else if (adaKeywordCmp (ADA_KEYWORD_BEGIN))
+					{
+						adaParse (ADA_CODE, token);
+					}
+					else if (adaKeywordCmp (ADA_KEYWORD_FOR))
+					{
+						/* just grab the automatic loop variable, and then parse the
+						 * loop (it may have something to tag which will be a 'child'
+						 * of the loop) */
+						adaParseLoopVar (token);
+						adaParse (ADA_CODE, token);
+					}
+					else if (adaKeywordCmp (ADA_KEYWORD_WHILE))
+					{
+						/* skip to the loop keyword */
+						skipPastKeyword (ADA_KEYWORD_LOOP);
+						skipWhiteSpace ();
+
+						/* parse the loop (it may have something to tag which will be
+						 * a 'child' of the loop) */
+						adaParse (ADA_CODE, token);
+					}
+					else if (adaKeywordCmp (ADA_KEYWORD_LOOP))
+					{
+						skipWhiteSpace ();
+
+						/* parse the loop (it may have something to tag which will be
+						 * a 'child' of the loop) */
+						adaParse (ADA_CODE, token);
+					}
+					else
+					{
+						/* otherwise, nothing was found so this is not a valid
+						 * identifier, delete it */
+						freeAdaToken (&parent->children, token);
+						token = NULL;
+					}
+				}
+				else
+				{
+					/* since nothing was found, simply skip to the end of this
+					 * statement */
+					skipPast (";");
+				}
+			}
+			/* else... No keyword tag fields found, look for others such as
+			 * loop and declare identifiers labels or just skip over this
+			 * line */
+
+			break;
+
+		case ADA_EXCEPTIONS:
+			if (adaKeywordCmp (ADA_KEYWORD_PRAGMA))
+			{
+				skipPast (";");
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_WHEN))
+			{
+				skipWhiteSpace ();
+				token = adaParseVariables (parent, ADA_KIND_AUTOMATIC_VARIABLE);
+			}
+			else if (adaKeywordCmp (ADA_KEYWORD_END))
+			{
+				/* if we have hit an end then we must see if the next word matches
+				 * the parent token's name.  If it does we hit the end of whatever
+				 * sort of block construct we were processing and we must
+				 * return */
+				skipWhiteSpace ();
+				if (adaCmp (parent->name))
+				{
+					skipPast (";");
+
+					/* return the token */
+					freeAdaTokenList (&genericParamsRoot.children);
+					return token;
+				}
+				else
+				{
+					/* otherwise, nothing was found so just skip until the end of
+					 * this statement */
+					skipPast (";");
+				}
+			}
+			else
+			{
+				/* otherwise, nothing was found so just skip until the end of
+				 * this statement */
+				skipPast (";");
+			}
+
+			break;
+
+		default:
+			Assert (0);
+		}
+	}
+
+	freeAdaTokenList (&genericParamsRoot.children);
+	return token;
+}
+
+static void storeAdaTags (adaTokenInfo *token, const char *parentScope)
+{
+	char *currentScope = NULL;
+	adaTokenInfo *tmp = NULL;
+
+	Assert (token);
+
+	/* do a spec transition if necessary */
+	if (token->isSpec == true)
+	{
+		makeSpec (&token->kind);
+
+		if (token->kind != ADA_KIND_UNDEFINED)
+		{
+			token->tag.kindIndex = token->kind;
+		}
+	}
+
+	/* fill in the scope data */
+	if (token->parent != NULL)
+	{
+		if (token->parent->kind != ADA_KIND_UNDEFINED)
+		{
+			token->tag.extensionFields.scopeKindIndex = token->parent->kind;
+			token->tag.extensionFields.scopeName = token->parent->name;
+		}
+	}
+
+	/* one check before we try to make a tag... If this is an anonymous
+	 * declare block then it's name is empty.  Give it one */
+	if (token->kind == ADA_KIND_ANONYMOUS && token->name == NULL)
+	{
+		token->name = (char *) AdaKeywords[ADA_KEYWORD_DECLARE];
+		token->tag.name = AdaKeywords[ADA_KEYWORD_DECLARE];
+	}
+
+	/* Now 'make' tags that have their options set, But only make anonymous
+	 * tags if they have children tags.  Also, don't make this tag if the file
+	 * scope flag is not set and this tag is a file scope tag. */
+	if ((token->kind > ADA_KIND_UNDEFINED) && (token->kind < ADA_KIND_COUNT) &&
+		(AdaKinds[token->kind].enabled == true) &&
+		(token->name != NULL) &&
+		((token->kind == ADA_KIND_ANONYMOUS && token->children.head != NULL) ||
+		 token->kind != ADA_KIND_ANONYMOUS) &&
+		((isXtagEnabled (XTAG_FILE_SCOPE) == true) ||
+		 ((isXtagEnabled (XTAG_FILE_SCOPE) == false) &&
+		  (token->tag.isFileScope == false))))
+	{
+		makeTagEntry (&token->tag);
+
+		/* before making the tag, if the --extra=+q flag is set we should create
+		 * an extra entry which is the full parent.tag name.  But only do this if
+		 * the parentScope flag is not NULL, and this token is not of a limited
+		 * scope type such as a record component, enum literal, label, etc. */
+		if ((isXtagEnabled (XTAG_QUALIFIED_TAGS) == true) &&
+			(token->kind != ADA_KIND_RECORD_COMPONENT) &&
+			(token->kind != ADA_KIND_ENUM_LITERAL) &&
+			(token->kind != ADA_KIND_FORMAL) &&
+			(token->kind != ADA_KIND_LABEL) &&
+			(token->kind != ADA_KIND_IDENTIFIER) &&
+			(token->kind != ADA_KIND_AUTOMATIC_VARIABLE) &&
+			(token->kind != ADA_KIND_ANONYMOUS))
+		{
+			if (parentScope != NULL)
+			{
+				/* first create our new scope which is the parent scope + '.' + the
+				 * current tag name. */
+				size_t parentScope_len = strlen (parentScope);
+				size_t name_len = strlen (token->name);
+				currentScope = xMalloc (parentScope_len + name_len + 2, char);
+				memcpy (currentScope, parentScope, parentScope_len);
+				currentScope[parentScope_len] = '.';
+				memcpy (&currentScope[parentScope_len + 1], token->name, name_len);
+				currentScope[parentScope_len + 1 + name_len] = '\0';
+
+				token->tag.name = currentScope;
+				markTagExtraBit (&token->tag, XTAG_QUALIFIED_TAGS);
+				makeTagEntry (&token->tag);
+			}
+			else
+			{
+				/* if the parent scope is null then the current token does not have
+				 * a parent tag to prepend onto the current scope.  Therefore, just
+				 * setup the current scope as a copy of the current tag name, but make
+				 * no extra entry. */
+				currentScope = token->name;
+			}
+		}
+	}
+
+	/* now make the child tags */
+	tmp = token->children.head;
+	while (tmp != NULL)
+	{
+		storeAdaTags (tmp, currentScope);
+		tmp = tmp->next;
+	}
+
+	/* we have to clear out the declare name here or else it may cause issues
+	 * when we try to process it's children, and when we try to free the token
+	 * data */
+	if (token->kind == ADA_KIND_ANONYMOUS &&
+		strncasecmp (token->name, AdaKeywords[ADA_KEYWORD_DECLARE],
+					 strlen (AdaKeywords[ADA_KEYWORD_DECLARE])) == 0)
+	{
+		token->name = NULL;
+		token->tag.name = NULL;
+	}
+
+	if ((currentScope != NULL) && (currentScope != token->name))
+	{
+		eFree ((void *) currentScope);
+	}
+}
+
+/* main parse function */
+static void findAdaTags (void)
+{
+	adaTokenInfo root;
+	adaTokenInfo *tmp;
+
+	/* init all global data now */
+	eof_reached = false;
+	line = NULL;
+	pos = 0;
+	matchLineNum = 0;
+
+	/* cannot just set matchFilePos to 0 because the fpos_t is not a simple
+	 * integer on all systems. */
+	matchFilePos = getInputFilePosition ();
+
+	/* init the root tag */
+	root.kind = ADA_KIND_UNDEFINED;
+	root.isSpec = false;
+	root.name = NULL;
+	root.parent = NULL;
+	root.isPrivate = false;
+	initAdaTokenList (&root.children);
+
+	/* read in the first line */
+	readNewLine ();
+	if (eof_reached)
+		goto out;
+
+	/* tokenize entire file */
+	while (!eof_reached && adaParse (ADA_ROOT, &root) != NULL);
+
+	/* store tags */
+	tmp = root.children.head;
+	while (tmp != NULL)
+	{
+		storeAdaTags (tmp, NULL);
+		tmp = tmp->next;
+	}
+
+ out:
+	/* clean up tokens */
+	freeAdaTokenList (&root.children);
+}
+
+/* parser definition function */
+extern parserDefinition* AdaParser (void)
+{
+	static const char *const extensions[] = { "adb", "ads", "Ada", "ada", NULL };
+	parserDefinition* def = parserNew ("Ada");
+	def->kindTable = AdaKinds;
+	def->kindCount = ADA_KIND_COUNT;
+	def->extensions = extensions;
+	def->parser = findAdaTags;
+	return def;
+}


Modified: meson.build
1 lines changed, 1 insertions(+), 0 deletions(-)
===================================================================
@@ -589,6 +589,7 @@ ctags = static_library('ctags',
 	'ctags/main/xtag_p.h',
 	'ctags/parsers/abaqus.c',
 	'ctags/parsers/abc.c',
+	'ctags/parsers/ada.c',
 	'ctags/parsers/asciidoc.c',
 	'ctags/parsers/asm.c',
 	'ctags/parsers/basic.c',


Modified: src/filetypes.c
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -176,7 +176,7 @@ static void init_builtin_filetypes(void)
 	FT_INIT( YAML,       NONE,         "YAML",             NULL,                      FILE,        MISC     );
 	FT_INIT( CMAKE,      NONE,         "CMake",            NULL,                      SOURCE_FILE, SCRIPT   );
 	FT_INIT( NSIS,       NSIS,         "NSIS",             NULL,                      SOURCE_FILE, SCRIPT   );
-	FT_INIT( ADA,        NONE,         "Ada",              NULL,                      SOURCE_FILE, COMPILED );
+	FT_INIT( ADA,        ADA,          "Ada",              NULL,                      SOURCE_FILE, COMPILED );
 	FT_INIT( FORTH,      NONE,         "Forth",            NULL,                      SOURCE_FILE, SCRIPT   );
 	FT_INIT( ASCIIDOC,   ASCIIDOC,     "Asciidoc",         NULL,                      SOURCE_FILE, MARKUP   );
 	FT_INIT( ABAQUS,     ABAQUS,       "Abaqus",           NULL,                      SOURCE_FILE, SCRIPT   );


Modified: src/tagmanager/tm_parser.c
40 lines changed, 39 insertions(+), 1 deletions(-)
===================================================================
@@ -1031,6 +1031,43 @@ static TMParserMapGroup group_TYPESCRIPT[] = {
 	{_("Other"), TM_ICON_MEMBER, tm_tag_member_t | tm_tag_enumerator_t},
 };
 
+static TMParserMapEntry map_ADA[] = {
+	{'P', tm_tag_package_t},     // packspec
+	{'p', tm_tag_package_t},     // package
+	{'T', tm_tag_typedef_t},     // typespec
+	{'t', tm_tag_typedef_t},     // type
+	{'U', tm_tag_undef_t},       // subspec
+	{'u', tm_tag_typedef_t},     // subtype
+	{'c', tm_tag_member_t},      // component
+	{'l', tm_tag_enumerator_t},  // literal
+	{'V', tm_tag_undef_t},       // varspec
+	{'v', tm_tag_variable_t},    // variable
+	{'f', tm_tag_undef_t},       // formal
+	{'n', tm_tag_macro_t},       // constant
+	{'x', tm_tag_undef_t},       // exception
+	{'R', tm_tag_prototype_t},   // subprogspec
+	{'r', tm_tag_function_t},    // subprogram
+	{'K', tm_tag_prototype_t},   // taskspec
+	{'k', tm_tag_method_t},      // task
+	{'O', tm_tag_undef_t},       // protectspec
+	{'o', tm_tag_undef_t},       // protected
+	{'E', tm_tag_undef_t},       // entryspec
+	{'e', tm_tag_undef_t},       // entry
+	{'b', tm_tag_undef_t},       // label
+	{'i', tm_tag_undef_t},       // identifier
+	{'a', tm_tag_undef_t},       // autovar
+	{'y', tm_tag_undef_t},       // anon
+};
+static TMParserMapGroup group_ADA[] = {
+	{_("Packages"), TM_ICON_NAMESPACE, tm_tag_package_t},
+	{_("Types"), TM_ICON_STRUCT, tm_tag_typedef_t},
+	{_("Functions"), TM_ICON_METHOD, tm_tag_function_t | tm_tag_prototype_t},
+	{_("Tasks"), TM_ICON_METHOD, tm_tag_method_t},
+	{_("Variables"), TM_ICON_VAR, tm_tag_variable_t},
+	{_("Constants"), TM_ICON_MACRO, tm_tag_macro_t},
+	{_("Other"), TM_ICON_MEMBER, tm_tag_member_t | tm_tag_enumerator_t},
+};
+
 typedef struct
 {
     TMParserMapEntry *entries;
@@ -1075,7 +1112,7 @@ static TMParserMap parser_map[] = {
 	MAP_ENTRY(HAXE),
 	MAP_ENTRY(REST),
 	MAP_ENTRY(HTML),
-	MAP_ENTRY(UNUSED1),
+	MAP_ENTRY(ADA),
 	MAP_ENTRY(CUDA),
 	MAP_ENTRY(MATLAB),
 	MAP_ENTRY(VALA),
@@ -1613,6 +1650,7 @@ gboolean tm_parser_has_full_scope(TMParserType lang)
 
 		/* These make use of the scope, but don't include nested hierarchy
 		 * (either as a parser limitation or a language semantic) */
+		case TM_PARSER_ADA:
 		case TM_PARSER_ASCIIDOC:
 		case TM_PARSER_CLOJURE:
 		case TM_PARSER_CONF:


Modified: src/tagmanager/tm_parser.h
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -89,7 +89,7 @@ enum
 	TM_PARSER_HAXE,
 	TM_PARSER_REST,
 	TM_PARSER_HTML,
-	TM_PARSER_UNUSED1,  /* dummy entry, replace with some new parser */
+	TM_PARSER_ADA,
 	TM_PARSER_CUDA,
 	TM_PARSER_MATLAB,
 	TM_PARSER_VALA,


Modified: src/tagmanager/tm_parsers.h
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -45,7 +45,7 @@
 	HaxeParser,\
 	RstParser, \
 	HtmlParser, \
-	LiterateHaskellParser,  /* dummy entry, replace with some new parser */  \
+	AdaParser, \
 	CUDAParser, \
 	MatLabParser, \
 	ValaParser, \


Modified: tests/ctags/Makefile.am
7 lines changed, 7 insertions(+), 0 deletions(-)
===================================================================
@@ -19,6 +19,13 @@ test_sources = \
 	actionscript/method-attributes.as	\
 	actionscript/packages.as		\
 	actionscript/sampler.as			\
+	ada-adb.adb						\
+	ada-ads.ads						\
+	ada-expression-function.adb		\
+	ada-expression-function-generic.adb	\
+	ada-overriding.ads				\
+	ada-package-generic.ads			\
+	ada-task.adb					\
 	angle_bracket.cpp				\
 	anonymous_functions.php			\
 	arraylist.js					\


Modified: tests/ctags/ada-adb.adb
64 lines changed, 64 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,64 @@
+--  Taken from altivec of GNAT examples (http://www.adacore.com/developers/code-samples/gnat-examples/)
+--  ====================================================================================================
+--  This example shows how to create and manipulate vectors by the mean of high
+--  level views.
+
+with GNAT.Altivec;                   use GNAT.Altivec;
+with GNAT.Altivec.Conversions;       use GNAT.Altivec.Conversions;
+with GNAT.Altivec.Vector_Operations; use GNAT.Altivec.Vector_Operations;
+with GNAT.Altivec.Vector_Types;      use GNAT.Altivec.Vector_Types;
+with GNAT.Altivec.Vector_Views;      use GNAT.Altivec.Vector_Views;
+
+with GNAT.IO;                        use GNAT.IO;
+
+procedure Altivec is
+
+   View_A   : constant VUI_View := (Values => (1, 2, 3, 4));
+   Vector_A : constant vector_unsigned_int := To_Vector (View_A);
+
+   View_B   : constant VUI_View := (Values => (1, 1, 1, 1));
+   Vector_B : constant vector_unsigned_int := To_Vector (View_B);
+
+   Vector_C : vector_unsigned_int;
+   View_C   : VUI_View;
+
+begin
+   Vector_C := vec_add (Vector_A, Vector_B);
+   --  C = A + B
+
+   View_C   := To_View (Vector_C);
+
+   for I in View_C.Values'Range loop
+      Put_Line (unsigned_int'Image (View_C.Values (I)));
+   end loop;
+
+end Altivec;
+
+procedure Greet is
+   type Days is (Monday, Tuesday, Wednesday,
+                 Thursday, Friday,
+                 Saturday, Sunday);
+
+   --  Declaration of a subtype
+   subtype Weekend_Days is
+     Days range Saturday .. Sunday;
+   --     ^ Constraint of the subtype
+
+   M : Days := Sunday;
+
+   S : Weekend_Days := M;
+   --  No error here, Days and Weekend_Days
+   --  are of the same type.
+begin
+   for I in Days loop
+      case I is
+         --  Just like a type, a subtype can
+         --  be used as a range
+         when Weekend_Days =>
+            Put_Line ("Week end!");
+         when others =>
+            Put_Line ("Hello on "
+                      & Days'Image (I));
+      end case;
+   end loop;
+end Greet;
\ No newline at end of file


Modified: tests/ctags/ada-adb.adb.tags
20 lines changed, 20 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,20 @@
+# format=tagmanager
+Altivec�16�0
+Days�4096�Greet�0
+Friday�4�Days�0
+Greet�16�0
+M�16384�Greet�0
+Monday�4�Days�0
+S�16384�Greet�0
+Saturday�4�Days�0
+Sunday�4�Days�0
+Thursday�4�Days�0
+Tuesday�4�Days�0
+Vector_A�65536�Altivec�0
+Vector_B�65536�Altivec�0
+Vector_C�16384�Altivec�0
+View_A�65536�Altivec�0
+View_B�65536�Altivec�0
+View_C�16384�Altivec�0
+Wednesday�4�Days�0
+Weekend_Days�4096�Greet�0


Modified: tests/ctags/ada-ads.ads
50 lines changed, 50 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,50 @@
+-- Object-oriented generalized stack.  This illustrates the use of a
+-- controlled type, which is one that has construction and destructions.
+-- It also shows how to create two related types in the same package so
+-- that they can share private declarations.  This sort of thing is
+-- accomplished in Java or C++ using nested classes, or using friend
+-- declarations in C++.
+--
+with Ada.Finalization; use Ada.Finalization;
+
+package GenStack is
+    -- This is the stack type.
+    type Stack is new Controlled with private;
+
+    -- This is the base type for nodes.  Client packages must derive their
+    -- nodes from StackData.  Since it comes from Controlled, the user can
+    -- override the Initialize, Adjust, and Finalize methods as needed.
+    type StackData is new Controlled with null record;
+
+    -- Initialization operations.
+    procedure Initialize(S: in out Stack);
+    procedure Adjust(S: in out Stack);
+    procedure Finalize(S: in out Stack);
+
+    -- Stack operations.
+    procedure Push(S: in out Stack; D: StackData'class);
+    procedure Pop(S: in out Stack; D: in out StackData'class);
+    procedure Top(S: Stack; Data: in out StackData'class);
+    function Empty(S: Stack) return Boolean;
+
+    private
+    -- Pointer to the node type.
+    type Node;
+    type Node_Ptr is access Node;
+
+    -- Here is the generalized stack itself.  We would just make it the
+    -- pointer itself, but it needs to be a record so it can be in a with.
+    type Stack is new Controlled with record
+        Head: Node_Ptr;
+    end record;
+
+    -- Now, we need a pointer to the data part.
+    type Data_Ptr is access StackData'Class;
+
+    -- This is the node type.
+    type Node is record
+        Data: Data_Ptr;
+        Next: Node_Ptr;
+    end record;
+
+end GenStack;


Modified: tests/ctags/ada-ads.ads.tags
17 lines changed, 17 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,17 @@
+# format=tagmanager
+Adjust�1024�GenStack�0
+Data�64�Node�0
+Data_Ptr�4096�GenStack�0
+Empty�1024�GenStack�0
+Finalize�1024�GenStack�0
+GenStack�512�0
+Head�64�Stack�0
+Initialize�1024�GenStack�0
+Next�64�Node�0
+Node�4096�GenStack�0
+Node_Ptr�4096�GenStack�0
+Pop�1024�GenStack�0
+Push�1024�GenStack�0
+Stack�4096�GenStack�0
+StackData�4096�GenStack�0
+Top�1024�GenStack�0


Modified: tests/ctags/ada-expression-function-generic.adb
18 lines changed, 18 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,18 @@
+procedure My_Package is
+
+  generic
+    type Unsigned_Type is range <>;
+  package Generic_Integer_Images is
+    function Digit_To_Character (X : Unsigned_Type) return Character;
+  end Generic_Integer_Images;
+
+  package body Generic_Integer_Images is
+    function Digit_To_Character (X : Unsigned_Type) return Character is
+      (Character'Val (0));
+  end Generic_Integer_Images;
+
+  type Signed_Address is range
+    -2**(Standard'Address_Size - 1) .. 2**(Standard'Address_Size - 1) - 1; 
+begin
+  null;
+end My_Package;


Modified: tests/ctags/ada-expression-function-generic.adb.tags
6 lines changed, 6 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,6 @@
+# format=tagmanager
+Digit_To_Character�16�Generic_Integer_Images�0
+Digit_To_Character�1024�Generic_Integer_Images�0
+Generic_Integer_Images�512�My_Package�0
+My_Package�16�0
+Signed_Address�4096�My_Package�0


Modified: tests/ctags/ada-expression-function.adb
38 lines changed, 38 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,38 @@
+with Ada.Text_IO;	
+
+procedure Test is
+
+  My_Type : Boolean := True;
+
+  procedure Tagged_Procedure is
+    function Boolean_As_String return String is
+      (case My_Type is
+         when True  => "True Value",
+         when False => "False Value");
+	 
+    function Another_Boolean_As_String return String is
+      (case My_Type is when True  => "; function dummy0 return String is (",
+         when False => "; function dummy1 return String is (");	      
+    procedure p0 is	
+    begin
+        Ada.Text_IO.put ("-0");
+    end p0;	 	 
+    function Yet_Boolean_As_String return String is
+      (case My_Type is
+         when True  => "1",
+         when False => "0");
+    procedure p1 is	
+    begin
+        Ada.Text_IO.put ("-1");
+    end p1;	 
+  begin
+    null;
+  end Tagged_Procedure;
+
+  procedure Not_Tagged_Procedure is
+  begin
+    null;
+  end Not_Tagged_Procedure;
+begin
+  null;
+end Test;


Modified: tests/ctags/ada-expression-function.adb.tags
10 lines changed, 10 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,10 @@
+# format=tagmanager
+Another_Boolean_As_String�16�Tagged_Procedure�0
+Boolean_As_String�16�Tagged_Procedure�0
+My_Type�16384�Test�0
+Not_Tagged_Procedure�16�Test�0
+Tagged_Procedure�16�Test�0
+Test�16�0
+Yet_Boolean_As_String�16�Tagged_Procedure�0
+p0�16�Tagged_Procedure�0
+p1�16�Tagged_Procedure�0


Modified: tests/ctags/ada-overriding.ads
42 lines changed, 42 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,42 @@
+--  Taken from #2382 submitted by @JulienPivard
+--  @summary
+--  Implémentation par une tâche.
+--  @description
+--  Implémentation par une tache de la classe synchronisé.
+--  @group Version tâche
+--
+--  package Carte_P.Tasche_P
+package Input
+   with
+      Pure           => False,
+      Preelaborate   => False,
+      Elaborate_Body => True,
+      Spark_Mode     => Off
+is
+
+   ---------------------------------------------------------------------------
+   task type Tasche_T is new Carte_T with
+   --  Implémentation par une tâche de l'interface Carte_T.
+
+      -----------------------------------
+      overriding
+      entry Coucou;
+      --  Implémentation par un accept.
+
+      -----------------------------------
+      --  overriding
+      --  entry Inutile;
+      --  Implémentation par un accept.
+      --  @param This
+      --  La carte.
+   end Tasche_T;
+   ---------------------------------------------------------------------------
+
+   overriding
+   procedure Inutile
+      (This : in out Tasche_T);
+   --  Implémentation par une procédure.
+   --  @param This
+   --  La carte.
+
+end Carte_P.Tasche_P;


Modified: tests/ctags/ada-overriding.ads.tags
4 lines changed, 4 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,4 @@
+# format=tagmanager
+Input�512�0
+Inutile�1024�Input�0
+Tasche_T�1024�Input�0


Modified: tests/ctags/ada-package-generic.ads
16 lines changed, 16 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,16 @@
+--  Taken from #2925 submitted by @koenmeersman
+package My_Package is
+
+  generic
+    type Num is digits <>;
+  package Conversions is
+    function From_Big_Real (Arg : Integer) return Num;
+  end Conversions;
+
+  type Missing_Tag is record
+    Num : Integer;
+  end record;
+
+  type Primary_Color is (Red, Green, Blue);
+
+end My_Package;


Modified: tests/ctags/ada-package-generic.ads.tags
10 lines changed, 10 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,10 @@
+# format=tagmanager
+Blue�4�Primary_Color�0
+Conversions�512�My_Package�0
+From_Big_Real�1024�Conversions�0
+Green�4�Primary_Color�0
+Missing_Tag�4096�My_Package�0
+My_Package�512�0
+Num�64�Missing_Tag�0
+Primary_Color�4096�My_Package�0
+Red�4�Primary_Color�0


Modified: tests/ctags/ada-task.adb
19 lines changed, 19 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,19 @@
+with Ada.Text_IO; use Ada.Text_IO;
+
+procedure Show_Simple_Tasks is
+   task T;
+   task T2;
+
+   task body T is
+   begin
+      Put_Line ("In task T");
+   end T;
+
+   task body T2 is
+   begin
+      Put_Line ("In task T2");
+   end T2;
+
+begin
+   Put_Line ("In main");
+end Show_Simple_Tasks;


Modified: tests/ctags/ada-task.adb.tags
6 lines changed, 6 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,6 @@
+# format=tagmanager
+Show_Simple_Tasks�16�0
+T�128�Show_Simple_Tasks�0
+T�1024�Show_Simple_Tasks�0
+T2�128�Show_Simple_Tasks�0
+T2�1024�Show_Simple_Tasks�0


Modified: tests/meson.build
7 lines changed, 7 insertions(+), 0 deletions(-)
===================================================================
@@ -18,6 +18,13 @@ ctags_tests = files([
 	'ctags/actionscript/method-attributes.as.tags',
 	'ctags/actionscript/packages.as.tags',
 	'ctags/actionscript/sampler.as.tags',
+	'ctags/ada-adb.adb.tags',
+	'ctags/ada-ads.ads.tags',
+	'ctags/ada-expression-function.adb.tags',
+	'ctags/ada-expression-function-generic.adb.tags',
+	'ctags/ada-overriding.ads.tags',
+	'ctags/ada-package-generic.ads.tags',
+	'ctags/ada-task.adb.tags',
 	'ctags/angle_bracket.cpp.tags',
 	'ctags/anonymous_functions.php.tags',
 	'ctags/arraylist.js.tags',



--------------
This E-Mail was brought to you by github_commit_mail.py (Source: https://github.com/geany/infrastructure).


More information about the Commits mailing list