【问题标题】:Parsing a sequence of expressions using yacc使用 yacc 解析表达式序列
【发布时间】:2013-07-29 20:49:23
【问题描述】:

我正在尝试解析不带分隔符的表达式序列,以便能够解析 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 和 reduce/reduce 冲突列表。一个示例 shift/reduce 冲突是

state 11: shift/reduce error on PLUS

状态 11 的 fsyacc 的输出是:

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

自从我学习编译器理论课程以来已经有一段时间了,所以虽然我知道什么是 shift/reduce 和 reduce/reduce 冲突,但我不习惯思考它们。特别是,我看不到 PLUS 的减少如何导致有效的解析。总而言之,对以下一个或多个问题的任何见解都将受到高度赞赏:

  1. 为什么我的语法看起来模棱两可?
  2. 我是否可以使用优先级和/或关联性规则来修复它,或者,如果不是,
  3. 我是否需要重写语法,如果需要,大致上,我该怎么做?
  4. yacc 是否适合此类构造?

【问题讨论】:

    标签: parsing yacc fsyacc


    【解决方案1】:

    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 + ')'; }
         ;
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-24
      • 1970-01-01
      • 2015-05-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多