I'm trying to parse SQL search conditions and having trouble getting the parser to differentiate logical (AND
, OR
) from other infix operators. I'm parsing them as different nodes (perhaps that's difficult to do), but simplifies the evaluation phase. Here's the relevant code snippet (I can include more if necessary).
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"
correctly parses to Comparison (Eq,Constant (Int32 1),Constant (Int32 1))
but once I try to join two comparisons with a logical operator, e.g., "1 = 1 or 2 = 2"
, it fails to parse with
Error in Ln: 1 Col: 7
1 = 1 or 2 = 2
^
Expecting: end of input or infix operator
: 7
I expected it to parse the 1
before the error as a scalar expression and upon hitting or
backtrack, realizing it's not an infix operator, return 1
as the complete scalar, and recognize it's parsing the left-hand side of a condition joined by logical operator or
.
Instead, it seems to continue assuming 1
begins a more complex scalar expression, possibly involving an infix operator.
Is there a problem with the code, or is the solution to parse AND
/OR
as infix operators (using the same OperatorPrecedenceParser
)? I'd rather not go that route, so I'm hoping I've made a simple mistake somewhere.
The complete code is on gist.
I think ultimately you'll find you need to treat and
and or
as infix operators with precedence rules because that is exactly what they are and is the reason why most parsers including fparsec and fsyacc have special features to handle them (i.e. resolve ambiguity through precedence and associativity rules).
You've found one case highlighting this, but consider another:
1 = 1 or 2 = 2 and 3 =3
should that parse as (1 = 1 or 2 = 2) and 3 = 3
or 1 = 1 or (2 = 2 and 3 = 3)
?