Parsing 有没有办法让这个语法成为LALR(1)?

Parsing 有没有办法让这个语法成为LALR(1)?,parsing,grammar,yacc,lalr,lr,Parsing,Grammar,Yacc,Lalr,Lr,我有这样的语法规则 A -> pq B -> pr Block -> { Astar Bstar } Astar -> Astar A | epsilon Bstar -> Bstar B | epsilon 有没有办法把这个语法转换成LALR(1)?据我所知,如果解析器在一个块内看到p,则存在移位/导出冲突。您的语言是正则的,并且与正则表达式\{(pq)*(pr)*\}等价。问题是,任何简单的语法转换都需要两个字符的前瞻,以查看

我有这样的语法规则

A -> pq
B -> pr

Block -> { Astar Bstar }

Astar -> Astar A
       | epsilon

Bstar -> Bstar B
       | epsilon

有没有办法把这个语法转换成LALR(1)?据我所知,如果解析器在一个块内看到
p
,则存在移位/导出冲突。

您的语言是正则的,并且与正则表达式
\{(pq)*(pr)*\}
等价。问题是,任何简单的语法转换都需要两个字符的前瞻,以查看
p
之后是否有
q
r

现在你已经把它标记为
yacc
parsing
,因此不清楚你是在寻找一个实用的答案“你如何处理yacc”还是一个理论上的答案“这种语言是否有LALR(1)语法”

对于实际答案,解决方案是,您将
A
B
的识别移动到lexer中,在该lexer中识别字符序列
pq
pr
作为标记
A
B
。由于lex/flex使用DFA并回溯到最长的匹配令牌,因此它们在这里不存在任意前瞻的问题

对于理论上的答案,您需要转换语法以消除前瞻的需要。有问题的结构是
(pq)*(pr)*
(大括号是不相关的),它相当于
p(qp)*(rp)*r | p(qp)*q | epsilon
,这表示类似以下语法:

Block -> { p Astar Bstar r }
       | { p Astar q }
       | { }
A -> q p
B -> r p
Astar -> Astar A | epsilon
Bstar -> Bstar B | epsilon
另一种方法是对
星型
规则进行分解,使其与空字符串不匹配:

Block -> { Aplus Bplus }
       | { Aplus }
       | { Bplus }
       | { }
A -> p q
B -> p r
Aplus -> Aplus A | A
Bplus -> Bplus B | B

为同一种语言提供两个非常不同的LALR(1)语法。

您的语言是正则的,并且与正则表达式
\{(pq)*(pr)*\}
等价。问题是,任何简单的语法转换都需要两个字符的前瞻,以查看
p
之后是否有
q
r

现在你已经把它标记为
yacc
parsing
,因此不清楚你是在寻找一个实用的答案“你如何处理yacc”还是一个理论上的答案“这种语言是否有LALR(1)语法”

对于实际答案,解决方案是,您将
A
B
的识别移动到lexer中,在该lexer中识别字符序列
pq
pr
作为标记
A
B
。由于lex/flex使用DFA并回溯到最长的匹配令牌,因此它们在这里不存在任意前瞻的问题

对于理论上的答案,您需要转换语法以消除前瞻的需要。有问题的结构是
(pq)*(pr)*
(大括号是不相关的),它相当于
p(qp)*(rp)*r | p(qp)*q | epsilon
,这表示类似以下语法:

Block -> { p Astar Bstar r }
       | { p Astar q }
       | { }
A -> q p
B -> r p
Astar -> Astar A | epsilon
Bstar -> Bstar B | epsilon
另一种方法是对
星型
规则进行分解,使其与空字符串不匹配:

Block -> { Aplus Bplus }
       | { Aplus }
       | { Bplus }
       | { }
A -> p q
B -> p r
Aplus -> Aplus A | A
Bplus -> Bplus B | B

为同一种语言提供两个非常不同的LALR(1)语法。

让我们从了解发生LALR(1)冲突的确切原因开始,然后看看是否可以修改语法使其成为LALR(1)

要了解语法不是LALR(1)的原因,让我们首先计算语法的LR(1)配置集:

(1)
S'     -> .Block [$]
Block  -> .{ Astar Bstar } [$]

