F# 解析括号表达式

F# 解析括号表达式,f#,fparsec,F#,Fparsec,对于SQL搜索条件,我有以下fsyacc语法(稍加修改的形式): scalar_expr: | ID { Identifier($1) } | constant { Constant($1) } | unary_op scalar_expr { Unary($1, $2) } | scalar_expr

对于SQL搜索条件,我有以下fsyacc语法(稍加修改的形式):

scalar_expr:
    | ID                                    { Identifier($1) }
    | constant                              { Constant($1) }
    | unary_op scalar_expr                  { Unary($1, $2) }
    | scalar_expr binary_op scalar_expr     { Binary($2, $1, $3) }
    | LPAREN scalar_expr RPAREN             { $2 }

search_condition:
    | search_condition OR search_condition  { Or($1, $3) }
    | search_condition AND search_condition { And($1, $3) }
    | scalar_expr comparison scalar_expr    { Comparison($2, $1, $3) }
    | LPAREN search_condition RPAREN        { $2 }
我已经在FParsec中重新实现了它(在a的帮助下)。以下是相关位:

let binOpp = OperatorPrecedenceParser()
let scalarExpr = binOpp.ExpressionParser
binOpp.TermParser <- 
  [ constant 
    id
    between lparen rparen scalarExpr ]
  |> choice

// binary/unary ops added here

let comparison = 
  let compareExpr = pipe3 scalarExpr compareOp scalarExpr (fun l op r -> Comparison(op, l, r))
  between lparen rparen compareExpr <|> compareExpr

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()
searchConditionRef:= 
  chainl1 comparison (andTerm <|> orTerm)        
  <|> between lparen rparen searchCondition
标量、比较和搜索条件都可能以类似的方式开始(open paren->constant->infix操作符),但本质上是根据最终遇到的操作符类型来区分的。例如,如果您点击
,您就知道期初参数属于整个条件,而不是左侧的比较。这是否通过回溯正确处理?如果是这样的话,在解析复杂表达式时,如何以这种方式失败
不消耗任何输入

处理标量、比较和搜索条件的可选括号由fsyacc语法中的左递归处理。我理解这需要在FParsec中考虑。但是从上面的错误来看,我无法想象如何摆脱广泛的回溯。

Meta:为什么FParsec标签在这个问题上不起作用

我将引用页面上的评论:

嵌套但不是相互递归的表达式语法使得这里的parens解析有点麻烦。问题是,当解析器在某些位置看到一个左括号时,它还不知道括号中的表达式是否需要解析为
scalarExpr
比较
搜索条件
。为了能够解析这样的表达式,您必须在打开方括号之后和关闭方括号之前对解析器错误引入一些有限的回溯,以便解析器可以使用一个子语法暂时解析带括号的表达式,并且在必要时使用不同的语法再次解析

let tryBetweenParens p = lparen >>? (p .>>? rparen)

let opp = OperatorPrecedenceParser<_,_,_>()
let scalarExpr = opp.ExpressionParser
opp.TermParser <- choice [constant; id; tryBetweenParens scalarExpr]

// ... 

let comparison = // doesn't currently allow chained comparisons ( e.g. 1 = 2 = 3)
    let compareExpr = pipe3 scalarExpr compareOp scalarExpr (fun l op r -> Comparison(op, l, r))
    compareExpr <|> tryBetweenParens compareExpr

// ...

let searchCondition, searchConditionRef = createParserForwardedToRef()
do searchConditionRef:= 
    chainl1 (comparison <|> between lparen rparen searchCondition) 
            (andTerm <|> orTerm)
让试着在双方之间p=lparen>>?(p.>>?rparen)
设opp=operatorreceidenceparser()
设scalarExpr=opp.ExpressionParser

回答“meta”问题的opp.TermParser:自从我发布了这个问题以来,我一直无法编辑它,所以我尝试删除“fparsec”标记(作为测试),结果成功了。不幸的是,我无法将其添加回(或进行任何其他编辑)。昨天我把它标出来让主持人注意。还在等。第二,谢谢你周到的回复。我要到星期一才能试试这个。我在上一个问题中发布了一个使用优先解析器的有效解决方案。您的代码处理的
是否具有不同的优先级?AND和OR运算符在原始问题中也具有相同的优先级。您可以通过让OR解析器调用AND解析器来处理不同的先例,例如,
expr
调用
term
,尽管使用OPP可能更容易。在任何情况下,都必须允许回溯,以便在不同的语法级别上解析括号。
let tryBetweenParens p = lparen >>? (p .>>? rparen)

let opp = OperatorPrecedenceParser<_,_,_>()
let scalarExpr = opp.ExpressionParser
opp.TermParser <- choice [constant; id; tryBetweenParens scalarExpr]

// ... 

let comparison = // doesn't currently allow chained comparisons ( e.g. 1 = 2 = 3)
    let compareExpr = pipe3 scalarExpr compareOp scalarExpr (fun l op r -> Comparison(op, l, r))
    compareExpr <|> tryBetweenParens compareExpr

// ...

let searchCondition, searchConditionRef = createParserForwardedToRef()
do searchConditionRef:= 
    chainl1 (comparison <|> between lparen rparen searchCondition) 
            (andTerm <|> orTerm)