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:
parent
8cf1fd1820
commit
04b239a5fb
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
185
vhdlpp/parse.y
185
vhdlpp/parse.y
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue