Flesh out VHDL parser engine.

Add enough rules to parse a simple VHDL program:
  Parse library and use clauses,
  Parse entity declarations, and
  Parse architecture bodies.

Add some parser infrastructure:
  Handle syntax errors with useful error messages,
  Include file name and line numbers in parse errors,
  Add some parser debug aids.
This commit is contained in:
Stephen Williams 2010-12-15 07:36:47 +02:00
parent 8cf1fd1820
commit 04b239a5fb
5 changed files with 278 additions and 5 deletions

View File

@ -25,6 +25,7 @@
# include "parse_api.h"
# include "lexor_keyword.h"
# include "parse.h"
extern int lexor_keyword_code (const char*str, unsigned len);
@ -35,7 +36,7 @@ extern int lexor_keyword_code (const char*str, unsigned len);
* the name as it exists in the list (and delete the passed string.)
* If the name is new, it will be added to the list.
*/
YYLTYPE yylloc;
extern YYLTYPE yylloc;
static int comment_enter;
@ -71,3 +72,22 @@ W [ \t\b\f\r]+
int rc = lexor_keyword_code(yytext, yyleng);
return rc;
}
/* Compound symbols */
"<=" { return LEQ; }
">=" { return GEQ; }
":=" { return VASSIGN; }
. { return yytext[0]; }
%%
extern void yyparse_set_filepath(const char*path);
void reset_lexor(FILE*fd, const char*path)
{
yylloc.first_line = 1;
yyrestart(fd);
yyparse_set_filepath(path);
}

View File

@ -17,7 +17,19 @@ struct lexor_keyword { const char*name; int mask; int tokenType; };
%%
abs, GN_KEYWORD_2008, K_abs
access, GN_KEYWORD_2008, K_access
all, GN_KEYWORD_2008, K_all
and, GN_KEYWORD_2008, K_and
architecture, GN_KEYWORD_2008, K_architecture
begin, GN_KEYWORD_2008, K_begin
end, GN_KEYWORD_2008, K_end
entity, GN_KEYWORD_2008, K_entity
in, GN_KEYWORD_2008, K_in
is, GN_KEYWORD_2008, K_is
library, GN_KEYWORD_2008, K_library
of, GN_KEYWORD_2008, K_of
out, GN_KEYWORD_2008, K_out
port, GN_KEYWORD_2008, K_port
use, GN_KEYWORD_2008, K_use
%%
int lexor_keyword_mask = GN_KEYWORD_2008;

View File

