我正在尝试解析SQL搜索条件,并且无法让解析器将逻辑(AND
,OR
)与其他中缀运算符区分开来。我将它们解析为不同的节点(可能很难做到),但简化了评估阶段。这是相关的代码片段(如果需要,我可以包含更多内容)。
let opp = OperatorPrecedenceParser<_,_,_>()
let scalarExpr = opp.ExpressionParser
opp.TermParser <- constant <|> id <|> between lparen rparen scalarExpr <|> scalarExpr
//infix operators added here
let comparison = //(e.g., 1 < 2)
let compareExpr = pipe3 scalarExpr compareOp scalarExpr (fun l op r -> Comparison(op, l, r))
between lparen rparen compareExpr <|> compareExpr
let andTerm = pstringCI "and" .>> ws
let orTerm = pstringCI "or" .>> ws
let searchCondition, searchConditionRef = createParserForwardedToRef()
searchConditionRef :=
[ comparison
pipe3 searchCondition andTerm searchCondition (fun l _ r -> And(l, r))
pipe3 searchCondition orTerm searchCondition (fun l _ r -> Or(l, r))
between lparen rparen searchCondition ]
|> choice
let filter : Parser<_,unit> = ws >>. searchCondition .>> eof
"1 = 1"
正确解析为Comparison (Eq,Constant (Int32 1),Constant (Int32 1))
但是一旦我尝试加入两个与逻辑运算符的比较,例如"1 = 1 or 2 = 2"
,就无法解析
Ln:1 Col:7中的错误 1 = 1或2 = 2
^
期望:输入结束或中缀操作员
:7
我希望它在错误之前解析1
作为标量表达式,并在点击or
回溯时,意识到它不是中缀运算符,返回1
作为完整标量,并且认识到它正在解析由逻辑运算符or
连接的条件的左侧。
相反,它似乎继续假设1
开始一个更复杂的标量表达式,可能涉及一个中缀运算符。
代码是否有问题,或者是将AND
/ OR
解析为中缀运算符(使用相同的OperatorPrecedenceParser
)的解决方案?我宁愿不去那条路,所以我希望我在某个地方犯了一个简单的错误。
complete code正在上演。
答案 0 :(得分:4)
我认为最终你会发现你需要将and
和or
视为具有优先级规则的中缀运算符,因为这正是它们的原因,也是大多数解析器包括fparsec和fsyacc的原因所在处理它们的特殊功能(即通过优先级和关联性规则解决歧义)。
你发现有一个案例强调了这一点,但考虑另一个案例:
1 = 1 or 2 = 2 and 3 =3
应解析为(1 = 1 or 2 = 2) and 3 = 3
还是1 = 1 or (2 = 2 and 3 = 3)
?
答案 1 :(得分:2)
您的解析器在第一个等式之后停止,因为choice
的{{1}}组合器将第一个参数解析器searchCondition
应用于输入,并且成功后只返回参数解析器的结果。然后,您会收到错误,因为comparison
无法在filter
之后解析eof
。
searchCondition
和choice
组合器不实施最长匹配规则,并且在发生错误后他们不回溯,如tutorial。所以你的<|>
解析器无法工作。
另一个问题是你的searchCondition
解析器是左递归的,因为第二个和第三个searchCondition
参数将尝试再次应用choice
而不先前消耗任何输入。左递归会导致堆栈溢出。
类似地,在searchCondition
定义的末尾<|> scalarExpr
是不必要的,并且可以导致无限递归。
当您将左递归解析器语法转换为FParsec时,您需要消除左递归。
修复opp.TermParser
解析器的一种方法是分解左侧表达式:
searchCondition
甚至更简单:
let andTerm = stringCIReturn "and" (fun l r -> And(l, r)) .>> ws
let orTerm = stringCIReturn "or" (fun l r -> Or(l, r)) .>> ws
let searchCondition, searchConditionRef = createParserForwardedToRef()
do searchConditionRef:=
let comparisonTerm =
comparison <|> between lparen rparen searchCondition
pipe2 comparisonTerm (many ((andTerm <|> orTerm) .>>. comparisonTerm))
(fun l opRList ->
List.fold (fun l (op, r) -> op l r) l opRList)
更新的 在语法中,parens解析也存在问题,请参阅下面的注释。
答案 2 :(得分:1)
为逻辑运算符创建单独的OperatorPrecedenceParser
似乎已修复它。
我替换了
let andTerm = pstringCI "and" .>> ws
let orTerm = pstringCI "or" .>> ws
let searchCondition, searchConditionRef = createParserForwardedToRef()
searchConditionRef :=
[ comparison
pipe3 searchCondition andTerm searchCondition (fun l _ r -> And(l, r))
pipe3 searchCondition orTerm searchCondition (fun l _ r -> Or(l, r))
between lparen rparen searchCondition ]
|> choice
与
let condOpp = OperatorPrecedenceParser()
let searchCondition = condOpp.ExpressionParser
condOpp.TermParser <- (attempt comparison) <|> between lparen rparen searchCondition <|> searchCondition
condOpp.AddOperator(InfixOperator("or", ws, 1, Assoc.Left, fun l r -> Or(l, r)))
condOpp.AddOperator(InfixOperator("and", ws, 2, Assoc.Left, fun l r -> And(l, r)))
(1 = 1 or 2 = 2) and 3 = 3
和1 = 1 or (2 = 2 and 3 = 3)
正确解析。