2217 lines
68 KiB
C
2217 lines
68 KiB
C
/* File: Ada.c
|
|
* Description: Enables extended Ada parsing support in Exuberant Ctags
|
|
* Version: 0.6
|
|
* Date: October 26, 2006
|
|
* Author: A. Aaron Cornelius (ADotAaronDotCorneliusAtgmailDotcom)
|
|
*
|
|
* 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 <setjmp.h>
|
|
|
|
#include "parse.h" /* always include */
|
|
#include "read.h" /* to define file fileReadLine() */
|
|
#include "entry.h" /* for the tag entry manipulation */
|
|
#include "routines.h" /* for generic malloc/realloc/free routines */
|
|
#include "options.h" /* for the Option structure */
|
|
#include "debug.h" /* for Assert */
|
|
|
|
typedef enum eAdaException
|
|
{
|
|
EXCEPTION_NONE,
|
|
EXCEPTION_EOF
|
|
} adaException;
|
|
|
|
static adaException exception;
|
|
|
|
typedef enum eAdaParseMode
|
|
{
|
|
ADA_ROOT,
|
|
ADA_DECLARATIONS,
|
|
ADA_CODE,
|
|
ADA_EXCEPTIONS,
|
|
ADA_GENERIC
|
|
} adaParseMode;
|
|
|
|
typedef enum eAdaKinds
|
|
{
|
|
ADA_KIND_SEPARATE = -2, /* for defining the parent token name of a child
|
|
* sub-unit */
|
|
ADA_KIND_UNDEFINED = -1, /* 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;
|
|
|
|
static kindOption AdaKinds[] =
|
|
{
|
|
{ TRUE, 'P', "packspec", "package specifications" },
|
|
{ TRUE, 'p', "package", "packages" },
|
|
{ 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', "annon", "loops and blocks with no identifier" }
|
|
};
|
|
|
|
typedef struct sAdaTokenList
|
|
{
|
|
int numTokens;
|
|
struct sAdaTokenInfo *head;
|
|
struct sAdaTokenInfo *tail;
|
|
} adaTokenList;
|
|
|
|
typedef struct sAdaTokenInfo
|
|
{
|
|
adaKind kind;
|
|
boolean isSpec;
|
|
boolean 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_OR,
|
|
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",
|
|
"or",
|
|
"package",
|
|
"pragma",
|
|
"private",
|
|
"procedure",
|
|
"protected",
|
|
"record",
|
|
"renames",
|
|
"select",
|
|
"separate",
|
|
"subtype",
|
|
"task",
|
|
"then",
|
|
"type",
|
|
"until",
|
|
"use",
|
|
"when",
|
|
"while",
|
|
"with"
|
|
};
|
|
|
|
/* a jump buffer for fail-safe error prevention */
|
|
static jmp_buf eofError;
|
|
|
|
/* a simple var to keep track of how many times we hit EOF... If we hit it
|
|
* say, about 1000 times, we will print an error, jump to the end of the
|
|
* program, and store what tags we have */
|
|
static int eofCount;
|
|
|
|
/* 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 fpos_t 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, boolean 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 boolean cmp(char *buf, int len, char *match);
|
|
static boolean adaCmp(char *match);
|
|
static boolean adaKeywordCmp(adaKeyword keyword);
|
|
static void skipUntilWhiteSpace(void);
|
|
static void skipWhiteSpace(void);
|
|
static void skipPast(char *past);
|
|
static void skipPastKeyword(adaKeyword keyword);
|
|
static void skipPastWord(void);
|
|
|
|
/* 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);
|
|
extern parserDefinition* AdaParser(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:
|
|
printf("Warning, non-spec type trying to be 'spec'ified\n");
|
|
*kind = ADA_KIND_UNDEFINED;
|
|
break;
|
|
}
|
|
} /* static void makeSpec(adaKind *kind) */
|
|
|
|
static adaTokenInfo *newAdaToken(const char *name, int len, adaKind kind,
|
|
boolean 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((char *) tmpName, (char *) name, len);
|
|
tmpName[len] = '\0';
|
|
}
|
|
|
|
/* init the tag */
|
|
initTagEntry(&token->tag, tmpName);
|
|
|
|
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_SEPARATE) ||
|
|
((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
|
|
{
|
|
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.kindName = AdaKinds[kind].name;
|
|
token->tag.kind = AdaKinds[kind].letter;
|
|
}
|
|
else
|
|
{
|
|
token->tag.kindName = "";
|
|
token->tag.kind = '\0';
|
|
}
|
|
|
|
/* setup the parent and children pointers */
|
|
initAdaTokenList(&token->children);
|
|
appendAdaToken(parent, token);
|
|
|
|
return token;
|
|
} /* static adaTokenInfo *newAdaToken(const char *name, int len, ... ) */
|
|
|
|
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->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->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);
|
|
} /* if(token != NULL) */
|
|
} /* static void freeAdaToken(adaTokenList *list, adaTokenInfo *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;
|
|
}
|
|
} /* if(parent != NULL && token != NULL) */
|
|
} /* static void appendAdaToken(adaTokenInfo *parent, adaTokenInfo *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 appendAdaTokenList(adaTokenInfo *parent, ... ) */
|
|
|
|
static void readNewLine(void)
|
|
{
|
|
while(TRUE)
|
|
{
|
|
line = (const char *) fileReadLine();
|
|
pos = 0;
|
|
|
|
if(line == NULL)
|
|
{
|
|
lineLen = 0;
|
|
exception = EXCEPTION_EOF;
|
|
eofCount++;
|
|
|
|
if(eofCount >= 1000)
|
|
{
|
|
printf("ERROR: Reached EOF %d times in file %s\n", eofCount,
|
|
getInputFileName());
|
|
|
|
longjmp(eofError, exception);
|
|
}
|
|
else
|
|
{
|
|
return;
|
|
}
|
|
} /* if(line == NULL) */
|
|
|
|
lineLen = strlen((char *) line);
|
|
|
|
if(lineLen > 0)
|
|
{
|
|
return;
|
|
}
|
|
} /* while(TRUE) */
|
|
} /* static void readNewLine(void) */
|
|
|
|
static void movePos(int amount)
|
|
{
|
|
pos += amount;
|
|
if(exception != EXCEPTION_EOF && 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)
|
|
|
|
static boolean cmp(char *buf, int len, char *match)
|
|
{
|
|
boolean status = FALSE;
|
|
|
|
/* 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;
|
|
}
|
|
|
|
/* 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, strlen(match)) == 0) &&
|
|
(strlen(match) == len ||
|
|
(strlen(match) < len &&
|
|
(isspace(buf[strlen(match)]) || isAdaComment(buf, strlen(match), len) ||
|
|
buf[strlen(match)] == '(' || buf[strlen(match)] == ')' ||
|
|
buf[strlen(match)] == ':' || buf[strlen(match)] == ';'))))
|
|
{
|
|
status = TRUE;
|
|
}
|
|
|
|
return status;
|
|
} /* static boolean cmp(char *buf, int len, char *match) */
|
|
|
|
static boolean adaCmp(char *match)
|
|
{
|
|
boolean status = FALSE;
|
|
|
|
/* first check to see if line is empty, if it is, throw an exception */
|
|
if(line == NULL)
|
|
{
|
|
exception = EXCEPTION_EOF;
|
|
return status;
|
|
}
|
|
|
|
status = cmp((char *) &line[pos], lineLen - pos, match);
|
|
|
|
/* if we match, increment the position pointer */
|
|
if(status == TRUE && match != NULL)
|
|
{
|
|
matchLineNum = getSourceLineNumber();
|
|
matchFilePos = getInputFilePosition();
|
|
|
|
movePos((strlen(match)));
|
|
}
|
|
|
|
return status;
|
|
} /* static boolean adaCmp(char *match) */
|
|
|
|
/* just a version of adaCmp that is a bit more optimized for keywords */
|
|
static boolean adaKeywordCmp(adaKeyword keyword)
|
|
{
|
|
boolean status = FALSE;
|
|
|
|
/* first check to see if line is empty, if it is, throw an exception */
|
|
if(line == NULL)
|
|
{
|
|
exception = EXCEPTION_EOF;
|
|
return status;
|
|
}
|
|
|
|
status = cmp((char *) &line[pos], lineLen - pos,
|
|
(char *) AdaKeywords[keyword]);
|
|
|
|
/* if we match, increment the position pointer */
|
|
if(status == TRUE)
|
|
{
|
|
matchLineNum = getSourceLineNumber();
|
|
matchFilePos = getInputFilePosition();
|
|
|
|
movePos((strlen(AdaKeywords[keyword])));
|
|
}
|
|
|
|
return status;
|
|
} /* static boolean adaKeywordCmp(adaKeyword keyword) */
|
|
|
|
static void skipUntilWhiteSpace(void)
|
|
{
|
|
/* first check for a comment line, because this would cause the isspace
|
|
* check to be true immediately */
|
|
while(exception != EXCEPTION_EOF && isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
}
|
|
|
|
while(exception != EXCEPTION_EOF && !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 *) fileReadLine();
|
|
pos = 0;
|
|
|
|
if(line == NULL)
|
|
{
|
|
lineLen = 0;
|
|
exception = EXCEPTION_EOF;
|
|
return;
|
|
}
|
|
|
|
lineLen = strlen((char *) line);
|
|
|
|
return;
|
|
} /* if(pos >= lineLen) */
|
|
|
|
/* now check for comments here */
|
|
while(exception != EXCEPTION_EOF && isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
}
|
|
} /* while(!isspace(line[pos])) */
|
|
} /* static void skipUntilWhiteSpace(void) */
|
|
|
|
static void skipWhiteSpace(void)
|
|
{
|
|
/* first check for a comment line, because this would cause the isspace
|
|
* check to fail immediately */
|
|
while(exception != EXCEPTION_EOF && isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
}
|
|
|
|
while(exception != EXCEPTION_EOF && isspace(line[pos]))
|
|
{
|
|
movePos(1);
|
|
|
|
/* now check for comments here */
|
|
while(exception != EXCEPTION_EOF && isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
}
|
|
} /* while(isspace(line[pos])) */
|
|
} /* static void skipWhiteSpace(void) */
|
|
|
|
static void skipPast(char *past)
|
|
{
|
|
/* first check for a comment line, because this would cause the isspace
|
|
* check to fail immediately */
|
|
while(exception != EXCEPTION_EOF && isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
}
|
|
|
|
/* now look for the keyword */
|
|
while(exception != EXCEPTION_EOF && !adaCmp(past))
|
|
{
|
|
movePos(1);
|
|
|
|
/* now check for comments here */
|
|
while(exception != EXCEPTION_EOF && isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
}
|
|
}
|
|
} /* static void skipPast(char *past) */
|
|
|
|
static void skipPastKeyword(adaKeyword keyword)
|
|
{
|
|
/* first check for a comment line, because this would cause the isspace
|
|
* check to fail immediately */
|
|
while(exception != EXCEPTION_EOF && isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
}
|
|
|
|
/* now look for the keyword */
|
|
while(exception != EXCEPTION_EOF && !adaKeywordCmp(keyword))
|
|
{
|
|
movePos(1);
|
|
|
|
/* now check for comments here */
|
|
while(exception != EXCEPTION_EOF && isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
}
|
|
}
|
|
} /* static void skipPastKeyword(adaKeyword keyword) */
|
|
|
|
static void skipPastWord(void)
|
|
{
|
|
/* first check for a comment line, because this would cause the isspace
|
|
* check to fail immediately */
|
|
while(exception != EXCEPTION_EOF && isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
}
|
|
|
|
/* now increment until we hit a non-word character... Specifically,
|
|
* whitespace, '(', ')', ':', and ';' */
|
|
while(exception != EXCEPTION_EOF && !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 *) fileReadLine();
|
|
pos = 0;
|
|
|
|
if(line == NULL)
|
|
{
|
|
lineLen = 0;
|
|
exception = EXCEPTION_EOF;
|
|
return;
|
|
}
|
|
|
|
lineLen = strlen((char *) line);
|
|
|
|
return;
|
|
} /* if(pos >= lineLen) */
|
|
|
|
/* now check for comments here */
|
|
while(exception != EXCEPTION_EOF && isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
}
|
|
} /* while(!isspace(line[pos])) */
|
|
} /* static void skipPastWord(void) */
|
|
|
|
static adaTokenInfo *adaParseBlock(adaTokenInfo *parent, adaKind kind)
|
|
{
|
|
int i;
|
|
adaTokenInfo *token;
|
|
boolean 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(line[pos] == '(')
|
|
{
|
|
while(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 "reanames" 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))
|
|
{
|
|
/* if this is a "new" something then no need to parse */
|
|
skipPast(";");
|
|
}
|
|
else
|
|
{
|
|
adaParse(ADA_DECLARATIONS, token);
|
|
}
|
|
|
|
break;
|
|
} /* if(adaKeywordCmp(ADA_KEYWORD_IS)) */
|
|
else if(adaKeywordCmp(ADA_KEYWORD_RENAMES))
|
|
{
|
|
skipPast(";");
|
|
break;
|
|
}
|
|
else if(adaCmp(";"))
|
|
{
|
|
token->isSpec = TRUE;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
/* nothing found, move to the next word */
|
|
skipUntilWhiteSpace();
|
|
}
|
|
} /* while(TRUE) - while the end of spec, or beginning of body not found */
|
|
|
|
return token;
|
|
} /* static adaTokenInfo *adaParseBlock(adaTokenInfo *parent, adaKind kind) */
|
|
|
|
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(line[pos] == '(' && token != NULL)
|
|
{
|
|
while(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(line[pos] == '(')
|
|
{
|
|
while(line[pos] != ')')
|
|
{
|
|
movePos(1);
|
|
adaParseVariables(token, ADA_KIND_AUTOMATIC_VARIABLE);
|
|
}
|
|
movePos(1);
|
|
}
|
|
} /* if(kind == ADA_KIND_ENTRY && tmpToken == NULL) */
|
|
} /* if(line[pos] == '(' && token != NULL) */
|
|
|
|
/* 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(exception != EXCEPTION_EOF && 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
|
|
{
|
|
adaParse(ADA_DECLARATIONS, token);
|
|
}
|
|
|
|
break;
|
|
} /* if(adaKeywordCmp(ADA_KEYWORD_IS)) */
|
|
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 */
|
|
skipPastWord();
|
|
}
|
|
} /* while(exception != EXCEPTION_EOF && token != NULL) */
|
|
|
|
return token;
|
|
} /* static adaTokenInfo *adaParseSubprogram(adaTokenInfo *parent, ... ) */
|
|
|
|
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(line[pos] == '(')
|
|
{
|
|
/* in this case there is a discriminant to this type, gather the
|
|
* variables */
|
|
while(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(line[pos] == '(')
|
|
{
|
|
movePos(1);
|
|
adaParseVariables(token, ADA_KIND_ENUM_LITERAL);
|
|
}
|
|
else if(adaKeywordCmp(ADA_KEYWORD_RECORD))
|
|
{
|
|
/* until we hit "end record" we need to gather type variables */
|
|
while(TRUE)
|
|
{
|
|
skipWhiteSpace();
|
|
|
|
if(adaKeywordCmp(ADA_KEYWORD_END))
|
|
{
|
|
skipWhiteSpace();
|
|
if(adaKeywordCmp(ADA_KEYWORD_RECORD))
|
|
{
|
|
break;
|
|
}
|
|
skipPast(";");
|
|
} /* if(adaKeywordCmp(ADA_KEYWORD_END)) */
|
|
/* 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(";");
|
|
}
|
|
} /* while(TRUE) - end of record not found */
|
|
} /* else if(adaKeywordCmp(ADA_KEYWORD_RECORD)) */
|
|
} /* if(adaKeywordCmp(ADA_KEYWORD_IS)) */
|
|
else
|
|
{
|
|
token->isSpec = TRUE;
|
|
}
|
|
|
|
skipPast(";");
|
|
|
|
return token;
|
|
} /* static adaTokenInfo *adaParseType(adaTokenInfo *parent, adaKind kind) */
|
|
|
|
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;
|
|
fpos_t *filePos = xMalloc(filePosSize, fpos_t);
|
|
|
|
/* skip any preliminary whitespace or comments */
|
|
skipWhiteSpace();
|
|
while(exception != EXCEPTION_EOF && isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
}
|
|
|
|
/* 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 = getSourceLineNumber();
|
|
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(TRUE)
|
|
{
|
|
/* 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;
|
|
}
|
|
} /* if(isAdaComment(buf, bufPos, bufLen)) */
|
|
/* 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 && i == 0 &&
|
|
(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,
|
|
(char *) AdaKeywords[ADA_KEYWORD_CONSTANT]) == TRUE)
|
|
{
|
|
kind = ADA_KIND_CONSTANT;
|
|
}
|
|
else if(cmp(&buf[tokenStart], bufLen - tokenStart,
|
|
(char *) 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(exception != EXCEPTION_EOF && bufPos >= bufLen)
|
|
{
|
|
readNewLine();
|
|
|
|
/* store the new file position for the start of this line */
|
|
filePosIndex++;
|
|
while(filePosIndex >= filePosSize)
|
|
{
|
|
filePosSize *= 2;
|
|
filePos = xRealloc(filePos, filePosSize, fpos_t);
|
|
}
|
|
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((char *) buf, bufLen + 1, char);
|
|
memcpy((void *) &buf[bufPos], (void *) line, lineLen);
|
|
buf[bufLen] = '\0';
|
|
} /* if(bufPos >= bufLen) */
|
|
} /* while(TRUE) */
|
|
|
|
/* 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++);
|
|
} /* if(isAdaComment(buf, i, varEndPos)) */
|
|
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((const char *) &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;
|
|
} /* if(tokenStart != -1 && (isspace(buf[i]) || buf[i] == ',')) */
|
|
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++;
|
|
}
|
|
} /* for(i = 0; i < varEndPos; i++) */
|
|
|
|
/* if token start was 'started' then we should store the last token */
|
|
if(tokenStart != -1)
|
|
{
|
|
token = newAdaToken((const char *) &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];
|
|
}
|
|
} /* if(varEndPos != -1) */
|
|
|
|
/* 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 *adaParseVariables(adaTokenInfo *parent, ... ) */
|
|
|
|
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 *adaParseLoopVar(adaTokenInfo *parent) */
|
|
|
|
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 EXCEPTION_EOF */
|
|
while(exception == EXCEPTION_NONE)
|
|
{
|
|
/* find the next place to start */
|
|
skipWhiteSpace();
|
|
|
|
/* check some universal things to check for first */
|
|
if(isAdaComment(line, pos, lineLen))
|
|
{
|
|
readNewLine();
|
|
continue;
|
|
}
|
|
else if(adaKeywordCmp(ADA_KEYWORD_PRAGMA) ||
|
|
adaKeywordCmp(ADA_KEYWORD_WITH) ||
|
|
adaKeywordCmp(ADA_KEYWORD_USE))
|
|
{
|
|
/* set the token to NULL so we accidentally don't pick up something
|
|
* from earlier */
|
|
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_GENERIC)) */
|
|
else if(adaKeywordCmp(ADA_KEYWORD_SEPARATE))
|
|
{
|
|
/* skip any possible whitespace */
|
|
skipWhiteSpace();
|
|
|
|
/* skip over the "(" until we hit the tag */
|
|
if(line[pos] == '(')
|
|
{
|
|
movePos(1);
|
|
skipWhiteSpace();
|
|
|
|
/* get length of tag */
|
|
for(i = 1; (pos + i) < lineLen && line[pos + i] != ')' &&
|
|
!isspace(line[pos + i]); i++);
|
|
|
|
/* 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) */
|
|
token = newAdaToken(&line[pos], i, ADA_KIND_SEPARATE, FALSE,
|
|
parent);
|
|
|
|
/* since this is a false top-level token, set parent to be
|
|
* token */
|
|
parent = token;
|
|
token = NULL;
|
|
|
|
/* skip past the ')' */
|
|
skipPast(")");
|
|
} /* if(line[pos] == '(') */
|
|
else
|
|
{
|
|
/* move to the end of this statement */
|
|
skipPast(";");
|
|
}
|
|
} /* else if(adaKeywordCmp(ADA_KEYWORD_SEPARATE)) */
|
|
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);
|
|
} /* if(token != NULL) */
|
|
|
|
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);
|
|
}
|
|
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_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_TYPE)) */
|
|
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(line[pos] == '(')
|
|
{
|
|
while(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 if(adaKeywordCmp(ADA_KEYWORD_WITH)) */
|
|
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, and set the mode back to ADA_ROOT */
|
|
appendAdaTokenList(token, &genericParamsRoot.children);
|
|
mode = ADA_ROOT;
|
|
} /* if(token != NULL) */
|
|
|
|
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;
|
|
} /* if(adaCmp(parent->name)) */
|
|
else
|
|
{
|
|
/* set the token to NULL so we accidentally don't pick up something
|
|
* from earlier */
|
|
token = NULL;
|
|
skipPast(";");
|
|
}
|
|
} /* else if(adaKeywordCmp(ADA_KEYWORD_END)) */
|
|
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 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);
|
|
}
|
|
} /* if(token != NULL) */
|
|
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);
|
|
} /* if(adaKeywordCmp(ADA_KEYWORD_DECLARE)) */
|
|
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_BEGIN)) */
|
|
else if(adaKeywordCmp(ADA_KEYWORD_EXCEPTION))
|
|
{
|
|
mode = ADA_EXCEPTIONS;
|
|
break;
|
|
} /* else if(adaKeywordCmp(ADA_KEYWORD_EXCEPTION)) */
|
|
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;
|
|
} /* if(adaCmp(parent->name)) */
|
|
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;
|
|
} /* if(adaCmp(parent->name)) */
|
|
} /* else if(adaKeywordCmp(ADA_KEYWORD_LOOP)) */
|
|
else
|
|
{
|
|
/* otherwise, nothing was found so just skip until the end of
|
|
* this statement */
|
|
skipPast(";");
|
|
}
|
|
} /* else if(adaKeywordCmp(ADA_KEYWORD_END)) */
|
|
else if(adaKeywordCmp(ADA_KEYWORD_ACCEPT))
|
|
{
|
|
adaParseSubprogram(parent, ADA_KIND_ENTRY);
|
|
} /* else if(adaKeywordCmp(ADA_KEYWORD_ACCEPT)) */
|
|
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((const char *) 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_FOR)) */
|
|
else if(adaKeywordCmp(ADA_KEYWORD_WHILE))
|
|
{
|
|
token = newAdaToken((const char *) 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_WHILE)) */
|
|
else if(adaKeywordCmp(ADA_KEYWORD_LOOP))
|
|
{
|
|
token = newAdaToken((const char *) 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(adaKeywordCmp(ADA_KEYWORD_LOOP)) */
|
|
else if(line != NULL &&
|
|
strncasecmp((char *) &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((char *) &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)
|
|
{
|
|
token = newAdaToken(&line[pos], i, ADA_KIND_LABEL, FALSE, parent);
|
|
skipPast(">>");
|
|
token = NULL;
|
|
}
|
|
} /* else if(strncasecmp(line[pos], "<<", strlen("<<")) == 0) */
|
|
/* 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
|
|
{
|
|
/* 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 we hit a non-identifier character (anything But letters, _
|
|
* and ':' then this is not an identifier */
|
|
if(!isalnum(line[pos + i]) && line[pos + i] != '_' &&
|
|
line[pos + i] != ':')
|
|
{
|
|
/* if this is not an identifier then we should just bail out of
|
|
* this loop now */
|
|
break;
|
|
}
|
|
else if((line[pos + i] == ':') && (line[pos + i + 1] != '='))
|
|
{
|
|
token = newAdaToken(&line[pos], i, ADA_KIND_IDENTIFIER, FALSE,
|
|
parent);
|
|
break;
|
|
}
|
|
} /* for(i = 1; (pos + i) < lineLen; i++) */
|
|
|
|
/* 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_WHILE)) */
|
|
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;
|
|
}
|
|
} /* if(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;
|
|
} /* if(adaCmp(parent->name)) */
|
|
else
|
|
{
|
|
/* otherwise, nothing was found so just skip until the end of
|
|
* this statement */
|
|
skipPast(";");
|
|
}
|
|
} /* else if(adaKeywordCmp(ADA_KEYWORD_END)) */
|
|
else
|
|
{
|
|
/* otherwise, nothing was found so just skip until the end of
|
|
* this statement */
|
|
skipPast(";");
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
Assert(0);
|
|
} /* switch(mode) */
|
|
} /* while(exception == EXCEPTION_NONE) */
|
|
|
|
freeAdaTokenList(&genericParamsRoot.children);
|
|
return token;
|
|
} /* static adaTokenInfo *adaParse(adaParseMode mode, adaTokenInfo *parent) */
|
|
|
|
static void storeAdaTags(adaTokenInfo *token, const char *parentScope)
|
|
{
|
|
char *currentScope = NULL;
|
|
adaTokenInfo *tmp = NULL;
|
|
|
|
if(token != NULL)
|
|
{
|
|
/* do a spec transition if necessary */
|
|
if(token->isSpec == TRUE)
|
|
{
|
|
makeSpec(&token->kind);
|
|
|
|
if(token->kind != ADA_KIND_UNDEFINED)
|
|
{
|
|
token->tag.kindName = AdaKinds[token->kind].name;
|
|
token->tag.kind = AdaKinds[token->kind].letter;
|
|
}
|
|
}
|
|
|
|
/* fill in the scope data */
|
|
if(token->parent != NULL)
|
|
{
|
|
if(token->parent->kind > ADA_KIND_UNDEFINED &&
|
|
token->parent->kind < ADA_KIND_COUNT)
|
|
{
|
|
token->tag.extensionFields.scope[0] =
|
|
AdaKinds[token->parent->kind].name;
|
|
token->tag.extensionFields.scope[1] = token->parent->name;
|
|
}
|
|
else if(token->parent->kind == ADA_KIND_SEPARATE)
|
|
{
|
|
token->tag.extensionFields.scope[0] =
|
|
AdaKeywords[ADA_KEYWORD_SEPARATE];
|
|
token->tag.extensionFields.scope[1] = token->parent->name;
|
|
}
|
|
} /* else if(token->parent->kind == ADA_KIND_ANONYMOUS) */
|
|
} /* if(token->parent != NULL) */
|
|
|
|
/* 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->kind == ADA_KIND_ANONYMOUS && token->children.head != NULL) ||
|
|
token->kind != ADA_KIND_ANONYMOUS) &&
|
|
((Option.include.fileScope == TRUE) ||
|
|
((Option.include.fileScope == 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((Option.include.qualifiedTags == 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. */
|
|
currentScope = xMalloc(strlen(parentScope) + strlen(token->name) + 2,
|
|
char);
|
|
strncpy((char *) currentScope, parentScope, strlen(parentScope));
|
|
currentScope[strlen(parentScope)] = '.';
|
|
strncpy((char *) ¤tScope[strlen(parentScope) + 1], token->name,
|
|
strlen(token->name));
|
|
currentScope[strlen(parentScope) + 1 + strlen(token->name)] = '\0';
|
|
|
|
token->tag.name = currentScope;
|
|
makeTagEntry(&token->tag);
|
|
} /* if(parentScope != NULL) */
|
|
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;
|
|
}
|
|
} /* if((Option.include.qualifiedTags == TRUE) && ... */
|
|
} /* if((token->kind > ADA_KIND_UNDEFINED) && ... */
|
|
|
|
/* 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((char *) AdaKeywords[ADA_KEYWORD_DECLARE])) == 0)
|
|
{
|
|
token->name = NULL;
|
|
token->tag.name = NULL;
|
|
}
|
|
|
|
if((currentScope != NULL) && (currentScope != token->name))
|
|
{
|
|
eFree((void *) currentScope);
|
|
}
|
|
} /* static void storeAdaTags(adaTokenInfo *token, const char *parentScope) */
|
|
|
|
/* main parse function */
|
|
static void findAdaTags(void)
|
|
{
|
|
adaTokenInfo root;
|
|
adaTokenInfo *tmp;
|
|
|
|
/* init all global data now */
|
|
exception = EXCEPTION_NONE;
|
|
line = NULL;
|
|
pos = 0;
|
|
matchLineNum = 0;
|
|
eofCount = 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;
|
|
initAdaTokenList(&root.children);
|
|
|
|
/* read in the first line */
|
|
readNewLine();
|
|
|
|
/* tokenize entire file */
|
|
exception = setjmp(eofError);
|
|
while(exception != EXCEPTION_EOF && adaParse(ADA_ROOT, &root) != NULL);
|
|
|
|
/* store tags */
|
|
tmp = root.children.head;
|
|
while(tmp != NULL)
|
|
{
|
|
storeAdaTags(tmp, NULL);
|
|
tmp = tmp->next;
|
|
}
|
|
|
|
/* clean up tokens */
|
|
freeAdaTokenList(&root.children);
|
|
} /* static void findAdaTags(void) */
|
|
|
|
/* parser definition function */
|
|
extern parserDefinition* AdaParser(void)
|
|
{
|
|
static const char *const extensions[] = { "adb", "ads", "ada", "Ada", NULL };
|
|
parserDefinition* def = parserNew("Ada");
|
|
def->kinds = AdaKinds;
|
|
def->kindCount = ADA_KIND_COUNT;
|
|
def->extensions = extensions;
|
|
def->parser = findAdaTags;
|
|
return def;
|
|
} /* extern parserDefinition* AdaParser(void) */
|