@chac 已经给了你一个很好的答案,向你展示了解决这个问题的常用方法。
让我以另一种方式阅读您的问题:您“应该删除 E 和 B 中的歧义,以便”您“可以在 Prolog 中编写 DCG 解析器”。这意味着,您只需要消除歧义,就可以在 Prolog 中编写 DCG 解析器。有一个好消息:编写 DCG 解析器根本不需要消除任何歧义!方法如下:
歧义的来源是像
C ::= C ; C
或其他运算符 + - 并列 div mod 和
让我坚持一个简化的语法:
E ::= E + E | “1”
我们可以将其编码为
e --> "1".
e --> e, "+", e.
不幸的是,Prolog 不会因类似的查询而终止
?- L = "1+1+1", phrase(e,L).
L = "1+1+1" ;
ERROR: Out of local stack
实际上,它终止了,但这只是因为我的计算机的内存是有限的......
甚至不适合:
?- L = "1", phrase(e,L).
L = "1" ;
ERROR: Out of local stack
这是一个模棱两可的问题吗?不!这只是 Prolog 的一个程序问题,不能直接处理左递归。这是一种让 Prolog 处理它的方法:
e([_|S],S) --> "1"。
e([_|S0],S) --> e(S0,S1), "+", e(S1,S)。
?- L = "1+1+1", 短语(e(L,[]),L)。
L = "1+1+1" ;
L = "1+1+1" ;
错误的。
?- L = "1", 短语(e(L,[]),L)。
L = "1" ;
错误的。
目前我们只定义了一个语法,大多数时候你也有兴趣看看对应的语法树:
e(整数 (1), [_|S],S) --> "1"。
e( plus(L,R), [_|S0],S) --> e( L, S0,S1), "+", e( R, S1,S)。
?- L = "1+1+1", 短语(e(Tree, L,[]),L)。
L = "1+1+1",
树 = plus(integer(1),plus(integer(1),integer(1))) ;
L = "1+1+1",
树 = plus(plus(integer(1),integer(1)),integer(1)) ;
错误的。
现在,我们看到plus
! 您的原始语法都将其接受为 (1+1)+1 和 1+(1+1),只要相应的语义保证观察到关联性,这本身就不是问题。大多数情况下,这被消除为左结合,因此意味着 (1+1)+1,但并非所有中缀运算符都是如此。