Scala - 组合器解析,备选方案的顺序似乎很重要
Scala - Combinator Parsing, order of alternatives seems to matter
我对 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(x,y,z)
中的p
,term
是常量或变量,如1
或x
, 或 predicate
, 而 predicate
是一个复合词, 如 p(x,y,z)
.此代码按原样运行良好,但如果我更改在 term
解析器中声明替代项的顺序,则会出现问题。也就是说,如果我将 term
解析器写成
def term: Parser[Any] = ident | floatingPointNumber | predicate
(predicate
是这里的最后一个选项,而它之前是第一个),然后它无法解析像 p(x,1,q(a,b,c),z)
这样的 "nested" 表达式(虽然它仍然适用于 "flat" 表达式,如 p(x,y,z)
)。有人可以指出我在这里缺少什么吗?
非常感谢
订单事项
原因是A | B
会先尝试满足A
,只有当A
失败时才会尝试B
。
这与正则表达式(或一般的上下文无关语法)中的 |
不同。
在这种情况下 "p(x,1,q(a,b,c),z)"
p
满足 ident
所以不会匹配其他备选方案(并且没有回溯)。
因此,在具有冲突前缀的备选方案中,您可以将接受较长字符串的备选方案放在第一位(或者您知道会导致满足更多短语的备选方案)。
最长的替代匹配
请注意,您可以使用 |||
组合器:
def term: Parser[Any] = ident ||| floatingPointNumber ||| predicate
根据Scala docs,|||
是一个解析器组合器匹配具有最长匹配组合的备选方案。
我假设您尝试通过 logic.parse(logic.term, "p(x,1,q(a,b,c),z)")
进行解析。
事实上,备选方案的顺序很重要,因为解析器将选择第一个备选方案来匹配给定的输入。在您的例子中,p
匹配 ident
定义。然后,解析器将 return 一个 ParseResult 包含匹配项(在您的情况下键入 Any
)和输入的其余部分,在您的情况下为 (x,1,q(a,b,c),z)
。
我对 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(x,y,z)
中的p
,term
是常量或变量,如1
或x
, 或 predicate
, 而 predicate
是一个复合词, 如 p(x,y,z)
.此代码按原样运行良好,但如果我更改在 term
解析器中声明替代项的顺序,则会出现问题。也就是说,如果我将 term
解析器写成
def term: Parser[Any] = ident | floatingPointNumber | predicate
(predicate
是这里的最后一个选项,而它之前是第一个),然后它无法解析像 p(x,1,q(a,b,c),z)
这样的 "nested" 表达式(虽然它仍然适用于 "flat" 表达式,如 p(x,y,z)
)。有人可以指出我在这里缺少什么吗?
非常感谢
订单事项
原因是A | B
会先尝试满足A
,只有当A
失败时才会尝试B
。
这与正则表达式(或一般的上下文无关语法)中的 |
不同。
在这种情况下 "p(x,1,q(a,b,c),z)"
p
满足 ident
所以不会匹配其他备选方案(并且没有回溯)。
因此,在具有冲突前缀的备选方案中,您可以将接受较长字符串的备选方案放在第一位(或者您知道会导致满足更多短语的备选方案)。
最长的替代匹配
请注意,您可以使用 |||
组合器:
def term: Parser[Any] = ident ||| floatingPointNumber ||| predicate
根据Scala docs,|||
是一个解析器组合器匹配具有最长匹配组合的备选方案。
我假设您尝试通过 logic.parse(logic.term, "p(x,1,q(a,b,c),z)")
进行解析。
事实上,备选方案的顺序很重要,因为解析器将选择第一个备选方案来匹配给定的输入。在您的例子中,p
匹配 ident
定义。然后,解析器将 return 一个 ParseResult 包含匹配项(在您的情况下键入 Any
)和输入的其余部分,在您的情况下为 (x,1,q(a,b,c),z)
。