mirror of
https://github.com/jart/cosmopolitan.git
synced 2024-05-02 19:58:45 +00:00
838 lines
18 KiB
C
838 lines
18 KiB
C
// clang-format off
|
|
/*
|
|
* $Id: vhdl.c 652 2008-04-18 03:51:47Z elliotth $
|
|
*
|
|
* Copyright (c) 2008, Nicolas Vincent
|
|
*
|
|
* This source code is released for free distribution under the terms of the
|
|
* GNU General Public License.
|
|
*
|
|
* This module contains functions for generating tags for VHDL files.
|
|
*/
|
|
|
|
/*
|
|
* INCLUDE FILES
|
|
*/
|
|
#include "third_party/ctags/general.h" /* must always come first */
|
|
|
|
#include "libc/str/str.h" /* to define isalpha () */
|
|
#include "libc/mem/alg.h"
|
|
#include "libc/str/str.h"
|
|
#include "libc/runtime/runtime.h"
|
|
|
|
#include "third_party/ctags/debug.h"
|
|
#include "third_party/ctags/entry.h"
|
|
#include "third_party/ctags/keyword.h"
|
|
#include "third_party/ctags/parse.h"
|
|
#include "third_party/ctags/read.h"
|
|
#include "third_party/ctags/routines.h"
|
|
#include "third_party/ctags/vstring.h"
|
|
|
|
/*
|
|
* MACROS
|
|
*/
|
|
#define isType(token,t) (boolean) ((token)->type == (t))
|
|
#define isKeyword(token,k) (boolean) ((token)->keyword == (k))
|
|
|
|
/*
|
|
* DATA DECLARATIONS
|
|
*/
|
|
typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
|
|
|
|
/*
|
|
* Used to specify type of keyword.
|
|
*/
|
|
typedef enum eKeywordId {
|
|
KEYWORD_NONE = -1,
|
|
KEYWORD_ABS,
|
|
KEYWORD_ACCESS,
|
|
KEYWORD_AFTER,
|
|
KEYWORD_ALIAS,
|
|
KEYWORD_ALL,
|
|
KEYWORD_AND,
|
|
KEYWORD_ARCHITECTURE,
|
|
KEYWORD_ARRAY,
|
|
KEYWORD_ASSERT,
|
|
KEYWORD_ATTRIBUTE,
|
|
KEYWORD_BEGIN,
|
|
KEYWORD_BLOCK,
|
|
KEYWORD_BODY,
|
|
KEYWORD_BUFFER,
|
|
KEYWORD_BUS,
|
|
KEYWORD_CASE,
|
|
KEYWORD_COMPONENT,
|
|
KEYWORD_CONFIGURATION,
|
|
KEYWORD_CONSTANT,
|
|
KEYWORD_DISCONNECT,
|
|
KEYWORD_DOWNTO,
|
|
KEYWORD_ELSE,
|
|
KEYWORD_ELSIF,
|
|
KEYWORD_END,
|
|
KEYWORD_ENTITY,
|
|
KEYWORD_EXIT,
|
|
KEYWORD_FILE,
|
|
KEYWORD_FOR,
|
|
KEYWORD_FUNCTION,
|
|
KEYWORD_GENERATE,
|
|
KEYWORD_GENERIC,
|
|
KEYWORD_GROUP,
|
|
KEYWORD_GUARDED,
|
|
KEYWORD_IF,
|
|
KEYWORD_IMPURE,
|
|
KEYWORD_IN,
|
|
KEYWORD_INERTIAL,
|
|
KEYWORD_INOUT,
|
|
KEYWORD_IS,
|
|
KEYWORD_LABEL,
|
|
KEYWORD_LIBRARY,
|
|
KEYWORD_LINKAGE,
|
|
KEYWORD_LITERAL,
|
|
KEYWORD_LOOP,
|
|
KEYWORD_MAP,
|
|
KEYWORD_MOD,
|
|
KEYWORD_NAND,
|
|
KEYWORD_NEW,
|
|
KEYWORD_NEXT,
|
|
KEYWORD_NOR,
|
|
KEYWORD_NOT,
|
|
KEYWORD_NULL,
|
|
KEYWORD_OF,
|
|
KEYWORD_ON,
|
|
KEYWORD_OPEN,
|
|
KEYWORD_OR,
|
|
KEYWORD_OTHERS,
|
|
KEYWORD_OUT,
|
|
KEYWORD_PACKAGE,
|
|
KEYWORD_PORT,
|
|
KEYWORD_POSTPONED,
|
|
KEYWORD_PROCEDURE,
|
|
KEYWORD_PROCESS,
|
|
KEYWORD_PURE,
|
|
KEYWORD_RANGE,
|
|
KEYWORD_RECORD,
|
|
KEYWORD_REGISTER,
|
|
KEYWORD_REJECT,
|
|
KEYWORD_RETURN,
|
|
KEYWORD_ROL,
|
|
KEYWORD_ROR,
|
|
KEYWORD_SELECT,
|
|
KEYWORD_SEVERITY,
|
|
KEYWORD_SIGNAL,
|
|
KEYWORD_SHARED,
|
|
KEYWORD_SLA,
|
|
KEYWORD_SLI,
|
|
KEYWORD_SRA,
|
|
KEYWORD_SRL,
|
|
KEYWORD_SUBTYPE,
|
|
KEYWORD_THEN,
|
|
KEYWORD_TO,
|
|
KEYWORD_TRANSPORT,
|
|
KEYWORD_TYPE,
|
|
KEYWORD_UNAFFECTED,
|
|
KEYWORD_UNITS,
|
|
KEYWORD_UNTIL,
|
|
KEYWORD_USE,
|
|
KEYWORD_VARIABLE,
|
|
KEYWORD_WAIT,
|
|
KEYWORD_WHEN,
|
|
KEYWORD_WHILE,
|
|
KEYWORD_WITH,
|
|
KEYWORD_XNOR,
|
|
KEYWORD_XOR
|
|
} keywordId;
|
|
|
|
/* Used to determine whether keyword is valid for the current language and
|
|
* what its ID is.
|
|
*/
|
|
typedef struct sKeywordDesc {
|
|
const char *name;
|
|
keywordId id;
|
|
} keywordDesc;
|
|
|
|
typedef enum eTokenType {
|
|
TOKEN_NONE, /* none */
|
|
TOKEN_OPEN_PAREN, /* ( */
|
|
TOKEN_CLOSE_PAREN, /* ) */
|
|
TOKEN_COMMA, /* the comma character */
|
|
TOKEN_IDENTIFIER,
|
|
TOKEN_KEYWORD,
|
|
TOKEN_PERIOD, /* . */
|
|
TOKEN_OPERATOR,
|
|
TOKEN_SEMICOLON, /* the semicolon character */
|
|
TOKEN_STRING
|
|
} tokenType;
|
|
|
|
typedef struct sTokenInfo {
|
|
tokenType type;
|
|
keywordId keyword;
|
|
vString *string; /* the name of the token */
|
|
vString *scope;
|
|
unsigned long lineNumber; /* line number of tag */
|
|
fpos_t filePosition; /* file position of line containing name */
|
|
} tokenInfo;
|
|
|
|
/*
|
|
* DATA DEFINITIONS
|
|
*/
|
|
static int Lang_vhdl;
|
|
static jmp_buf Exception;
|
|
|
|
/* Used to index into the VhdlKinds table. */
|
|
typedef enum {
|
|
VHDLTAG_UNDEFINED = -1,
|
|
VHDLTAG_CONSTANT,
|
|
VHDLTAG_TYPE,
|
|
VHDLTAG_SUBTYPE,
|
|
VHDLTAG_RECORD,
|
|
VHDLTAG_ENTITY,
|
|
VHDLTAG_COMPONENT,
|
|
VHDLTAG_PROTOTYPE,
|
|
VHDLTAG_FUNCTION,
|
|
VHDLTAG_PROCEDURE,
|
|
VHDLTAG_PACKAGE,
|
|
VHDLTAG_LOCAL
|
|
} vhdlKind;
|
|
|
|
static kindOption VhdlKinds[] = {
|
|
{TRUE, 'c', "constant", "constant declarations"},
|
|
{TRUE, 't', "type", "type definitions"},
|
|
{TRUE, 'T', "subtype", "subtype definitions"},
|
|
{TRUE, 'r', "record", "record names"},
|
|
{TRUE, 'e', "entity", "entity declarations"},
|
|
{FALSE, 'C', "component", "component declarations"},
|
|
{FALSE, 'd', "prototype", "prototypes"},
|
|
{TRUE, 'f', "function", "function prototypes and declarations"},
|
|
{TRUE, 'p', "procedure", "procedure prototypes and declarations"},
|
|
{TRUE, 'P', "package", "package definitions"},
|
|
{FALSE, 'l', "local", "local definitions"}
|
|
};
|
|
|
|
static keywordDesc VhdlKeywordTable[] = {
|
|
{"abs", KEYWORD_ABS},
|
|
{"access", KEYWORD_ACCESS},
|
|
{"after", KEYWORD_AFTER},
|
|
{"alias", KEYWORD_ALIAS},
|
|
{"all", KEYWORD_ALL},
|
|
{"and", KEYWORD_AND},
|
|
{"architecture", KEYWORD_ARCHITECTURE},
|
|
{"array", KEYWORD_ARRAY},
|
|
{"assert", KEYWORD_ASSERT},
|
|
{"attribute", KEYWORD_ATTRIBUTE},
|
|
{"begin", KEYWORD_BEGIN},
|
|
{"block", KEYWORD_BLOCK},
|
|
{"body", KEYWORD_BODY},
|
|
{"buffer", KEYWORD_BUFFER},
|
|
{"bus", KEYWORD_BUS},
|
|
{"case", KEYWORD_CASE},
|
|
{"component", KEYWORD_COMPONENT},
|
|
{"configuration", KEYWORD_CONFIGURATION},
|
|
{"constant", KEYWORD_CONSTANT},
|
|
{"disconnect", KEYWORD_DISCONNECT},
|
|
{"downto", KEYWORD_DOWNTO},
|
|
{"else", KEYWORD_ELSE},
|
|
{"elsif", KEYWORD_ELSIF},
|
|
{"end", KEYWORD_END},
|
|
{"entity", KEYWORD_ENTITY},
|
|
{"exit", KEYWORD_EXIT},
|
|
{"file", KEYWORD_FILE},
|
|
{"for", KEYWORD_FOR},
|
|
{"function", KEYWORD_FUNCTION},
|
|
{"generate", KEYWORD_GENERATE},
|
|
{"generic", KEYWORD_GENERIC},
|
|
{"group", KEYWORD_GROUP},
|
|
{"guarded", KEYWORD_GUARDED},
|
|
{"if", KEYWORD_IF},
|
|
{"impure", KEYWORD_IMPURE},
|
|
{"in", KEYWORD_IN},
|
|
{"inertial", KEYWORD_INERTIAL},
|
|
{"inout", KEYWORD_INOUT},
|
|
{"is", KEYWORD_IS},
|
|
{"label", KEYWORD_LABEL},
|
|
{"library", KEYWORD_LIBRARY},
|
|
{"linkage", KEYWORD_LINKAGE},
|
|
{"literal", KEYWORD_LITERAL},
|
|
{"loop", KEYWORD_LOOP},
|
|
{"map", KEYWORD_MAP},
|
|
{"mod", KEYWORD_MOD},
|
|
{"nand", KEYWORD_NAND},
|
|
{"new", KEYWORD_NEW},
|
|
{"next", KEYWORD_NEXT},
|
|
{"nor", KEYWORD_NOR},
|
|
{"not", KEYWORD_NOT},
|
|
{"null", KEYWORD_NULL},
|
|
{"of", KEYWORD_OF},
|
|
{"on", KEYWORD_ON},
|
|
{"open", KEYWORD_OPEN},
|
|
{"or", KEYWORD_OR},
|
|
{"others", KEYWORD_OTHERS},
|
|
{"out", KEYWORD_OUT},
|
|
{"package", KEYWORD_PACKAGE},
|
|
{"port", KEYWORD_PORT},
|
|
{"postponed", KEYWORD_POSTPONED},
|
|
{"procedure", KEYWORD_PROCEDURE},
|
|
{"process", KEYWORD_PROCESS},
|
|
{"pure", KEYWORD_PURE},
|
|
{"range", KEYWORD_RANGE},
|
|
{"record", KEYWORD_RECORD},
|
|
{"register", KEYWORD_REGISTER},
|
|
{"reject", KEYWORD_REJECT},
|
|
{"return", KEYWORD_RETURN},
|
|
{"rol", KEYWORD_ROL},
|
|
{"ror", KEYWORD_ROR},
|
|
{"select", KEYWORD_SELECT},
|
|
{"severity", KEYWORD_SEVERITY},
|
|
{"signal", KEYWORD_SIGNAL},
|
|
{"shared", KEYWORD_SHARED},
|
|
{"sla", KEYWORD_SLA},
|
|
{"sli", KEYWORD_SLI},
|
|
{"sra", KEYWORD_SRA},
|
|
{"srl", KEYWORD_SRL},
|
|
{"subtype", KEYWORD_SUBTYPE},
|
|
{"then", KEYWORD_THEN},
|
|
{"to", KEYWORD_TO},
|
|
{"transport", KEYWORD_TRANSPORT},
|
|
{"type", KEYWORD_TYPE},
|
|
{"unaffected", KEYWORD_UNAFFECTED},
|
|
{"units", KEYWORD_UNITS},
|
|
{"until", KEYWORD_UNTIL},
|
|
{"use", KEYWORD_USE},
|
|
{"variable", KEYWORD_VARIABLE},
|
|
{"wait", KEYWORD_WAIT},
|
|
{"when", KEYWORD_WHEN},
|
|
{"while", KEYWORD_WHILE},
|
|
{"with", KEYWORD_WITH},
|
|
{"xnor", KEYWORD_XNOR},
|
|
{"xor", KEYWORD_XOR}
|
|
};
|
|
|
|
/*
|
|
* FUNCTION DECLARATIONS
|
|
*/
|
|
static void parseKeywords (tokenInfo * const token, boolean local);
|
|
|
|
/*
|
|
* FUNCTION DEFINITIONS
|
|
*/
|
|
|
|
static boolean isIdentChar1 (const int c)
|
|
{
|
|
return (boolean) (isalpha (c) || c == '_');
|
|
}
|
|
|
|
static boolean isIdentChar (const int c)
|
|
{
|
|
return (boolean) (isalpha (c) || isdigit (c) || c == '_');
|
|
}
|
|
|
|
static boolean isIdentifierMatch (const tokenInfo * const token,
|
|
const vString * const name)
|
|
{
|
|
return (boolean) (isType (token, TOKEN_IDENTIFIER) &&
|
|
strcasecmp (vStringValue (token->string), vStringValue (name)) == 0);
|
|
/* XXX this is copy/paste from eiffel.c and slightly modified */
|
|
/* shouldn't we use strNcasecmp ? */
|
|
}
|
|
|
|
static boolean isKeywordOrIdent (const tokenInfo * const token,
|
|
const keywordId keyword, const vString * const name)
|
|
{
|
|
return (boolean) (isKeyword (token, keyword) ||
|
|
isIdentifierMatch (token, name));
|
|
}
|
|
|
|
static tokenInfo *newToken (void)
|
|
{
|
|
tokenInfo *const token = xMalloc (1, tokenInfo);
|
|
token->type = TOKEN_NONE;
|
|
token->keyword = KEYWORD_NONE;
|
|
token->string = vStringNew ();
|
|
token->scope = vStringNew ();
|
|
token->lineNumber = getSourceLineNumber ();
|
|
token->filePosition = getInputFilePosition ();
|
|
return token;
|
|
}
|
|
|
|
static void deleteToken (tokenInfo * const token)
|
|
{
|
|
if (token != NULL)
|
|
{
|
|
vStringDelete (token->string);
|
|
vStringDelete (token->scope);
|
|
eFree (token);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Parsing functions
|
|
*/
|
|
|
|
static void parseString (vString * const string, const int delimiter)
|
|
{
|
|
boolean end = FALSE;
|
|
while (!end)
|
|
{
|
|
int c = fileGetc ();
|
|
if (c == EOF)
|
|
end = TRUE;
|
|
else if (c == '\\')
|
|
{
|
|
c = fileGetc (); /* This maybe a ' or ". */
|
|
vStringPut (string, c);
|
|
}
|
|
else if (c == delimiter)
|
|
end = TRUE;
|
|
else
|
|
vStringPut (string, c);
|
|
}
|
|
vStringTerminate (string);
|
|
}
|
|
|
|
/* Read a VHDL identifier beginning with "firstChar" and place it into "name".
|
|
*/
|
|
static void parseIdentifier (vString * const string, const int firstChar)
|
|
{
|
|
int c = firstChar;
|
|
Assert (isIdentChar1 (c));
|
|
do
|
|
{
|
|
vStringPut (string, c);
|
|
c = fileGetc ();
|
|
} while (isIdentChar (c));
|
|
vStringTerminate (string);
|
|
if (!isspace (c))
|
|
fileUngetc (c); /* unget non-identifier character */
|
|
}
|
|
|
|
static void readToken (tokenInfo * const token)
|
|
{
|
|
int c;
|
|
|
|
token->type = TOKEN_NONE;
|
|
token->keyword = KEYWORD_NONE;
|
|
vStringClear (token->string);
|
|
|
|
getNextChar:
|
|
do
|
|
{
|
|
c = fileGetc ();
|
|
token->lineNumber = getSourceLineNumber ();
|
|
token->filePosition = getInputFilePosition ();
|
|
}
|
|
while (c == '\t' || c == ' ' || c == '\n');
|
|
|
|
switch (c)
|
|
{
|
|
case EOF:
|
|
longjmp (Exception, (int) ExceptionEOF);
|
|
break;
|
|
case '(':
|
|
token->type = TOKEN_OPEN_PAREN;
|
|
break;
|
|
case ')':
|
|
token->type = TOKEN_CLOSE_PAREN;
|
|
break;
|
|
case ';':
|
|
token->type = TOKEN_SEMICOLON;
|
|
break;
|
|
case '.':
|
|
token->type = TOKEN_PERIOD;
|
|
break;
|
|
case ',':
|
|
token->type = TOKEN_COMMA;
|
|
break;
|
|
case '\'': /* only single char are inside simple quotes */
|
|
break; /* or it is for attributes so we don't care */
|
|
case '"':
|
|
token->type = TOKEN_STRING;
|
|
parseString (token->string, c);
|
|
token->lineNumber = getSourceLineNumber ();
|
|
token->filePosition = getInputFilePosition ();
|
|
break;
|
|
case '-':
|
|
c = fileGetc ();
|
|
if (c == '-') /* start of a comment */
|
|
{
|
|
fileSkipToCharacter ('\n');
|
|
goto getNextChar;
|
|
}
|
|
else
|
|
{
|
|
if (!isspace (c))
|
|
fileUngetc (c);
|
|
token->type = TOKEN_OPERATOR;
|
|
}
|
|
break;
|
|
default:
|
|
if (!isIdentChar1 (c))
|
|
token->type = TOKEN_NONE;
|
|
else
|
|
{
|
|
parseIdentifier (token->string, c);
|
|
token->lineNumber = getSourceLineNumber ();
|
|
token->filePosition = getInputFilePosition ();
|
|
token->keyword = analyzeToken (token->string, Lang_vhdl);
|
|
if (isKeyword (token, KEYWORD_NONE))
|
|
token->type = TOKEN_IDENTIFIER;
|
|
else
|
|
token->type = TOKEN_KEYWORD;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void skipToKeyword (const keywordId keyword)
|
|
{
|
|
tokenInfo *const token = newToken ();
|
|
do
|
|
{
|
|
readToken (token);
|
|
}
|
|
while (!isKeyword (token, keyword));
|
|
deleteToken (token);
|
|
}
|
|
|
|
static void skipToMatched (tokenInfo * const token)
|
|
{
|
|
int nest_level = 0;
|
|
tokenType open_token;
|
|
tokenType close_token;
|
|
|
|
switch (token->type)
|
|
{
|
|
case TOKEN_OPEN_PAREN:
|
|
open_token = TOKEN_OPEN_PAREN;
|
|
close_token = TOKEN_CLOSE_PAREN;
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* This routine will skip to a matching closing token.
|
|
* It will also handle nested tokens like the (, ) below.
|
|
* ( name varchar(30), text binary(10) )
|
|
*/
|
|
if (isType (token, open_token))
|
|
{
|
|
nest_level++;
|
|
while (!(isType (token, close_token) && (nest_level == 0)))
|
|
{
|
|
readToken (token);
|
|
if (isType (token, open_token))
|
|
{
|
|
nest_level++;
|
|
}
|
|
if (isType (token, close_token))
|
|
{
|
|
if (nest_level > 0)
|
|
{
|
|
nest_level--;
|
|
}
|
|
}
|
|
}
|
|
readToken (token);
|
|
}
|
|
}
|
|
|
|
static void makeConstTag (tokenInfo * const token, const vhdlKind kind)
|
|
{
|
|
if (VhdlKinds[kind].enabled)
|
|
{
|
|
const char *const name = vStringValue (token->string);
|
|
tagEntryInfo e;
|
|
initTagEntry (&e, name);
|
|
e.lineNumber = token->lineNumber;
|
|
e.filePosition = token->filePosition;
|
|
e.kindName = VhdlKinds[kind].name;
|
|
e.kind = VhdlKinds[kind].letter;
|
|
makeTagEntry (&e);
|
|
}
|
|
}
|
|
|
|
static void makeVhdlTag (tokenInfo * const token, const vhdlKind kind)
|
|
{
|
|
if (VhdlKinds[kind].enabled)
|
|
{
|
|
/*
|
|
* If a scope has been added to the token, change the token
|
|
* string to include the scope when making the tag.
|
|
*/
|
|
if (vStringLength (token->scope) > 0)
|
|
{
|
|
vString *fulltag = vStringNew ();
|
|
vStringCopy (fulltag, token->scope);
|
|
vStringCatS (fulltag, ".");
|
|
vStringCatS (fulltag, vStringValue (token->string));
|
|
vStringTerminate (fulltag);
|
|
vStringCopy (token->string, fulltag);
|
|
vStringDelete (fulltag);
|
|
}
|
|
makeConstTag (token, kind);
|
|
}
|
|
}
|
|
|
|
static void initialize (const langType language)
|
|
{
|
|
size_t i;
|
|
const size_t count =
|
|
sizeof (VhdlKeywordTable) / sizeof (VhdlKeywordTable[0]);
|
|
Lang_vhdl = language;
|
|
for (i = 0; i < count; ++i)
|
|
{
|
|
const keywordDesc *const p = &VhdlKeywordTable[i];
|
|
addKeyword (p->name, language, (int) p->id);
|
|
}
|
|
}
|
|
|
|
static void parsePackage (tokenInfo * const token)
|
|
{
|
|
tokenInfo *const name = newToken ();
|
|
Assert (isKeyword (token, KEYWORD_PACKAGE));
|
|
readToken (token);
|
|
if (isKeyword (token, KEYWORD_BODY))
|
|
{
|
|
readToken (name);
|
|
makeVhdlTag (name, VHDLTAG_PACKAGE);
|
|
}
|
|
else if (isType (token, TOKEN_IDENTIFIER))
|
|
{
|
|
makeVhdlTag (token, VHDLTAG_PACKAGE);
|
|
}
|
|
deleteToken (name);
|
|
}
|
|
|
|
static void parseModule (tokenInfo * const token)
|
|
{
|
|
tokenInfo *const name = newToken ();
|
|
const vhdlKind kind = isKeyword (token, KEYWORD_ENTITY) ?
|
|
VHDLTAG_ENTITY : VHDLTAG_COMPONENT;
|
|
Assert (isKeyword (token, KEYWORD_ENTITY) ||
|
|
isKeyword (token, KEYWORD_COMPONENT));
|
|
readToken (name);
|
|
if (kind == VHDLTAG_COMPONENT)
|
|
{
|
|
makeVhdlTag (name, VHDLTAG_COMPONENT);
|
|
skipToKeyword (KEYWORD_END);
|
|
fileSkipToCharacter (';');
|
|
}
|
|
else
|
|
{
|
|
readToken (token);
|
|
if (isKeyword (token, KEYWORD_IS))
|
|
{
|
|
makeVhdlTag (name, VHDLTAG_ENTITY);
|
|
skipToKeyword (KEYWORD_END);
|
|
fileSkipToCharacter (';');
|
|
}
|
|
}
|
|
deleteToken (name);
|
|
}
|
|
|
|
static void parseRecord (tokenInfo * const token)
|
|
{
|
|
tokenInfo *const name = newToken ();
|
|
Assert (isKeyword (token, KEYWORD_RECORD));
|
|
readToken (name);
|
|
do
|
|
{
|
|
readToken (token); /* should be a colon */
|
|
fileSkipToCharacter (';');
|
|
makeVhdlTag (name, VHDLTAG_RECORD);
|
|
readToken (name);
|
|
}
|
|
while (!isKeyword (name, KEYWORD_END));
|
|
fileSkipToCharacter (';');
|
|
deleteToken (name);
|
|
}
|
|
|
|
static void parseTypes (tokenInfo * const token)
|
|
{
|
|
tokenInfo *const name = newToken ();
|
|
const vhdlKind kind = isKeyword (token, KEYWORD_TYPE) ?
|
|
VHDLTAG_TYPE : VHDLTAG_SUBTYPE;
|
|
Assert (isKeyword (token, KEYWORD_TYPE) ||
|
|
isKeyword (token, KEYWORD_SUBTYPE));
|
|
readToken (name);
|
|
readToken (token);
|
|
if (isKeyword (token, KEYWORD_IS))
|
|
{
|
|
readToken (token); /* type */
|
|
if (isKeyword (token, KEYWORD_RECORD))
|
|
{
|
|
makeVhdlTag (name, kind);
|
|
/*TODO: make tags of the record's names */
|
|
parseRecord (token);
|
|
}
|
|
else
|
|
{
|
|
makeVhdlTag (name, kind);
|
|
}
|
|
}
|
|
deleteToken (name);
|
|
}
|
|
|
|
static void parseConstant (boolean local)
|
|
{
|
|
tokenInfo *const name = newToken ();
|
|
readToken (name);
|
|
if (local)
|
|
{
|
|
makeVhdlTag (name, VHDLTAG_LOCAL);
|
|
}
|
|
else
|
|
{
|
|
makeVhdlTag (name, VHDLTAG_CONSTANT);
|
|
}
|
|
fileSkipToCharacter (';');
|
|
deleteToken (name);
|
|
}
|
|
|
|
static void parseSubProgram (tokenInfo * const token)
|
|
{
|
|
tokenInfo *const name = newToken ();
|
|
boolean endSubProgram = FALSE;
|
|
const vhdlKind kind = isKeyword (token, KEYWORD_FUNCTION) ?
|
|
VHDLTAG_FUNCTION : VHDLTAG_PROCEDURE;
|
|
Assert (isKeyword (token, KEYWORD_FUNCTION) ||
|
|
isKeyword (token, KEYWORD_PROCEDURE));
|
|
readToken (name); /* the name of the function or procedure */
|
|
readToken (token);
|
|
if (isType (token, TOKEN_OPEN_PAREN))
|
|
{
|
|
skipToMatched (token);
|
|
}
|
|
|
|
if (kind == VHDLTAG_FUNCTION)
|
|
{
|
|
if (isKeyword (token, KEYWORD_RETURN))
|
|
{
|
|
/* Read datatype */
|
|
readToken (token);
|
|
while (! isKeyword (token, KEYWORD_IS) &&
|
|
! isType (token, TOKEN_SEMICOLON))
|
|
{
|
|
readToken (token);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (isType (token, TOKEN_SEMICOLON))
|
|
{
|
|
makeVhdlTag (name, VHDLTAG_PROTOTYPE);
|
|
}
|
|
else if (isKeyword (token, KEYWORD_IS))
|
|
{
|
|
if (kind == VHDLTAG_FUNCTION)
|
|
{
|
|
makeVhdlTag (name, VHDLTAG_FUNCTION);
|
|
do
|
|
{
|
|
readToken (token);
|
|
if (isKeyword (token, KEYWORD_END))
|
|
{
|
|
readToken (token);
|
|
endSubProgram = isKeywordOrIdent (token,
|
|
KEYWORD_FUNCTION, name->string);
|
|
fileSkipToCharacter (';');
|
|
}
|
|
else
|
|
{
|
|
parseKeywords (token, TRUE);
|
|
}
|
|
} while (!endSubProgram);
|
|
}
|
|
else
|
|
{
|
|
makeVhdlTag (name, VHDLTAG_PROCEDURE);
|
|
do
|
|
{
|
|
readToken (token);
|
|
if (isKeyword (token, KEYWORD_END))
|
|
{
|
|
readToken (token);
|
|
endSubProgram = isKeywordOrIdent (token,
|
|
KEYWORD_PROCEDURE, name->string);
|
|
fileSkipToCharacter (';');
|
|
}
|
|
else
|
|
{
|
|
parseKeywords (token, TRUE);
|
|
}
|
|
} while (!endSubProgram);
|
|
}
|
|
}
|
|
deleteToken (name);
|
|
}
|
|
|
|
/* TODO */
|
|
/* records */
|
|
static void parseKeywords (tokenInfo * const token, boolean local)
|
|
{
|
|
switch (token->keyword)
|
|
{
|
|
case KEYWORD_END:
|
|
fileSkipToCharacter (';');
|
|
break;
|
|
case KEYWORD_CONSTANT:
|
|
parseConstant (local);
|
|
break;
|
|
case KEYWORD_TYPE:
|
|
parseTypes (token);
|
|
break;
|
|
case KEYWORD_SUBTYPE:
|
|
parseTypes (token);
|
|
break;
|
|
case KEYWORD_ENTITY:
|
|
parseModule (token);
|
|
break;
|
|
case KEYWORD_COMPONENT:
|
|
parseModule (token);
|
|
break;
|
|
case KEYWORD_FUNCTION:
|
|
parseSubProgram (token);
|
|
break;
|
|
case KEYWORD_PROCEDURE:
|
|
parseSubProgram (token);
|
|
break;
|
|
case KEYWORD_PACKAGE:
|
|
parsePackage (token);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void parseVhdlFile (tokenInfo * const token)
|
|
{
|
|
do
|
|
{
|
|
readToken (token);
|
|
parseKeywords (token, FALSE);
|
|
} while (!isKeyword (token, KEYWORD_END));
|
|
}
|
|
|
|
static void findVhdlTags (void)
|
|
{
|
|
tokenInfo *const token = newToken ();
|
|
exception_t exception = (exception_t) (setjmp (Exception));
|
|
|
|
while (exception == ExceptionNone)
|
|
parseVhdlFile (token);
|
|
|
|
deleteToken (token);
|
|
}
|
|
|
|
extern parserDefinition *VhdlParser (void)
|
|
{
|
|
static const char *const extensions[] = { "vhdl", "vhd", NULL };
|
|
parserDefinition *def = parserNew ("VHDL");
|
|
def->kinds = VhdlKinds;
|
|
def->kindCount = KIND_COUNT (VhdlKinds);
|
|
def->extensions = extensions;
|
|
def->parser = findVhdlTags;
|
|
def->initialize = initialize;
|
|
return def;
|
|
}
|
|
|
|
/* vi:set tabstop=4 shiftwidth=4 noet: */
|