(2)
S'     -> Block. [$]

(3)
Block -> { . Astar Bstar } [$]
Astar -> .Astar A [ }p ]
Astar -> .epsilon [ }p ]

(4)
Block -> { Astar . Bstar } [$]
Astar -> Astar .A [ }p]
A     -> .pq [}p]
Bstar -> .epsilon [ }p ]
Bstar -> . Bstar B [ }p ]
在这一点上,我们可以停止,因为符号p的状态(4)中有一个shift/reduce约束:您是为
a->.pq[{p]
移动p,还是减少
BStar->.epsilon[}p]
?由于LR(1)语法中存在移位/减少冲突,因此该语法根本不是LR(1),这意味着它不可能是LALR(1)(因为每个LALR(1)语法也是一个LR(1)语法)

从根本上说,问题是当解析器看到一个
p
时,它无法判断它是在看
a
的开头(这意味着它需要移动它),还是看不到剩下的
a
,它在看a
B
的开头(这意味着它需要减少
Bstar->epsilon

为了解决这个问题,让我们看看如果我们做一个小调整会发生什么。我们遇到的问题是,解析器需要在看到
p
时立即确定是移位还是缩减。如果我们通过查看
p
,然后查看后续字符,给它时间延迟决策,会怎么样?要做到这一点,让我们通过重写来稍微修改一下语法

Bstar -> epsilon
Bstar -> Bstar B
作为

现在,解析器可以在决定做什么之前查看更多标记。如果它正在查看
pq
,它知道它没有查看任何与B相关的内容。如果它看到
pr
,它就知道它在看a B,因此可以开始制作第二类产品。让我们看看如果我们这样做,LR(1)状态会发生什么:

(1)
S'     -> .Block [$]
Block  -> .{ Astar Bstar } [$]

(2)
S'     -> Block. [$]

(3)
Block -> { . Astar Bstar } [$]
Astar -> .Astar A [ }p ]
Astar -> .epsilon [ }p ]

(4)
Block -> { Astar . Bstar } [$]
Astar -> Astar .A [ }p]
A     -> .pq [}p]
Bstar -> .epsilon [ } ]
Bstar -> . B Bstar [ } ]
B     -> .pr [}]

(5)
Block -> { Astar Bstar . } [$]

(6)
Block -> { Astar Bstar } . [$]

(7)
A     -> p.q [}p]
B     -> p.r [}]

(8)
A     -> .pq [}p]

(9)
B     -> pr. [}]

(10)
Bstar -> B . Bstar [ } ]
Bstar -> . B Bstar [ } ]
B     -> .pr [}]

(11)
B     -> p.r [}]
请注意,我们原来的shift/reduce冲突已经消失,并且这个新语法不再有任何shift/reduce冲突。此外,由于没有任何具有相同核心的状态对,因此上述状态集也是我们在LALR(1)表中拥有的状态集。因此,上面的语法确实是LALR(1),我们根本没有改变语法的含义


希望这有帮助

让我们先看看为什么会出现LALR(1)冲突,然后看看是否可以修改语法使之成为LALR(1)

要了解语法不是LALR(1)的原因,让我们首先计算语法的LR(1)配置集:

(1)
S'     -> .Block [$]
Block  -> .{ Astar Bstar } [$]

(2)
S'     -> Block. [$]

(3)
Block -> { . Astar Bstar } [$]
Astar -> .Astar A [ }p ]
Astar -> .epsilon [ }p ]

(4)
Block -> { Astar . Bstar } [$]
Astar -> Astar .A [ }p]
A     -> .pq [}p]
Bstar -> .epsilon [ }p ]
Bstar -> . Bstar B [ }p ]
在这一点上,我们可以停止,因为符号p的状态(4)中有一个shift/reduce约束:您是为
a->.pq[{p]
移动p,还是减少
BStar->.epsilon[}p]
?由于LR(1)语法中存在移位/减少冲突,因此该语法根本不是LR(1),这意味着它不可能是LALR(1)(因为每个LALR(1)语法也是一个LR(1)语法)

从根本上说,问题在于当解析器看到
p
时,它无法分辨