这是我的野牛代码:
%}
%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命令然后抛出错误。
我应该如何更改程序,以免打印错误的输入?
答案 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; }
当然,如果给它多行输入,这仍然会给你一个错误(打印输出后)。