我正在为类似于SQL的语言开发解析器,我遇到了创建一些语言规则的问题,例如:expression IS NULL
和expression IN (expression1, expression2, ...)
,逻辑和数学运算符之间具有优先级。
我上传了一个GitHub测试项目https://github.com/anpv/SpracheTest/,但这种变体并不好 我试图使用以下规则:
private static readonly Parser<AstNode> InOperator =
from expr in Parse.Ref(() => Expression)
from inKeyword in Parse.IgnoreCase("in").Token()
from values in Parse
.Ref(() => Expression)
.DelimitedBy(Comma)
.Contained(OpenParenthesis, CloseParenthesis)
select new InOperator(expr, values);
private static readonly Parser<AstNode> IsNullOperator =
from expr in Parse.Ref(() => Expression)
from isNullKeyword in Parse
.IgnoreCase("is")
.Then(_ => Parse.WhiteSpace.AtLeastOnce())
.Then(_ => Parse.IgnoreCase("null"))
select new IsNullOperator(expr);
private static readonly Parser<AstNode> Equality =
Parse
.ChainOperator(Eq, IsNullOperator.Or(InOperator).Or(Additive), MakeBinary);
会在ParseException
或ScriptParser.ParseExpression("1 is null")
等代码中引发ScriptParser.ParseExpression("1 in (1, 2, 3)"): "Parsing failure: Left recursion in the grammar."
。
我如何预测Expression,或者是否存在其他变体来解决这个问题?
答案 0 :(得分:6)
不幸的是,答案是Sprache无法解析左递归语法。我偶然发现源代码中的评论,讨论了在研究这个问题时我是如何删除对左递归语法的错误支持的(这也是我发现你的问题的方式) - 请参阅source code。
为了解决这个问题,您需要重新组织解析方式。例如,如果您正在编写一个简单的表达式解析器,那么这是您必须处理的常见问题。在网上搜索有很多关于如何从语法中删除左递归的讨论,特别是对于表达式。
在您的情况下,我希望您需要执行以下操作:
term := everything simple in an expression (like "1", "2", "3", etc.)
expression := term [ IN ( expression*) | IS NULL | "+" expression | "-" expression | etc.]
或类似 - 基本上 - 你必须自己解除递归。通过这样做,我能够解决表达式问题。我怀疑任何基本的编译器书可能都有关于如何“规范化”语法的部分。
它使从解析器返回的任何对象构建更多的痛苦,但是在select语句中而不是“select new Expression(arg1,arg2)”我将其更改为函数调用,并且函数根据参数决定返回的特定对象。