如何构造yacc代码以支持可选的非终结符

时间:2011-02-06 03:50:03

标签: yacc bison

在yacc中建模可选数据的最佳方法是什么?我有以下声明:

StmtBlock           :    '{' VariableDeclList StmtList '}' {  $$ = new StmtBlock($2, $3); }
                    ;

VariableDeclList和StmtList都是可选的(epsilon),所以我将它们建模如下:

VariableDeclList    :    VariableDeclList VariableDecl  { ($$=$1)->Append($2); }
                    |    { $$ = new List<VarDecl*>; }

StmtList            :    StmtList Stmt { ($$=$1)->Append($2); }
                    |    { $$ = new List<Stmt*>; }
                    ;

唯一的问题是我认为这会导致转移/减少冲突。当我尝试编译我的代码时,我的y.ouput文件具有以下内容:

State 74 conflicts: 1 shift/reduce
...
state 74

   38 StmtBlock: '{' VariableDeclList . StmtList '}'
   39 VariableDeclList: VariableDeclList . VariableDecl

    T_Bool        shift, and go to state 2
    T_Int         shift, and go to state 3
    T_Double      shift, and go to state 4
    T_String      shift, and go to state 5
    T_Identifier  shift, and go to state 8

    T_Identifier  [reduce using rule 18 (Epsilon)]
    $default      reduce using rule 18 (Epsilon)

    VariableDecl  go to state 80
    Variable      go to state 13
    Type          go to state 34
    Epsilon       go to state 81
    StmtList      go to state 82
...

有没有更合适的方法对此进行建模?

1 个答案:

答案 0 :(得分:0)

如您所知,如果无法通过第一个令牌区分 Stmt VariableDecl ,这将导致转移/减少冲突 - 在您的示例中为{ {1}}可能也会开始。你可以尝试一些事情:

  • 将两个列表组合成一个 VariableDeclOrStmtList ,它可以包含以任何方式混合的 Stmt VariableDecl 。这比你的更通用,所以你需要进行一次检查,以确保在第一个 Stmt

    VariableDecls >
  • 使用更多的前瞻来确定差异 - 使用bison的GLR模式,或类似btyacc之类的东西,可以推迟判断某些东西是 Stmt 还是 VariableDecl 直到看到足够的决定

  • 在这些结构的开头使用不同的标记,以明确区分它们。这些可以是您的语言中的真实令牌,也可以是词法分析器根据一些额外的上下文插入的合成令牌(这意味着在词法分析器中基本上做更多的前瞻)。

修改

对于第一个建议,您将有一条规则:

T_Identifier

其中VarDeclOrStmtList : VarDeclOrStmtList VariableDecl { ($$=$1)->Append($2); } | VarDeclOrStmtList Stmt { ($$=$1)->Append($2); } | { $$ = new List<Node *>; } ; NodeVarDecl的公共基类。然后,在StmtBlock操作中,调用遍历Stmt的函数,将其拆分为List<Node *>List<VarDecl *>,并在格式不正确时给出错误消息。