使用yacc解析一系列表达式

时间:2013-07-29 20:49:23

标签: parsing yacc fsyacc

我试图解析一系列没有分隔符的表达式,以便能够解析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上的减少如何导致有效的解析。总而言之,任何对以下一个或多个问题的见解都将受到高度赞赏:

  1. 为什么我的语法看似含糊不清?
  2. 我可以使用优先级和/或关联性规则修复它,如果不是,
  3. 我是否需要重写语法,如果是这样,粗略地说,我该怎么做?
  4. yacc是否适合这样的构造工具?

1 个答案:

答案 0 :(得分:3)

<强> 1。为什么我的语法看似含糊不清?

你的语法 含糊不清。这不是幻觉。

假设f是一个函数。

f  x + 7

那是f(x) + 7还是f(x+7)?你的语法会同时产生。

IIRC,功能应用程序绑定非常紧密并且与左侧相关联。因此,上面的表达式应解析为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 + ')'; }
     ;