user2999349
user2999349

Reputation: 879

Haskell parsing queries (Parsec)

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!

Upvotes: 1

Views: 136

Answers (1)

Fyodor Soikin
Fyodor Soikin

Reputation: 80880

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

Upvotes: 2

Related Questions