From 89aa08e1aa2e7811c4bf91b33eafe5d479130bb4 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Sat, 19 Mar 2011 18:26:43 -0700 Subject: [PATCH] Add parser support for architecture declaratives and component instantiations Also include support for "sorry" messages so that we have a place to hang unsupported but properly parsed constructs. In the process of doing this, I also encountered and add parser support for indexed names. And matching "sorry" messages. --- vhdlpp/main.cc | 9 +- vhdlpp/parse.y | 228 ++++++++++++++++++++++++++++++++++++++++----- vhdlpp/parse_api.h | 6 +- 3 files changed, 215 insertions(+), 28 deletions(-) diff --git a/vhdlpp/main.cc b/vhdlpp/main.cc index c230160fd..10455aff9 100644 --- a/vhdlpp/main.cc +++ b/vhdlpp/main.cc @@ -96,6 +96,7 @@ int main(int argc, char*argv[]) for (int idx = optind ; idx < argc ; idx += 1) { parse_errors = 0; + parse_sorrys = 0; FILE*fd = fopen(argv[idx], "r"); if (fd == 0) { perror(argv[idx]); @@ -105,10 +106,14 @@ int main(int argc, char*argv[]) reset_lexor(fd, argv[idx]); rc = yyparse(); if (verbose_flag) - fprintf(stderr, "yyparse() returns %d, parse_errors=%d\n", rc, parse_errors); + fprintf(stderr, "yyparse() returns %d, parse_errors=%d, parse_sorrys=%d\n", rc, parse_errors, parse_sorrys); if (parse_errors > 0) { - fprintf(stderr, "%d errors parsing %s\n", parse_errors, argv[idx]); + fprintf(stderr, "Encountered %d errors parsing %s\n", parse_errors, argv[idx]); + return 2; + } + if (parse_sorrys > 0) { + fprintf(stderr, "Encountered %d unsupported constructs parsing %s\n", parse_sorrys, argv[idx]); return 2; } diff --git a/vhdlpp/parse.y b/vhdlpp/parse.y index 21da62035..649364479 100644 --- a/vhdlpp/parse.y +++ b/vhdlpp/parse.y @@ -52,6 +52,7 @@ inline void FILE_NAME(LineInfo*tmp, const struct yyltype&where) static void yyerror(const char*msg); int parse_errors = 0; +int parse_sorrys = 0; %} @@ -114,13 +115,17 @@ int parse_errors = 0; %type direction -%type interface_element interface_list entity_header port_clause +%type name +%type interface_element interface_list entity_header +%type port_clause port_clause_opt %type mode -%type concurrent_statement concurrent_signal_assignment_statement +%type concurrent_statement component_instantiation_statement concurrent_signal_assignment_statement %type architecture_statement_part -%type expression expression_logical factor primary relation +%type expression factor primary relation +%type expression_logical expression_logical_and expression_logical_or +%type expression_logical_xnor expression_logical_xor %type shift_expression simple_expression term waveform_element %type waveform waveform_elements @@ -138,29 +143,31 @@ design_file : design_units ; architecture_body : K_architecture IDENTIFIER K_of IDENTIFIER - K_is + K_is block_declarative_items_opt K_begin architecture_statement_part K_end K_architecture_opt identifier_opt ';' - { Architecture*tmp = new Architecture(lex_strings.make($2), *$7); + { Architecture*tmp = new Architecture(lex_strings.make($2), *$8); FILE_NAME(tmp, @1); bind_architecture_to_entity($4, tmp); - if ($10 && tmp->get_name() != $10) + if ($11 && tmp->get_name() != $11) errormsg(@2, "Architecture name doesn't match closing name\n"); delete[]$2; delete[]$4; - delete $7; - if ($10) delete[]$10 + delete $8; + if ($11) delete[]$11; } | K_architecture IDENTIFIER K_of IDENTIFIER - K_is + K_is block_declarative_items_opt K_begin error K_end K_architecture_opt identifier_opt ';' - { errormsg(@1, "Syntax error in architecture statement.\n"); yyerrok; } + { errormsg(@8, "Errors in architecture statements.\n"); yyerrok; } | K_architecture error ';' - { errormsg(@1, "Syntax error in architecture body.\n"); yyerrok; } + { errormsg(@2, "Errors in architecture body.\n"); yyerrok; } ; - /* The architecture_statement_part is a list of concurrent - statements. */ +/* + * The architecture_statement_part is a list of concurrent + * statements. + */ architecture_statement_part : architecture_statement_part concurrent_statement { std::list*tmp = $1; @@ -172,10 +179,85 @@ architecture_statement_part if ($1) tmp->push_back($1); $$ = tmp; } + + | error ';' + { $$ = 0; + errormsg(@1, "Syntax error in architecture statement.\n"); + yyerrok; + } + ; + +association_element + : name ARROW name + ; + +association_list + : association_list ',' association_element + | association_element + ; + +block_declarative_item + : K_signal identifier_list ':' subtype_indication ';' + { sorrymsg(@1, "Signal declarations are not supported.\n"); } + + | K_component IDENTIFIER K_is_opt + port_clause_opt + K_end K_component identifier_opt ';' + { perm_string name = lex_strings.make($2); + if ($7 && name != $7) { + errormsg(@7, "Identifier %s doesn't match component name %s\n", + $7, name.str()); + } + + delete[]$2; + if ($4) delete $4; + delete[]$7; + sorrymsg(@1, "Component declarations not supported.\n"); + } + + /* Various error handling rules for block_declarative_item... */ + + | K_signal error ';' + { errormsg(@2, "Syntax error declaring signals.\n"); yyerrok; } + | error ';' + { errormsg(@1, "Syntax error in block declarations.\n"); yyerrok; } + + | K_component IDENTIFIER K_is_opt error K_end K_component identifier_opt ';' + { errormsg(@4, "Syntax error in component declaration.\n"); yyerrok; } + ; + +/* + * The block_declarative_items rule matches "{ block_declarative_item }" + * which is a synonym for "architecture_declarative_part" and + * "block_declarative_part". + */ +block_declarative_items + : block_declarative_items block_declarative_item + | block_declarative_item + ; + +block_declarative_items_opt + : block_declarative_items + | + ; + +component_instantiation_statement + : IDENTIFIER ':' IDENTIFIER port_map_aspect_opt ';' + { sorrymsg(@1, "Component instantiation statements are not supported.\n"); + delete[]$1; + delete[]$3; + $$ = 0; + } + | IDENTIFIER ':' IDENTIFIER error ';' + { errormsg(@4, "Errors in component instantiation.\n"); + delete[]$1; + delete[]$3; + $$ = 0; + } ; concurrent_signal_assignment_statement - : IDENTIFIER LEQ waveform ';' + : name LEQ waveform ';' { perm_string targ_name = lex_strings.make($1); SignalAssignment*tmp = new SignalAssignment(targ_name, *$3); FILE_NAME(tmp, @1); @@ -184,7 +266,7 @@ concurrent_signal_assignment_statement delete[]$1; delete $3; } - | IDENTIFIER LEQ error ';' + | name LEQ error ';' { errormsg(@2, "Syntax error in signal assignment waveform.\n"); $$ = 0; yyerrok; @@ -192,7 +274,8 @@ concurrent_signal_assignment_statement ; concurrent_statement - : concurrent_signal_assignment_statement + : component_instantiation_statement + | concurrent_signal_assignment_statement ; context_clause : context_items | ; @@ -267,19 +350,42 @@ expression { $$ = $1; } ; +/* + * The expression_logical matches the logical_expression from the + * standard. Note that this is a little more tricky then usual because + * of the strange VHDL rules for logical expressions. We have to + * account for things like this: + * + * and and ... + * + * which is matched by the standard rule: + * + * logical_expression ::= + * ... + * relation { and relation } + * + * The {} is important, and implies that "and" can be strung together + * with other "and" operators without parentheses. This is true for + * "and", "or", "xor", and "xnor". The tricky part is that these + * cannot be mixed. For example, this is not OK: + * + * and or + * + * Also note that "nand" and "nor" can not be chained in this manner. + */ expression_logical : relation { $$ = $1; } - | relation K_and relation + | relation K_and expression_logical_and { ExpLogical*tmp = new ExpLogical(ExpLogical::AND, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } - | relation K_or relation + | relation K_or expression_logical_or { ExpLogical*tmp = new ExpLogical(ExpLogical::OR, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } - | relation K_xor relation + | relation K_xor expression_logical_xor { ExpLogical*tmp = new ExpLogical(ExpLogical::XOR, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; @@ -294,13 +400,53 @@ expression_logical FILE_NAME(tmp, @2); $$ = tmp; } - | relation K_xnor relation + | relation K_xnor expression_logical_xnor { ExpLogical*tmp = new ExpLogical(ExpLogical::XNOR, $1, $3); FILE_NAME(tmp, @2); $$ = tmp; } ; +expression_logical_and + : relation + { $$ = $1; } + | expression_logical_and K_and relation + { ExpLogical*tmp = new ExpLogical(ExpLogical::AND, $1, $3); + FILE_NAME(tmp, @2); + $$ = tmp; + } + ; + +expression_logical_or + : relation + { $$ = $1; } + | expression_logical_or K_or relation + { ExpLogical*tmp = new ExpLogical(ExpLogical::OR, $1, $3); + FILE_NAME(tmp, @2); + $$ = tmp; + } + ; + +expression_logical_xnor + : relation + { $$ = $1; } + | expression_logical_xnor K_xnor relation + { ExpLogical*tmp = new ExpLogical(ExpLogical::XNOR, $1, $3); + FILE_NAME(tmp, @2); + $$ = tmp; + } + ; + +expression_logical_xor + : relation + { $$ = $1; } + | expression_logical_xor K_xor relation + { ExpLogical*tmp = new ExpLogical(ExpLogical::XOR, $1, $3); + FILE_NAME(tmp, @2); + $$ = tmp; + } + ; + factor : primary { $$ = $1; } @@ -407,6 +553,15 @@ mode | K_out { $$ = PORT_OUT; } ; +name + : IDENTIFIER + { $$ = $1; } + | IDENTIFIER '(' expression ')' + { sorrymsg(@3, "Indexed names not supported yet.\n"); + $$ = $1; + } + ; + port_clause : K_port '(' interface_list ')' ';' { $$ = $3; } @@ -417,8 +572,19 @@ port_clause } ; +port_clause_opt : port_clause {$$ = $1;} | {$$ = 0;} ; + +port_map_aspect + : K_port K_map '(' association_list ')' + ; + +port_map_aspect_opt + : port_map_aspect + | + ; + primary - : IDENTIFIER + : name { ExpName*tmp = new ExpName(lex_strings.make($1)); FILE_NAME(tmp, @1); delete[]$1; @@ -546,12 +712,13 @@ waveform_element /* Some keywords are optional in some contexts. In all such cases, a similar rule is used, as described here. */ K_architecture_opt : K_architecture | ; -K_entity_opt : K_entity | ; +K_entity_opt : K_entity | ; +K_is_opt : K_is | ; %% -static void yyerror(const char*msg) +static void yyerror(const char* /*msg*/) { - fprintf(stderr, "%s\n", msg); + //fprintf(stderr, "%s\n", msg); parse_errors += 1; } @@ -562,12 +729,23 @@ void errormsg(const YYLTYPE&loc, const char*fmt, ...) va_list ap; va_start(ap, fmt); - fprintf(stderr, "%s:%d: ", file_path, loc.first_line); + fprintf(stderr, "%s:%d: error: ", file_path, loc.first_line); vfprintf(stderr, fmt, ap); va_end(ap); parse_errors += 1; } +void sorrymsg(const YYLTYPE&loc, const char*fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + + fprintf(stderr, "%s:%d: sorry: ", file_path, loc.first_line); + vfprintf(stderr, fmt, ap); + va_end(ap); + parse_sorrys += 1; +} + /* * This is used only by the lexor, to set the file path used in error * messages. diff --git a/vhdlpp/parse_api.h b/vhdlpp/parse_api.h index e675d4e45..73ce4201d 100644 --- a/vhdlpp/parse_api.h +++ b/vhdlpp/parse_api.h @@ -60,6 +60,8 @@ extern int yyparse(void); */ extern void errormsg(const YYLTYPE&loc, const char*fmt, ...) __attribute__((format (printf, 2, 3))); +extern void sorrymsg(const YYLTYPE&loc, const char*fmt, ...) __attribute__((format (printf, 2, 3))); + /* * Set this to a non-zero value to enable parser debug output. */ @@ -68,8 +70,10 @@ 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.) + * caller sets its initial value.) The sorrys are the count of + * unsupported constructs that are encountered. */ extern int parse_errors; +extern int parse_sorrys; #endif