1 /*
2 *   $Id: vhdl.c 652 2008-04-18 03:51:47Z elliotth $
3 *
4 *   Copyright (c) 2008, Nicolas Vincent
5 *
6 *   This source code is released for free distribution under the terms of the
7 *   GNU General Public License.
8 *
9 *   This module contains functions for generating tags for VHDL files.
10 */
11 
12 /*
13  *   INCLUDE FILES
14  */
15 #include "general.h"	/* must always come first */
16 
17 #include <ctype.h>	/* to define isalpha () */
18 #include <string.h>
19 #include <setjmp.h>
20 
21 #include "debug.h"
22 #include "entry.h"
23 #include "keyword.h"
24 #include "parse.h"
25 #include "read.h"
26 #include "routines.h"
27 #include "vstring.h"
28 
29 /*
30  *   MACROS
31  */
32 #define isType(token,t)     (boolean) ((token)->type == (t))
33 #define isKeyword(token,k)  (boolean) ((token)->keyword == (k))
34 
35 /*
36  *   DATA DECLARATIONS
37  */
38 typedef enum eException { ExceptionNone, ExceptionEOF } exception_t;
39 
40 /*
41  * Used to specify type of keyword.
42  */
43 typedef enum eKeywordId {
44 	KEYWORD_NONE = -1,
45 	KEYWORD_ABS,
46 	KEYWORD_ACCESS,
47 	KEYWORD_AFTER,
48 	KEYWORD_ALIAS,
49 	KEYWORD_ALL,
50 	KEYWORD_AND,
51 	KEYWORD_ARCHITECTURE,
52 	KEYWORD_ARRAY,
53 	KEYWORD_ASSERT,
54 	KEYWORD_ATTRIBUTE,
55 	KEYWORD_BEGIN,
56 	KEYWORD_BLOCK,
57 	KEYWORD_BODY,
58 	KEYWORD_BUFFER,
59 	KEYWORD_BUS,
60 	KEYWORD_CASE,
61 	KEYWORD_COMPONENT,
62 	KEYWORD_CONFIGURATION,
63 	KEYWORD_CONSTANT,
64 	KEYWORD_DISCONNECT,
65 	KEYWORD_DOWNTO,
66 	KEYWORD_ELSE,
67 	KEYWORD_ELSIF,
68 	KEYWORD_END,
69 	KEYWORD_ENTITY,
70 	KEYWORD_EXIT,
71 	KEYWORD_FILE,
72 	KEYWORD_FOR,
73 	KEYWORD_FUNCTION,
74 	KEYWORD_GENERATE,
75 	KEYWORD_GENERIC,
76 	KEYWORD_GROUP,
77 	KEYWORD_GUARDED,
78 	KEYWORD_IF,
79 	KEYWORD_IMPURE,
80 	KEYWORD_IN,
81 	KEYWORD_INERTIAL,
82 	KEYWORD_INOUT,
83 	KEYWORD_IS,
84 	KEYWORD_LABEL,
85 	KEYWORD_LIBRARY,
86 	KEYWORD_LINKAGE,
87 	KEYWORD_LITERAL,
88 	KEYWORD_LOOP,
89 	KEYWORD_MAP,
90 	KEYWORD_MOD,
91 	KEYWORD_NAND,
92 	KEYWORD_NEW,
93 	KEYWORD_NEXT,
94 	KEYWORD_NOR,
95 	KEYWORD_NOT,
96 	KEYWORD_NULL,
97 	KEYWORD_OF,
98 	KEYWORD_ON,
99 	KEYWORD_OPEN,
100 	KEYWORD_OR,
101 	KEYWORD_OTHERS,
102 	KEYWORD_OUT,
103 	KEYWORD_PACKAGE,
104 	KEYWORD_PORT,
105 	KEYWORD_POSTPONED,
106 	KEYWORD_PROCEDURE,
107 	KEYWORD_PROCESS,
108 	KEYWORD_PURE,
109 	KEYWORD_RANGE,
110 	KEYWORD_RECORD,
111 	KEYWORD_REGISTER,
112 	KEYWORD_REJECT,
113 	KEYWORD_RETURN,
114 	KEYWORD_ROL,
115 	KEYWORD_ROR,
116 	KEYWORD_SELECT,
117 	KEYWORD_SEVERITY,
118 	KEYWORD_SIGNAL,
119 	KEYWORD_SHARED,
120 	KEYWORD_SLA,
121 	KEYWORD_SLI,
122 	KEYWORD_SRA,
123 	KEYWORD_SRL,
124 	KEYWORD_SUBTYPE,
125 	KEYWORD_THEN,
126 	KEYWORD_TO,
127 	KEYWORD_TRANSPORT,
128 	KEYWORD_TYPE,
129 	KEYWORD_UNAFFECTED,
130 	KEYWORD_UNITS,
131 	KEYWORD_UNTIL,
132 	KEYWORD_USE,
133 	KEYWORD_VARIABLE,
134 	KEYWORD_WAIT,
135 	KEYWORD_WHEN,
136 	KEYWORD_WHILE,
137 	KEYWORD_WITH,
138 	KEYWORD_XNOR,
139 	KEYWORD_XOR
140 } keywordId;
141 
142 /*  Used to determine whether keyword is valid for the current language and
143  *  what its ID is.
144  */
145 typedef struct sKeywordDesc {
146 	const char *name;
147 	keywordId id;
148 } keywordDesc;
149 
150 typedef enum eTokenType {
151 	TOKEN_NONE,		/* none */
152 	TOKEN_OPEN_PAREN,	/* ( */
153 	TOKEN_CLOSE_PAREN,	/* ) */
154 	TOKEN_COMMA,		/* the comma character */
155 	TOKEN_IDENTIFIER,
156 	TOKEN_KEYWORD,
157 	TOKEN_PERIOD,		/* . */
158 	TOKEN_OPERATOR,
159 	TOKEN_SEMICOLON,	/* the semicolon character */
160 	TOKEN_STRING
161 } tokenType;
162 
163 typedef struct sTokenInfo {
164 	tokenType type;
165 	keywordId keyword;
166 	vString *string;		/* the name of the token */
167 	vString *scope;
168 	unsigned long lineNumber;	/* line number of tag */
169 	long filePosition;		/* file position of line containing name */
170 } tokenInfo;
171 
172 /*
173  *   DATA DEFINITIONS
174  */
175 static int Lang_vhdl;
176 static jmp_buf Exception;
177 
178 /* Used to index into the VhdlKinds table. */
179 typedef enum {
180 	VHDLTAG_UNDEFINED = -1,
181 	VHDLTAG_CONSTANT,
182 	VHDLTAG_TYPE,
183 	VHDLTAG_SUBTYPE,
184 	VHDLTAG_RECORD,
185 	VHDLTAG_ENTITY,
186 	VHDLTAG_COMPONENT,
187 	VHDLTAG_PROTOTYPE,
188 	VHDLTAG_FUNCTION,
189 	VHDLTAG_PROCEDURE,
190 	VHDLTAG_PACKAGE,
191 	VHDLTAG_LOCAL
192 } vhdlKind;
193 
194 static kindOption VhdlKinds[] = {
195 	{TRUE, 'c', "constant", "constant declarations"},
196 	{TRUE, 't', "type", "type definitions"},
197 	{TRUE, 'T', "subtype", "subtype definitions"},
198 	{TRUE, 'r', "record", "record names"},
199 	{TRUE, 'e', "entity", "entity declarations"},
200 	{FALSE, 'C', "component", "component declarations"},
201 	{FALSE, 'd', "prototype", "prototypes"},
202 	{TRUE, 'f', "function", "function prototypes and declarations"},
203 	{TRUE, 'p', "procedure", "procedure prototypes and declarations"},
204 	{TRUE, 'P', "package", "package definitions"},
205 	{FALSE, 'l', "local", "local definitions"}
206 };
207 
208 static keywordDesc VhdlKeywordTable[] = {
209 	{"abs", KEYWORD_ABS},
210 	{"access", KEYWORD_ACCESS},
211 	{"after", KEYWORD_AFTER},
212 	{"alias", KEYWORD_ALIAS},
213 	{"all", KEYWORD_ALL},
214 	{"and", KEYWORD_AND},
215 	{"architecture", KEYWORD_ARCHITECTURE},
216 	{"array", KEYWORD_ARRAY},
217 	{"assert", KEYWORD_ASSERT},
218 	{"attribute", KEYWORD_ATTRIBUTE},
219 	{"begin", KEYWORD_BEGIN},
220 	{"block", KEYWORD_BLOCK},
221 	{"body", KEYWORD_BODY},
222 	{"buffer", KEYWORD_BUFFER},
223 	{"bus", KEYWORD_BUS},
224 	{"case", KEYWORD_CASE},
225 	{"component", KEYWORD_COMPONENT},
226 	{"configuration", KEYWORD_CONFIGURATION},
227 	{"constant", KEYWORD_CONSTANT},
228 	{"disconnect", KEYWORD_DISCONNECT},
229 	{"downto", KEYWORD_DOWNTO},
230 	{"else", KEYWORD_ELSE},
231 	{"elsif", KEYWORD_ELSIF},
232 	{"end", KEYWORD_END},
233 	{"entity", KEYWORD_ENTITY},
234 	{"exit", KEYWORD_EXIT},
235 	{"file", KEYWORD_FILE},
236 	{"for", KEYWORD_FOR},
237 	{"function", KEYWORD_FUNCTION},
238 	{"generate", KEYWORD_GENERATE},
239 	{"generic", KEYWORD_GENERIC},
240 	{"group", KEYWORD_GROUP},
241 	{"guarded", KEYWORD_GUARDED},
242 	{"if", KEYWORD_IF},
243 	{"impure", KEYWORD_IMPURE},
244 	{"in", KEYWORD_IN},
245 	{"inertial", KEYWORD_INERTIAL},
246 	{"inout", KEYWORD_INOUT},
247 	{"is", KEYWORD_IS},
248 	{"label", KEYWORD_LABEL},
249 	{"library", KEYWORD_LIBRARY},
250 	{"linkage", KEYWORD_LINKAGE},
251 	{"literal", KEYWORD_LITERAL},
252 	{"loop", KEYWORD_LOOP},
253 	{"map", KEYWORD_MAP},
254 	{"mod", KEYWORD_MOD},
255 	{"nand", KEYWORD_NAND},
256 	{"new", KEYWORD_NEW},
257 	{"next", KEYWORD_NEXT},
258 	{"nor", KEYWORD_NOR},
259 	{"not", KEYWORD_NOT},
260 	{"null", KEYWORD_NULL},
261 	{"of", KEYWORD_OF},
262 	{"on", KEYWORD_ON},
263 	{"open", KEYWORD_OPEN},
264 	{"or", KEYWORD_OR},
265 	{"others", KEYWORD_OTHERS},
266 	{"out", KEYWORD_OUT},
267 	{"package", KEYWORD_PACKAGE},
268 	{"port", KEYWORD_PORT},
269 	{"postponed", KEYWORD_POSTPONED},
270 	{"procedure", KEYWORD_PROCEDURE},
271 	{"process", KEYWORD_PROCESS},
272 	{"pure", KEYWORD_PURE},
273 	{"range", KEYWORD_RANGE},
274 	{"record", KEYWORD_RECORD},
275 	{"register", KEYWORD_REGISTER},
276 	{"reject", KEYWORD_REJECT},
277 	{"return", KEYWORD_RETURN},
278 	{"rol", KEYWORD_ROL},
279 	{"ror", KEYWORD_ROR},
280 	{"select", KEYWORD_SELECT},
281 	{"severity", KEYWORD_SEVERITY},
282 	{"signal", KEYWORD_SIGNAL},
283 	{"shared", KEYWORD_SHARED},
284 	{"sla", KEYWORD_SLA},
285 	{"sli", KEYWORD_SLI},
286 	{"sra", KEYWORD_SRA},
287 	{"srl", KEYWORD_SRL},
288 	{"subtype", KEYWORD_SUBTYPE},
289 	{"then", KEYWORD_THEN},
290 	{"to", KEYWORD_TO},
291 	{"transport", KEYWORD_TRANSPORT},
292 	{"type", KEYWORD_TYPE},
293 	{"unaffected", KEYWORD_UNAFFECTED},
294 	{"units", KEYWORD_UNITS},
295 	{"until", KEYWORD_UNTIL},
296 	{"use", KEYWORD_USE},
297 	{"variable", KEYWORD_VARIABLE},
298 	{"wait", KEYWORD_WAIT},
299 	{"when", KEYWORD_WHEN},
300 	{"while", KEYWORD_WHILE},
301 	{"with", KEYWORD_WITH},
302 	{"xnor", KEYWORD_XNOR},
303 	{"xor", KEYWORD_XOR}
304 };
305 
306 /*
307  *   FUNCTION DECLARATIONS
308  */
309 static void parseKeywords (tokenInfo * const token, boolean local);
310 
311 /*
312  *   FUNCTION DEFINITIONS
313  */
314 
isIdentChar1(const int c)315 static boolean isIdentChar1 (const int c)
316 {
317 	return (boolean) (isalpha (c) || c == '_');
318 }
319 
isIdentChar(const int c)320 static boolean isIdentChar (const int c)
321 {
322 	return (boolean) (isalpha (c) || isdigit (c) || c == '_');
323 }
324 
isIdentifierMatch(const tokenInfo * const token,const vString * const name)325 static boolean isIdentifierMatch (const tokenInfo * const token,
326 	const vString * const name)
327 {
328 	return (boolean) (isType (token, TOKEN_IDENTIFIER) &&
329 		strcasecmp (vStringValue (token->string), vStringValue (name)) == 0);
330 	/* XXX this is copy/paste from eiffel.c and slightly modified */
331 	/* shouldn't we use strNcasecmp ? */
332 }
333 
isKeywordOrIdent(const tokenInfo * const token,const keywordId keyword,const vString * const name)334 static boolean isKeywordOrIdent (const tokenInfo * const token,
335 	const keywordId keyword, const vString * const name)
336 {
337 	return (boolean) (isKeyword (token, keyword) ||
338 		isIdentifierMatch (token, name));
339 }
340 
newToken(void)341 static tokenInfo *newToken (void)
342 {
343 	tokenInfo *const token = xMalloc (1, tokenInfo);
344 	token->type = TOKEN_NONE;
345 	token->keyword = KEYWORD_NONE;
346 	token->string = vStringNew ();
347 	token->scope = vStringNew ();
348 	token->lineNumber = getSourceLineNumber ();
349 	token->filePosition = getInputFilePosition ();
350 	return token;
351 }
352 
deleteToken(tokenInfo * const token)353 static void deleteToken (tokenInfo * const token)
354 {
355 	if (token != NULL)
356 	{
357 		vStringDelete (token->string);
358 		vStringDelete (token->scope);
359 		eFree (token);
360 	}
361 }
362 
363 /*
364  *   Parsing functions
365  */
366 
parseString(vString * const string,const int delimiter)367 static void parseString (vString * const string, const int delimiter)
368 {
369 	boolean end = FALSE;
370 	while (!end)
371 	{
372 		int c = fileGetc ();
373 		if (c == EOF)
374 			end = TRUE;
375 		else if (c == '\\')
376 		{
377 			c = fileGetc ();	/* This maybe a ' or ". */
378 			vStringPut (string, c);
379 		}
380 		else if (c == delimiter)
381 			end = TRUE;
382 		else
383 			vStringPut (string, c);
384 	}
385 	vStringTerminate (string);
386 }
387 
388 /*  Read a VHDL identifier beginning with "firstChar" and place it into "name".
389 */
parseIdentifier(vString * const string,const int firstChar)390 static void parseIdentifier (vString * const string, const int firstChar)
391 {
392 	int c = firstChar;
393 	Assert (isIdentChar1 (c));
394 	do
395 	{
396 		vStringPut (string, c);
397 		c = fileGetc ();
398 	} while (isIdentChar (c));
399 	vStringTerminate (string);
400 	if (!isspace (c))
401 		fileUngetc (c);	/* unget non-identifier character */
402 }
403 
readToken(tokenInfo * const token)404 static void readToken (tokenInfo * const token)
405 {
406 	int c;
407 
408 	token->type = TOKEN_NONE;
409 	token->keyword = KEYWORD_NONE;
410 	vStringClear (token->string);
411 
412   getNextChar:
413 	do
414 	{
415 		c = fileGetc ();
416 		token->lineNumber = getSourceLineNumber ();
417 		token->filePosition = getInputFilePosition ();
418 	}
419 	while (c == '\t' || c == ' ' || c == '\n');
420 
421 	switch (c)
422 	{
423 	case EOF:
424 		longjmp (Exception, (int) ExceptionEOF);
425 		break;
426 	case '(':
427 		token->type = TOKEN_OPEN_PAREN;
428 		break;
429 	case ')':
430 		token->type = TOKEN_CLOSE_PAREN;
431 		break;
432 	case ';':
433 		token->type = TOKEN_SEMICOLON;
434 		break;
435 	case '.':
436 		token->type = TOKEN_PERIOD;
437 		break;
438 	case ',':
439 		token->type = TOKEN_COMMA;
440 		break;
441 	case '\'':	/* only single char are inside simple quotes */
442 		break;	/* or it is for attributes so we don't care */
443 	case '"':
444 		token->type = TOKEN_STRING;
445 		parseString (token->string, c);
446 		token->lineNumber = getSourceLineNumber ();
447 		token->filePosition = getInputFilePosition ();
448 		break;
449 	case '-':
450 		c = fileGetc ();
451 		if (c == '-')	/* start of a comment */
452 		{
453 			fileSkipToCharacter ('\n');
454 			goto getNextChar;
455 		}
456 		else
457 		{
458 			if (!isspace (c))
459 				fileUngetc (c);
460 			token->type = TOKEN_OPERATOR;
461 		}
462 		break;
463 	default:
464 		if (!isIdentChar1 (c))
465 			token->type = TOKEN_NONE;
466 		else
467 		{
468 			parseIdentifier (token->string, c);
469 			token->lineNumber = getSourceLineNumber ();
470 			token->filePosition = getInputFilePosition ();
471 			token->keyword = analyzeToken (token->string, Lang_vhdl);
472 			if (isKeyword (token, KEYWORD_NONE))
473 				token->type = TOKEN_IDENTIFIER;
474 			else
475 				token->type = TOKEN_KEYWORD;
476 		}
477 		break;
478 	}
479 }
480 
skipToKeyword(const keywordId keyword)481 static void skipToKeyword (const keywordId keyword)
482 {
483 	tokenInfo *const token = newToken ();
484 	do
485 	{
486 		readToken (token);
487 	}
488 	while (!isKeyword (token, keyword));
489 	deleteToken (token);
490 }
491 
skipToMatched(tokenInfo * const token)492 static void skipToMatched (tokenInfo * const token)
493 {
494 	int nest_level = 0;
495 	tokenType open_token;
496 	tokenType close_token;
497 
498 	switch (token->type)
499 	{
500 	case TOKEN_OPEN_PAREN:
501 		open_token = TOKEN_OPEN_PAREN;
502 		close_token = TOKEN_CLOSE_PAREN;
503 		break;
504 	default:
505 		return;
506 	}
507 
508 	/*
509 	 * This routine will skip to a matching closing token.
510 	 * It will also handle nested tokens like the (, ) below.
511 	 *   (  name varchar(30), text binary(10)  )
512 	 */
513 	if (isType (token, open_token))
514 	{
515 		nest_level++;
516 		while (!(isType (token, close_token) && (nest_level == 0)))
517 		{
518 			readToken (token);
519 			if (isType (token, open_token))
520 			{
521 				nest_level++;
522 			}
523 			if (isType (token, close_token))
524 			{
525 				if (nest_level > 0)
526 				{
527 					nest_level--;
528 				}
529 			}
530 		}
531 		readToken (token);
532 	}
533 }
534 
makeConstTag(tokenInfo * const token,const vhdlKind kind)535 static void makeConstTag (tokenInfo * const token, const vhdlKind kind)
536 {
537 	if (VhdlKinds[kind].enabled)
538 	{
539 		const char *const name = vStringValue (token->string);
540 		tagEntryInfo e;
541 		initTagEntry (&e, name);
542 		e.lineNumber = token->lineNumber;
543 		e.filePosition = token->filePosition;
544 		e.kindName = VhdlKinds[kind].name;
545 		e.kind = VhdlKinds[kind].letter;
546 		makeTagEntry (&e);
547 	}
548 }
549 
makeVhdlTag(tokenInfo * const token,const vhdlKind kind)550 static void makeVhdlTag (tokenInfo * const token, const vhdlKind kind)
551 {
552 	if (VhdlKinds[kind].enabled)
553 	{
554 		/*
555 		 * If a scope has been added to the token, change the token
556 		 * string to include the scope when making the tag.
557 		 */
558 		if (vStringLength (token->scope) > 0)
559 		{
560 			vString *fulltag = vStringNew ();
561 			vStringCopy (fulltag, token->scope);
562 			vStringCatS (fulltag, ".");
563 			vStringCatS (fulltag, vStringValue (token->string));
564 			vStringTerminate (fulltag);
565 			vStringCopy (token->string, fulltag);
566 			vStringDelete (fulltag);
567 		}
568 		makeConstTag (token, kind);
569 	}
570 }
571 
initialize(const langType language)572 static void initialize (const langType language)
573 {
574 	size_t i;
575 	const size_t count =
576 		sizeof (VhdlKeywordTable) / sizeof (VhdlKeywordTable[0]);
577 	Lang_vhdl = language;
578 	for (i = 0; i < count; ++i)
579 	{
580 		const keywordDesc *const p = &VhdlKeywordTable[i];
581 		addKeyword (p->name, language, (int) p->id);
582 	}
583 }
584 
parsePackage(tokenInfo * const token)585 static void parsePackage (tokenInfo * const token)
586 {
587 	tokenInfo *const name = newToken ();
588 	Assert (isKeyword (token, KEYWORD_PACKAGE));
589 	readToken (token);
590 	if (isKeyword (token, KEYWORD_BODY))
591 	{
592 		readToken (name);
593 		makeVhdlTag (name, VHDLTAG_PACKAGE);
594 	}
595 	else if (isType (token, TOKEN_IDENTIFIER))
596 	{
597 		makeVhdlTag (token, VHDLTAG_PACKAGE);
598 	}
599 	deleteToken (name);
600 }
601 
parseModule(tokenInfo * const token)602 static void parseModule (tokenInfo * const token)
603 {
604 	tokenInfo *const name = newToken ();
605 	const vhdlKind kind = isKeyword (token, KEYWORD_ENTITY) ?
606 		VHDLTAG_ENTITY : VHDLTAG_COMPONENT;
607 	Assert (isKeyword (token, KEYWORD_ENTITY) ||
608 		isKeyword (token, KEYWORD_COMPONENT));
609 	readToken (name);
610 	if (kind == VHDLTAG_COMPONENT)
611 	{
612 		makeVhdlTag (name, VHDLTAG_COMPONENT);
613 		skipToKeyword (KEYWORD_END);
614 		fileSkipToCharacter (';');
615 	}
616 	else
617 	{
618 		readToken (token);
619 		if (isKeyword (token, KEYWORD_IS))
620 		{
621 			makeVhdlTag (name, VHDLTAG_ENTITY);
622 			skipToKeyword (KEYWORD_END);
623 			fileSkipToCharacter (';');
624 		}
625 	}
626 	deleteToken (name);
627 }
628 
parseRecord(tokenInfo * const token)629 static void parseRecord (tokenInfo * const token)
630 {
631 	tokenInfo *const name = newToken ();
632 	Assert (isKeyword (token, KEYWORD_RECORD));
633 	readToken (name);
634 	do
635 	{
636 		readToken (token);	/* should be a colon */
637 		fileSkipToCharacter (';');
638 		makeVhdlTag (name, VHDLTAG_RECORD);
639 		readToken (name);
640 	}
641 	while (!isKeyword (name, KEYWORD_END));
642 	fileSkipToCharacter (';');
643 	deleteToken (name);
644 }
645 
parseTypes(tokenInfo * const token)646 static void parseTypes (tokenInfo * const token)
647 {
648 	tokenInfo *const name = newToken ();
649 	const vhdlKind kind = isKeyword (token, KEYWORD_TYPE) ?
650 		VHDLTAG_TYPE : VHDLTAG_SUBTYPE;
651 	Assert (isKeyword (token, KEYWORD_TYPE) ||
652 		isKeyword (token, KEYWORD_SUBTYPE));
653 	readToken (name);
654 	readToken (token);
655 	if (isKeyword (token, KEYWORD_IS))
656 	{
657 		readToken (token);	/* type */
658 		if (isKeyword (token, KEYWORD_RECORD))
659 		{
660 			makeVhdlTag (name, kind);
661 			/*TODO: make tags of the record's names */
662 			parseRecord (token);
663 		}
664 		else
665 		{
666 			makeVhdlTag (name, kind);
667 		}
668 	}
669 	deleteToken (name);
670 }
671 
parseConstant(boolean local)672 static void parseConstant (boolean local)
673 {
674 	tokenInfo *const name = newToken ();
675 	readToken (name);
676 	if (local)
677 	{
678 		makeVhdlTag (name, VHDLTAG_LOCAL);
679 	}
680 	else
681 	{
682 		makeVhdlTag (name, VHDLTAG_CONSTANT);
683 	}
684 	fileSkipToCharacter (';');
685 	deleteToken (name);
686 }
687 
parseSubProgram(tokenInfo * const token)688 static void parseSubProgram (tokenInfo * const token)
689 {
690 	tokenInfo *const name = newToken ();
691 	boolean endSubProgram = FALSE;
692 	const vhdlKind kind = isKeyword (token, KEYWORD_FUNCTION) ?
693 		VHDLTAG_FUNCTION : VHDLTAG_PROCEDURE;
694 	Assert (isKeyword (token, KEYWORD_FUNCTION) ||
695 		isKeyword (token, KEYWORD_PROCEDURE));
696 	readToken (name);	/* the name of the function or procedure */
697 	readToken (token);
698 	if (isType (token, TOKEN_OPEN_PAREN))
699 	{
700 		skipToMatched (token);
701 	}
702 
703 	if (kind == VHDLTAG_FUNCTION)
704 	{
705 		if (isKeyword (token, KEYWORD_RETURN))
706 		{
707 			/* Read datatype */
708 			readToken (token);
709 			while (! isKeyword (token, KEYWORD_IS) &&
710 					! isType (token, TOKEN_SEMICOLON))
711 			{
712 				readToken (token);
713 			}
714 		}
715 	}
716 
717 	if (isType (token, TOKEN_SEMICOLON))
718 	{
719 		makeVhdlTag (name, VHDLTAG_PROTOTYPE);
720 	}
721 	else if (isKeyword (token, KEYWORD_IS))
722 	{
723 		if (kind == VHDLTAG_FUNCTION)
724 		{
725 			makeVhdlTag (name, VHDLTAG_FUNCTION);
726 			do
727 			{
728 				readToken (token);
729 				if (isKeyword (token, KEYWORD_END))
730 				{
731 					readToken (token);
732 					endSubProgram = isKeywordOrIdent (token,
733 						KEYWORD_FUNCTION, name->string);
734 					fileSkipToCharacter (';');
735 				}
736 				else
737 				{
738 					parseKeywords (token, TRUE);
739 				}
740 			} while (!endSubProgram);
741 		}
742 		else
743 		{
744 			makeVhdlTag (name, VHDLTAG_PROCEDURE);
745 			do
746 			{
747 				readToken (token);
748 				if (isKeyword (token, KEYWORD_END))
749 				{
750 					readToken (token);
751 					endSubProgram = isKeywordOrIdent (token,
752 						KEYWORD_PROCEDURE, name->string);
753 					fileSkipToCharacter (';');
754 				}
755 				else
756 				{
757 					parseKeywords (token, TRUE);
758 				}
759 			} while (!endSubProgram);
760 		}
761 	}
762 	deleteToken (name);
763 }
764 
765 /* TODO */
766 /* records */
parseKeywords(tokenInfo * const token,boolean local)767 static void parseKeywords (tokenInfo * const token, boolean local)
768 {
769 	switch (token->keyword)
770 	{
771 	case KEYWORD_END:
772 		fileSkipToCharacter (';');
773 		break;
774 	case KEYWORD_CONSTANT:
775 		parseConstant (local);
776 		break;
777 	case KEYWORD_TYPE:
778 		parseTypes (token);
779 		break;
780 	case KEYWORD_SUBTYPE:
781 		parseTypes (token);
782 		break;
783 	case KEYWORD_ENTITY:
784 		parseModule (token);
785 		break;
786 	case KEYWORD_COMPONENT:
787 		parseModule (token);
788 		break;
789 	case KEYWORD_FUNCTION:
790 		parseSubProgram (token);
791 		break;
792 	case KEYWORD_PROCEDURE:
793 		parseSubProgram (token);
794 		break;
795 	case KEYWORD_PACKAGE:
796 		parsePackage (token);
797 		break;
798 	default:
799 		break;
800 	}
801 }
802 
parseVhdlFile(tokenInfo * const token)803 static void parseVhdlFile (tokenInfo * const token)
804 {
805 	do
806 	{
807 		readToken (token);
808 		parseKeywords (token, FALSE);
809 	} while (!isKeyword (token, KEYWORD_END));
810 }
811 
findVhdlTags(void)812 static void findVhdlTags (void)
813 {
814 	tokenInfo *const token = newToken ();
815 	exception_t exception = (exception_t) (setjmp (Exception));
816 
817 	while (exception == ExceptionNone)
818 		parseVhdlFile (token);
819 
820 	deleteToken (token);
821 }
822 
VhdlParser(void)823 extern parserDefinition *VhdlParser (void)
824 {
825 	static const char *const extensions[] = { "vhdl", "vhd", NULL };
826 	parserDefinition *def = parserNew ("VHDL");
827 	def->kinds = VhdlKinds;
828 	def->kindCount = KIND_COUNT (VhdlKinds);
829 	def->extensions = extensions;
830 	def->parser = findVhdlTags;
831 	def->initialize = initialize;
832 	return def;
833 }
834 
835 /* vi:set tabstop=4 shiftwidth=4 noet: */
836