Search code examples
parsinghaskelleval

Haskell : Non-Exhaustive Pattern In Function Prevents Another Function From Executing Even Though Its Not Used


I'm trying to implement car, cdr, and cons functionality into a toy language I'm writing however when I try to execute my car function through main, I get the following error:

./parser "car [1 2 3]"
parser: parser.hs:(48,27)-(55,45): Non-exhaustive patterns in case

The function on lines 48-55 is the following:

parseOp :: Parser HVal
parseOp = (many1 letter <|> string "+" <|> string "-" <|> string "*" <|> string "/" <|> string "%" <|> string "&&" <|> string "||") >>= 
      (\x -> return $ case x of
                "&&" -> Op And
                "||" -> Op Or
                "+" -> Op Add
                "-" -> Op Sub
                "*" -> Op Mult
                "/" -> Op Div
                "%" -> Op Mod)

I'm really unsure why the error message points to this function because it has nothing to do with the list functionality. The car function is working however because I was able to successfully execute it through GHCI. I know my problem is due to parsing but I don't see where it is. The following are the functions that relate to lists. I can't see from them how they are influenced by parseOp.

data HVal = Number Integer
          | String String
          | Boolean Bool
          | List [HVal]
          | Op Op
          | Expr Op HVal HVal
          | Car [HVal]
           deriving (Read)

car :: [HVal] -> HVal
car xs = head xs


parseListFunctions :: Parser HVal
parseListFunctions = do
            _ <- string "car "
            _ <- char '['
            x <- parseList
            _ <- char ']'
            return $ Car [x]
            

parseExpr :: Parser HVal
parseExpr =  parseNumber
         <|> parseOp
         <|> parseBool
     <|> parseListFunctions 
         <|> do
        _ <- char '['
                x <- parseList
                _ <- char ']'
                return x
         <|> do
            _ <- char '('
            x <- parseExpression
                _ <- char ')'
                return x

eval :: HVal -> HVal
eval val@(Number _) = val
eval val@(String _) = val
eval val@(Boolean _) = val
eval val@(List _) = val -- Look at list eval NOT WORKING
eval val@(Op _) = val
eval (Expr op x y) = eval $ evalExpr (eval x) op (eval y)
eval (Car xs) = eval $ car xs

The removal of many1 letter in parseOp transfers the same error to the following function parseBool:

parseBool :: Parser HVal
parseBool = many1 letter >>= (\x -> return $ case x of
                                               "True" -> Boolean True
                                               "False" -> Boolean False)

Solution

  • You write

    parseExpr = ... <|> parseOp <|> ... <|> parseListFunctions <|> ...
    

    and so

    car ...
    

    is passed to parseOp first, then parseListFunctions. The parseOp parser succeeds in the

    many1 letter
    

    branch, and so in the \x -> return $ case x of ..., x is bound to "car". Because parseOp succeeds (and returns an error value with an embedded, not-yet-evaluated inexhaustive case error!), parseListFunctions is never tried.

    You will need to modify your grammar to reduce the ambiguity in it, so that these conflicts where multiple branches may match do not arise.