2013-07-12 57 views
0

我想用Flex & Bison实现表达式验证工具。在我的工具中,我接受以';'结尾的表达式并检查表达式内部是否有问题。发生错误时,我想获取错误标记的正确位置。问题是,当发生不止一个错误时,我总是弄错了位置。如何以正确的顺序报告错误消息

解析器:

%{ 
# include <stdio.h> 
# include <stdlib.h> 
# include "roofexp.h" 
# include "symbol.h" 
%} 

%locations 

%union { 
    struct ast *a; 
    double d; 
    struct symbol *s;  /* which symbol */ 
    struct symlist *sl; 
    int fn;   /* which function */ 
    char *str; 
} 

/* edeclare tokens */ 
%token <d> NUMBER 
%token <str> STRING 
%token <s> NAME 
%token <fn> FUNC 
%token EOL 

%token IF THEN ELSE WHILE DO LET 


%nonassoc <fn> CMP 
%right '=' 
%left '+' '-' 
%left '*' '/' 
%nonassoc '|' UMINUS 

%type <a> exp stmt list explist 

%start calclist 

%% 
calclist: /* nothing */ 
    | calclist stmt ';' { 
          if(debug) 
           dumpast($2, 0); 
          printf("= %4.4g\n> ", eval($2)); 
          treefree($2); 
          free_string_table(); 
          FreeSymbolTable(); 
         } 
    | calclist error EOL { YYERROR; } 
; 

stmt: IF exp THEN list   { $$ = newflow('I', $2, $4, NULL); } 
    | IF exp THEN list ELSE list { $$ = newflow('I', $2, $4, $6); } 
    | exp 
; 

list: /* nothing */ { $$ = NULL; } 
    | stmt ';' list { if ($3 == NULL) 
        $$ = $1; 
         else 
      $$ = newast('L', $1, $3); 
        } 
    ; 

exp: exp CMP exp   { $$ = newcmp($2, $1, $3); } 
    | exp '+' exp   { $$ = newast('+', $1,$3); } 
    | exp '-' exp   { $$ = newast('-', $1,$3);} 
    | exp '*' exp   { $$ = newast('*', $1,$3); } 
    | exp '/' exp   { 
           $$ = newast('/', $1, $3); 
          } 
    | '|' exp    { $$ = newast('|', $2, NULL); } 
    | '(' exp ')'   { $$ = $2; } 
    | '-' exp %prec UMINUS { $$ = newast('M', $2, NULL); } 
    | NUMBER    { $$ = newnum($1); } 
    | STRING    { $$ = newstr($1); add_string($1); } 
    | FUNC '(' explist ')' { $$ = newfunc($1, $3); } 
    | NAME     { $$ = newref($1); } 
    | NAME '=' exp   { $$ = newasgn($1, $3); } 
    | NAME '(' explist ')' { $$ = newcall($1, $3); } 
; 

explist: exp     
     | exp ',' explist { $$ = newast('L', $1, $3); } 
; 

词法分析器:

%% 
%{ 
#include <stdio.h> 
#include <stdlib.h> 
#include <math.h> 
# include "roofexp.h" 
# include "roofexp.tab.h" 
# include "symbol.h" 

/* handle locations */ 
int yycolumn = 1; 
#define YY_USER_ACTION \ 
    yylloc.first_line = yylloc.last_line = yylineno; \ 
    yylloc.first_column = yycolumn; \ 
    yylloc.last_column = yycolumn + yyleng - 1; \ 
    yycolumn += yyleng; 
%} 

%option yylineno noyywrap 
/* float exponent */ 
EXP ([Ee][-+]?[0-9]+) 

%% 
/* single character ops */ 
"#" | 
"+" | 
"-" | 
"*" | 
"/" | 
"=" | 
"|" | 
"," | 
";" | 
"(" | 
")"  { return yytext[0]; } 

/* comparison ops */ 
">"  { yylval.fn = 1; return CMP; } 
"<"  { yylval.fn = 2; return CMP; } 
"<>" { yylval.fn = 3; return CMP; } 
"==" { yylval.fn = 4; return CMP; } 
">=" { yylval.fn = 5; return CMP; } 
"<=" { yylval.fn = 6; return CMP; } 

/* keywords */ 

"if" { return IF; } 
"then" { return THEN; } 
"else" { return ELSE; } 
"while" { return WHILE; } 
"do" { return DO; } 
"let" { return LET;} 

/* built in functions */ 
"sin" { yylval.fn = FUNC_sin; return FUNC; } 
"cos" { yylval.fn = FUNC_cos; return FUNC; } 
"pow" { yylval.fn = FUNC_pow; return FUNC; } 
"GetDz" { yylval.fn = FUNC_GetDz; return FUNC;} 

/* debug hack */ 
"debug"[0-9]+ { debug = atoi(&yytext[5]); printf("debug set to %d\n", debug); } 

/* names */ 
[_a-zA-Z][_a-zA-Z0-9]* { 
         if(LookupSymbolTable(yytext, 0, VARIABLE) == NULL) 
          yyerror("未定义的变量: %s", yytext); 
         else 
          yylval.s = lookup(yytext); return NAME; 
         } 

[0-9]+"."[0-9]*{EXP}? | 
"."?[0-9]+{EXP}? { yylval.d = atof(yytext); return NUMBER; } 

\"[^\"\n]*\" { printf("string=%s\n", yytext); } 
\"[^\"\n]*$  { yyerror("unterminated string literal: %s\n", yytext); } 

"//".* 
[ \t] 
\n  { yycolumn = 1; } 
.  { yyerror("Mystery character %c\n", *yytext); } 
%% 

表达:

pow(2)+ 
pow(2, 4) 
; 

回声:

3-1: error: at ';': too few arguments for call 

但是正确的位置应该是1-1! 我的词法分析器和解析器有什么问题。如果我想获得正确的位置,我该怎么做?

回答

1

如果您显示生成错误消息的代码,但是我的猜测是yyerror函数仅使用当前值yyloc(这将对应于最后一个读取的令牌),这将有所帮助。因此,如果错误(如“太少的参数”)直到表达式末尾的分号才被诊断出来,则yyloc将具有分号的位置,如您的示例所示。

当您将野牛指定为%locations时,野牛会为每个非终端以及非终端保留一个源范围。 (默认情况下,范围从生产中第一个组件的起始位置开始到最后一个组件的末尾)。您可以使用@N(对于组件N)或@$(对于整个范围的减少)。

但是,在您的calclist stmt ';'操作中使用@$(假设您在调用eval过程中产生了错误)不会给您更高的精度。在这一点上您能够做的最好的事情是将错误报告为源代码范围1:1-3:1中的某处。为了生成更准确的消息,您需要在AST中的每个节点中包含位置。那么eval只需要知道哪个AST节点导致错误。

当您解析函数调用时,您可能会产生像too few arguments这样的错误,假设您知道每个函数在该点需要多少个参数。但这很难保持,不太一般。

虽然野牛做了很多维护位置所需的工作,但您必须自己保留位置信息与AST节点的关联。这样做的通常方法是在每个AST节点中包含一个YYLTYPE结构(您的struct ast);您可以将相应的位置复制到创建节点的操作中的AST节点。

相关问题