为什么野牛不接受这个语法文件?



当我使用命令bison -d -o parser.java parser.y从语法文件parser.y生成解析器时,Bison产生以下错误:

:8.8-10: syntax error, unexpected string, expecting char or identifier or type

文件parser.y:

%{
import java.util.;
import java.io.;
%}
%start PROGRAM
%token number identifier function break call if else let read return while write
%token "(" ")" "{" "}" ";" "=" "+" "-" "" "/" "%" "<" ">" " <= " " >= " "==" "!=" "&" "|" "~" "!"
%left "+" "-"
%left "" "/" "%"
%left "&" "|"
%nonassoc "!"
%type <Node> PROGRAM FUNCTION PARAMLIST BLOCK STATEMENT IF ELSE EXPR
%type <String> identifier
%type <Integer> number
%union {
Node node;
String identifier;
int number;
}
%%
PROGRAM:
| PROGRAM FUNCTION
| BLOCK
;
FUNCTION:
function identifier '(' PARAMLIST ')' BLOCK
;
PARAMLIST:
identifier
| identifier ',' PARAMLIST
|
;
BLOCK:
'{' STATEMENT '}'
;
STATEMENT:
BREAK
| CALL ';'
| IF
| LET
| READ
| RETURN
| WHILE
| WRITE
;
BREAK:
break ';'
;
CALL:
call identifier '(' ARGLIST ')'
;
ARGLIST:
EXPR
| EXPR ',' ARGLIST
|
;
IF:
if EXPR BLOCK ELSE
;
ELSE:
else BLOCK
|
;
LET:
let identifier '=' EXPR ';'
| let identifier '=' CALL ';'
;
READ:
read identifier ';'
;
RETURN:
return EXPR ';'
;
WHILE:
while EXPR BLOCK
;
WRITE:
write EXPR ';'
;
EXPR:
number
| identifier
| '(' EXPR ')'
| '!' EXPR
| '~' EXPR
| EXPR '+' EXPR
| EXPR '-' EXPR
| EXPR '*' EXPR
| EXPR '/' EXPR
| EXPR '%' EXPR
| EXPR '&' EXPR
| EXPR '|' EXPR
| EXPR '<' EXPR
| EXPR '>' EXPR
| EXPR "<=" EXPR
| EXPR ">=" EXPR
| EXPR "==" EXPR
| EXPR "!=" EXPR
;
%%
int yyerror(String s) {
System.err.println("error: " + s);
}

Bison不允许您使用%token声明声明带引号的令牌名称(例如"(")。它知道它们是符号;它们不可能是别的东西。

您使用%token声明来声明令牌的符号名称,在编写词法分析器时您会发现这很有用。在声明中,符号名首先出现,可选地后面跟着双引号括起来的别名。你想重复多少次就重复多少次。例如,您可以这样写:

%token TK_LE "<=" TK_GE ">="
然后,您可以在语法中使用符号名称或别名,但使用别名会使语法更具可读性。此外,Bison在构造错误消息时使用别名,这是一件好事,因为"期望tk_分号";不是一个很好的方式与用户沟通,需要一个";"

请记住,单引号的单字符令牌,如'(',与双引号的别名不同。在语法中,您使用'(',但试图声明"("。如果您成功地声明了"(",那么您将得到一个"未使用的标记"。警告。因为'('不需要符号名,你可以删除声明。只有像"<="这样的多字符标记才需要它们。(注意,引号内的空格很重要。" <= ""<="不一样)

符号令牌名称用作Java值,因此它们的名称不会与变量或Java关键字冲突。例如,不能使用break作为符号令牌名称。尝试这样做将导致编译错误。

因此,习惯上用ALL_CAPS来写令牌名称,而用小写来写非终结符。生成的代码中不使用非终结符名称,因此您可以使用任何您想要的名称。

您颠倒了这个约定,这将在编译生成的解析器时导致各种错误,并且对于我们这些习惯了标准样式的人来说,这很难阅读。

其他注意事项:

  1. bison Java接口不使用%union声明。%type声明就足够了。

  2. 您缺少许多操作符的优先级声明,特别是比较操作符。这将导致大量解析器冲突。请确保按正确的顺序书写优先级。

最新更新