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!我的词法分析器和解析器出了什么问题。如果我想获得正确的位置,我应该怎么做?

4

1 回答 1

1

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

当您指定%locationsbison 时,bison 会为每个非终端以及非终端保留一个源范围。(默认情况下,范围从生产中的第一个组件的开始到最后一个组件的结束。)您可以使用@N(for component N) 或@$(for整个归约范围) 从野牛操作访问 location 结构)。

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

当然,您可能会too few arguments在解析函数调用时产生错误,假设您知道此时每个函数需要多少个参数。但这更难维护,也更不通用。

尽管 bison 做了很多维护位置所需的工作,但您必须自己保持位置信息与 AST 节点的关联。通常的做法是YYLTYPE在每个 AST 节点(你的struct ast)中包含一个结构;您可以在创建节点的操作中将适当的位置复制到 AST 节点中。

于 2013-07-12T16:25:22.100 回答