@ -38,17 +38,33 @@ const char NOTICE[] =
" 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n"
;
# include <stdio.h>
# include <stdlib.h>
# include "parse_api.h"
# include <cstdio>
# include <cstdlib>
# include <cstring>
#if defined(HAVE_GETOPT_H)
# include <getopt.h>
#endif
static void process_debug_token(const char*word)
{
if (strcmp(word, "yydebug") == 0) {
yydebug = 1;
} else if (strcmp(word, "no-yydebug") == 0) {
yydebug = 0;
}
}
int main(int argc, char*argv[])
{
int opt;
int rc;
while ( (opt=getopt(argc, argv, "vV")) != EOF) switch (opt) {
while ( (opt=getopt(argc, argv, "D:vV")) != EOF) switch (opt) {
case 'D':
process_debug_token(optarg);
break;
case 'v':
fprintf(stderr, "Icarus Verilog VHDL Parse version "
@ -66,5 +82,25 @@ int main(int argc, char*argv[])
}
for (int idx = optind ; idx < argc ; idx += 1) {
parse_errors = 0;
FILE*fd = fopen(argv[idx], "r");
if (fd == 0) {
perror(argv[idx]);
return 1;
}
reset_lexor(fd, argv[idx]);
rc = yyparse();
fprintf(stderr, "yyparse() returns %d, parse_errors=%d\n", rc, parse_errors);
if (parse_errors > 0) {
fprintf(stderr, "%d errors parsing %s\n", parse_errors, argv[idx]);
return 2;
}
fclose(fd);
}
return 0;
}

View File

@ -21,9 +21,12 @@
# include "vhdlpp_config.h"
# include "parse_api.h"
# include <cstdarg>
static void yyerror(const char*msg);
static void errormsg(const YYLTYPE&loc, const char*msg, ...);
int parse_errors = 0;
%}
/* The keywords are all tokens. */
@ -53,12 +56,192 @@ static void yyerror(const char*msg);
%token K_xnor K_xor
/* Identifiers that are not keywords are identifiers. */
%token IDENTIFIER
/* compound symbols */
%token LEQ GEQ VASSIGN
%%
main : ;
/* The design_file is the root for the VHDL parse. */
design_file : design_units ;
architecture_body
: K_architecture IDENTIFIER
K_of IDENTIFIER
K_is
K_begin architecture_statement_part K_end K_architecture_opt ';'
| K_architecture IDENTIFIER
K_of IDENTIFIER
K_is
K_begin error K_end K_architecture_opt ';'
{ errormsg(@1, "Syntax error in architecture statement.\n"); yyerrok; }
| K_architecture error ';'
{ errormsg(@1, "Syntax error in architecture body.\n"); yyerrok; }
;
/* The architecture_statement_part is a list of concurrent
statements. */
architecture_statement_part
: architecture_statement_part concurrent_statement
| concurrent_statement
;
concurrent_signal_assignment_statement
: IDENTIFIER LEQ waveform ';'
;
concurrent_statement
: concurrent_signal_assignment_statement
;
context_clause : context_items | ;
context_item
: library_clause
| use_clause
;
context_items
: context_items context_item
| context_item
;
design_unit
: context_clause library_unit
| error { errormsg(@1, "Invalid design_unit\n"); }
;
design_units
: design_units design_unit
| design_unit
;
entity_declaration
: K_entity IDENTIFIER K_is entity_header K_end K_entity ';'
;
entity_header
: port_clause
;
expression
: expression_logical
;
expression_logical
: relation K_and relation
| relation K_or relation
;
factor : primary ;
/* The interface_element is also an interface_declaration */
interface_element
: IDENTIFIER ':' mode IDENTIFIER
;
interface_list
: interface_list ';' interface_element
| interface_element
;
library_clause
: K_library logical_name_list ';'
| K_library error ';'
{ errormsg(@1, "Syntax error in library clause.\n"); yyerrok; }
;
/* Collapse the primary_unit and secondary_unit of the library_unit
into this single set of rules. */
library_unit
: entity_declaration
| architecture_body
;
logical_name : IDENTIFIER ;
logical_name_list
: logical_name_list ',' logical_name
| logical_name
;
mode : K_in | K_out ;
port_clause : K_port '(' interface_list ')' ';' ;
primary
: IDENTIFIER
;
relation : shift_expression ;
selected_name
: IDENTIFIER '.' K_all
| IDENTIFIER '.' IDENTIFIER '.' K_all
;
selected_names
: selected_names ',' selected_name
| selected_name
;
shift_expression : simple_expression ;
simple_expression : term ;
term : factor ;
use_clause
: K_use selected_names ';'
| K_use error ';'
{ errormsg(@1, "Syntax error in use clause.\n"); yyerrok; }
;
waveform
: waveform_elements
| K_unaffected
;
waveform_elements
: waveform_elements ',' waveform_element
| waveform_element
;
waveform_element
: expression
| K_null
;
/* Some keywords are optional in some contexts. In all such cases, a
similar rule is used, as described here. */
K_architecture_opt : K_architecture | ;
%%
static void yyerror(const char*msg)
{
fprintf(stderr, "%s\n", msg);
parse_errors += 1;
}
static const char*file_path = "";
static void errormsg(const YYLTYPE&loc, const char*fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s:%d: ", file_path, loc.first_line);
vfprintf(stderr, fmt, ap);
va_end(ap);
parse_errors += 1;
}
/*
* This is used only by the lexor, to set the file path used in error
* messages.
*/
void yyparse_set_filepath(const char*path)
{
file_path = path;
}

View File

@ -19,6 +19,8 @@
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
# include <cstdio>
/*
* The vlltype supports the passing of detailed source file location
* information between the lexical analyzer and the parser. Defining
@ -33,6 +35,26 @@ struct yyltype {
};
# define YYLTYPE struct yyltype
/*
* The reset_lexor function takes the fd and makes it the input file
* for the lexor. The path argument is used in lexor/parser error messages.
*/
extern void reset_lexor(FILE*fd, const char*path);
extern int yylex(void);
extern int yyparse(void);
/*
* Set this to a non-zero value to enable parser debug output.
*/
extern int yydebug;
/*
* The parser counts the errors that is handed in the parse_errors
* variable. For a clean compile, this value should not change. (The
* caller sets its initial value.)
*/
extern int parse_errors;
#endif