I'm trying to make a queries parser in Haskell, but don't understand how I'm supposed to allow different optional paths of parser logic. My attempt:
query :: Parser Query
query = do
-- add more queries
reserved "SELECT"
select <- sequenceOfExpr
reserved "FROM"
table <- identifier
semi
return $ Select select table (BoolConst True)
<|> do
reserved "SELECT"
select <- sequenceOfExpr
reserved "FROM"
table <- identifier
reserved "WHERE"
whereQ <- bExpression
semi
return $ Select select table whereQ
<|> do
reserved "INSERT"
insert <- sequenceOfExpr
reserved "INTO"
table <- identifier
semi
return $ Insert insert table
<|> do
reserved "REMOVE"
reserved "FROM"
table <- identifier
reserved "WHERE"
whereQ <- bExpression
semi
return $ Remove table whereQ
<|> do
reserved "CREATE"
table <- identifier
fields <- sequenceOfExpr
semi
return $ Create table fields
<|> do
reserved "DROP"
table <- identifier
semi
return $ Drop table
Which works when parsing a string that corresponds to the first do stmt structure, e.g.:
"SELECT testField FROM testTable;"
but not for the others. E.g. when parsing:
"SELECT testField FROM testTable WHERE TRUE"
Instead of trying the other paths, it returns:
unexpected "W"
expecting ";"
In other words it seems like it only tries the first logic. What am I doing wrong?
Any help would be much appreciated!
This happens because the SELECT FROM
alternative has succeeded and returned its result, the parsing never got to trying the SELECT FROM WHERE
alternative.
In this specific case, I would just flip their order: try SELECT FROM WHERE
first, and if that doesn't work, fall back to SELECT FROM
. You would also need to wrap it in a try
in order for the parser to roll back to the beginning of the query.
Alternatively, you could make the WHERE
parsing a conditional part of the SELECT FROM
parser, something like this:
do
reserved "SELECT"
select <- sequenceOfExpr
reserved "FROM"
table <- identifier
whereQ <- try (reserved "WHERE" *> bExpression) <|> (pure $ BoolConst True)
semi
return $ Select select table whereQ