/*
 *	 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 "selectors.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

#ifndef DEBUG
#define VERBOSE(...) do { \
		verbose("%s:%ld:%s:%d:Internal Error:", getInputFileName(), getInputLineNumber(), __FILE__, __LINE__); \
		verbose(__VA_ARGS__); \
	} while (0)
#else
#define VERBOSE(...) do { \
		fprintf(stderr, "%s:%ld:%s:%d:Internal Error:", getInputFileName(), getInputLineNumber(), __FILE__, __LINE__); \
		fprintf(stderr, __VA_ARGS__); \
	} while (0)
#endif

/*
 *	 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_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_DEFINE,
	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,
	K_VIRTUAL,
} verilogKind;

typedef enum {
	R_MODULE_DECL,
} verilogModuleRole;

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 */
	bool				virtual;		/* has virtual */
} tokenInfo;

typedef enum {
	F_PARAMETER,
} verilogField;

/*
 *	 DATA DEFINITIONS
 */
static int Ungetc;
static int Lang_verilog;
static int Lang_systemverilog;

static roleDefinition VerilogModuleRoles [] = {
 { true, "decl", "declaring instances" },
};

static roleDefinition SystemVerilogModuleRoles [] = {
 { true, "decl", "declaring instances" },
};

static kindDefinition VerilogKinds [] = {
 { true, 'c', "constant",	"constants (parameter, specparam)" },
 { true, 'd', "define",		"text macros" },
 { true, 'e', "event",		"events" },
 { true, 'f', "function",	"functions" },
 { true, 'm', "module",		"modules",
   .referenceOnly = false, ATTACH_ROLES(VerilogModuleRoles) },
 { 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 (parameter, specparam, enum values)" },
 { true, 'd', "define",		"text macros" },
 { true, 'e', "event",		"events" },
 { true, 'f', "function",	"functions" },
 { true, 'm', "module",		"modules",
   .referenceOnly = false, ATTACH_ROLES(SystemVerilogModuleRoles) },
 { 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_VIRTUAL,		{ 1, 0 } },
};

static tokenInfo *currentContext = NULL;
static ptrArray *tagContents;
static fieldDefinition *fieldTable = NULL;

// IEEE Std 1364-2005 LRM, Appendix B "List of Keywords"
static const 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"
static const 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"
static const 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"
static const 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 tagIdsInPort (tokenInfo *const token, int c, verilogKind kind, bool mayPortDecl);
static int tagIdsInDataDecl (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;
	token->virtual = 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 = ' ';	/* 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 == '$');
}

// check if double colon.
static bool isDoubleColon (int c)
{
	if (c != ':')
		return false;
	c = vGetc ();
	if (c == ':') {
		return true;
	} else {
		vUngetc (c);
		return false;
	}
}

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)
{
	int prev = EOF; // The previous char of 'c' never be a EOF.
	while (!((prev != '\\') && (c == '\n')) && (c != EOF)) {
		prev = c;
		// Getc() does not work for a comment in multi-line macro
		c = getcFromInputFile ();
	}
	Assert(c == '\n' || c == EOF);
	return c;
}

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 if (localToken->kind == K_IDENTIFIER)
		{
			swapToken (token, localToken);
			c = skipWhite (c);
			if (c == '(')
				c = skipPastMatch ("()");
			break;
		}
		else
		{
			VERBOSE ("Unexpected input: localToken->kind %d\n", localToken->kind);
			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 which 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 = (unsigned char) vStringChar (token->name, i);
			if (i == 0)
			{
				// treat a text-macro as an identifier (#3712)
				if (!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 (void)
{
	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 createTagFull (tokenInfo *const token, verilogKind kind, int role, tokenInfo *const typeref)
{
	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 */
	if (role == ROLE_DEFINITION_INDEX)
		initTagEntry (&tag, vStringValue (token->name), kind);
	else
		initRefTagEntry (&tag, vStringValue (token->name), kind, role);
	updateTagLine (&tag, token->lineNumber, 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 (typeref)
	{
		tag.extensionFields.typeRef [0] = getNameForKind (typeref->kind);
		tag.extensionFields.typeRef [1] = vStringValue (typeref->name);
	}

	if (token->parameter)
		attachParserField (&tag, fieldTable [F_PARAMETER].ftype, "");

	makeTagEntry (&tag);

	if (isXtagEnabled (XTAG_QUALIFIED_TAGS) && currentContext->kind != K_UNDEFINED
		&& role == ROLE_DEFINITION_INDEX)
	{
		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 && role == ROLE_DEFINITION_INDEX)
	{
		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);
			createTagFull (content, content->kind, ROLE_DEFINITION_INDEX, NULL);
		}

		/* Drop temporary contexts */
		if (isTempContext (currentContext))
			dropContext ();
	}

	/* Clear no longer required inheritance information */
	vStringClear (token->inheritance);
}

