From 04b239a5fbb908f93d5fca00fc175326671b2ee9 Mon Sep 17 00:00:00 2001 From: Stephen Williams Date: Wed, 15 Dec 2010 07:36:47 +0200 Subject: [PATCH] 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. --- vhdlpp/lexor.lex | 22 ++++- vhdlpp/lexor_keyword.gperf | 12 +++ vhdlpp/main.cc | 42 ++++++++- vhdlpp/parse.y | 185 ++++++++++++++++++++++++++++++++++++- vhdlpp/parse_api.h | 22 +++++ 5 files changed, 278 insertions(+), 5 deletions(-) diff --git a/vhdlpp/lexor.lex b/vhdlpp/lexor.lex index 42ea32bda..eb6fd0579 100644 --- a/vhdlpp/lexor.lex +++ b/vhdlpp/lexor.lex @@ -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); +} diff --git a/vhdlpp/lexor_keyword.gperf b/vhdlpp/lexor_keyword.gperf index 69dd9d324..f97b68884 100644 --- a/vhdlpp/lexor_keyword.gperf +++ b/vhdlpp/lexor_keyword.gperf @@ -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; diff --git a/vhdlpp/main.cc b/vhdlpp/main.cc index f42548fae..7beedc93f 100644 --- a/vhdlpp/main.cc +++ b/vhdlpp/main.cc @@ -38,17 +38,33 @@ const char NOTICE[] = " 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.\n" ; -# include -# include +# include "parse_api.h" +# include +# include +# include #if defined(HAVE_GETOPT_H) # include #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; } diff --git a/vhdlpp/parse.y b/vhdlpp/parse.y index c05e13ea5..77919ac4d 100644 --- a/vhdlpp/parse.y +++ b/vhdlpp/parse.y @@ -21,9 +21,12 @@ # include "vhdlpp_config.h" # include "parse_api.h" +# include 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; } diff --git a/vhdlpp/parse_api.h b/vhdlpp/parse_api.h index 31d528dce..7f24c9458 100644 --- a/vhdlpp/parse_api.h +++ b/vhdlpp/parse_api.h @@ -19,6 +19,8 @@ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ +# include + /* * 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