我对Scala相对较新,我正在努力掌握组合器解析。所以我有一些代码可以解析逻辑原子(一阶逻辑中的谓词),如p(x,y,z)
或p(x,1,q(a,b,c),z)
。我有这段小代码:
class Logic extends JavaTokenParsers {
def functor: Parser[Any] = ident
def term: Parser[Any] = predicate | ident | floatingPointNumber
def predicate: Parser[Any] = functor~"("~repsep(term,",")~")"
}
Functor
是谓词符号,与p
中的p(x,y,z)
一样,term
是常量或变量,如1
或x
,或者predicate
和predicate
是一个复合词,如p(x,y,z)
。这段代码可以正常工作,但是如果我改变在term
解析器中声明替代方法的顺序,则会出现问题。也就是说,如果我像{/ p>那样编写term
解析器
def term: Parser[Any] = ident | floatingPointNumber | predicate
(predicate
是这里的最后一个选择,虽然它是先前的),然后它无法解析像p(x,1,q(a,b,c),z)
这样的“嵌套”表达式(虽然它仍适用于像{{1这样的“平面”表达式}})。有人可以说明我在这里缺少什么吗?
非常感谢
答案 0 :(得分:6)
原因是A | B
将首先尝试满足A
,并且B
仅在A
失败时才会尝试|
。
这与正则表达式(或一般的无上下文语法)中的"p(x,1,q(a,b,c),z)"
不同。
在这种情况下,p
ident
满足|||
,因此其他替代品不会匹配(并且没有回溯)。
出于这个原因,在具有冲突前缀的替代方案中,你可以先放一个接受较长字符串的字符串(或者你知道的那个字符串会产生更多短语)。
请注意,您可以使用def term: Parser[Any] = ident ||| floatingPointNumber ||| predicate
组合器:
|||
根据Scala docs,{{1}}是一个解析器组合器,匹配具有最长匹配组合的替代方案。
答案 1 :(得分:3)
我假设您尝试通过logic.parse(logic.term, "p(x,1,q(a,b,c),z)")
解析。
事实上,替代方案的顺序很重要,因为解析器将选择匹配给定输入的第一个替代方案。在您的情况下,p
符合ident
定义。然后解析器将返回一个ParseResult,其中包含匹配项(在您的情况下为Any
类型)和输入的其余部分,在您的情况下为(x,1,q(a,b,c),z)
。