[geany/geany] ddb05f: Use uctags verilog parser

Jiří Techet git-noreply at xxxxx
Mon Mar 14 19:10:07 UTC 2022


Branch:      refs/heads/master
Author:      Jiří Techet <techet at gmail.com>
Committer:   Jiří Techet <techet at gmail.com>
Date:        Mon, 14 Mar 2022 19:10:07 UTC
Commit:      ddb05fff2d9bc8391a16e49c329d75e697b674f7
             https://github.com/geany/geany/commit/ddb05fff2d9bc8391a16e49c329d75e697b674f7

Log Message:
-----------
Use uctags verilog parser

The new parser is token-based and appears to be significantly improved.


Modified Paths:
--------------
    ctags/Makefile.am
    ctags/parsers/geany_verilog.c
    ctags/parsers/verilog.c

Modified: ctags/Makefile.am
2 lines changed, 1 insertions(+), 1 deletions(-)
===================================================================
@@ -92,7 +92,7 @@ parsers = \
 	parsers/geany_tcl.c \
 	parsers/geany_tex.c \
 	parsers/txt2tags.c \
-	parsers/geany_verilog.c \
+	parsers/verilog.c \
 	parsers/geany_vhdl.c
 
 # skip cmd.c and mini-geany.c which define main()


Modified: ctags/parsers/geany_verilog.c
332 lines changed, 0 insertions(+), 332 deletions(-)
===================================================================
@@ -1,332 +0,0 @@
-/*
-*   Copyright (c) 2003, Darren Hiebert
-* 
-*   This source code is released for free distribution under the terms of the
-*   GNU General Public License version 2 or (at your option) any later version.
-* 
-*   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 FILES
- */
-#include "general.h"  /* must always come first */
-
-#include <string.h>
-#include <setjmp.h>
-
-#include "debug.h"
-#include "keyword.h"
-#include "parse.h"
-#include "read.h"
-#include "vstring.h"
-#include "geany_lcpp.h"
-#include "routines.h"
-#include "xtag.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;
-
-/*
- *   DATA DEFINITIONS
- */
-static int Ungetc;
-static int Lang_verilog;
-static jmp_buf Exception;
-
-static kindDefinition 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 keywordTable 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 = ARRAY_SIZE (VerilogKeywordTable);
-	Lang_verilog = language;
-	for (i = 0  ;  i < count  ;  ++i)
-	{
-		const keywordTable* const p = &VerilogKeywordTable [i];
-		addKeyword (p->name, language, (int) p->id);
-	}
-}
-
-static void vUngetc (int c)
-{
-	Assert (Ungetc == '\0');
-	Ungetc = c;
-}
-
-static int vGetc (void)
-{
-	int c;
-	if (Ungetc == '\0')
-		c = getcFromInputFile ();
-	else
-	{
-		c = Ungetc;
-		Ungetc = '\0';
-	}
-	if (c == '/')
-	{
-		int c2 = getcFromInputFile ();
-		if (c2 == EOF)
-			longjmp (Exception, (int) ExceptionEOF);
-		else if (c2 == '/')  /* strip comment until end-of-line */
-		{
-			do
-				c = getcFromInputFile ();
-			while (c != '\n'  &&  c != EOF);
-		}
-		else if (c2 == '*')  /* strip block comment */
-		{
-			c = lcppSkipOverCComment();
-		}
-		else
-		{
-			ungetcToInputFile (c2);
-		}
-	}
-	else if (c == '"')  /* strip string contents */
-	{
-		int c2;
-		do
-			c2 = getcFromInputFile ();
-		while (c2 != '"'  &&  c2 != EOF);
-		c = '@';
-	}
-	if (c == EOF)
-		longjmp (Exception, (int) ExceptionEOF);
-	return c;
-}
-
-static bool isIdentifierCharacter (const int c)
-{
-	return (bool)(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 bool readIdentifier (vString *const name, int c)
-{
-	vStringClear (name);
-	if (isIdentifierCharacter (c))
-	{
-		while (isIdentifierCharacter (c))
-		{
-			vStringPut (name, c);
-			c = vGetc ();
-		}
-		vUngetc (c);
-	}
-	return (bool)(name->length > 0);
-}
-
-static void tagNameList (const verilogKind kind, int c)
-{
-	vString *name = vStringNew ();
-	bool repeat;
-	Assert (isIdentifierCharacter (c));
-	do
-	{
-		repeat = false;
-		if (isIdentifierCharacter (c))
-		{
-			readIdentifier (name, c);
-			makeSimpleTag (name, 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 && vStringChar (name, 0) == '`')
-	{
-		/* Bug #961001: Verilog compiler directives are line-based. */
-		int c = skipWhite (vGetc ());
-		readIdentifier (name, c);
-		makeSimpleTag (name, 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 bool 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->kindTable  = VerilogKinds;
-	def->kindCount  = ARRAY_SIZE (VerilogKinds);
-	def->extensions = extensions;
-	def->parser     = findVerilogTags;
-	def->initialize = initialize;
-	return def;
-}


Modified: ctags/parsers/verilog.c
2024 lines changed, 2024 insertions(+), 0 deletions(-)
===================================================================
@@ -0,0 +1,2024 @@
+/*
+ *   Copyright (c) 2003, Darren Hiebert
+ *   Copyright (c) 2017, Vitor Antunes
+ *   Copyright (c) 2020, Hiroo Hayashi
+ *
+ *   This source code is released for free distribution under the terms of the
+ *   GNU General Public License version 2 or (at your option) any later version.
+ *
+ *   This module contains functions for generating tags for the Verilog or
+ *   SystemVerilog HDL (Hardware Description Language).
+ *
+ *   References:
+ *       IEEE Std 1800-2017, SystemVerilog Language Reference Manual
+ *          https://ieeexplore.ieee.org/document/8299595
+ *       SystemVerilog IEEE Std 1800-2012 Grammer
+ *          https://insights.sigasi.com/tech/systemverilog.ebnf/
+ *       Verilog Formal Syntax Specification
+ *          http://www.verilog.com/VerilogBNF.html
+ */
+
+/*
+ *   INCLUDE FILES
+ */
+#include "general.h"  /* must always come first */
+
+#include <string.h>
+
+#include "debug.h"
+#include "entry.h"
+#include "keyword.h"
+#include "options.h"
+#include "parse.h"
+#include "read.h"
+#include "routines.h"
+#include "xtag.h"
+#include "ptrarray.h"
+
+/*
+ *   MACROS
+ */
+#define NUMBER_LANGUAGES    2   /* Indicates number of defined indexes */
+#define IDX_SYSTEMVERILOG   0
+#define IDX_VERILOG         1
+
+/*
+ *   DATA DECLARATIONS
+ */
+
+/* A callback function searching a symbol from the cork symbol table assumes
+ * this kind definitions are shared in Verilog and SystemVerilog parsers.
+ * If you will separate the definitions for the parsers, you must revise the
+ * code related to the symbol table. */
+typedef enum {
+	/* parser private items */
+	K_IGNORE = -16,	/* Verilog/SystemVerilog keywords to be ignored */
+	K_DEFINE,
+	K_DIRECTIVE,
+	K_END,
+	K_END_DE,	/* End of Design Elements */
+	K_IDENTIFIER,
+	K_LOCALPARAM,
+	K_PARAMETER,
+	K_IMPORT,
+	K_WITH,
+
+	K_UNDEFINED = KEYWORD_NONE,
+	/* the followings items are also used as indices for VerilogKinds[] and SystemVerilogKinds[] */
+	K_CONSTANT= 0,
+	K_EVENT,
+	K_FUNCTION,
+	K_MODULE,
+	K_NET,
+	K_PORT,
+	K_REGISTER,
+	K_TASK,
+	K_BLOCK,
+	K_INSTANCE,
+	K_ASSERTION,
+	K_CLASS,
+	K_COVERGROUP,
+	K_ENUM,
+	K_INTERFACE,
+	K_MODPORT,
+	K_PACKAGE,
+	K_PROGRAM,
+	K_PROTOTYPE,
+	K_PROPERTY,
+	K_STRUCT,
+	K_TYPEDEF,
+	K_CHECKER,
+	K_CLOCKING,
+	K_SEQUENCE,
+	K_MEMBER,
+	K_IFCLASS,	/* interface class */
+	K_CONSTRAINT,
+	K_NETTYPE,
+} verilogKind;
+
+typedef struct {
+	const char *keyword;
+	verilogKind kind;
+	short isValid [NUMBER_LANGUAGES];
+} keywordAssoc;
+
+typedef struct sTokenInfo {
+	verilogKind         kind;
+	vString*            name;          /* the name of the token */
+	unsigned long       lineNumber;    /* line number where token was found */
+	MIOPos              filePosition;  /* file position where token was found */
+	struct sTokenInfo*  scope;         /* context of keyword */
+	int                 nestLevel;     /* Current nest level */
+	verilogKind         lastKind;      /* Kind of last found tag */
+	vString*            blockName;     /* Current block name */
+	vString*            inheritance;   /* Class inheritance */
+	bool                prototype;     /* Is only a prototype */
+	bool                classScope;    /* Context is local to the current sub-context */
+	bool				parameter;	   /* parameter which can be overridden */
+	bool				hasParamList;  /* module definition has a parameter port list */
+} tokenInfo;
+
+typedef enum {
+	F_PARAMETER,
+} verilogField;
+
+/*
+ *   DATA DEFINITIONS
+ */
+static int Ungetc;
+static int Lang_verilog;
+static int Lang_systemverilog;
+
+static kindDefinition 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",  "variable data types" },
+ { true, 't', "task",      "tasks" },
+ { true, 'b', "block",     "blocks (begin, fork)" },
+ { true, 'i', "instance",  "instances of module" },
+};
+
+static kindDefinition SystemVerilogKinds [] = {
+ { true, 'c', "constant",  "constants (define, parameter, specparam, enum values)" },
+ { 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",  "variable data types" },
+ { true, 't', "task",      "tasks" },
+ { true, 'b', "block",     "blocks (begin, fork)" },
+ { true, 'i', "instance",  "instances of module or interface" },
+ { true, 'A', "assert",    "assertions (assert, assume, cover, restrict)" },
+ { true, 'C', "class",     "classes" },
+ { true, 'V', "covergroup","covergroups" },
+ { true, 'E', "enum",      "enumerators" },
+ { true, 'I', "interface", "interfaces" },
+ { true, 'M', "modport",   "modports" },
+ { true, 'K', "package",   "packages" },
+ { true, 'P', "program",   "programs" },
+ { false,'Q', "prototype", "prototypes (extern, pure)" },
+ { true, 'R', "property",  "properties" },
+ { true, 'S', "struct",    "structs and unions" },
+ { true, 'T', "typedef",   "type declarations" },
+ { true, 'H', "checker",   "checkers" },
+ { true, 'L', "clocking",  "clocking" },
+ { true, 'q', "sequence",  "sequences" },
+ { true, 'w', "member",    "struct and union members" },
+ { true, 'l', "ifclass",   "interface class" },
+ { true, 'O', "constraint","constraints" },
+ { true, 'N', "nettype",   "nettype declarations" },
+};
+
+static const keywordAssoc KeywordTable [] = {
+	/*                 	             	  SystemVerilog */
+	/*                 	             	  |  Verilog    */
+	/* keyword         	keyword ID   	  |  |          */
+	{ "`define",       	K_DEFINE,   	{ 1, 1 } },
+	{ "begin",         	K_BLOCK,     	{ 1, 1 } },
+	{ "end",           	K_END,       	{ 1, 1 } },
+	{ "endfunction",   	K_END_DE,    	{ 1, 1 } },
+	{ "endmodule",     	K_END_DE,    	{ 1, 1 } },
+	{ "endtask",       	K_END_DE,    	{ 1, 1 } },
+	{ "event",         	K_EVENT,     	{ 1, 1 } },
+	{ "fork",          	K_BLOCK,     	{ 1, 1 } },
+	{ "function",      	K_FUNCTION,  	{ 1, 1 } },
+	{ "genvar",        	K_REGISTER,  	{ 1, 1 } },
+	{ "inout",         	K_PORT,      	{ 1, 1 } },
+	{ "input",         	K_PORT,      	{ 1, 1 } },
+	{ "integer",       	K_REGISTER,  	{ 1, 1 } },
+	{ "join",          	K_END,       	{ 1, 1 } },
+	{ "localparam",    	K_LOCALPARAM,  	{ 1, 1 } },
+	{ "module",        	K_MODULE,    	{ 1, 1 } },
+	{ "output",        	K_PORT,      	{ 1, 1 } },
+	{ "parameter",     	K_PARAMETER,  	{ 1, 1 } },
+	{ "real",          	K_REGISTER,  	{ 1, 1 } },
+	{ "realtime",      	K_REGISTER,  	{ 1, 1 } },
+	{ "reg",           	K_REGISTER,  	{ 1, 1 } },
+	{ "signed",        	K_IGNORE,    	{ 1, 1 } },
+	{ "specparam",     	K_CONSTANT,  	{ 1, 1 } },
+	{ "supply0",       	K_NET,       	{ 1, 1 } },
+	{ "supply1",       	K_NET,       	{ 1, 1 } },
+	{ "task",          	K_TASK,      	{ 1, 1 } },
+	{ "time",          	K_REGISTER,  	{ 1, 1 } },
+	{ "tri",           	K_NET,       	{ 1, 1 } },
+	{ "triand",        	K_NET,       	{ 1, 1 } },
+	{ "trior",         	K_NET,       	{ 1, 1 } },
+	{ "trireg",        	K_NET,       	{ 1, 1 } },
+	{ "tri0",          	K_NET,       	{ 1, 1 } },
+	{ "tri1",          	K_NET,       	{ 1, 1 } },
+	{ "uwire",         	K_NET,       	{ 1, 1 } },
+	{ "wand",          	K_NET,       	{ 1, 1 } },
+	{ "wire",          	K_NET,       	{ 1, 1 } },
+	{ "wor",           	K_NET,       	{ 1, 1 } },
+	{ "assert",        	K_ASSERTION, 	{ 1, 0 } },
+	{ "assume",        	K_ASSERTION, 	{ 1, 0 } },
+	{ "bit",           	K_REGISTER,  	{ 1, 0 } },
+	{ "byte",          	K_REGISTER,  	{ 1, 0 } },
+	{ "chandle",       	K_REGISTER,  	{ 1, 0 } },
+	{ "checker",       	K_CHECKER,  	{ 1, 0 } },
+	{ "class",         	K_CLASS,     	{ 1, 0 } },
+	{ "constraint",   	K_CONSTRAINT, 	{ 1, 0 } },
+	{ "cover",         	K_ASSERTION, 	{ 1, 0 } },
+	{ "clocking",       K_CLOCKING,     { 1, 0 } },
+	{ "covergroup",    	K_COVERGROUP,	{ 1, 0 } },
+	{ "endchecker",    	K_END_DE,    	{ 1, 0 } },
+	{ "endclass",      	K_END_DE,    	{ 1, 0 } },
+	{ "endclocking",    K_END_DE,     	{ 1, 0 } },
+	{ "endgroup",      	K_END_DE,    	{ 1, 0 } },
+	{ "endinterface",  	K_END_DE,    	{ 1, 0 } },
+	{ "endpackage",    	K_END_DE,    	{ 1, 0 } },
+	{ "endprogram",    	K_END_DE,    	{ 1, 0 } },
+	{ "endproperty",   	K_END_DE,    	{ 1, 0 } },
+	{ "endsequence",   	K_END_DE,    	{ 1, 0 } },
+	{ "enum",          	K_ENUM,      	{ 1, 0 } },
+	{ "extern",        	K_PROTOTYPE, 	{ 1, 0 } },
+	{ "import",        	K_IMPORT,	  	{ 1, 0 } },
+	{ "int",           	K_REGISTER,  	{ 1, 0 } },
+	{ "interconnect",  	K_NET,       	{ 1, 0 } },
+	{ "interface",     	K_INTERFACE, 	{ 1, 0 } },
+	{ "join_any",      	K_END,       	{ 1, 0 } },
+	{ "join_none",     	K_END,       	{ 1, 0 } },
+	{ "logic",         	K_REGISTER,  	{ 1, 0 } },
+	{ "longint",       	K_REGISTER,  	{ 1, 0 } },
+	{ "modport",       	K_MODPORT,   	{ 1, 0 } },
+	{ "package",       	K_PACKAGE,   	{ 1, 0 } },
+	{ "program",       	K_PROGRAM,   	{ 1, 0 } },
+	{ "property",      	K_PROPERTY,  	{ 1, 0 } },
+	{ "pure",          	K_PROTOTYPE, 	{ 1, 0 } },
+	{ "ref",           	K_PORT,      	{ 1, 0 } },
+	{ "restrict",      	K_ASSERTION, 	{ 1, 0 } },
+	{ "sequence",      	K_SEQUENCE,  	{ 1, 0 } },
+	{ "shortint",      	K_REGISTER,  	{ 1, 0 } },
+	{ "shortreal",     	K_REGISTER,  	{ 1, 0 } },
+	{ "string",        	K_REGISTER,  	{ 1, 0 } },
+	{ "struct",        	K_STRUCT,    	{ 1, 0 } },
+	{ "type",          	K_REGISTER,  	{ 1, 0 } },
+	{ "typedef",       	K_TYPEDEF,   	{ 1, 0 } },
+	{ "union",         	K_STRUCT,    	{ 1, 0 } },
+	{ "var",           	K_REGISTER,  	{ 1, 0 } },
+	{ "void",          	K_REGISTER,  	{ 1, 0 } },
+	{ "with",          	K_WITH,			{ 1, 0 } },
+	{ "nettype",       	K_NETTYPE,		{ 1, 0 } },
+//	{ "virtual",       	K_PROTOTYPE,	{ 1, 0 } },		// do not add for now
+};
+
+static tokenInfo *currentContext = NULL;
+static ptrArray *tagContents;
+static fieldDefinition *fieldTable = NULL;
+
+// IEEE Std 1364-2005 LRM, Appendix B "List of Keywords"
+const static struct keywordGroup verilogKeywords = {
+	.value = K_IGNORE,
+	.addingUnlessExisting = true,
+	.keywords = {
+		"always", "and", "assign", "automatic", "begin", "buf", "bufif0",
+		"bufif1", "case", "casex", "casez", "cell", "cmos", "config",
+		"deassign", "default", "defparam", "design", "disable", "edge",
+		"else", "end", "endcase", "endconfig", "endfunction", "endgenerate",
+		"endmodule", "endprimitive", "endspecify", "endtable", "endtask",
+		"event", "for", "force", "forever", "fork", "function", "generate",
+		"genvar", "highz0", "highz1", "if", "ifnone", "incdir", "include",
+		"initial", "inout", "input", "instance", "integer", "join", "large",
+		"liblist", "library", "localparam", "macromodule", "medium", "module",
+		"nand", "negedge", "nmos", "nor", "noshowcancelled", "not", "notif0",
+		"notif1", "or", "output", "parameter", "pmos", "posedge", "primitive",
+		"pull0", "pull1", "pulldown", "pullup", "pulsestyle_onevent",
+		"pulsestyle_ondetect", "rcmos", "real", "realtime", "reg", "release",
+		"repeat", "rnmos", "rpmos", "rtran", "rtranif0", "rtranif1",
+		"scalared", "showcancelled", "signed", "small", "specify",
+		"specparam", "strong0", "strong1", "supply0", "supply1", "table",
+		"task", "time", "tran", "tranif0", "tranif1", "tri", "tri0", "tri1",
+		"triand", "trior", "trireg", "unsigned1", "use", "uwire", "vectored",
+		"wait", "wand", "weak0", "weak1", "while", "wire", "wor", "xnor", "xor",
+		NULL
+	},
+};
+// IEEE Std 1800-2017 LRM, Annex B "Keywords"
+const static struct keywordGroup systemVerilogKeywords = {
+	.value = K_IGNORE,
+	.addingUnlessExisting = true,
+	.keywords = {
+		"accept_on", "alias", "always", "always_comb", "always_ff",
+		"always_latch", "and", "assert", "assign", "assume", "automatic",
+		"before", "begin", "bind", "bins", "binsof", "bit", "break", "buf",
+		"bufif0", "bufif1", "byte", "case", "casex", "casez", "cell",
+		"chandle", "checker", "class", "clocking", "cmos", "config", "const",
+		"constraint", "context", "continue", "cover", "covergroup",
+		"coverpoint", "cross", "deassign", "default", "defparam", "design",
+		"disable", "dist", "do", "edge", "else", "end", "endcase",
+		"endchecker", "endclass", "endclocking", "endconfig", "endfunction",
+		"endgenerate", "endgroup", "endinterface", "endmodule", "endpackage",
+		"endprimitive", "endprogram", "endproperty", "endspecify",
+		"endsequence", "endtable", "endtask", "enum", "event", "eventually",
+		"expect", "export", "extends", "extern", "final", "first_match",
+		"for", "force", "foreach", "forever", "fork", "forkjoin", "function",
+		"generate", "genvar", "global", "highz0", "highz1", "if", "iff",
+		"ifnone", "ignore_bins", "illegal_bins", "implements", "implies",
+		"import", "incdir", "include", "initial", "inout", "input", "inside",
+		"instance", "int", "integer", "interconnect", "interface",
+		"intersect", "join", "join_any", "join_none", "large", "let",
+		"liblist", "library", "local", "localparam", "logic", "longint",
+		"macromodule", "matches", "medium", "modport", "module", "nand",
+		"negedge", "nettype", "new", "nexttime", "nmos", "nor",
+		"noshowcancelled", "not", "notif0", "notif1", "null", "or", "output",
+		"package", "packed", "parameter", "pmos", "posedge", "primitive",
+		"priority", "program", "property", "protected", "pull0", "pull1",
+		"pulldown", "pullup", "pulsestyle_ondetect", "pulsestyle_onevent",
+		"pure", "rand", "randc", "randcase", "randsequence", "rcmos", "real",
+		"realtime", "ref", "reg", "reject_on", "release", "repeat",
+		"restrict", "return", "rnmos", "rpmos", "rtran", "rtranif0",
+		"rtranif1", "s_always", "s_eventually", "s_nexttime", "s_until",
+		"s_until_with", "scalared", "sequence", "shortint", "shortreal",
+		"showcancelled", "signed", "small", "soft", "solve", "specify",
+		"specparam", "static", "string", "strong", "strong0", "strong1",
+		"struct", "super", "supply0", "supply1", "sync_accept_on",
+		"sync_reject_on", "table", "tagged", "task", "this", "throughout",
+		"time", "timeprecision", "timeunit", "tran", "tranif0", "tranif1",
+		"tri", "tri0", "tri1", "triand", "trior", "trireg", "type", "typedef",
+		"union", "unique", "unique0", "unsigned", "until", "until_with",
+		"untyped", "use", "uwire", "var", "vectored", "virtual", "void",
+		"wait", "wait_order", "wand", "weak", "weak0", "weak1", "while",
+		"wildcard", "wire", "with", "within", "wor", "xnor", "xor",
+		NULL
+	},
+};
+
+// IEEE Std 1364-2005 LRM, "19. Compiler directives"
+const static struct keywordGroup verilogDirectives = {
+	.value = K_DIRECTIVE,
+	.addingUnlessExisting = true,
+	.keywords = {
+		"`begin_keywords", "`celldefine", "`default_nettype", "`define",
+		"`else", "`elsif", "`end_keywords", "`endcelldefine", "`endif",
+		"`ifdef", "`ifndef", "`include", "`line", "`nounconnected_drive",
+		"`pragma", "`resetall", "`timescale", "`unconnected_drive", "`undef",
+		NULL
+	},
+};
+
+// IEEE Std 1800-2017 LRM, "22. Compiler directives"
+const static struct keywordGroup systemVerilogDirectives = {
+	.value = K_DIRECTIVE,
+	.addingUnlessExisting = true,
+	.keywords = {
+		"`__LINE__", "`begin_keywords", "`celldefine", "`default_nettype",
+		"`define", "`else", "`elsif", "`end_keywords", "`endcelldefine",
+		"`endif", "`ifdef", "`ifndef", "`include", "`line",
+		"`nounconnected_drive", "`pragma", "`resetall", "`timescale",
+		"`unconnected_drive", "`undef", "`undefineall",
+		NULL
+	},
+};
+
+// .enabled field cannot be shared by two languages
+static fieldDefinition VerilogFields[] = {
+	{ .name = "parameter",
+	  .description = "parameter whose value can be overridden.",
+	  .enabled = false,
+	  .dataType = FIELDTYPE_BOOL },
+};
+
+static fieldDefinition SystemVerilogFields[] = {
+	{ .name = "parameter",
+	  .description = "parameter whose value can be overridden.",
+	  .enabled = false,
+	  .dataType = FIELDTYPE_BOOL },
+};
+
+/*
+ *   PROTOTYPE DEFINITIONS
+ */
+
+static bool isIdentifier (tokenInfo* token);
+static int processDefine (tokenInfo *const token, int c);
+static int processType (tokenInfo* token, int c, verilogKind* kind, bool* with);
+static int pushEnumNames (tokenInfo* token, int c);
+static int pushMembers (tokenInfo* token, int c);
+static int readWordToken (tokenInfo *const token, int c);
+static int readWordTokenNoSkip (tokenInfo *const token, int c);
+static int skipBlockName (tokenInfo *const token, int c);
+static int skipClockEvent (tokenInfo* token, int c);
+static int skipDelay (tokenInfo* token, int c);
+static int tagIdentifierList (tokenInfo *const token, int c, verilogKind kind, bool mayPortDecl);
+static int tagNameList (tokenInfo* token, int c, verilogKind kind);
+
+/*
+ *   FUNCTION DEFINITIONS
+ */
+
+static short isContainer (verilogKind kind)
+{
+	switch (kind)
+	{
+		case K_MODULE:
+		case K_TASK:
+		case K_FUNCTION:
+		case K_BLOCK:
+		case K_CHECKER:
+		case K_CLASS:
+		case K_CLOCKING:
+		case K_COVERGROUP:
+		case K_IFCLASS:
+		case K_INTERFACE:
+		case K_PACKAGE:
+		case K_PROGRAM:
+		case K_PROPERTY:
+		case K_SEQUENCE:
+		case K_TYPEDEF:
+		case K_NETTYPE:
+		case K_ENUM:
+		case K_STRUCT:
+			return true;
+		default:
+			return false;
+	}
+}
+
+static short isTempContext (tokenInfo const* token)
+{
+	switch (token->kind)
+	{
+		case K_TYPEDEF:
+		case K_NETTYPE:
+		case K_ENUM:
+		case K_STRUCT:
+			return true;
+		default:
+			return false;
+	}
+}
+
+static void clearToken (tokenInfo *token)
+{
+	token->kind = K_UNDEFINED;	// to be set by updateKind()
+	vStringClear (token->name);
+	token->lineNumber = getInputLineNumber ();
+	token->filePosition = getInputFilePosition ();
+	token->scope = NULL;
+	token->nestLevel = 0;
+	token->lastKind = K_UNDEFINED;
+	vStringClear (token->blockName);
+	vStringClear (token->inheritance);
+	token->prototype = false;
+	token->classScope = false;
+	token->parameter = false;
+	token->hasParamList = false;
+}
+
+static tokenInfo *newToken (void)
+{
+	tokenInfo *const token = xMalloc (1, tokenInfo);
+	token->name = vStringNew ();
+	token->blockName = vStringNew ();
+	token->inheritance = vStringNew ();
+	clearToken (token);
+	return token;
+}
+
+static tokenInfo *dupToken (tokenInfo *token)
+{
+	tokenInfo *dup = newToken ();
+	tokenInfo tmp = *dup;	// save vStrings, name, blockName, and inheritance
+	*dup = *token;
+	// revert vStrings allocated for dup
+	dup->name = tmp.name;
+	dup->blockName = tmp.blockName;
+	dup->inheritance = tmp.inheritance;
+	// copy contents of vStrings
+	vStringCopy (dup->name, token->name);
+	vStringCopy (dup->blockName, token->blockName);
+	vStringCopy (dup->inheritance, token->inheritance);
+	return dup;
+}
+
+static void deleteToken (tokenInfo * const token)
+{
+	if (token != NULL)
+	{
+		vStringDelete (token->name);
+		vStringDelete (token->blockName);
+		vStringDelete (token->inheritance);
+		eFree (token);
+	}
+}
+
+static tokenInfo *pushToken (tokenInfo * const token, tokenInfo * const tokenPush)
+{
+	tokenPush->scope = token;
+	return tokenPush;
+}
+
+static tokenInfo *popToken (tokenInfo * const token)
+{
+	tokenInfo *localToken;
+	if (token != NULL)
+	{
+		localToken = token->scope;
+		deleteToken (token);
+		return localToken;
+	}
+	return NULL;
+}
+
+static void pruneTokens (tokenInfo * token)
+{
+	while ((token = popToken (token)))
+		;
+}
+
+static void swapToken (tokenInfo *t0, tokenInfo *t1)
+{
+	tokenInfo tmp = *t0;
+	*t0 = *t1;
+	*t1 = tmp;
+}
+
+static const char *getNameForKind (const verilogKind kind)
+{
+	if (isInputLanguage (Lang_systemverilog))
+		return (SystemVerilogKinds[kind]).name;
+	else /* isInputLanguage (Lang_verilog) */
+		return (VerilogKinds[kind]).name;
+}
+
+static char kindEnabled (const verilogKind kind)
+{
+	if (isInputLanguage (Lang_systemverilog))
+		return SystemVerilogKinds[kind].enabled;
+	else /* isInputLanguage (Lang_verilog) */
+		return VerilogKinds[kind].enabled;
+}
+
+static void buildKeywordHash (const langType language, unsigned int idx)
+{
+	size_t i;
+	const size_t count = ARRAY_SIZE (KeywordTable);
+	for (i = 0  ;  i < count  ;  ++i)
+	{
+		const keywordAssoc *p = &KeywordTable [i];
+		if (p->isValid [idx])
+			addKeyword (p->keyword, language, (int) p->kind);
+	}
+}
+
+static void initializeVerilog (const langType language)
+{
+	Lang_verilog = language;
+	buildKeywordHash (language, IDX_VERILOG);
+	addKeywordGroup (&verilogKeywords, language);
+	addKeywordGroup (&verilogDirectives, language);
+	if (tagContents == NULL)
+		tagContents = ptrArrayNew ((ptrArrayDeleteFunc)deleteToken);
+
+}
+
+static void initializeSystemVerilog (const langType language)
+{
+	Lang_systemverilog = language;
+	buildKeywordHash (language, IDX_SYSTEMVERILOG);
+	addKeywordGroup (&systemVerilogKeywords, language);
+	addKeywordGroup (&systemVerilogDirectives, language);
+	if (tagContents == NULL)
+		tagContents = ptrArrayNew ((ptrArrayDeleteFunc)deleteToken);
+}
+
+static void vUngetc (int c)
+{
+	Assert (Ungetc == '\0');
+	Ungetc = c;
+}
+
+/* Mostly copied from cppSkipOverCComment() in cpreprocessor.c.
+ *
+ * cppSkipOverCComment() uses the internal ungetc buffer of
+ * CPreProcessor.  On the other hand, the Verilog parser uses
+ * getcFromInputFile() directly. getcFromInputFile() uses just
+ * another internal ungetc buffer. Using them mixed way will
+ * cause a trouble. */
+static int verilogSkipOverCComment (void)
+{
+	int c =  getcFromInputFile ();
+
+	while (c != EOF)
+	{
+		if (c != '*')
+			c = getcFromInputFile ();
+		else
+		{
+			const int next = getcFromInputFile ();
+
+			if (next != '/')
+				c = next;
+			else
+			{
+				c = SPACE;  /* replace comment with space */
+				break;
+			}
+		}
+	}
+	return c;
+}
+
+static int _vGetc (bool inSkipPastMatch)
+{
+	int c;
+	if (Ungetc == '\0')
+		c = getcFromInputFile ();
+	else
+	{
+		c = Ungetc;
+		Ungetc = '\0';
+	}
+	if (c == '/')
+	{
+		int c2 = getcFromInputFile ();
+		if (c2 == EOF)
+			return EOF;
+		else if (c2 == '/')  /* strip comment until end-of-line */
+		{
+			do
+				c = getcFromInputFile ();
+			while (c != '\n'  &&  c != EOF);
+		}
+		else if (c2 == '*')  /* strip block comment */
+			c = verilogSkipOverCComment ();
+		else
+			ungetcToInputFile (c2);
+	}
+	// replace a string with "@" only in skipPastMatch()
+	// because the string may contain parens, etc.
+	else if (inSkipPastMatch && c == '"')
+	{
+		int c2;
+		do
+			c2 = getcFromInputFile ();
+		while (c2 != '"'  &&  c2 != EOF);
+		c = '@';
+	}
+	return c;
+}
+
+static int vGetc (void)
+{
+	return _vGetc (false);
+}
+
+// Is the first charactor in an identifier? [a-zA-Z_`]
+static bool isWordToken (const int c)
+{
+	return (isalpha (c) || c == '_' || c == '`');
+}
+
+// Is a charactor in an identifier? [a-zA-Z0-9_`$]
+static bool isIdentifierCharacter (const int c)
+{
+	return (isalnum (c) || 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 (true);
+		if (c == begin)
+			++matchLevel;
+		else if (c == end)
+			--matchLevel;
+	}
+	while (matchLevel > 0 && c != EOF);
+	return skipWhite (vGetc ());
+}
+
+static int skipDimension (int c)
+{
+	while (c == '[' && c != EOF)
+		c = skipPastMatch ("[]");
+	return c;
+}
+
+static int skipToSemiColon (int c)
+{
+	while (c != ';' && c != EOF)
+		c = vGetc ();
+	return c;	// ';' or EOF
+}
+
+static int skipString (int c)
+{
+	if (c == '"')
+	{
+		do
+			c = vGetc ();
+		while (c != '"' && c != EOF);
+	}
+	c = skipWhite (vGetc ());
+	return c;
+}
+
+static int skipExpression (int c)
+{
+	while (c != ','  &&  c != ';' && c != ')' && c != '}' && c != ']' && c != EOF)
+	{
+		if (c == '(')
+			c = skipPastMatch ("()");
+		else if (c == '{')
+			c = skipPastMatch ("{}");
+		else if (c == '[')
+			c = skipPastMatch ("[]");
+		else if (c == '"')
+			c = skipString (c);
+		else
+			c = skipWhite (vGetc ());
+	}
+	return c;
+}
+
+// Skip to newline. The newline preceded by a backslash ( \ ) is ignored.
+// Should be used after readWordTokenNoSkip() for compiler directives
+static int skipToNewLine (int c)
+{
+	bool escape = false;
+	for ( ; (c != '\n' || escape) &&  c != EOF; c = vGetc ())
+		escape = (c == '\\');
+
+	return c;	// '\n' or EOF
+}
+
+static int skipMacro (int c, tokenInfo *token)
+{
+	tokenInfo *localToken = newToken ();	// don't update token outside
+	while (c == '`')	// to support back-to-back compiler directives
+	{
+		c = readWordTokenNoSkip (localToken, c);
+		/* Skip compiler directive other than `define */
+		if (localToken->kind == K_DIRECTIVE)
+		{
+			c = skipToNewLine (c);
+			c = skipWhite (c);
+		}
+		/* Skip `define */
+		else if (localToken->kind == K_DEFINE)
+		{
+			c = skipWhite (c);
+			c = processDefine (localToken, c);
+		}
+		/* return macro expansion */
+		else
+		{
+			swapToken (token, localToken);
+			c = skipWhite (c);
+			if (c == '(')
+				c = skipPastMatch ("()");
+			break;
+		}
+	}
+	deleteToken (localToken);
+	return c;
+}
+
+static void _updateKind (tokenInfo *const token)
+{
+	verilogKind kind = (verilogKind) lookupKeyword (vStringValue (token->name), getInputLanguage () );
+	token->kind = ((kind == K_UNDEFINED) && isIdentifier (token)) ? K_IDENTIFIER : kind;
+}
+
+/* read an identifier, keyword, number, compiler directive, or macro identifier */
+static int _readWordToken (tokenInfo *const token, int c, bool skip)
+{
+	Assert (isWordToken (c));
+
+	clearToken (token);
+	do
+	{
+		vStringPut (token->name, c);
+		c = vGetc ();
+	} while (isIdentifierCharacter (c));
+	_updateKind (token);
+
+	if (skip)
+		return skipWhite (c);
+	else
+		return c;
+}
+
+// read a word token starting with "c".
+// returns the first charactor of the next token.
+static int readWordToken (tokenInfo *const token, int c)
+{
+	return _readWordToken (token, c, true);
+}
+
+// read a word token starting with "c".
+// returns the next charactor of the token read.
+// for compiler directives.  Since they are line-based, skipWhite() cannot be used.
+static int readWordTokenNoSkip (tokenInfo *const token, int c)
+{
+	return _readWordToken (token, c, false);
+}
+
+/* check if an identifier:
+ *   simple_identifier ::= [ a-zA-Z_ ] { [ a-zA-Z0-9_$ ] } */
+static bool isIdentifier (tokenInfo* token)
+{
+	if (token->kind == K_UNDEFINED)
+	{
+		for (int i = 0; i < vStringLength (token->name); i++)
+		{
+			int c = vStringChar (token->name, i);
+			if (i == 0)
+			{
+				if (c == '`' || !isWordToken (c))
+					return false;
+			}
+			else
+			{
+				if (!isIdentifierCharacter (c))
+					return false;
+			}
+		}
+		return true;
+	}
+	else
+		return false;
+}
+
+static void createContext (verilogKind kind, vString* const name)
+{
+	tokenInfo *const scope = newToken ();
+	vStringCopy (scope->name, name);
+	scope->kind = kind;
+
+	if (scope)
+	{
+		vString *contextName = vStringNew ();
+
+		/* Determine full context name */
+		if (currentContext->kind != K_UNDEFINED)
+		{
+			vStringCopy (contextName, currentContext->name);
+			vStringPut (contextName, '.');
+		}
+		vStringCat (contextName, scope->name);
+		/* Create context */
+		currentContext = pushToken (currentContext, scope);
+		vStringCopy (currentContext->name, contextName);
+		vStringDelete (contextName);
+		verbose ("Created new context %s (kind %d)\n", vStringValue (currentContext->name), currentContext->kind);
+	}
+}
+
+static void dropContext ()
+{
+	verbose ("Dropping context %s\n", vStringValue (currentContext->name));
+	currentContext = popToken (currentContext);
+}
+
+/* Drop context, but only if an end token is found */
+static int dropEndContext (tokenInfo *const token, int c)
+{
+	verbose ("current context %s; context kind %0d; nest level %0d\n", vStringValue (currentContext->name), currentContext->kind, currentContext->nestLevel);
+	if ((currentContext->kind == K_COVERGROUP && strcmp (vStringValue (token->name), "endgroup") == 0)
+	    || (currentContext->kind == K_IFCLASS && strcmp (vStringValue (token->name), "endclass") == 0))
+	{
+		dropContext ();
+		c = skipBlockName (token ,c);
+	}
+	else if (currentContext->kind != K_UNDEFINED)
+	{
+		vString *endTokenName = vStringNewInit ("end");
+		vStringCatS (endTokenName, getNameForKind (currentContext->kind));
+		if (strcmp (vStringValue (token->name), vStringValue (endTokenName)) == 0)
+		{
+			dropContext ();
+			c = skipBlockName (token ,c);
+			if (currentContext->classScope)
+			{
+				verbose ("Dropping local context %s\n", vStringValue (currentContext->name));
+				currentContext = popToken (currentContext);
+			}
+		}
+		vStringDelete (endTokenName);
+	}
+	else
+		verbose ("Unexpected current context %s\n", vStringValue (currentContext->name));
+	return c;
+}
+
+
+static void createTag (tokenInfo *const token, verilogKind kind)
+{
+	tagEntryInfo tag;
+
+	if (kind == K_LOCALPARAM)
+		kind = K_CONSTANT;
+	else if (kind == K_PARAMETER)
+	{
+		kind = K_CONSTANT;
+		// See LRM 2017 6.20.1 Parameter declaration syntax
+		if (currentContext->kind != K_CLASS && currentContext->kind != K_PACKAGE && !currentContext->hasParamList)
+			token->parameter = true;
+	}
+	Assert (kind >= 0 && kind != K_UNDEFINED && kind != K_IDENTIFIER);
+	Assert (vStringLength (token->name) > 0);
+
+	/* check if a container before kind is modified by prototype */
+	/* BTW should we create a context for a prototype? */
+	bool container = isContainer (kind);
+
+	/* Determine if kind is prototype */
+	if (currentContext->prototype)
+		kind = K_PROTOTYPE;
+
+	/* Do nothing if tag kind is disabled */
+	if (! kindEnabled (kind))
+	{
+		verbose ("kind disabled\n");
+		return;
+	}
+
+	/* Create tag */
+	initTagEntry (&tag, vStringValue (token->name), kind);
+	tag.lineNumber = token->lineNumber;
+	tag.filePosition = token->filePosition;
+
+	verbose ("Adding tag %s (kind %d)", vStringValue (token->name), kind);
+	if (currentContext->kind != K_UNDEFINED)
+	{
+		verbose (" to context %s\n", vStringValue (currentContext->name));
+		currentContext->lastKind = kind;
+		tag.extensionFields.scopeKindIndex = currentContext->kind;
+		tag.extensionFields.scopeName = vStringValue (currentContext->name);
+	}
+	verbose ("\n");
+	if (vStringLength (token->inheritance) > 0)
+	{
+		tag.extensionFields.inheritance = vStringValue (token->inheritance);
+		verbose ("Class %s extends %s\n", vStringValue (token->name), tag.extensionFields.inheritance);
+	}
+
+	if (token->parameter)
+		attachParserField (&tag, false, fieldTable [F_PARAMETER].ftype, "");
+
+	makeTagEntry (&tag);
+
+	if (isXtagEnabled (XTAG_QUALIFIED_TAGS) && currentContext->kind != K_UNDEFINED)
+	{
+		vString *const scopedName = vStringNew ();
+
+		vStringCopy (scopedName, currentContext->name);
+		vStringPut (scopedName, '.');
+		vStringCat (scopedName, token->name);
+		tag.name = vStringValue (scopedName);
+
+		markTagExtraBit (&tag, XTAG_QUALIFIED_TAGS);
+		makeTagEntry (&tag);
+
+		vStringDelete (scopedName);
+	}
+
+	/* Push token as context if it is a container */
+	if (container)
+	{
+		createContext (kind, token->name);
+
+		/* Put found contents in context */
+		verbose ("Putting tagContents: %d element(s)\n",
+				 ptrArrayCount (tagContents));
+		for (unsigned int i = 0; i < ptrArrayCount (tagContents); i++)
+		{
+			tokenInfo *content = ptrArrayItem (tagContents, i);
+			createTag (content, content->kind);
+		}
+
+		/* Drop temporary contexts */
+		if (isTempContext (currentContext))
+			dropContext ();
+	}
+
+	/* Clear no longer required inheritance information */
+	vStringClear (token->inheritance);
+}
+
+static int skipBlockName (tokenInfo *const token, int c)
+{
+	if (c == ':')
+	{
+		c = skipWhite (vGetc ());
+		if (isWordToken (c))
+			c = readWordToken (token, c);
+	}
+	return c;
+}
+
+// begin, fork
+static int processBlock (tokenInfo *const token, int c)
+{
+	if (c == ':')	// tag an optional block identifier
+	{
+		c = skipWhite (vGetc ());
+		if (isWordToken (c))
+		{
+			c = readWordToken (token, c);
+			verbose ("Found block: %s\n", vStringValue (token->name));
+			createTag (token, K_BLOCK);
+			verbose ("Current context %s\n", vStringValue (currentContext->name));
+		}
+	}
+	currentContext->nestLevel++;	// increment after creating a context
+	return c;
+}
+
+// end, join, join_any, join_none
+static int processEnd (tokenInfo *const token, int c)
+{
+	if (currentContext->nestLevel > 0)	// for sanity check
+		currentContext->nestLevel--;
+	if (currentContext->kind == K_BLOCK && currentContext->nestLevel == 0)
+		dropContext ();
+
+	c = skipBlockName (token, c);
+	return c;
+}
+
+static int processPortList (tokenInfo *token, int c, bool mayPortDecl)
+{
+	if (c == '(')
+	{
+		c = skipWhite (vGetc ());	// skip '('
+		c = tagIdentifierList (token, c, K_PORT, mayPortDecl);
+		if (c == ')')	// sanity check
+			c = skipWhite (vGetc ());
+		else
+			verbose ("Unexpected input: %c\n", c);
+	}
+	return c;
+}
+
+static int skipParameterAssignment (int c)
+{
+	if (c == '#')
+	{
+		c = skipWhite (vGetc ());
+		if (c == '(')
+			c = skipPastMatch ("()");
+	}
+	return c;
+}
+
+// Functions are treated differently because they may also include the type of the return value.
+// Tasks are treated in the same way, although not having a return value.
+//
+// function [ lifetime ] function_data_type_or_implicit [ interface_identifier . | class_scope ] function_identifier [ ( [ tf_port_list ] ) ] ;
+// task     [ lifetime ] task_body_declaration          [ interface_identifier . | class_scope ] task_identifier     [ ( [ tf_port_list ] ) ] ;
+static int processFunction (tokenInfo *const token, int c)
+{
+	verilogKind kind = token->kind;	// K_FUNCTION or K_TASK
+
+	/* Search for function name
+	 * Last identifier found before a '(' or a ';' is the function name */
+	while (c != '(' && c != ';' && c != EOF)
+	{
+		if (isWordToken (c))
+			c = readWordToken (token, c);
+		else
+			c = skipWhite (vGetc ());
+		/* skip parameter assignment of a class type
+		 *    ex. function uvm_port_base #(IF) get_if(int index=0); */
+		c = skipParameterAssignment (c);
+
+		/* Identify class type prefixes and create respective context*/
+		if (isInputLanguage (Lang_systemverilog) && c == ':')
+		{
+			c = vGetc ();
+			if (c == ':')
+			{
+				verbose ("Found function declaration with class type %s\n", vStringValue (token->name));
+				createContext (K_CLASS, token->name);
+				currentContext->classScope = true;
+			}
+			else
+				vUngetc (c);
+		}
+	}
+	verbose ("Found function: %s\n", vStringValue (token->name));
+	createTag (token, kind);
+
+	/* Get port list from function */
+	c = skipWhite (c);
+	c = processPortList (token, c, false);
+	return c;
+}
+
+// ( enum | union ) [ enum_base_type ] { < enum_name_declaration > }  { [ ... ] }
+static int processEnum (tokenInfo *const token, int c)
+{
+	tokenInfo* enumToken = dupToken (token);	// save enum token
+
+	/* skip enum_base_type */
+	while (isWordToken (c))
+		c = readWordToken (token, c);
+	c = skipDimension (c);
+
+	/* Search enum elements */
+	c = pushEnumNames (token, c);
+
+	/* Skip bus width definition */
+	c = skipDimension (c);
+
+	/* Following identifiers are tag names */
+	verbose ("Find enum tags. Token %s kind %d\n", vStringValue (enumToken->name), enumToken->kind);
+	c = tagNameList (enumToken, c, enumToken->kind);
+	deleteToken (enumToken);
+
+	// Clean up the tag content list at the end of the declaration to support multiple variables
+	//   enum { ... } foo, bar;
+	ptrArrayClear (tagContents);
+	return c;
+}
+
+// [ struct | union [ tagged ] ] [ packed [ signed | unsigned ] ] { struct_union_member { struct_union_member } } { [ ... ] }
+static int processStruct (tokenInfo *const token, int c)
+{
+	verilogKind kind = token->kind;	// K_STRUCT, K_TYPEDEF, or K_NETTYPE
+
+	/* Skip packed, signed, and unsigned */
+	while (isWordToken (c))
+		c = readWordToken (token, c);
+
+	/* create a list of members */
+	c = pushMembers (token, c);
+
+	/* Skip packed_dimension */
+	c = skipDimension (c);
+
+	/* Following identifiers are tag names */
+	verbose ("Find struct|union tags. Token %s kind %d\n", vStringValue (token->name), token->kind);
+	c = tagNameList (token, c, kind);
+	ptrArrayClear (tagContents);
+	return c;
+}
+
+// data_declaration ::=
+//       [ const ] [ var ] [ static | automatic ] data_type_or_implicit list_of_variable_decl_assignments ;
+//     | typedef data_type type_identifier { [ ... ] } ;
+//     | typedef interface_instance_identifier [ ... ] . type_identifier type_identifier ; // interface based typedef
+//     | typedef [ enum | struct | union | class | interface class ] type_identifier ;
+//     | import < package_import_item > ;
+//     | nettype data_type net_type_identifier [ with [ class_type :: | package_identifier :: | $unit :: ] tf_identifier ] ;
+//     | nettype [ class_type :: | package_identifier :: | $unit :: ] net_type_identifier net_type_identifier ;
+static int processTypedef (tokenInfo *const token, int c)
+{
+	verilogKind kindSave = token->kind;	// K_TYPEDEF or K_NETTYPE
+	verilogKind kind = K_UNDEFINED;
+	bool not_used;
+	if (isWordToken (c))
+	{
+		c = readWordToken (token, c);
+		kind = token->kind;
+	}
+	// forward typedef (LRM 6.18) is tagged as prototype
+	//   (I don't know why...)
+	switch (kind)
+	{
+		case K_CLASS:
+		case K_INTERFACE:
+			currentContext->prototype = true;
+			break;
+		case K_ENUM:
+		case K_STRUCT:
+			if (isWordToken (c))
+			{
+				c = readWordToken (token, c);
+				if (token->kind == K_IDENTIFIER && c == ';')
+					currentContext->prototype = true;
+			}
+			break;
+		case K_IDENTIFIER:
+			// interface based typedef
+			c = skipDimension (c);
+			if (c == '.')
+			{
+				c = skipWhite (vGetc ());
+				if (isWordToken (c))
+					c = readWordToken (token, c);
+			}
+			if (c == ';')
+				currentContext->prototype = true;
+			break;
+		default:
+			; // do nothing
+	}
+	c = processType (token, c, &kind, &not_used);
+
+	createTag (token, kindSave);
+
+	ptrArrayClear (tagContents);
+	return c;
+}
+
+static int processParameterList (tokenInfo *token, int c)
+{
+	bool parameter = true;	// default "parameter"
+
+	if (c != '#')
+		return c;
+	c = skipWhite (vGetc ());
+
+	if (c != '(')
+		return c;
+	c = skipWhite (vGetc ());
+
+	while (c != ')' && c != EOF)
+	{
+		if (isWordToken (c))
+		{
+			c = readWordToken (token, c);
+			verbose ("Found parameter %s\n", vStringValue (token->name));
+			if (token->kind == K_IDENTIFIER)
+			{
+				if (c == ',' || c == ')' || c == '=')	// ignore user defined type
+				{
+					tokenInfo *param = dupToken (token);
+					param->kind = K_CONSTANT;
+					param->parameter = parameter;
+					ptrArrayAdd (tagContents, param);
+					if (c == '=')
+						c = skipExpression (vGetc ());
+					else if (c == ',')
+						c = skipWhite (vGetc ());
+					else	// ')'
+						break;
+				}
+			}
+			else if (token->kind == K_PARAMETER)
+				parameter = true;
+			else if (token->kind == K_LOCALPARAM)
+				parameter = false;
+		}
+		else
+			c = skipWhite (vGetc ());
+		// unpacked array is not allowed for a parameter
+	}
+	c = skipWhite (vGetc ());	// skip ')'
+	return c;
+}
+
+// [ virtual ] class [ static | automatic ] class_identifier [ parameter_port_list ]
+//     [ extends class_type [ ( list_of_arguments ) ] ] [ implements < interface_class_type > ] ;
+// interface class class_identifier [ parameter_port_list ] [ extends < interface_class_type > ] ;
+static int processClass (tokenInfo *const token, int c, verilogKind kind)
+{
+	tokenInfo *classToken;
+
+	/* Get identifiers */
+	while (isWordToken (c))
+	{
+		c = readWordToken (token, c);
+		// skip static or automatic
+		if (token->kind != K_IGNORE)
+			break;
+	}
+
+	if (token->kind != K_IDENTIFIER)
+	{
+		verbose ("Unexpected input: class name is expected.\n");
+		return c;
+	}
+
+	/* save token */
+	classToken = dupToken (token);
+
+	/* Find class parameters list */
+	c = processParameterList (token, c);
+
+	/* Search for inheritance information */
+	if (isWordToken (c))
+	{
+		c = readWordToken (token, c);
+		if (strcmp (vStringValue (token->name), "extends") == 0)
+		{
+			if (isWordToken (c))
+				c = readWordToken (token, c);
+			vStringCopy (classToken->inheritance, token->name);
+			verbose ("Inheritance %s\n", vStringValue (classToken->inheritance));
+		}
+	}
+	// process implements: FIXME
+
+	createTag (classToken, kind);
+	deleteToken (classToken);
+	ptrArrayClear (tagContents);
+	return c;
+}
+
+// constraint_declaration ::= [ static ] constraint constraint_identifier '{' { constraint_block_item } '}'
+// constraint_prototype ::= [ extern | pure ] [ static ] constraint constraint_identifier ;
+static int processConstraint (tokenInfo *const token, int c)
+{
+	verilogKind kind;
+	if (isWordToken (c))
+		c = readWordToken (token, c);
+	if (c == '{')
+	{
+		c = skipPastMatch ("{}");
+		kind = K_CONSTRAINT;
+	}
+	else
+		kind = K_PROTOTYPE;
+	createTag (token, kind);
+	return c;
+}
+
+static int processDefine (tokenInfo *const token, int c)
+{
+	/* Bug #961001: Verilog compiler directives are line-based. */
+	if (isWordToken (c))
+	{
+		c = readWordTokenNoSkip (token, c);
+		createTag (token, K_CONSTANT);
+	}
+	c = skipToNewLine (c);
+	c = skipWhite (c);
+	return c;
+}
+
+// immediate_assertion_statement ::=
+//     ( assert | asume | cover ) [ #0 | final ] '(' expression ')' block
+// concurrent_assertion_statement ::=
+//     ( assert | assume ) property ( property_spec ) action_block
+//   | expect ( property_spec ) action_block  # ignore : processed as same as "if"
+//   | cover property ( property_spec ) statement_or_null
+//   | cover sequence ( [clocking_event ] [ disable iff ( expression_or_dist ) ] sequence_expr ) statement_or_null
+//   | restrict property ( property_spec ) ;
+static int processAssertion (tokenInfo *const token, int c)
+{
+	if (vStringLength (currentContext->blockName) > 0)
+	{
+		vStringCopy (token->name, currentContext->blockName);
+		vStringClear (currentContext->blockName);	// clear block name not to be reused
+		createTag (token, K_ASSERTION);
+	}
+	// skip final | property | sequence
+	if (isWordToken (c))
+		c = readWordToken (token, c);
+	// skip #0
+	c = skipDelay (token, c);
+	// skip ( ... )
+	if (c == '(')
+		c = skipPastMatch ("()");
+	return c;
+}
+
+// non-ANSI type
+// ( module | interface | program ) [ static | automatic ] identifier { package_import_declaration } [ parameter_port_list ] ( port { , port } ) ;
+// ANSI type
+// ( module | interface | program ) [ static | automatic ] identifier { package_import_declaration } [ parameter_port_list ] [ ( [ < { (* ... *) } ansi_port_declaration > ] ) ] ;
+//
+// interface class class_identifier [ parameter_port_list ] [ extends < interface_class_type > ] ;
+static int processDesignElementL (tokenInfo *const token, int c)
+{
+	verilogKind kind = token->kind;
+
+	while (isWordToken (c))
+	{
+		c = readWordToken (token, c);
+		// interface class
+		if (token->kind == K_CLASS)
+			return processClass (token, c, K_IFCLASS);
+		// skip static or automatic
+		else if (token->kind != K_IGNORE)
+			break;
+	}
+	if (token->kind == K_IDENTIFIER)
+		createTag (token, kind);	// identifier
+
+	// skip package_import_declaration
+	while (isWordToken (c))
+	{
+		c = readWordToken (token, c);
+		if (token->kind == K_IMPORT)
+		{
+			c = skipToSemiColon (c);
+			c = skipWhite (vGetc ());	// skip semicolon
+		}
+		else
+		{
+			verbose ("Unexpected input\n");
+			return c;
+		}
+	}
+	if (c == '#')	// parameter_port_list
+	{
+		c = processParameterList (token, c);
+
+		/* Put found parameters in context */
+		verbose ("Putting parameters: %d element(s)\n",
+				ptrArrayCount (tagContents));
+		for (unsigned int i = 0; i < ptrArrayCount (tagContents); i++)
+		{
+			tokenInfo *content = ptrArrayItem (tagContents, i);
+			createTag (content, K_CONSTANT);
+		}
+		ptrArrayClear (tagContents);
+		// disable parameter property on parameter declaration statement
+		currentContext->hasParamList = true;
+	}
+	// Process ANSI/non-ANSI port list in main loop
+	c = processPortList (token, c, true);
+	return c;
+}
+
+// ( checker | property | sequence ) identifier [ ( [ port_list ] ) ] ;
+// covergroup identifier [ ( [ port_list ] ) ] [ coverage_event ] ;
+//   coverage_event ::= clocking_event | with function sample ( ... ) | @@( ... )
+// package identifier ;
+// modport < identifier ( < ports_declaration > ) > ;
+// [ default | global ] clocking [ identifier ] ( @ identifier | @ ( event_expression ) )
+static int processDesignElementS (tokenInfo *const token, int c)
+{
+	verilogKind kind = token->kind;
+
+	if (isWordToken (c))
+		c = readWordToken (token, c);
+	else
+		return c;
+
+	createTag (token, kind);	// identifier
+
+	/* Get port list if required */
+	if (c == '(')	// port_list
+	{
+		if (kind == K_MODPORT)
+			c = skipPastMatch ("()");	// ignore port list
+		else
+			c = processPortList (token, c, false);
+	}
+	// skip clocking_event for clocking block or coverage_event for covergroup
+	// "with function sample ()" is processed in the main loop
+	if (c == '@')
+		c = skipClockEvent (token, c);
+	return c;
+}
+
+static int skipDelay (tokenInfo* token, int c)
+{
+	if (c == '#')
+	{
+		c = skipWhite (vGetc ());
+		if (c == '(')
+			c = skipPastMatch ("()");
+		else if (c == '#')
+			// a dirty hack for "x ##delay1 y[*min:max];"
+			c = skipToSemiColon (vGetc ());
+		else	// time literals
+		{
+			while (isIdentifierCharacter (c) || c == '.')
+				c = vGetc ();
+			c = skipWhite (c);
+		}
+	}
+	return c;
+}
+
+static int skipClockEvent (tokenInfo* token, int c)
+{
+	if (c == '@')
+	{
+		c = skipWhite (vGetc ());
+		// for @@ ( ... ) : coverage_event
+		if (c == '@')
+			c = skipWhite (vGetc ());
+		if (c == '(')
+			c = skipPastMatch ("()");
+		else if (isWordToken (c))
+			c = readWordToken (token, c);
+	}
+	return c;
+}
+
+static int pushEnumNames (tokenInfo* token, int c)
+{
+	if (c == '{')
+	{
+		c = skipWhite (vGetc ());
+		while (c != '}' && c != EOF)
+		{
+			if (!isWordToken (c))
+			{
+				verbose ("Unexpected input: %c\n", c);
+				return c;
+			}
+			c = readWordToken (token, c);
+
+			token->kind = K_CONSTANT;
+			ptrArrayAdd (tagContents, dupToken (token));
+			verbose ("Pushed enum element \"%s\"\n", vStringValue (token->name));
+
+			/* Skip element ranges */
+			/* TODO Implement element ranges */
+			c = skipDimension (c);
+
+			/* Skip value assignments */
+			if (c == '=')
+				c = skipExpression (vGetc ());
+
+			/* Skip comma */
+			if (c == ',')
+				c = skipWhite (vGetc ());
+		}
+		c = skipWhite (vGetc ());	// skip '}'
+	}
+	return c;
+}
+
+// create a list of struct/union members
+static int pushMembers (tokenInfo* token, int c)
+{
+	if (c == '{')
+	{
+		c = skipWhite (vGetc ());
+		while (c != '}' && c != EOF)
+		{
+			verilogKind kind = K_UNDEFINED;	// set kind of context for processType()
+			bool not_used;
+			if (!isWordToken (c))
+			{
+				verbose ("Unexpected input: %c\n", c);
+				return c;
+			}
+			c = readWordToken (token, c);
+
+			c = processType (token, c, &kind, &not_used);
+			while (true)
+			{
+				token->kind = K_MEMBER;
+				ptrArrayAdd (tagContents, dupToken (token));
+				verbose ("Pushed struct/union member \"%s\"\n", vStringValue (token->name));
+
+				/* Skip unpacked dimensions */
+				c = skipDimension (c);
+
+				/* Skip value assignments */
+				if (c == '=')
+					c = skipExpression (vGetc ());
+
+				if (c != ',' || c == EOF)
+					break;		// should be ';'
+
+				c = skipWhite (vGetc ());	// skip ','
+				if (isWordToken (c))
+					c = readWordToken (token, c);
+				else
+				{
+					verbose ("Unexpected input.\n");
+					break;
+				}
+			}
+			/* Skip semicolon */
+			if (c == ';')
+				c = skipWhite (vGetc ());
+			/* End of enum elements list */
+		}
+		c = skipWhite (vGetc ());	// skip '}'
+	}
+	return c;
+}
+
+// input
+//   kind: kind of context
+// output
+//   kind: kind of type
+//   token: identifier token (unless K_IDENTIFIER nor K_UNDEFINED)
+static int processType (tokenInfo* token, int c, verilogKind* kind, bool* with)
+{
+	verilogKind actualKind = K_UNDEFINED;
+	tokenInfo *tokenSaved;
+	*with = false;
+	do
+	{
+		c = skipDimension (c);
+		c = skipDelay (token, c);	// class parameter #(...)
+		if (c == '{')	// skip enum, struct, or union member
+		{
+			if (*kind == K_ENUM)
+				c = pushEnumNames (token, c);
+			else if (*kind == K_STRUCT)
+				c = pushMembers (token, c);
+			else	// for a nested structure
+				c = skipPastMatch ("{}");
+		}
+		c = skipDimension (c);
+		c = skipMacro (c, token);
+
+		// break on ',', ';', ')', '}', or other unexpected charactors
+		if (!isWordToken (c))
+			break;
+
+		tokenSaved = dupToken (token);
+		c = readWordToken (token, c);
+		// break on "with"
+		if (token->kind == K_WITH)
+		{
+ 			swapToken (token, tokenSaved);
+			deleteToken (tokenSaved);
+			*with = true;	// inform to caller
+			break;
+		}
+		deleteToken (tokenSaved);
+
+		// fix kind of user defined type
+		if (*kind == K_IDENTIFIER)
+		{
+			if (token->kind == K_NET)
+				actualKind = K_NET;
+			else if (token->kind == K_REGISTER)
+				actualKind = K_REGISTER;
+			else if (token->kind == K_PORT)
+				actualKind = K_PORT;
+			else if (token->kind == K_IDENTIFIER)
+			{	// identifier of a user defined type
+				*kind = K_REGISTER;	// FIXME: consider kind of the user defined type
+				break;
+			}
+			else
+			{
+				verbose ("Unexpected input\n");	// FIXME: x dist {}, with
+				break;
+			}
+		}
+	} while (c != '`' && c != EOF);	// break on compiler directive
+
+	// skip unpacked dimension (or packed dimension after type-words)
+	c = skipDimension (skipWhite (c));
+
+	if (*kind == K_UNDEFINED && *kind != K_PORT)
+		*kind = actualKind;
+	return c;
+}
+
+// class_type ::=
+//       ps_class_identifier [ # ( ... ) ] { :: class_identifier [ # ( ... ) ] }
+// "interface_identifier ." is also handled
+static int skipClassType (tokenInfo* token, int c)
+{
+	while (c == '#' || c == ':' || c == '.')
+	{
+		if (c == '#')
+		{
+			c = skipWhite (vGetc ());
+			// a dirty hack for "x ##delay1 y[*min:max];"
+			if (c == '#')
+				return skipToSemiColon (vGetc ());
+			c = skipPastMatch ("()");
+		}
+		else if (c == ':')
+		{
+			c = skipWhite (vGetc ());
+			if (c != ':')
+			{
+				verbose ("Unexpected input.\n");
+				vUngetc (c);
+				return ':';
+			}
+			c = skipWhite (vGetc ());
+			if (isWordToken (c))
+				c = readWordToken (token, c);
+		}
+		else	// c == '.' : interface_identifier .
+		{
+			c = skipWhite (vGetc ());
+			if (isWordToken (c))
+				c = readWordToken (token, c);
+		}
+	}
+	return c;
+}
+
+// Tag a list of identifiers
+// data_type :: =
+//   ...
+//   | virtual [ interface ] identifier [ # ( [ ... ] ) ]  [ . identifier ]
+//   | [ class_type :: | identifier :: | $unit :: ] identifier { [ ... ] }
+//   | [ identifier :: | $unit :: ] identifier [ # ( ... ) ] { :: identifier [ # ( ... ) ] }
+//   | ...
+//
+//   mayPortDecl: may be a ANSI port declaration.  true for module, interface, or program.
+static int tagIdentifierList (tokenInfo *const token, int c, verilogKind kind, bool mayPortDecl)
+{
+	bool first_port = true;
+	bool enableTag = true;
+	verilogKind localKind;
+	bool not_used;
+
+	while (c != ')' && c != EOF)	// skip empty port, "()"
+	{
+		// skip attribute_instance: (* ... *)
+		if (c == '(')
+			c = skipPastMatch ("()");
+
+		// skip port direction, "virtual", or "interface"
+		while (isWordToken (c))
+		{
+			c = readWordToken (token, c);
+			if (token->kind == K_PORT || token->kind == K_IGNORE || token->kind == K_INTERFACE)
+				mayPortDecl = false;	// now never be a non-ANSI port
+			else
+				break;
+		}
+		if (token->kind == K_IDENTIFIER)
+			c = skipClassType (token, c);
+		c = skipMacro (c, token);	// `ifdef, `else, `endif, etc. (between identifiers)
+
+		if (isWordToken (c))
+		{
+			c = readWordToken (token, c);
+			if (token->kind == K_IDENTIFIER)
+			{
+				mayPortDecl = false;
+				c = skipClassType (token, c);
+			}
+		}
+		// aoid tagging enum and struct items
+		localKind = token->kind == K_ENUM || token->kind == K_STRUCT ? K_PORT : token->kind;
+		c = processType (token, c, &localKind, &not_used);
+
+		// LRM 23.2.2.3 Rules for determining port kind, data type, and direction
+		// If the direction, port kind, and data type are all omitted for
+		// the first port in the port list, ... non-ANSI style, ...
+		if (mayPortDecl && first_port)
+		{
+			first_port = false;
+			if (localKind == K_IDENTIFIER)
+				enableTag = false;	// don't tag for non-ANSI port
+		}
+		if (enableTag && token->kind == K_IDENTIFIER)
+			createTag (token, kind);
+
+		if (c == '=')
+			c = skipExpression (vGetc ());
+
+		c = skipMacro (c, token);	// `ifdef, `else, `endif, etc. (before comma)
+		if (c != ',' || c == EOF)
+			break;
+		c = skipWhite (vGetc ());	// skip ','
+		c = skipMacro (c, token);	// `ifdef, `else, `endif, etc. (after comma)
+	}
+	return c;
+}
+
+static int tagNameList (tokenInfo* token, int c, verilogKind kind)
+{
+	c = skipClassType (token, c);
+	if (c == ':' || c == ';')	// ## (cycle delay) or unexpected input
+		return c;
+
+	// skip drive|charge strength or type_reference, dimensions, and delay for net
+	if (c == '(')
+		c = skipPastMatch ("()");
+	c = skipDimension (c);
+	if (c == '.')
+		return c;	// foo[...].bar = ..;
+	c = skipDelay (token, c);
+
+	while (c != EOF)
+	{
+		bool with = false;
+		c = processType (token, c, &kind, &with);	// update token and kind
+
+		if (c == '=' || c == ',' || c == ';' || c == ')' || c == '`' || with)
+		{
+			// ignore an empty token or procedual assignment: foo = bar;
+			if (kind != K_UNDEFINED && kind != K_IDENTIFIER && token->kind != K_UNDEFINED)
+				createTag (token, kind);
+			if (c == '=')
+				c = skipExpression (c);
+		}
+		else if (c == '(' || c == '[')	// should be instance
+		{
+			c = skipDimension (c); // name_of_instance {unpacked_dimension}
+			c = skipPastMatch ("()"); // list_of_port_connections
+
+			// if without the next "if" clause, get a instance named: `add_t from the following example
+			// var `add_t(foo) = '0;
+			if (c == ';' || c == ',')
+			{
+				verbose ("find instance: %s with kind %s\n", vStringValue (token->name), getNameForKind (K_INSTANCE));
+				createTag (token, K_INSTANCE);
+			}
+		}
+		c = skipMacro (c, token);	// `ifdef, `else, `endif, etc. (before comma)
+		if (c != ',' || c == EOF)
+			break;
+		c = skipWhite (vGetc ());	// skip ','
+		c = skipMacro (c, token);	// `ifdef, `else, `endif, etc. (after comma)
+	}
+	return c;
+}
+
+static int findTag (tokenInfo *const token, int c)
+{
+	verbose ("Checking token %s of kind %d\n", vStringValue (token->name), token->kind);
+
+	switch (token->kind)
+	{
+		case K_CONSTANT:
+		case K_EVENT:
+		case K_LOCALPARAM:
+		case K_NET:
+		case K_PARAMETER:
+		case K_PORT:
+		case K_REGISTER:
+			if (token->kind == K_PORT && currentContext->kind == K_CLOCKING)
+				c = skipToSemiColon (c); // clocking items are not port definitions
+			else
+				c = tagNameList (token, c, token->kind);
+			break;
+		case K_IDENTIFIER:
+			{
+				if (c == '[')	// for a case label foo[x]:
+					c = skipPastMatch ("[]");
+
+				if (c == ':')
+					; /* label */
+				else if (c == ',' || c == '{')	// "foo, ..." or "coverpoint foo { ... }"
+					c = skipWhite (vGetc ());
+				else if (c == '(')	// task, function, or method call
+					c = skipPastMatch ("()");
+				else if (c == '=')	// assignment
+					c = skipExpression (skipWhite (vGetc ()));
+				else
+					c = tagNameList (token, c, token->kind); /* user defined type */
+			}
+			break;
+		case K_CLASS:
+			c = processClass (token, c, K_CLASS);
+			break;
+		case K_TYPEDEF:
+		case K_NETTYPE:
+			c = processTypedef (token, c);
+			break;
+		case K_ENUM:
+			c = processEnum (token, c);
+			break;
+		case K_STRUCT:
+			c = processStruct (token, c);
+			break;
+		case K_PROTOTYPE:
+		case K_IMPORT:
+		case K_WITH:
+			currentContext->prototype = true;
+			break;
+
+		case K_INTERFACE:
+		case K_MODULE:
+		case K_PROGRAM:
+			c = processDesignElementL (token, c);
+			break;
+		case K_CHECKER:
+		case K_CLOCKING:
+		case K_COVERGROUP:
+		case K_MODPORT:
+		case K_PACKAGE:
+		case K_PROPERTY:
+		case K_SEQUENCE:
+			c = processDesignElementS (token, c);
+			break;
+		case K_END_DE:
+			c = dropEndContext (token, c);
+			break;
+		case K_BLOCK:
+			c = processBlock (token, c);
+			break;
+		case K_END:
+			c = processEnd (token, c);
+			break;
+		case K_FUNCTION:
+		case K_TASK:
+			c = processFunction (token, c);
+			break;
+		case K_ASSERTION:
+			c = processAssertion (token, c);
+			break;
+		case K_CONSTRAINT:
+			c = processConstraint (token, c);
+			break;
+
+		case K_DEFINE:
+			c = processDefine (token, c);
+			break;
+
+		case K_IGNORE:
+			break;
+		default:
+			verbose ("Unexpected kind->token %d\n", token->kind);
+	}
+	return c;
+}
+
+static void findVerilogTags (void)
+{
+	tokenInfo *const token = newToken ();
+	int c = skipWhite (vGetc ());
+	currentContext = newToken ();
+	fieldTable = isInputLanguage (Lang_verilog) ? VerilogFields : SystemVerilogFields;
+	ptrArrayClear (tagContents);
+
+	while (c != EOF)
+	{
+		switch (c)
+		{
+			case ':':
+				/* Store current block name whenever a : is found
+				 * This is used later by any tag type that requires this information */
+				vStringCopy (currentContext->blockName, token->name);
+				c = skipWhite (vGetc ());
+				break;
+			case ';':
+				/* Drop context on prototypes because they don't have an
+				 * end statement */
+				if (currentContext->scope && currentContext->scope->prototype)
+					dropContext ();
+
+				/* Prototypes end at the end of statement */
+				currentContext->prototype = false;
+				c = skipWhite (vGetc ());
+				break;
+			case '(':	// ignore locally declared variables in a for-loop (LRM 12.7.1)
+				c = skipPastMatch ("()");;
+				break;
+			case '{':
+				c = skipPastMatch ("{}");;
+				break;
+			case '#':
+				c = skipDelay (token, c);
+				break;
+			case '@':
+				c = skipClockEvent (token, c);
+				break;
+			case '"':
+				c = skipString (c);
+				break;
+			default :
+				if (isWordToken (c))
+				{
+					c = readWordTokenNoSkip (token, c);
+					if (token->kind == K_DIRECTIVE)
+					{
+						// Skip compiler directives which are line-based.
+						c = skipToNewLine (c);
+						c = skipWhite (c);
+					}
+					else if (token->kind != K_UNDEFINED)
+						c = findTag (token, skipWhite (c));
+				}
+				else
+					c = skipWhite (vGetc ());
+		}
+	}
+	deleteToken (token);
+	pruneTokens (currentContext);
+	currentContext = NULL;
+}
+
+extern parserDefinition* VerilogParser (void)
+{
+	static const char *const extensions [] = { "v", NULL };
+	parserDefinition* def = parserNew ("Verilog");
+	def->kindTable  = VerilogKinds;
+	def->kindCount  = ARRAY_SIZE (VerilogKinds);
+	def->fieldTable = VerilogFields;
+	def->fieldCount = ARRAY_SIZE (VerilogFields);
+	def->extensions = extensions;
+	def->parser     = findVerilogTags;
+	def->initialize = initializeVerilog;
+	return def;
+}
+
+extern parserDefinition* SystemVerilogParser (void)
+{
+	static const char *const extensions [] = { "sv", "svh", "svi", NULL };
+	parserDefinition* def = parserNew ("SystemVerilog");
+	def->kindTable  = SystemVerilogKinds;
+	def->kindCount  = ARRAY_SIZE (SystemVerilogKinds);
+	def->fieldTable = SystemVerilogFields;
+	def->fieldCount = ARRAY_SIZE (SystemVerilogFields);
+	def->extensions = extensions;
+	def->parser     = findVerilogTags;
+	def->initialize = initializeSystemVerilog;
+	return def;
+}



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


More information about the Commits mailing list