1 /*
2 *   Copyright (c) 2008, Nicolas Vincent
3 *
4 *   This source code is released for free distribution under the terms of the
5 *   GNU General Public License version 2 or (at your option) any later version.
6 *
7 *   This module contains functions for generating tags for VHDL files.
8 *
9 *   References:
10 *     https://rti.etf.bg.ac.rs/rti/ri5rvl/tutorial/TUTORIAL/IEEE/HTML/1076_TOC.HTM
11 *     https://tams.informatik.uni-hamburg.de/vhdl/tools/grammar/vhdl93-bnf.html
12 *     http://www.vhdl.renerta.com/mobile/index.html
13 *     https://www.hdlworks.com/hdl_corner/vhdl_ref/
14 *     https://www.ics.uci.edu/~jmoorkan/vhdlref/Synario%20VHDL%20Manual.pdf
15 *     http://atlas.physics.arizona.edu/~kjohns/downloads/vhdl/VHDL-xilinx-help.pdf
16 *     http://www.csit-sun.pub.ro/resources/xilinx/synvhdl.pdf
17 *     https://edg.uchicago.edu/~tang/VHDLref.pdf
18 */
19 
20 /*
21  *   INCLUDE FILES
22  */
23 #include "general.h"	/* must always come first */
24 
25 #include <ctype.h>	/* to define isalpha () */
26 #include <string.h>
27 
28 #include "debug.h"
29 #include "entry.h"
30 #include "keyword.h"
31 #include "parse.h"
32 #include "read.h"
33 #include "routines.h"
34 #include "vstring.h"
35 #include "trace.h"
36 
37 /*
38  *   MACROS
39  */
40 #define isType(token,t)     (bool) ((token)->type == (t))
41 #define isKeyword(token,k)  (bool) ((token)->keyword == (k))
42 #define isIdentChar1(c) (isalpha (c) || (c) == '_')
43 #define isIdentChar(c) (isalpha (c) || isdigit (c) || (c) == '_')
44 
45 /*
46  *   DATA DECLARATIONS
47  */
48 
49 /*
50  * Used to specify type of keyword.
51  */
52 enum eKeywordId {
53 	KEYWORD_ABS,
54 	KEYWORD_ACCESS,
55 	KEYWORD_AFTER,
56 	KEYWORD_ALIAS,
57 	KEYWORD_ALL,
58 	KEYWORD_AND,
59 	KEYWORD_ARCHITECTURE,
60 	KEYWORD_ARRAY,
61 	KEYWORD_ASSERT,
62 	KEYWORD_ATTRIBUTE,
63 	KEYWORD_BEGIN,
64 	KEYWORD_BLOCK,
65 	KEYWORD_BODY,
66 	KEYWORD_BUFFER,
67 	KEYWORD_BUS,
68 	KEYWORD_CASE,
69 	KEYWORD_COMPONENT,
70 	KEYWORD_CONFIGURATION,
71 	KEYWORD_CONSTANT,
72 	KEYWORD_DISCONNECT,
73 	KEYWORD_DOWNTO,
74 	KEYWORD_ELSE,
75 	KEYWORD_ELSIF,
76 	KEYWORD_END,
77 	KEYWORD_ENTITY,
78 	KEYWORD_EXIT,
79 	KEYWORD_FILE,
80 	KEYWORD_FOR,
81 	KEYWORD_FUNCTION,
82 	KEYWORD_GENERATE,
83 	KEYWORD_GENERIC,
84 	KEYWORD_GROUP,
85 	KEYWORD_GUARDED,
86 	KEYWORD_IF,
87 	KEYWORD_IMPURE,
88 	KEYWORD_IN,
89 	KEYWORD_INERTIAL,
90 	KEYWORD_INOUT,
91 	KEYWORD_IS,
92 	KEYWORD_LABEL,
93 	KEYWORD_LIBRARY,
94 	KEYWORD_LINKAGE,
95 	KEYWORD_LITERAL,
96 	KEYWORD_LOOP,
97 	KEYWORD_MAP,
98 	KEYWORD_MOD,
99 	KEYWORD_NAND,
100 	KEYWORD_NEW,
101 	KEYWORD_NEXT,
102 	KEYWORD_NOR,
103 	KEYWORD_NOT,
104 	KEYWORD_NULL,
105 	KEYWORD_OF,
106 	KEYWORD_ON,
107 	KEYWORD_OPEN,
108 	KEYWORD_OR,
109 	KEYWORD_OTHERS,
110 	KEYWORD_OUT,
111 	KEYWORD_PACKAGE,
112 	KEYWORD_PORT,
113 	KEYWORD_POSTPONED,
114 	KEYWORD_PROCEDURE,
115 	KEYWORD_PROCESS,
116 	KEYWORD_PURE,
117 	KEYWORD_RANGE,
118 	KEYWORD_RECORD,
119 	KEYWORD_REGISTER,
120 	KEYWORD_REJECT,
121 	KEYWORD_RETURN,
122 	KEYWORD_ROL,
123 	KEYWORD_ROR,
124 	KEYWORD_SELECT,
125 	KEYWORD_SEVERITY,
126 	KEYWORD_SIGNAL,
127 	KEYWORD_SHARED,
128 	KEYWORD_SLA,
129 	KEYWORD_SLI,
130 	KEYWORD_SRA,
131 	KEYWORD_SRL,
132 	KEYWORD_SUBTYPE,
133 	KEYWORD_THEN,
134 	KEYWORD_TO,
135 	KEYWORD_TRANSPORT,
136 	KEYWORD_TYPE,
137 	KEYWORD_UNAFFECTED,
138 	KEYWORD_UNITS,
139 	KEYWORD_UNTIL,
140 	KEYWORD_USE,
141 	KEYWORD_VARIABLE,
142 	KEYWORD_WAIT,
143 	KEYWORD_WHEN,
144 	KEYWORD_WHILE,
145 	KEYWORD_WITH,
146 	KEYWORD_XNOR,
147 	KEYWORD_XOR
148 };
149 typedef int keywordId; /* to allow KEYWORD_NONE */
150 
151 typedef enum eTokenType {
152 	TOKEN_NONE,		/* none */
153 	TOKEN_EOF,		/* end-of-file */
154 	TOKEN_OPEN_PAREN,	/* ( */
155 	TOKEN_CLOSE_PAREN,	/* ) */
156 	TOKEN_COMMA,		/* the comma character */
157 	TOKEN_IDENTIFIER,
158 	TOKEN_KEYWORD,
159 	TOKEN_PERIOD,		/* . */
160 	TOKEN_OPERATOR,
161 	TOKEN_SEMICOLON,	/* the semicolon character */
162 	TOKEN_COLON,		/* : */
163 	TOKEN_STRING
164 } tokenType;
165 
166 typedef struct sTokenInfo {
167 	tokenType type;
168 	keywordId keyword;
169 	vString *string;		/* the name of the token */
170 	unsigned long lineNumber;	/* line number of tag */
171 	MIOPos filePosition;		/* file position of line containing name */
172 } tokenInfo;
173 
174 /*
175  *   DATA DEFINITIONS
176  */
177 static int Lang_vhdl;
178 
179 typedef enum {
180 	VHDL_ENTITY_DESIGNED,
181 } vhdlEntityRole;
182 
183 static roleDefinition VhdlEntityRoles [] = {
184 	{ true, "desigend",
185 	  "designed by an architecture" },
186 };
187 
188 /* Used to index into the VhdlKinds table. */
189 typedef enum {
190 	VHDLTAG_UNDEFINED = -1,
191 	VHDLTAG_CONSTANT,
192 	VHDLTAG_TYPE,
193 	VHDLTAG_SUBTYPE,
194 	VHDLTAG_RECORD,
195 	VHDLTAG_ENTITY,
196 	VHDLTAG_COMPONENT,
197 	VHDLTAG_PROTOTYPE,
198 	VHDLTAG_FUNCTION,
199 	VHDLTAG_PROCEDURE,
200 	VHDLTAG_PACKAGE,
201 	VHDLTAG_LOCAL,
202 	VHDLTAG_ARCHITECTURE,
203 	VHDLTAG_PORT,
204 	VHDLTAG_GENERIC,
205 	VHDLTAG_SIGNAL,
206 	VHDLTAG_PROCESS,
207 	VHDLTAG_VARIABLE,
208 	VHDLTAG_ALIAS,
209 } vhdlKind;
210 
211 static kindDefinition VhdlKinds[] = {
212 	{true, 'c', "constant", "constant declarations"},
213 	{true, 't', "type", "type definitions"},
214 	{true, 'T', "subtype", "subtype definitions"},
215 	{true, 'r', "record", "record names"},
216 	{true, 'e', "entity", "entity declarations",
217 	 .referenceOnly = false, ATTACH_ROLES(VhdlEntityRoles)},
218 	{false, 'C', "component", "component declarations"},
219 	{false, 'd', "prototype", "prototypes"},
220 	{true, 'f', "function", "function prototypes and declarations"},
221 	{true, 'p', "procedure", "procedure prototypes and declarations"},
222 	{true, 'P', "package", "package definitions"},
223 	{false, 'l', "local", "local definitions"},
224 	{true, 'a', "architecture", "architectures"},
225 	{true, 'q', "port", "port declarations"},
226 	{true, 'g', "generic", "generic declarations"},
227 	{true , 's', "signal", "signal declarations"},
228 	{true, 'Q',  "process", "processes"},
229 	{true, 'v',  "variable", "variables"},
230 	{true, 'A',  "alias", "aliases"},
231 };
232 
233 static const keywordTable VhdlKeywordTable[] = {
234 	{"abs", KEYWORD_ABS},
235 	{"access", KEYWORD_ACCESS},
236 	{"after", KEYWORD_AFTER},
237 	{"alias", KEYWORD_ALIAS},
238 	{"all", KEYWORD_ALL},
239 	{"and", KEYWORD_AND},
240 	{"architecture", KEYWORD_ARCHITECTURE},
241 	{"array", KEYWORD_ARRAY},
242 	{"assert", KEYWORD_ASSERT},
243 	{"attribute", KEYWORD_ATTRIBUTE},
244 	{"begin", KEYWORD_BEGIN},
245 	{"block", KEYWORD_BLOCK},
246 	{"body", KEYWORD_BODY},
247 	{"buffer", KEYWORD_BUFFER},
248 	{"bus", KEYWORD_BUS},
249 	{"case", KEYWORD_CASE},
250 	{"component", KEYWORD_COMPONENT},
251 	{"configuration", KEYWORD_CONFIGURATION},
252 	{"constant", KEYWORD_CONSTANT},
253 	{"disconnect", KEYWORD_DISCONNECT},
254 	{"downto", KEYWORD_DOWNTO},
255 	{"else", KEYWORD_ELSE},
256 	{"elsif", KEYWORD_ELSIF},
257 	{"end", KEYWORD_END},
258 	{"entity", KEYWORD_ENTITY},
259 	{"exit", KEYWORD_EXIT},
260 	{"file", KEYWORD_FILE},
261 	{"for", KEYWORD_FOR},
262 	{"function", KEYWORD_FUNCTION},
263 	{"generate", KEYWORD_GENERATE},
264 	{"generic", KEYWORD_GENERIC},
265 	{"group", KEYWORD_GROUP},
266 	{"guarded", KEYWORD_GUARDED},
267 	{"if", KEYWORD_IF},
268 	{"impure", KEYWORD_IMPURE},
269 	{"in", KEYWORD_IN},
270 	{"inertial", KEYWORD_INERTIAL},
271 	{"inout", KEYWORD_INOUT},
272 	{"is", KEYWORD_IS},
273 	{"label", KEYWORD_LABEL},
274 	{"library", KEYWORD_LIBRARY},
275 	{"linkage", KEYWORD_LINKAGE},
276 	{"literal", KEYWORD_LITERAL},
277 	{"loop", KEYWORD_LOOP},
278 	{"map", KEYWORD_MAP},
279 	{"mod", KEYWORD_MOD},
280 	{"nand", KEYWORD_NAND},
281 	{"new", KEYWORD_NEW},
282 	{"next", KEYWORD_NEXT},
283 	{"nor", KEYWORD_NOR},
284 	{"not", KEYWORD_NOT},
285 	{"null", KEYWORD_NULL},
286 	{"of", KEYWORD_OF},
287 	{"on", KEYWORD_ON},
288 	{"open", KEYWORD_OPEN},
289 	{"or", KEYWORD_OR},
290 	{"others", KEYWORD_OTHERS},
291 	{"out", KEYWORD_OUT},
292 	{"package", KEYWORD_PACKAGE},
293 	{"port", KEYWORD_PORT},
294 	{"postponed", KEYWORD_POSTPONED},
295 	{"procedure", KEYWORD_PROCEDURE},
296 	{"process", KEYWORD_PROCESS},
297 	{"pure", KEYWORD_PURE},
298 	{"range", KEYWORD_RANGE},
299 	{"record", KEYWORD_RECORD},
300 	{"register", KEYWORD_REGISTER},
301 	{"reject", KEYWORD_REJECT},
302 	{"return", KEYWORD_RETURN},
303 	{"rol", KEYWORD_ROL},
304 	{"ror", KEYWORD_ROR},
305 	{"select", KEYWORD_SELECT},
306 	{"severity", KEYWORD_SEVERITY},
307 	{"signal", KEYWORD_SIGNAL},
308 	{"shared", KEYWORD_SHARED},
309 	{"sla", KEYWORD_SLA},
310 	{"sli", KEYWORD_SLI},
311 	{"sra", KEYWORD_SRA},
312 	{"srl", KEYWORD_SRL},
313 	{"subtype", KEYWORD_SUBTYPE},
314 	{"then", KEYWORD_THEN},
315 	{"to", KEYWORD_TO},
316 	{"transport", KEYWORD_TRANSPORT},
317 	{"type", KEYWORD_TYPE},
318 	{"unaffected", KEYWORD_UNAFFECTED},
319 	{"units", KEYWORD_UNITS},
320 	{"until", KEYWORD_UNTIL},
321 	{"use", KEYWORD_USE},
322 	{"variable", KEYWORD_VARIABLE},
323 	{"wait", KEYWORD_WAIT},
324 	{"when", KEYWORD_WHEN},
325 	{"while", KEYWORD_WHILE},
326 	{"with", KEYWORD_WITH},
327 	{"xnor", KEYWORD_XNOR},
328 	{"xor", KEYWORD_XOR}
329 };
330 
331 typedef enum {
332 	F_ARCHITECTURE,
333 } vhdlField;
334 
335 static fieldDefinition VhdlFields [] = {
336 	{ .name = "architecture",
337 	  .description = "architecture designing the entity",
338 	  .enabled = true },
339 };
340 
341 /*
342  *   FUNCTION DECLARATIONS
343  */
344 static void parseKeywords (tokenInfo * const token, tokenInfo * const label, int parent);
345 
346 /*
347  *   FUNCTION DEFINITIONS
348  */
isIdentifierMatch(const tokenInfo * const token,const char * name)349 static bool isIdentifierMatch (const tokenInfo * const token,
350 	const char *name)
351 {
352 	return (bool) (isType (token, TOKEN_IDENTIFIER) &&
353 		strncasecmp (vStringValue (token->string), name,
354 					 vStringLength (token->string)) == 0);
355 }
356 
isSemicolonOrKeywordOrIdent(const tokenInfo * const token,const keywordId keyword,const char * name)357 static bool isSemicolonOrKeywordOrIdent (const tokenInfo * const token,
358 	const keywordId keyword, const char *name)
359 {
360 	return (bool) (isType (token, TOKEN_SEMICOLON)
361 				   || isKeyword (token, keyword)
362 				   || isIdentifierMatch (token, name));
363 }
364 
newToken(void)365 static tokenInfo *newToken (void)
366 {
367 	tokenInfo *const token = xMalloc (1, tokenInfo);
368 	token->type = TOKEN_NONE;
369 	token->keyword = KEYWORD_NONE;
370 	token->string = vStringNew ();
371 	token->lineNumber = getInputLineNumber ();
372 	token->filePosition = getInputFilePosition ();
373 	return token;
374 }
375 
copyToken(tokenInfo * const src)376 static tokenInfo *copyToken (tokenInfo * const src)
377 {
378 	tokenInfo *dst = newToken ();
379 	vStringCopy (dst->string, src->string);
380 	return dst;
381 }
382 
deleteToken(tokenInfo * const token)383 static void deleteToken (tokenInfo * const token)
384 {
385 	if (token != NULL)
386 	{
387 		vStringDelete (token->string);
388 		eFree (token);
389 	}
390 }
391 
392 /*
393  *   Parsing functions
394  */
395 
parseString(vString * const string,const int delimiter)396 static void parseString (vString * const string, const int delimiter)
397 {
398 	bool end = false;
399 	while (!end)
400 	{
401 		int c = getcFromInputFile ();
402 		if (c == EOF)
403 			end = true;
404 		else if (c == '\\')
405 		{
406 			c = getcFromInputFile ();	/* This maybe a ' or ". */
407 			vStringPut (string, c);
408 		}
409 		else if (c == delimiter)
410 			end = true;
411 		else
412 			vStringPut (string, c);
413 	}
414 }
415 
416 /*  Read a VHDL identifier beginning with "firstChar" and place it into "name".
417 */
parseIdentifier(vString * const string,const int firstChar)418 static void parseIdentifier (vString * const string, const int firstChar)
419 {
420 	int c = firstChar;
421 	Assert (isIdentChar1 (c));
422 	do
423 	{
424 		vStringPut (string, c);
425 		c = getcFromInputFile ();
426 	} while (isIdentChar (c));
427 	if (!isspace (c))
428 		ungetcToInputFile (c);	/* unget non-identifier character */
429 }
430 
readToken(tokenInfo * const token)431 static void readToken (tokenInfo * const token)
432 {
433 	int c;
434 
435 	token->type = TOKEN_NONE;
436 	token->keyword = KEYWORD_NONE;
437 	vStringClear (token->string);
438 
439   getNextChar:
440 	do
441 	{
442 		c = getcFromInputFile ();
443 		token->lineNumber = getInputLineNumber ();
444 		token->filePosition = getInputFilePosition ();
445 	}
446 	while (c == '\t' || c == ' ' || c == '\n');
447 
448 	switch (c)
449 	{
450 	case EOF:
451 		token->type = TOKEN_EOF;
452 		break;
453 	case '(':
454 		token->type = TOKEN_OPEN_PAREN;
455 		break;
456 	case ')':
457 		token->type = TOKEN_CLOSE_PAREN;
458 		break;
459 	case ';':
460 		token->type = TOKEN_SEMICOLON;
461 		break;
462 	case ':':
463 		token->type = TOKEN_COLON;
464 		break;
465 	case '.':
466 		token->type = TOKEN_PERIOD;
467 		break;
468 	case ',':
469 		token->type = TOKEN_COMMA;
470 		break;
471 	case '\'':	/* only single char are inside simple quotes */
472 		break;	/* or it is for attributes so we don't care */
473 	case '"':
474 		token->type = TOKEN_STRING;
475 		parseString (token->string, c);
476 		token->lineNumber = getInputLineNumber ();
477 		token->filePosition = getInputFilePosition ();
478 		break;
479 	case '-':
480 		c = getcFromInputFile ();
481 		if (c == '-')	/* start of a comment */
482 		{
483 			skipToCharacterInInputFile ('\n');
484 			goto getNextChar;
485 		}
486 		else
487 		{
488 			if (!isspace (c))
489 				ungetcToInputFile (c);
490 			token->type = TOKEN_OPERATOR;
491 		}
492 		break;
493 	default:
494 		if (!isIdentChar1 (c))
495 			token->type = TOKEN_NONE;
496 		else
497 		{
498 			parseIdentifier (token->string, c);
499 			token->lineNumber = getInputLineNumber ();
500 			token->filePosition = getInputFilePosition ();
501 			token->keyword = lookupCaseKeyword (vStringValue (token->string), Lang_vhdl);
502 			if (isKeyword (token, KEYWORD_NONE))
503 				token->type = TOKEN_IDENTIFIER;
504 			else
505 				token->type = TOKEN_KEYWORD;
506 		}
507 		break;
508 	}
509 }
510 
skipToKeyword(const keywordId keyword)511 static bool skipToKeyword (const keywordId keyword)
512 {
513 	tokenInfo *const token = newToken ();
514 	do
515 	{
516 		readToken (token);
517 	}
518 	while (!isType (token, TOKEN_EOF) && !isKeyword (token, keyword));
519 
520 	bool r = isKeyword (token, keyword);
521 	deleteToken (token);
522 	return r;
523 }
524 
skipToMatched(tokenInfo * const token)525 static void skipToMatched (tokenInfo * const token)
526 {
527 	int nest_level = 0;
528 	tokenType open_token;
529 	tokenType close_token;
530 
531 	switch (token->type)
532 	{
533 	case TOKEN_OPEN_PAREN:
534 		open_token = TOKEN_OPEN_PAREN;
535 		close_token = TOKEN_CLOSE_PAREN;
536 		break;
537 	default:
538 		return;
539 	}
540 
541 	/*
542 	 * This routine will skip to a matching closing token.
543 	 * It will also handle nested tokens like the (, ) below.
544 	 *   (  name varchar(30), text binary(10)  )
545 	 */
546 	if (isType (token, open_token))
547 	{
548 		nest_level++;
549 		while (!(isType (token, close_token) && (nest_level == 0)) && !isType (token, TOKEN_EOF))
550 		{
551 			readToken (token);
552 			if (isType (token, open_token))
553 			{
554 				nest_level++;
555 			}
556 			if (isType (token, close_token))
557 			{
558 				if (nest_level > 0)
559 				{
560 					nest_level--;
561 				}
562 			}
563 		}
564 		readToken (token);
565 	}
566 }
567 
makeVhdlTagWithScope(tokenInfo * const token,const vhdlKind kind,int parent)568 static int makeVhdlTagWithScope (tokenInfo * const token, const vhdlKind kind, int parent)
569 {
570 	const char *const name = vStringValue (token->string);
571 	tagEntryInfo e;
572 	initTagEntry (&e, name, kind);
573 	e.lineNumber = token->lineNumber;
574 	e.filePosition = token->filePosition;
575 	e.extensionFields.scopeIndex = parent;
576 	return makeTagEntry (&e);
577 }
578 
makeVhdlTag(tokenInfo * const token,const vhdlKind kind)579 static int makeVhdlTag (tokenInfo * const token, const vhdlKind kind)
580 {
581 	return makeVhdlTagWithScope (token, kind, CORK_NIL);
582 }
583 
initialize(const langType language)584 static void initialize (const langType language)
585 {
586 	Lang_vhdl = language;
587 }
588 
parseTillEnd(tokenInfo * const token,int parent,const int end_keyword)589 static void parseTillEnd (tokenInfo * const token, int parent, const int end_keyword)
590 {
591 	bool ended = false;
592 	tagEntryInfo *e = getEntryInCorkQueue (parent);
593 	/* If e is NULL, the input may be broken as VHDL code
594 	 * or unsupported syntax in this parser. */
595 
596 	do
597 	{
598 		readToken (token);
599 		if (isKeyword (token, KEYWORD_END))
600 		{
601 			readToken (token);
602 			if (e)
603 				ended = isSemicolonOrKeywordOrIdent (token,
604 													 end_keyword, e->name);
605 			if (!isType (token, TOKEN_SEMICOLON))
606 				skipToCharacterInInputFile (';');
607 			if (ended)
608 				e->extensionFields.endLine = getInputLineNumber ();
609 		}
610 		else
611 		{
612 			if (isType (token, TOKEN_EOF))
613 			{
614 				ended = true;
615 			}
616 			else
617 			{
618 				parseKeywords (token, NULL, parent);
619 			}
620 		}
621 	} while (!ended);
622 }
623 
parseTillBegin(tokenInfo * const token,int parent)624 static void parseTillBegin (tokenInfo * const token, int parent)
625 {
626 	bool begun = false;
627 	do
628 	{
629 		readToken (token);
630 		if (isKeyword (token, KEYWORD_BEGIN)
631 			|| isType (token, TOKEN_EOF))
632 			begun = true;
633 		else
634 			parseKeywords (token, NULL, parent);
635 	} while (!begun);
636 }
637 
parsePackage(tokenInfo * const token)638 static void parsePackage (tokenInfo * const token)
639 {
640 	tokenInfo *const name = newToken ();
641 	tokenInfo *token_for_tagging = NULL;
642 	Assert (isKeyword (token, KEYWORD_PACKAGE));
643 	readToken (token);
644 	if (isKeyword (token, KEYWORD_BODY))
645 	{
646 		readToken (name);
647 		token_for_tagging = name;
648 	}
649 	else if (isType (token, TOKEN_IDENTIFIER))
650 		token_for_tagging = token;
651 
652 	if (token_for_tagging)
653 	{
654 		int index = makeVhdlTag (token_for_tagging, VHDLTAG_PACKAGE);
655 		parseTillEnd (token, index, KEYWORD_PACKAGE);
656 	}
657 
658 	deleteToken (name);
659 }
660 
661 
parseDeclElement(tokenInfo * const token,vhdlKind kind,int parent,bool ended_with_semicolon)662 static void parseDeclElement (tokenInfo * const token,
663 							  vhdlKind kind, int parent,
664 							  bool ended_with_semicolon)
665 {
666 	TRACE_ENTER ();
667 	while (! (isType (token, TOKEN_EOF)
668 			  || isType (token, TOKEN_CLOSE_PAREN)
669 			  || (ended_with_semicolon && isType (token, TOKEN_SEMICOLON))))
670 	{
671 		if (isType (token, TOKEN_IDENTIFIER))
672 		{
673 			makeVhdlTagWithScope (token, kind, parent);
674 			readToken (token);
675 		}
676 		else if (isType (token, TOKEN_COMMA))
677 			readToken (token);
678 		else if (isType (token, TOKEN_COLON))
679 		{
680 			do
681 			{
682 				readToken (token);
683 				skipToMatched (token);
684 				if (isType (token, TOKEN_CLOSE_PAREN)
685 					|| isType (token, TOKEN_SEMICOLON))
686 					break;
687 			}
688 			while (!isType (token, TOKEN_EOF));
689 		}
690 		else
691 		{
692 			/* Unexpected */
693 			readToken (token);
694 		}
695 	}
696 	TRACE_LEAVE ();
697 }
698 
parseModuleDecl(tokenInfo * const token,int parent)699 static void parseModuleDecl (tokenInfo * const token, int parent)
700 {
701 	TRACE_ENTER ();
702 	while (! (isKeyword (token, KEYWORD_END)
703 			  || isType (token, TOKEN_EOF)))
704 	{
705 		vhdlKind kind = VHDLTAG_UNDEFINED;
706 		if (isKeyword (token, KEYWORD_PORT))
707 			kind = VHDLTAG_PORT;
708 		else if (isKeyword (token, KEYWORD_GENERIC))
709 			kind = VHDLTAG_GENERIC;
710 
711 		if (kind != VHDLTAG_UNDEFINED)
712 		{
713 			readToken (token);
714 			if (isType (token, TOKEN_OPEN_PAREN))
715 			{
716 				readToken (token);
717 				parseDeclElement (token, kind, parent, false);
718 			}
719 		}
720 		else
721 			readToken (token);
722 	}
723 	TRACE_LEAVE ();
724 }
725 
parseModule(tokenInfo * const token,int parent)726 static void parseModule (tokenInfo * const token, int parent)
727 {
728 	tokenInfo *const name = newToken ();
729 	const vhdlKind kind = isKeyword (token, KEYWORD_ENTITY) ?
730 		VHDLTAG_ENTITY : VHDLTAG_COMPONENT;
731 	Assert (isKeyword (token, KEYWORD_ENTITY) ||
732 		isKeyword (token, KEYWORD_COMPONENT));
733 	readToken (name);
734 	readToken (token);
735 	if (kind == VHDLTAG_COMPONENT || isKeyword (token, KEYWORD_IS))
736 	{
737 		int index = makeVhdlTagWithScope (name, kind, parent);
738 		if (isKeyword (token, KEYWORD_IS))
739 			readToken (token);
740 		parseModuleDecl (token, index);
741 
742 		bool ended = isKeyword (token, KEYWORD_END);
743 		if (!ended)
744 			ended = skipToKeyword (KEYWORD_END);
745 		skipToCharacterInInputFile (';');
746 
747 		if (ended)
748 		{
749 			tagEntryInfo *e = getEntryInCorkQueue (index);
750 			if (e)
751 				e->extensionFields.endLine = getInputLineNumber ();
752 		}
753 
754 		if (kind == VHDLTAG_ENTITY)
755 			registerEntry (index);
756 	}
757 	deleteToken (name);
758 }
759 
parseRecord(tokenInfo * const token,int parent)760 static void parseRecord (tokenInfo * const token, int parent)
761 {
762 	tokenInfo *const name = newToken ();
763 	Assert (isKeyword (token, KEYWORD_RECORD));
764 	readToken (name);
765 	do
766 	{
767 		readToken (token);	/* should be a colon */
768 		skipToCharacterInInputFile (';');
769 		makeVhdlTagWithScope (name, VHDLTAG_RECORD, parent);
770 		readToken (name);
771 	}
772 	while (!isKeyword (name, KEYWORD_END) && !isType (name, TOKEN_EOF));
773 	skipToCharacterInInputFile (';');
774 
775 	if (isKeyword (name, KEYWORD_END))
776 	{
777 		tagEntryInfo *e = getEntryInCorkQueue (parent);
778 		if (e)
779 			e->extensionFields.endLine = getInputLineNumber ();
780 	}
781 
782 	deleteToken (name);
783 }
784 
parseTypes(tokenInfo * const token,int parent)785 static void parseTypes (tokenInfo * const token, int parent)
786 {
787 	tokenInfo *const name = newToken ();
788 	const vhdlKind kind = isKeyword (token, KEYWORD_TYPE) ?
789 		VHDLTAG_TYPE : VHDLTAG_SUBTYPE;
790 	Assert (isKeyword (token, KEYWORD_TYPE) ||
791 		isKeyword (token, KEYWORD_SUBTYPE));
792 	readToken (name);
793 	readToken (token);
794 	if (isKeyword (token, KEYWORD_IS))
795 	{
796 		readToken (token);	/* type */
797 		if (isKeyword (token, KEYWORD_RECORD))
798 		{
799 			int index = makeVhdlTagWithScope (name, kind, parent);
800 			/*TODO: make tags of the record's names */
801 			parseRecord (token, index);
802 		}
803 		else
804 		{
805 			makeVhdlTagWithScope (name, kind, parent);
806 		}
807 	}
808 	deleteToken (name);
809 }
810 
parseConstant(int parent)811 static void parseConstant (int parent)
812 {
813 	vhdlKind parent_kind = VHDLTAG_UNDEFINED;
814 	tagEntryInfo *e = getEntryInCorkQueue (parent);
815 	if (e)
816 		parent_kind = e->kindIndex;
817 
818 	vhdlKind kind;
819 	switch (parent_kind)
820 	{
821 	case VHDLTAG_FUNCTION:
822 	case VHDLTAG_PROCEDURE:
823 		kind = VHDLTAG_LOCAL;
824 		break;
825 	default:
826 		kind = VHDLTAG_CONSTANT;
827 		break;
828 	}
829 
830 	tokenInfo *const name = newToken ();
831 	readToken (name);
832 	makeVhdlTagWithScope (name, kind, parent);
833 	skipToCharacterInInputFile (';');
834 	deleteToken (name);
835 }
836 
parseSubProgram(tokenInfo * const token,int parent)837 static void parseSubProgram (tokenInfo * const token, int parent)
838 {
839 	tokenInfo *const name = newToken ();
840 	const vhdlKind kind = isKeyword (token, KEYWORD_FUNCTION) ?
841 		VHDLTAG_FUNCTION : VHDLTAG_PROCEDURE;
842 	const int end_keyword = token->keyword;
843 	Assert (isKeyword (token, KEYWORD_FUNCTION) ||
844 		isKeyword (token, KEYWORD_PROCEDURE));
845 	readToken (name);	/* the name of the function or procedure */
846 	readToken (token);
847 	if (isType (token, TOKEN_OPEN_PAREN))
848 	{
849 		skipToMatched (token);
850 	}
851 
852 	if (kind == VHDLTAG_FUNCTION)
853 	{
854 		if (isKeyword (token, KEYWORD_RETURN))
855 		{
856 			/* Read datatype */
857 			readToken (token);
858 			while (! isKeyword (token, KEYWORD_IS) &&
859 					! isType (token, TOKEN_SEMICOLON) &&
860 					! isType (token, TOKEN_EOF))
861 			{
862 				readToken (token);
863 			}
864 		}
865 	}
866 
867 	if (isType (token, TOKEN_SEMICOLON))
868 	{
869 		makeVhdlTagWithScope (name, VHDLTAG_PROTOTYPE, parent);
870 	}
871 	else if (isKeyword (token, KEYWORD_IS))
872 	{
873 		int index = makeVhdlTagWithScope (name, kind, parent);
874 		parseTillEnd (token, index, end_keyword);
875 	}
876 	deleteToken (name);
877 }
878 
879 /*  architecture behavioral of ent is*/
parseArchitecture(tokenInfo * const token)880 static void parseArchitecture (tokenInfo * const token)
881 {
882 	tokenInfo *const name = newToken ();
883 
884 	readToken (name);
885 	if (!isType (name, TOKEN_IDENTIFIER))
886 	{
887 		skipToKeyword (KEYWORD_END);
888 		skipToCharacterInInputFile (';');
889 		deleteToken (name);
890 		return;
891 	}
892 
893 	int index = makeVhdlTag (name, VHDLTAG_ARCHITECTURE);
894 	readToken (token);
895 	if (isKeyword (token, KEYWORD_OF))
896 	{
897 		readToken (token);
898 		if (isType (token, TOKEN_IDENTIFIER))
899 		{
900 			/* Filling scope field of this architecture.
901 			   If the definition for the entity can be found in the symbol table,
902 			   use its cork as the scope. If not, use the reference tag for the
903 			   entity as fallback. */
904 			int role_index = makeSimpleRefTag (token->string,
905 											   VHDLTAG_ENTITY, VHDL_ENTITY_DESIGNED);
906 			int entity_index = anyKindEntryInScope (CORK_NIL,
907 													vStringValue (token->string),
908 													VHDLTAG_ENTITY);
909 			tagEntryInfo *e = getEntryInCorkQueue (index);
910 			if (e)
911 			{
912 				e->extensionFields.scopeIndex = (
913 					entity_index == CORK_NIL
914 					? role_index
915 					: entity_index);
916 
917 				/* TODO: append thes architecture name to
918 				 * architecture: field of *e*. */
919 			}
920 
921 			attachParserFieldToCorkEntry (role_index,
922 										  VhdlFields[F_ARCHITECTURE].ftype,
923 										  vStringValue (name->string));
924 
925 			readToken (token);
926 			if (isKeyword (token, KEYWORD_IS))
927 			{
928 				parseTillBegin (token, index);
929 				parseTillEnd (token, index, KEYWORD_ARCHITECTURE);
930 			}
931 		}
932 	}
933 	deleteToken (name);
934 }
935 
parseSignal(tokenInfo * const token,int parent)936 static void parseSignal (tokenInfo * const token, int parent)
937 {
938 	readToken (token);
939 	parseDeclElement (token, VHDLTAG_SIGNAL, parent, true);
940 }
941 
parseLabel(tokenInfo * const name,int parent)942 static void parseLabel (tokenInfo * const name, int parent)
943 {
944 	tokenInfo *const token = newToken ();
945 
946 	readToken (token);
947 	if (isType (token, TOKEN_COLON))
948 	{
949 		readToken (token);
950 		if (isType (token, TOKEN_KEYWORD))
951 			parseKeywords (token, name, parent);
952 	}
953 	deleteToken (token);
954 }
955 
parseProcess(tokenInfo * const token,tokenInfo * const label,int parent)956 static void parseProcess (tokenInfo * const token, tokenInfo * const label, int parent)
957 {
958 	tokenInfo *process = label? label: copyToken (token);
959 
960 	if (label == NULL)
961 	{
962 		process->type = TOKEN_IDENTIFIER;
963 		vStringClear (process->string);
964 		anonGenerate (process->string, "anonProcess", VHDLTAG_PROCESS);
965 	}
966 
967 	int index = makeVhdlTagWithScope (process, VHDLTAG_PROCESS, parent);
968 
969 	if (label == NULL)
970 	{
971 		tagEntryInfo *e = getEntryInCorkQueue (index);
972 		if (e)
973 			markTagExtraBit (e, XTAG_ANONYMOUS);
974 		deleteToken (process);
975 	}
976 
977 	skipToMatched (token);
978 	parseTillBegin (token, index);
979 	parseTillEnd (token, index, KEYWORD_PROCESS);
980 }
981 
parseVariable(tokenInfo * const token,int parent)982 static void parseVariable (tokenInfo * const token, int parent)
983 {
984 	readToken (token);
985 	parseDeclElement (token, VHDLTAG_VARIABLE, parent, true);
986 }
987 
parseAlias(tokenInfo * const token,int parent)988 static void parseAlias (tokenInfo * const token, int parent)
989 {
990 	readToken (token);
991 	parseDeclElement (token, VHDLTAG_ALIAS, parent, true);
992 }
993 
994 /* TODO */
995 /* records */
parseKeywords(tokenInfo * const token,tokenInfo * const label,int index)996 static void parseKeywords (tokenInfo * const token, tokenInfo * const label, int index)
997 {
998 	switch (token->keyword)
999 	{
1000 	case KEYWORD_END:
1001 		skipToCharacterInInputFile (';');
1002 		break;
1003 	case KEYWORD_CONSTANT:
1004 		parseConstant (index);
1005 		break;
1006 	case KEYWORD_TYPE:
1007 		parseTypes (token, index);
1008 		break;
1009 	case KEYWORD_SUBTYPE:
1010 		parseTypes (token, index);
1011 		break;
1012 	case KEYWORD_ENTITY:
1013 		parseModule (token, index);
1014 		break;
1015 	case KEYWORD_COMPONENT:
1016 		parseModule (token, index);
1017 		break;
1018 	case KEYWORD_FUNCTION:
1019 		parseSubProgram (token, index);
1020 		break;
1021 	case KEYWORD_PROCEDURE:
1022 		parseSubProgram (token, index);
1023 		break;
1024 	case KEYWORD_PACKAGE:
1025 		parsePackage (token);
1026 		break;
1027 	case KEYWORD_ARCHITECTURE:
1028 		parseArchitecture (token);
1029 		break;
1030 	case KEYWORD_SIGNAL:
1031 		parseSignal (token, index);
1032 		break;
1033 	case KEYWORD_PROCESS:
1034 		parseProcess (token, label, index);
1035 		break;
1036 	case KEYWORD_VARIABLE:
1037 		parseVariable (token, index);
1038 		break;
1039 	case KEYWORD_ALIAS:
1040 		parseAlias (token, index);
1041 		break;
1042 	default:
1043 		if (isType (token, TOKEN_IDENTIFIER))
1044 			parseLabel (token, index);
1045 		break;
1046 	}
1047 }
1048 
parseVhdlFile(tokenInfo * const token)1049 static tokenType parseVhdlFile (tokenInfo * const token)
1050 {
1051 	do
1052 	{
1053 		readToken (token);
1054 		parseKeywords (token, NULL, CORK_NIL);
1055 	} while (!isKeyword (token, KEYWORD_END) && !isType (token, TOKEN_EOF));
1056 	return token->type;
1057 }
1058 
findVhdlTags(void)1059 static void findVhdlTags (void)
1060 {
1061 	tokenInfo *const token = newToken ();
1062 
1063 	while (parseVhdlFile (token) != TOKEN_EOF);
1064 
1065 	deleteToken (token);
1066 }
1067 
VhdlParser(void)1068 extern parserDefinition *VhdlParser (void)
1069 {
1070 	static const char *const extensions[] = { "vhdl", "vhd", NULL };
1071 	parserDefinition *def = parserNew ("VHDL");
1072 	def->kindTable = VhdlKinds;
1073 	def->kindCount = ARRAY_SIZE (VhdlKinds);
1074 	def->extensions = extensions;
1075 	def->parser = findVhdlTags;
1076 	def->initialize = initialize;
1077 	def->keywordTable = VhdlKeywordTable;
1078 	def->keywordCount = ARRAY_SIZE (VhdlKeywordTable);
1079 	def->fieldTable = VhdlFields;
1080 	def->fieldCount = ARRAY_SIZE (VhdlFields);
1081 	def->useCork = CORK_QUEUE|CORK_SYMTAB;
1082 	return def;
1083 }
1084