北美野牛解析器计算器

时间:2013-11-22 10:16:43

标签: c++ c bison flex-lexer

这是我的野牛代码:

%}

%union  {   
int    int_val;
}


%left '+' '-'
%nonassoc '(' ')'

%token INTEGER PRINT 
%type <int_val> expr_int INTEGER

%%
program: command '\n'   { return 0; }
        ;
command: print_expr
        ;
print_expr: PRINT expr_int          { cout<<$2<<endl; }
expr_int: expr_int '+' expr_int     { $$ = $1 + $3; }
        | expr_int '-' expr_int     { $$ = $1 - $3; }
        | '(' expr_int ')'                  { $$ = $2; }
        | INTEGER
        ;

这是灵活代码:

%}

INTEGER     [1-9][0-9]*|0
BINARY      [-+]
WS              [ \t]+
BRACKET     [\(\)]

%%
print{WS}               { return PRINT; }
{INTEGER}               {   yylval.int_val=atoi(yytext); return INTEGER; }
{BINARY}|\n     {   return *yytext; }
{BRACKET}               {   return *yytext; }
{WS}                    {}
.                       { return *yytext; }

%%

//////////////////////////////////////////////////
int yywrap(void) { return 1;  }  // Callback at end of file

该计划的无效输入是:

print 5

输出:

5

输入:

print (1+1)

输出:

2

但出于某种原因,对于以下输入我不会立即得到错误:

print (1+1))

输出:

2
some error

输入:

print 5!

输出:

5
some error

我想立即打印错误,不提交print命令然后抛出错误。

我应该如何更改程序,以免打印错误的输入?

4 个答案:

答案 0 :(得分:1)

下载John Levine的“flex&amp; bison”一书或gnu的“bison”手册。两者都包含一个可以参考的中缀计算器。

你写的语法“'('expr_int')'”在检测到语法不正确')'in“(1 + 1))'之前减少到expr_int。这就是解析器的作用:

(1 + 1)) => ( expr_int )) => expr_int) 

然后看到错误。为了捕获错误,您必须更改解析器以在缩减之前查看错误,并且必须对要处理的所有错误执行此操作。因此你会写(在这种情况下):

expr_int'('expr_int')'')'{这是错误讯息}

在长期回答之后,简短的回答是,生成包含所有可能错误的实例的解析器是不切实际的。你所拥有的对于你正在做的事情是好的。您应该探索的是如何(优雅地)从错误中恢复而不是放弃解析。

您的“程序”和“命令”非终端可以合并为:

program: print-expr '\n' { return 0; }

另外,您可以将正则表达式重写为效果良好:

%%
INTEGER     [0-9]+
WS          [ \t]+

%%
print/{WS}              { return PRINT; }
{INTEGER}               { yylval.int_val=atoi(yytext); return INTEGER; }
'('                     { return '(';     }
')'                     { return ')';     }
'+'                     { return '+';     }
'-'                     { return '-';     }
{WS}*                   {}
\n                      { return '\n';    }
.                       { return *yytext; }  // do you really want to do this?

%%

答案 1 :(得分:0)

为您的语言创建行尾令牌(例如;),并在遇到此行尾令牌时准确地说明所有行语句。

答案 2 :(得分:0)

那是因为您在解析代码时正在执行代码。好的旧野牛计算器旨在教你如何编写语法,而不是实现完整的编译器/解释器。

构建编译器/解释器的常规方法如下:

lexer -> parser -> semantic analyser -> code generator -> interpreter

在你的情况下,建立一个完全成熟的编译器可能会有点过分。您需要的是将结果存储在某处,并且只在yyparse返回后输出它而没有错误。

答案 3 :(得分:0)

在yacc / bison中,一旦所讨论的规则被减少,就会执行与语义操作相关联的代码,这可能在规则的令牌被移位之后立即发生,然后查看任何后续上下文以查看是否有没有错误(所谓的“默认”减少,用于使解析表更小)。

如果要在读取(并识别)整行之前避免打印答案,则需要在具有打印消息的操作的规则中包含换行符。在您的情况下,您可以将换行符从program规则移至print_expr规则:

program: command   { return 0; } ;
print_expr: PRINT expr_int '\n' { cout<<$2<<endl; }

当然,如果给它多行输入,这仍然会给你一个错误(打印输出后)。