294 lines
6.9 KiB
C
294 lines
6.9 KiB
C
/*
|
|
* $Id: verilog.c 753 2010-02-27 17:53:32Z elliotth $
|
|
*
|
|
* Copyright (c) 2003, Darren Hiebert
|
|
*
|
|
* 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 the Verilog HDL
|
|
* (Hardware Description Language).
|
|
*
|
|
* Language definition documents:
|
|
* http://www.eg.bucknell.edu/~cs320/verilog/verilog-manual.html
|
|
* http://www.sutherland-hdl.com/on-line_ref_guide/vlog_ref_top.html
|
|
* http://www.verilog.com/VerilogBNF.html
|
|
* http://eesun.free.fr/DOC/VERILOG/verilog_manual1.html
|
|
*/
|
|
#include "third_party/ctags/general.h"
|
|
/* must always come first */
|
|
#include "third_party/ctags/debug.h"
|
|
#include "third_party/ctags/get.h"
|
|
#include "third_party/ctags/keyword.h"
|
|
#include "third_party/ctags/parse.h"
|
|
#include "third_party/ctags/read.h"
|
|
#include "third_party/ctags/vstring.h"
|
|
|
|
/*
|
|
* DATA DECLARATIONS
|
|
*/
|
|
typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
|
|
|
|
typedef enum {
|
|
K_UNDEFINED = -1,
|
|
K_CONSTANT,
|
|
K_EVENT,
|
|
K_FUNCTION,
|
|
K_MODULE,
|
|
K_NET,
|
|
K_PORT,
|
|
K_REGISTER,
|
|
K_TASK
|
|
} verilogKind;
|
|
|
|
typedef struct {
|
|
const char *keyword;
|
|
verilogKind kind;
|
|
} keywordAssoc;
|
|
|
|
/*
|
|
* DATA DEFINITIONS
|
|
*/
|
|
static int Ungetc;
|
|
static int Lang_verilog;
|
|
static jmp_buf Exception;
|
|
|
|
static kindOption VerilogKinds[] = {
|
|
{TRUE, 'c', "constant", "constants (define, parameter, specparam)"},
|
|
{TRUE, 'e', "event", "events"},
|
|
{TRUE, 'f', "function", "functions"},
|
|
{TRUE, 'm', "module", "modules"},
|
|
{TRUE, 'n', "net", "net data types"},
|
|
{TRUE, 'p', "port", "ports"},
|
|
{TRUE, 'r', "register", "register data types"},
|
|
{TRUE, 't', "task", "tasks"},
|
|
};
|
|
|
|
static keywordAssoc VerilogKeywordTable[] = {
|
|
{"`define", K_CONSTANT},
|
|
{"event", K_EVENT},
|
|
{"function", K_FUNCTION},
|
|
{"inout", K_PORT},
|
|
{"input", K_PORT},
|
|
{"integer", K_REGISTER},
|
|
{"module", K_MODULE},
|
|
{"output", K_PORT},
|
|
{"parameter", K_CONSTANT},
|
|
{"real", K_REGISTER},
|
|
{"realtime", K_REGISTER},
|
|
{"reg", K_REGISTER},
|
|
{"specparam", K_CONSTANT},
|
|
{"supply0", K_NET},
|
|
{"supply1", K_NET},
|
|
{"task", K_TASK},
|
|
{"time", K_REGISTER},
|
|
{"tri0", K_NET},
|
|
{"tri1", K_NET},
|
|
{"triand", K_NET},
|
|
{"tri", K_NET},
|
|
{"trior", K_NET},
|
|
{"trireg", K_NET},
|
|
{"wand", K_NET},
|
|
{"wire", K_NET},
|
|
{"wor", K_NET},
|
|
};
|
|
|
|
/*
|
|
* FUNCTION DEFINITIONS
|
|
*/
|
|
|
|
static void initialize(const langType language) {
|
|
size_t i;
|
|
const size_t count =
|
|
sizeof(VerilogKeywordTable) / sizeof(VerilogKeywordTable[0]);
|
|
Lang_verilog = language;
|
|
for (i = 0; i < count; ++i) {
|
|
const keywordAssoc *const p = &VerilogKeywordTable[i];
|
|
addKeyword(p->keyword, language, (int)p->kind);
|
|
}
|
|
}
|
|
|
|
static void vUngetc(int c) {
|
|
Assert(Ungetc == '\0');
|
|
Ungetc = c;
|
|
}
|
|
|
|
static int vGetc(void) {
|
|
int c;
|
|
if (Ungetc == '\0')
|
|
c = fileGetc();
|
|
else {
|
|
c = Ungetc;
|
|
Ungetc = '\0';
|
|
}
|
|
if (c == '/') {
|
|
int c2 = fileGetc();
|
|
if (c2 == EOF)
|
|
longjmp(Exception, (int)ExceptionEOF);
|
|
else if (c2 == '/') /* strip comment until end-of-line */
|
|
{
|
|
do
|
|
c = fileGetc();
|
|
while (c != '\n' && c != EOF);
|
|
} else if (c2 == '*') /* strip block comment */
|
|
{
|
|
c = skipOverCComment();
|
|
} else {
|
|
fileUngetc(c2);
|
|
}
|
|
} else if (c == '"') /* strip string contents */
|
|
{
|
|
int c2;
|
|
do
|
|
c2 = fileGetc();
|
|
while (c2 != '"' && c2 != EOF);
|
|
c = '@';
|
|
}
|
|
if (c == EOF) longjmp(Exception, (int)ExceptionEOF);
|
|
return c;
|
|
}
|
|
|
|
static boolean isIdentifierCharacter(const int c) {
|
|
return (boolean)(isalnum(c) || c == '_' || c == '`');
|
|
}
|
|
|
|
static int skipWhite(int c) {
|
|
while (isspace(c)) c = vGetc();
|
|
return c;
|
|
}
|
|
|
|
static int skipPastMatch(const char *const pair) {
|
|
const int begin = pair[0], end = pair[1];
|
|
int matchLevel = 1;
|
|
int c;
|
|
do {
|
|
c = vGetc();
|
|
if (c == begin)
|
|
++matchLevel;
|
|
else if (c == end)
|
|
--matchLevel;
|
|
} while (matchLevel > 0);
|
|
return vGetc();
|
|
}
|
|
|
|
static boolean readIdentifier(vString *const name, int c) {
|
|
vStringClear(name);
|
|
if (isIdentifierCharacter(c)) {
|
|
while (isIdentifierCharacter(c)) {
|
|
vStringPut(name, c);
|
|
c = vGetc();
|
|
}
|
|
vUngetc(c);
|
|
vStringTerminate(name);
|
|
}
|
|
return (boolean)(name->length > 0);
|
|
}
|
|
|
|
static void tagNameList(const verilogKind kind, int c) {
|
|
vString *name = vStringNew();
|
|
boolean repeat;
|
|
Assert(isIdentifierCharacter(c));
|
|
do {
|
|
repeat = FALSE;
|
|
if (isIdentifierCharacter(c)) {
|
|
readIdentifier(name, c);
|
|
makeSimpleTag(name, VerilogKinds, kind);
|
|
} else
|
|
break;
|
|
c = skipWhite(vGetc());
|
|
if (c == '[') c = skipPastMatch("[]");
|
|
c = skipWhite(c);
|
|
if (c == '=') {
|
|
c = skipWhite(vGetc());
|
|
if (c == '{')
|
|
skipPastMatch("{}");
|
|
else {
|
|
do
|
|
c = vGetc();
|
|
while (c != ',' && c != ';');
|
|
}
|
|
}
|
|
if (c == ',') {
|
|
c = skipWhite(vGetc());
|
|
repeat = TRUE;
|
|
} else
|
|
repeat = FALSE;
|
|
} while (repeat);
|
|
vStringDelete(name);
|
|
vUngetc(c);
|
|
}
|
|
|
|
static void findTag(vString *const name) {
|
|
const verilogKind kind =
|
|
(verilogKind)lookupKeyword(vStringValue(name), Lang_verilog);
|
|
if (kind == K_CONSTANT && vStringItem(name, 0) == '`') {
|
|
/* Bug #961001: Verilog compiler directives are line-based. */
|
|
int c = skipWhite(vGetc());
|
|
readIdentifier(name, c);
|
|
makeSimpleTag(name, VerilogKinds, kind);
|
|
/* Skip the rest of the line. */
|
|
do {
|
|
c = vGetc();
|
|
} while (c != '\n');
|
|
vUngetc(c);
|
|
} else if (kind != K_UNDEFINED) {
|
|
int c = skipWhite(vGetc());
|
|
|
|
/* Many keywords can have bit width.
|
|
* reg [3:0] net_name;
|
|
* inout [(`DBUSWIDTH-1):0] databus;
|
|
*/
|
|
if (c == '(') c = skipPastMatch("()");
|
|
c = skipWhite(c);
|
|
if (c == '[') c = skipPastMatch("[]");
|
|
c = skipWhite(c);
|
|
if (c == '#') {
|
|
c = vGetc();
|
|
if (c == '(') c = skipPastMatch("()");
|
|
}
|
|
c = skipWhite(c);
|
|
if (isIdentifierCharacter(c)) tagNameList(kind, c);
|
|
}
|
|
}
|
|
|
|
static void findVerilogTags(void) {
|
|
vString *const name = vStringNew();
|
|
volatile boolean newStatement = TRUE;
|
|
volatile int c = '\0';
|
|
exception_t exception = (exception_t)setjmp(Exception);
|
|
|
|
if (exception == ExceptionNone)
|
|
while (c != EOF) {
|
|
c = vGetc();
|
|
switch (c) {
|
|
case ';':
|
|
case '\n':
|
|
newStatement = TRUE;
|
|
break;
|
|
|
|
case ' ':
|
|
case '\t':
|
|
break;
|
|
|
|
default:
|
|
if (newStatement && readIdentifier(name, c)) findTag(name);
|
|
newStatement = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
vStringDelete(name);
|
|
}
|
|
|
|
extern parserDefinition *VerilogParser(void) {
|
|
static const char *const extensions[] = {"v", NULL};
|
|
parserDefinition *def = parserNew("Verilog");
|
|
def->kinds = VerilogKinds;
|
|
def->kindCount = KIND_COUNT(VerilogKinds);
|
|
def->extensions = extensions;
|
|
def->parser = findVerilogTags;
|
|
def->initialize = initialize;
|
|
return def;
|
|
}
|
|
|
|
/* vi:set tabstop=4 shiftwidth=4: */
|