2013-07-12 29 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節點。