static void createTagWithTypeRef (tokenInfo *const token, verilogKind kind, tokenInfo *const typeref)
{
	createTagFull (token, kind, ROLE_DEFINITION_INDEX, typeref);
}

static void createTag (tokenInfo *const token, verilogKind kind)
{
	createTagWithTypeRef (token, kind, NULL);
}

static void createRefTag (tokenInfo *const token, verilogKind kind, int role)
{
	createTagFull (token, kind, role, NULL);
}

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 = tagIdsInPort (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) && isDoubleColon(c))
		{
			verbose ("Found function declaration with class type %s\n", vStringValue (token->name));
			createContext (K_CLASS, token->name);
			currentContext->classScope = true;
		}
	}
	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 = tagIdsInDataDecl (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 = tagIdsInDataDecl (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_DEFINE);
	}
	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;
}

// data_declaration ::=
//	 ...
//	 import < package_identifier :: identifier | package_identifier :: * > ;
// dpi_import_export ::=
//	 import ( "DPI-C" | "DPI" ) [ context | pure ] [ c_identifier = ] function data_type_or_void function_identifier [ ( [ tf_port_list ] ) ] ;
// | import ( "DPI-C" | "DPI" ) [ context ]        [ c_identifier = ] task task_identifier [ ( [ tf_port_list ] ) ] ;
// | export ( "DPI-C" | "DPI" ) [ c_identifier = ] function function_identifier ;
// | export ( "DPI-C" | "DPI" ) [ c_identifier = ] task     task_identifier ;
static int processImport (tokenInfo *const token, int c)
{
	if (c == '"') {	// dpi_import: we don't care about export.
		currentContext->prototype = true;
	} else {
		c = skipToSemiColon (c);
		c = skipWhite (vGetc ());	// skip semicolon
	}
	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 == ':')
		{
			if (!isDoubleColon(c))
			{
				VERBOSE ("Unexpected input.\n");
				return c;
			}
			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 in a port list
// 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 tagIdsInPort (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;
}

// Tag a list of identifiers in a data declaration
static int tagIdsInDataDecl (tokenInfo* token, int c, verilogKind kind)
{
	if (token->kind != K_NET)
		c = skipClassType (token, c);
	if (c == ';')
		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);	// ## (cycle delay)

	tokenInfo *tokenSaved = dupToken(token); // maybe a module_identifier
	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 an 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 == ',')
			{
				tokenSaved->kind = K_MODULE;	// for typeRef field
				verbose ("find instance: %s with kind %s\n", vStringValue (token->name), getNameForKind (K_INSTANCE));
				createTagWithTypeRef (token, K_INSTANCE, tokenSaved);
				createRefTag (tokenSaved, K_MODULE, R_MODULE_DECL);
			}
		}
		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)
	}
	deleteToken (tokenSaved);
	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 = tagIdsInDataDecl (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 == '.' || c == '{')	// "foo, ...", "foo.bar,...", 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 = tagIdsInDataDecl (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_IMPORT:
			c = processImport (token, c);
			break;
		case K_PROTOTYPE:
		case K_WITH:
			currentContext->prototype = true;
			break;

		case K_INTERFACE:
			// a virtual interface variable
			if (currentContext->virtual)
				break;
			// fallthrough
		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_VIRTUAL:
			currentContext->virtual = true;
			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 */
				if (!isDoubleColon(c))
					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))
				{
					// NoSkip for compiler directives
					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)
							 || (token->kind == K_IDENTIFIER && vStringChar(token->name, 0) == '`'))
						// ignore an undefined token and text-macro identifier, i.e.`foo(bar) (#3712)
						break;
					else
						// call findTag() after skipping whitespaces
						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");
	static selectLanguage selectors[] = { selectVOrVerilogByKeywords, NULL };
	def->versionCurrent = 1;
	def->versionAge = 1;
	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;
	def->selectLanguage  = selectors;
	return def;
}

extern parserDefinition* SystemVerilogParser (void)
{
	static const char *const extensions [] = { "sv", "svh", "svi", NULL };
	parserDefinition* def = parserNew ("SystemVerilog");
	def->versionCurrent = 1;
	def->versionAge = 1;
	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;
}
