byacc shift / reduce

时间:2012-04-16 18:36:36

标签: yacc shift-reduce-conflict

我无法解决这个问题以及转移减少问题。

添加';'到最后并没有解决问题,因为我无法改变语言,它需要像下面的例子一样去。是否有任何预先操作数?

示例如下:

变量可以声明为:作为指针或int作为整数,因此,这两个都是有效的:

<int> a = 0
int a = 1

代码:

%left '<'

declaration: variable
           | declaration variable

variable : type tNAME '=' expr
         | type tNAME

type : '<' type '>'
     | tINT

expr : tINTEGER
     | expr '<' expr

显然在expr之后给出了一个转移/减少问题。因为它可以为“less”运算符的expr移位,或者为另一个变量声明减少。

我希望在变量声明上给出优先权,并尝试创建%nonassoc prec_aux并放在'&lt;'之后输入'&gt;' %prec prec_aux和类型tNAME之后但它没有解决我的问题:S

我该如何解决这个问题?

输出是:

很好的数字hwo可以在回复时发布换行符和代码......所以这里输出结果:

35: shift/reduce conflict (shift 47, reduce 7) on '<'
state 35
    variable : type tNAME '=' expr .  (7)
    expr : expr . '+' expr  (26)
    expr : expr . '-' expr  (27)
    expr : expr . '*' expr  (28)
    expr : expr . '/' expr  (29)
    expr : expr . '%' expr  (30)
    expr : expr . '<' expr  (31)
    expr : expr . '>' expr  (32)

    '>'  shift 46
    '<'  shift 47
    '+'  shift 48
    '-'  shift 49
    '*'  shift 50
    '/'  shift 51
    '%'  shift 52
    $end  reduce 7
    tINT  reduce 7

输出和错误似乎是我提到的那个。


有没有人知道一个不同的解决方案,除了在一个不是真正的选项的语言中添加一个新的终端?

我认为解决方法是重写语法,以便以某种方式可以预见并查看它是'&lt;'之后的类型还是expr但我没看到怎么做。

优先权不太可能起作用,因为它是相同的角色。有没有办法为我们定义的类型提供优先权?如声明?

提前致谢

3 个答案:

答案 0 :(得分:2)

你的语法在这样的文字中混淆了:

int a = b
<int> c

那'&lt;'在第二行可能是第一个声明中表达式的一部分。它必须进一步向前看才能找到答案。

这就是大多数语言都有语句终止符的原因。这不会产生冲突:

%%

%token tNAME;
%token tINT;
%token tINTEGER;
%token tTERM;

%left '<';

declaration: variable
           | declaration variable

variable : type tNAME '=' expr tTERM
         | type tNAME tTERM

type : '<' type '>'
     | tINT

expr : tINTEGER
     | expr '<' expr

在创建解析器时,它有助于了解如何设计语法以消除可能的冲突。为此,您需要了解解析器的工作方式,这超出了本答案的范围:)

答案 1 :(得分:1)

这里的基本问题是你需要比使用yacc / bison获得的1个令牌更多的前瞻。当解析器看到一个<时,它无法判断它是否完成了preivous声明,并且它看了一个括号类型的开头,或者这是一个小于运算符。你可以在这里做两件基本的事情:

  • 使用解析方法,例如bison的%glr-parser选项或btyacc,它可以处理非LR(1)语法

  • 使用词法分析器做额外的预测并返回消除歧义的标记

对于后者,你会让词法分析者在'&lt;'之后做额外的预测并返回一个不同的标记,如果它后跟一个看起来像一个类型的东西。最简单的方法是使用flex的/超前运算符。例如:

"<"/[ \t\n\r]*"<"    return OPEN_ANGLE;
"<"/[ \t\n\r]*"int"  return OPEN_ANGLE;
"<"                  return '<';

然后,您将野兔规则更改为OPEN_ANGLE类型而不是<

type : OPEN_ANGLE type '>'
     | tINT

expr : tINTEGER
     | expr '<' expr

对于更复杂的问题,您可以使用弹性开始状态,甚至可以在词法分析器和解析器之间插入整个标记过滤器/变换传递。

答案 2 :(得分:0)

这是修复,但并不完全令人满意:

%{
%}

%token tNAME tINT tINTEGER

%left '<'
%left '+'
%nonassoc '=' /* <-- LOOK */

%%

declaration: variable
           | declaration variable

variable : type tNAME '=' expr
         | type tNAME

type : '<' type '>'
     | tINT

