2 * $Id: vhdl.c 652 2008-04-18 03:51:47Z elliotth $
4 * Copyright (c) 2008, Nicolas Vincent
6 * This source code is released for free distribution under the terms of the
7 * GNU General Public License.
9 * This module contains functions for generating tags for VHDL files.
15 #include "general.h" /* must always come first */
17 #include <ctype.h> /* to define isalpha () */
32 #define isType(token,t) (boolean) ((token)->type == (t))
33 #define isKeyword(token,k) (boolean) ((token)->keyword == (k))
38 typedef enum eException
{ ExceptionNone
, ExceptionEOF
} exception_t
;
41 * Used to specify type of keyword.
43 typedef enum eKeywordId
{
62 KEYWORD_CONFIGURATION
,
142 /* Used to determine whether keyword is valid for the current language and
145 typedef struct sKeywordDesc
{
150 typedef enum eTokenType
{
151 TOKEN_NONE
, /* none */
152 TOKEN_OPEN_PAREN
, /* ( */
153 TOKEN_CLOSE_PAREN
, /* ) */
154 TOKEN_COMMA
, /* the comma character */
157 TOKEN_PERIOD
, /* . */
159 TOKEN_SEMICOLON
, /* the semicolon character */
163 typedef struct sTokenInfo
{
166 vString
*string
; /* the name of the token */
168 unsigned long lineNumber
; /* line number of tag */
169 fpos_t filePosition
; /* file position of line containing name */
175 static int Lang_vhdl
;
176 static jmp_buf Exception
;
178 /* Used to index into the VhdlKinds table. */
180 VHDLTAG_UNDEFINED
= -1,
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"}
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
},
243 {"impure", KEYWORD_IMPURE
},
245 {"inertial", KEYWORD_INERTIAL
},
246 {"inout", KEYWORD_INOUT
},
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
},
263 {"open", KEYWORD_OPEN
},
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
},
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
},
307 * FUNCTION DECLARATIONS
309 static void parseKeywords (tokenInfo
* const token
, boolean local
);
312 * FUNCTION DEFINITIONS
315 static boolean
isIdentChar1 (const int c
)
317 return (boolean
) (isalpha (c
) || c
== '_');
320 static boolean
isIdentChar (const int c
)
322 return (boolean
) (isalpha (c
) || isdigit (c
) || c
== '_');
325 static boolean
isIdentifierMatch (const tokenInfo
* const token
,
326 const vString
* const name
)
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 ? */
334 static boolean
isKeywordOrIdent (const tokenInfo
* const token
,
335 const keywordId keyword
, const vString
* const name
)
337 return (boolean
) (isKeyword (token
, keyword
) ||
338 isIdentifierMatch (token
, name
));
341 static tokenInfo
*newToken (void)
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 ();
353 static void deleteToken (tokenInfo
* const token
)
357 vStringDelete (token
->string
);
358 vStringDelete (token
->scope
);
367 static void parseString (vString
* const string
, const int delimiter
)
377 c
= fileGetc (); /* This maybe a ' or ". */
378 vStringPut (string
, c
);
380 else if (c
== delimiter
)
383 vStringPut (string
, c
);
385 vStringTerminate (string
);
388 /* Read a VHDL identifier beginning with "firstChar" and place it into "name".
390 static void parseIdentifier (vString
* const string
, const int firstChar
)
393 Assert (isIdentChar1 (c
));
396 vStringPut (string
, c
);
398 } while (isIdentChar (c
));
399 vStringTerminate (string
);
401 fileUngetc (c
); /* unget non-identifier character */
404 static void readToken (tokenInfo
* const token
)
408 token
->type
= TOKEN_NONE
;
409 token
->keyword
= KEYWORD_NONE
;
410 vStringClear (token
->string
);
416 token
->lineNumber
= getSourceLineNumber ();
417 token
->filePosition
= getInputFilePosition ();
419 while (c
== '\t' || c
== ' ' || c
== '\n');
424 longjmp (Exception
, (int) ExceptionEOF
);
427 token
->type
= TOKEN_OPEN_PAREN
;
430 token
->type
= TOKEN_CLOSE_PAREN
;
433 token
->type
= TOKEN_SEMICOLON
;
436 token
->type
= TOKEN_PERIOD
;
439 token
->type
= TOKEN_COMMA
;
441 case '\'': /* only single char are inside simple quotes */
442 break; /* or it is for attributes so we don't care */
444 token
->type
= TOKEN_STRING
;
445 parseString (token
->string
, c
);
446 token
->lineNumber
= getSourceLineNumber ();
447 token
->filePosition
= getInputFilePosition ();
451 if (c
== '-') /* start of a comment */
453 fileSkipToCharacter ('\n');
460 token
->type
= TOKEN_OPERATOR
;
464 if (!isIdentChar1 (c
))
465 token
->type
= TOKEN_NONE
;
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
;
475 token
->type
= TOKEN_KEYWORD
;
481 static void skipToKeyword (const keywordId keyword
)
483 tokenInfo
*const token
= newToken ();
488 while (!isKeyword (token
, keyword
));
492 static void skipToMatched (tokenInfo
* const token
)
495 tokenType open_token
;
496 tokenType close_token
;
500 case TOKEN_OPEN_PAREN
:
501 open_token
= TOKEN_OPEN_PAREN
;
502 close_token
= TOKEN_CLOSE_PAREN
;
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) )
513 if (isType (token
, open_token
))
516 while (!(isType (token
, close_token
) && (nest_level
== 0)))
519 if (isType (token
, open_token
))
523 if (isType (token
, close_token
))
535 static void makeConstTag (tokenInfo
* const token
, const vhdlKind kind
)
537 if (VhdlKinds
[kind
].enabled
)
539 const char *const name
= vStringValue (token
->string
);
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
;
550 static void makeVhdlTag (tokenInfo
* const token
, const vhdlKind kind
)
552 if (VhdlKinds
[kind
].enabled
)
555 * If a scope has been added to the token, change the token
556 * string to include the scope when making the tag.
558 if (vStringLength (token
->scope
) > 0)
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
);
568 makeConstTag (token
, kind
);
572 static void initialize (const langType language
)
576 sizeof (VhdlKeywordTable
) / sizeof (VhdlKeywordTable
[0]);
577 Lang_vhdl
= language
;
578 for (i
= 0; i
< count
; ++i
)
580 const keywordDesc
*const p
= &VhdlKeywordTable
[i
];
581 addKeyword (p
->name
, language
, (int) p
->id
);
585 static void parsePackage (tokenInfo
* const token
)
587 tokenInfo
*const name
= newToken ();
588 Assert (isKeyword (token
, KEYWORD_PACKAGE
));
590 if (isKeyword (token
, KEYWORD_BODY
))
593 makeVhdlTag (name
, VHDLTAG_PACKAGE
);
595 else if (isType (token
, TOKEN_IDENTIFIER
))
597 makeVhdlTag (token
, VHDLTAG_PACKAGE
);
602 static void parseModule (tokenInfo
* const token
)
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
));
610 if (kind
== VHDLTAG_COMPONENT
)
612 makeVhdlTag (name
, VHDLTAG_COMPONENT
);
613 skipToKeyword (KEYWORD_END
);
614 fileSkipToCharacter (';');
619 if (isKeyword (token
, KEYWORD_IS
))
621 makeVhdlTag (name
, VHDLTAG_ENTITY
);
622 skipToKeyword (KEYWORD_END
);
623 fileSkipToCharacter (';');
629 static void parseRecord (tokenInfo
* const token
)
631 tokenInfo
*const name
= newToken ();
632 Assert (isKeyword (token
, KEYWORD_RECORD
));
636 readToken (token
); /* should be a colon */
637 fileSkipToCharacter (';');
638 makeVhdlTag (name
, VHDLTAG_RECORD
);
641 while (!isKeyword (name
, KEYWORD_END
));
642 fileSkipToCharacter (';');
646 static void parseTypes (tokenInfo
* const token
)
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
));
655 if (isKeyword (token
, KEYWORD_IS
))
657 readToken (token
); /* type */
658 if (isKeyword (token
, KEYWORD_RECORD
))
660 makeVhdlTag (name
, kind
);
661 /*TODO: make tags of the record's names */
666 makeVhdlTag (name
, kind
);
672 static void parseConstant (boolean local
)
674 tokenInfo
*const name
= newToken ();
678 makeVhdlTag (name
, VHDLTAG_LOCAL
);
682 makeVhdlTag (name
, VHDLTAG_CONSTANT
);
684 fileSkipToCharacter (';');
688 static void parseSubProgram (tokenInfo
* const token
)
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 */
698 if (isType (token
, TOKEN_OPEN_PAREN
))
700 skipToMatched (token
);
703 if (kind
== VHDLTAG_FUNCTION
)
705 if (isKeyword (token
, KEYWORD_RETURN
))
709 while (! isKeyword (token
, KEYWORD_IS
) &&
710 ! isType (token
, TOKEN_SEMICOLON
))
717 if (isType (token
, TOKEN_SEMICOLON
))
719 makeVhdlTag (name
, VHDLTAG_PROTOTYPE
);
721 else if (isKeyword (token
, KEYWORD_IS
))
723 if (kind
== VHDLTAG_FUNCTION
)
725 makeVhdlTag (name
, VHDLTAG_FUNCTION
);
729 if (isKeyword (token
, KEYWORD_END
))
732 endSubProgram
= isKeywordOrIdent (token
,
733 KEYWORD_FUNCTION
, name
->string
);
734 fileSkipToCharacter (';');
738 parseKeywords (token
, TRUE
);
740 } while (!endSubProgram
);
744 makeVhdlTag (name
, VHDLTAG_PROCEDURE
);
748 if (isKeyword (token
, KEYWORD_END
))
751 endSubProgram
= isKeywordOrIdent (token
,
752 KEYWORD_PROCEDURE
, name
->string
);
753 fileSkipToCharacter (';');
757 parseKeywords (token
, TRUE
);
759 } while (!endSubProgram
);
767 static void parseKeywords (tokenInfo
* const token
, boolean local
)
769 switch (token
->keyword
)
772 fileSkipToCharacter (';');
774 case KEYWORD_CONSTANT
:
775 parseConstant (local
);
780 case KEYWORD_SUBTYPE
:
786 case KEYWORD_COMPONENT
:
789 case KEYWORD_FUNCTION
:
790 parseSubProgram (token
);
792 case KEYWORD_PROCEDURE
:
793 parseSubProgram (token
);
795 case KEYWORD_PACKAGE
:
796 parsePackage (token
);
803 static void parseVhdlFile (tokenInfo
* const token
)
808 parseKeywords (token
, FALSE
);
809 } while (!isKeyword (token
, KEYWORD_END
));
812 static void findVhdlTags (void)
814 tokenInfo
*const token
= newToken ();
815 exception_t exception
= (exception_t
) (setjmp (Exception
));
817 while (exception
== ExceptionNone
)
818 parseVhdlFile (token
);
823 extern parserDefinition
*VhdlParser (void)
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
;
835 /* vi:set tabstop=4 shiftwidth=4 noet: */