我试图解析一系列没有分隔符的表达式,以便能够解析ML / F#样式函数调用:
myfunc expr1 expr2 expr3
然而,表达式序列给出了一个移位/减少冲突的列表。
我的猜测是冲突是由我的语法的递归性质引起的,但我不知道如何解决这些冲突。
我的(简化)优先规则和语法如下所示:
/* Lowest precedence */
%left PLUS
%left TIMES
%left LPAR
/* Highest precedence */
Expr:
| CSTINT { CstI $1 }
| LPAR Expr RPAR { $2 }
| Expr TIMES Expr { Prim("*", $1, $3) }
| Expr PLUS Expr { Prim("+", $1, $3) }
| NAME ExprList { Call(Var $1, $2) }
ExprList:
| { [] }
| Expr ExprList { $1::$2 }
当我把它传递给fsyacc时,我得到一个shift / reduce和减少/减少冲突的列表。示例移位/减少冲突是
state 11: shift/reduce error on PLUS
fsyacc对州11的输出是:
state 11:
items:
Expr -> Expr . 'TIMES' Expr
Expr -> Expr . 'PLUS' Expr
ExprList -> Expr . ExprList
actions:
action 'EOF' (noprec): reduce ExprList -->
action 'LPAR' (explicit left 10000): shift 6
action 'RPAR' (noprec): reduce ExprList -->
action 'COMMA' (noprec): reduce ExprList -->
action 'PLUS' (explicit left 9998): shift 13
action 'TIMES' (explicit left 9999): shift 12
action 'NAME' (noprec): shift 14
action 'CSTINT' (noprec): shift 5
action 'error' (noprec): reduce ExprList -->
action '#' (noprec): reduce ExprList -->
action '$$' (noprec): reduce ExprList -->
immediate action: <none>
gotos:
goto Expr: 11
goto ExprList: 16
自从我参加编译理论课程已经有一段时间了,所以虽然我知道什么是转移/减少和减少/减少冲突,但我不习惯考虑它们。特别是,我没有看到PLUS
上的减少如何导致有效的解析。总而言之,任何对以下一个或多个问题的见解都将受到高度赞赏:
答案 0 :(得分:3)
<强> 1。为什么我的语法看似含糊不清?
你的语法 含糊不清。这不是幻觉。
假设f是一个函数。
f x + 7
那是f(x) + 7
还是f(x+7)
?你的语法会同时产生。
f(x) + 7
。
<强> 2。我可以使用优先级和/或关联性规则修复它,如果没有,
您可以使用优先级和关联性规则消除功能应用程序的歧义;你只需要用%prec
声明它的优先级。然而,它最终看起来有点难看......
第3。我是否需要重写语法,如果是这样,粗略地说,我该怎么做?
......我认为将函数应用程序表示为Name ExprList
并不正确。如果你一次一个地讨论一个参数,至少在构建AST时它会更清晰,如果你在语法中而不是使用优先规则(它实际上不是为隐形运算符设计),它看起来更漂亮。见下文。
<强> 4。 yacc是否适合这样的构造?
当然,为什么不呢?
这是两个工作(据我所知)yacc语法。第一个使用优先级声明的一切;第二个分离出功能应用程序,我认为它更清洁:
// grammar1.y:
%left '+'
%left '*'
%left ATOM ';' '(' ')'
%%
program: /* empty */ { $$ = ""; }
| program statement ';' { std::cout << $2 << std::endl; }
| program ';'
;
statement: expr
;
expr: ATOM
| '(' expr ')' { $$ = $2; }
| expr expr %prec ATOM { $$ = '(' + $1 + ' ' + $2 + ')'; }
| expr '+' expr { $$ = "(+ " + $1 + ' ' + $3 + ')'; }
| expr '*' expr { $$ = "(* " + $1 + ' ' + $3 + ')'; }
;
// grammar2.y
%token ATOM
%left '+'
%left '*'
%%
program: /* empty */ { $$ = ""; }
| program statement ';' { std::cout << $2 << std::endl; }
| program ';'
;
statement: expr
;
term : ATOM
| '(' expr ')' { $$ = $2; }
;
apply: term
| apply term { $$ = '(' + $1 + ' ' + $2 + ')'; }
;
expr : apply
| expr '+' expr { $$ = "(+ " + $1 + ' ' + $3 + ')'; }
| expr '*' expr { $$ = "(* " + $1 + ' ' + $3 + ')'; }
;