2013-07-12 3 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); } 
%% 

Expression:

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

Echo:

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

Но правильное положение должно быть 1-1! Что не так с моим лексером и парсером. И если я хочу получить правильное положение, как я буду делать?

ответ

1

Это поможет, если вы указали код, который генерирует сообщение об ошибке, но я предполагаю, что ваша функция yyerror просто использует текущее значение yyloc, которое будет соответствовать последнему прочитанному токену. Следовательно, если ошибки (например, «слишком мало аргументов») не диагностируются до точки с запятой в конце выражения, то yyloc будет располагать точку с запятой, как показано в вашем примере.

Когда вы укажете %locations для бизона, то bison сохраняет исходный диапазон для всех нетерминальных, а также для нетерминалов. (По умолчанию диапазон начинается с начала первого компонента в производстве до конца последнего.) Вы можете получить доступ к структуре местоположения из действия бисона, используя @N (для компонента N) или @$ (для всего диапазона сокращения).

Однако, используя @$ в вашем действии calclist stmt ';' (при условии, что ваша ошибка возникла во время разговора до eval), вы не получите больше точности. Лучшее, что вы могли бы сделать в этот момент, - сообщить об ошибке как где-то в диапазоне исходного кода 1:1-3:1. Чтобы получать более точные сообщения, вам нужно будет указать местоположение в каждом узле AST. Тогда eval должен знать только, какой из узлов AST вызвал ошибку.

Конечно, вы можете создавать ошибки, такие как too few arguments при анализе вызова функции, если вы знаете, сколько аргументов требуется каждой функции в этой точке. Но это сложнее поддерживать и быть менее общим.

Несмотря на то, что бизон выполняет большую работу, необходимую для поддержания местоположения, вам необходимо поддерживать связь информации о местоположении с узлом AST самостоятельно. Обычный способ сделать это - включить структуру YYLTYPE в каждый узел AST (ваш struct ast); вы можете скопировать соответствующее местоположение в узел AST в действии, которое создает узел.

Смежные вопросы