expr : tINTEGER
     | expr '<' expr
     | expr '+' expr
     ;

这个问题是这两个LR项目之间的冲突:点终点:

variable : type tNAME '=' expr_no_less .

和这一个:

expr : expr . '<' expr

请注意,这两个有不同的运算符。正如您所想的那样,并不是涉及'&lt;'的不同作品之间的冲突。操作

通过将=添加到优先级排名,我们可以解决冲突诊断消失的问题。

请注意,我给了=一个高优先级。这将通过支持减少来解决冲突。这意味着你不能使用'&lt;'表达式作为初始化程序:

int name = 4 < 3    // syntax error

当&lt;可以看出,int name = 4希望减少,并且<必须是下一个声明的开头,作为type制作的一部分。

要允许<关系表达式用作初始值设定项,请将括号的支持添加到表达式语法中。然后用户可以括起来:

int foo = (4 < 3) <int> bar = (2 < 1) 

如果没有更强大的解析方法或黑客,就没有办法解决这个问题。

如果您在%nonassoc之前移动%left '<',优先级较低,该怎么办?然后转变将受到青睐。不幸的是,这导致您无法在声明后写下另一个<int>声明。

int foo = 3 <int> bar = 4
             ^ // error: the machine shifted and is now doing:   expr '<' . expr.

这是解决冲突的错误方法;你希望能够写出多个这样的声明。

另一个注意事项:

我的TXR语言实现了与Parse Expression Grammars相当的东西,可以很好地处理这种语法。这基本上是LL(无限),胜过LALR(1)。

我们甚至不需要单独的词法分析器和解析器!这只是因为一个符号前瞻的限制所必需的东西,以及1970年代硬件对最高效率的需求。

shell命令行的示例输出,通过转换为类似Lisp的抽象语法树来演示解析,该语法树绑定到变量dl(声明列表)。所以这完成了语义动作,产生了一个可以在TXR Lisp中进一步处理的输出。通过调用intern将标识符转换为Lisp符号,并将数字转换为数字对象。

$ txr -l type.txr  -
int x = 3 < 4 int y
(dl (decl x int (< 3 4)) (decl y int nil))

$ txr -l type.txr  -
< int > x = 3 < 4 < int > y
(dl (decl x (pointer int) (< 3 4)) (decl y (pointer int) nil))

$ txr -l type.txr  -
int x = 3 + 4 < 9 < int > y < int > z = 4 + 3 int w
(dl (decl x int (+ 3 (< 4 9))) (decl y (pointer int) nil) 
 (decl z (pointer int) (+ 4 3)) (decl w int nil))

$ txr -l type.txr  -
<<<int>>>x=42  
(dl (decl x (pointer (pointer (pointer int))) 42))

type.txr)的源代码:

@(define ws)@/[ \t]*/@(end)
@(define int)@(ws)int@(ws)@(end)
@(define num (n))@(ws)@{n /[0-9]+/}@(ws)@(filter :tonumber n)@(end)
@(define id (id))@\
   @(ws)@{id /[A-Za-z_][A-Za-z_0-9]*/}@(ws)@\
   @(set id @(intern id))@\
@(end)
@(define type (ty))@\
  @(local l)@\
  @(cases)@\
    @(int)@\
    @(bind ty @(progn 'int))@\
  @(or)@\
    <@(type l)>@\
    @(bind ty @(progn '(pointer ,l)))@\
  @(end)@\
@(end)
@(define expr (e))@\
  @(local e1 op e2)@\
  @(cases)@\
    @(additive e1)@{op /[<>]/}@(expr e2)@\
    @(bind e @(progn '(,(intern op) ,e1 ,e2)))@\
  @(or)@\
    @(additive e)@\
  @(end)@\
@(end)
@(define additive (e))@\
  @(local e1 op e2)@\
  @(cases)@\
    @(num e1)@{op /[+\-]/}@(expr e2)@\
    @(bind e @(progn '(,(intern op) ,e1 ,e2)))@\
  @(or)@\
    @(num e)@\
  @(end)@\
@(end)
@(define decl (d))@\
  @(local type id expr)@\
  @(type type)@(id id)@\
  @(maybe)=@(expr expr)@(or)@(bind expr nil)@(end)@\
  @(bind d @(progn '(decl ,id ,type ,expr)))@\
@(end)
@(define decls (dl))@\
  @(coll :gap 0)@(decl dl)@(end)@\
@(end)
@(freeform)
@(decls dl)