bounty将在3小时后过期。回答此问题可获得+500声望奖励。typeduke希望引起更多人对此问题的关注。
请看下面这个我在Kotlin中使用解析器组合子库better-parse实现的解析器,它解析(应该解析,而不是解析)简单的算术表达式:
object CalcGrammar: Grammar<Int>() {
val num by regexToken("""\d+""")
val pls by literalToken("+")
val min by literalToken("-")
val mul by literalToken("*")
val div by literalToken("/")
val lpr by literalToken("(")
val rpr by literalToken(")")
val wsp by regexToken("\\s+", ignore = true)
// expr ::= term + expr | term - expr | term
// term ::= fact * term | fact / term | fact
// fact ::= (expr) | -fact | int
val fact: Parser<Int> by
skip(lpr) and parser(::expr) and skip(rpr) or
(skip(min) and parser(::fact) map { -it }) or
(num map { it.text.toInt() })
val term by
leftAssociative(fact, mul) { a, _, b -> a * b } or
leftAssociative(fact, div) { a, _, b -> a / b } or
fact
val expr by
leftAssociative(term, pls) { a, _, b -> a + b } or
leftAssociative(term, min) { a, _, b -> a - b } or
term
override val rootParser by expr
}
但是,当我解析-2 + 4 - 5 + 6
时,我得到了这个ParseException
:
Could not parse input: UnparsedRemainder(startsWith=min@8 for "-" at 7 (1:8))
起初,我认为问题在于我没有将自递归编入expr
结果式中,也就是说,代码没有准确地表示语法:
// Reference to `expr` is missing on the right-hand side...
val expr = ... leftAssociative(term, min) ...
// ...although the corresponding production defines it.
// expr ::= ... term - expr ...
但是后来我注意到作为库文档的一部分提供的official example for an arithmetic parser结果几乎是相同的afaict也省略了这一点。
如果不是这个我又做错了什么?我怎么才能让它成功呢?
1条答案
按热度按时间uyhoqukh1#
我并不完全熟悉这个库,但是看起来您从语法到代码的翻译对于库的语法来说太字面了,而且库实际上隐式地处理了您显式编写的大部分内容,而且作为这种“易用性”的一部分,它似乎打破了看似正确的代码。
对于初学者,您会发现无论是否将
or term
链接到expr
的末尾,将or fact
链接到term
的末尾,代码的行为都完全相同。基于此,我可以得出结论,当将
leftAssociative
链接在一起时,这些or
不能按预期工作,事实上,如果您尝试解析除法,也会遇到同样的问题。我相信正是由于这个原因,您提供的示例链接结合了加法和减法(就像乘法和除法一样)转换成一个更动态的leftAssociative
调用。如果将相同的工作复制到自己的代码中,它将完美地运行: