megaman2
megaman2

Reputation: 1

Haskell - How to include the Tokens in the error output?

I had just started taking Haskell class and in one of my assignment, the given test case is suppose to throw an error along with the tokens at the end of the error. I managed to show the error but couldn't figure out how to show it along with the tokens from the test case.

The test cases that were given is check "(+ 4 (3 5 ))" and the expected output is

*** Exception: Parse Error: invalid expression [OpenParen,PosNum 3.0,PosNum 5.0,CloseParen,CloseParen]

And this is the output that I got:

*** Exception: Parse Error: invalid expression
CallStack (from HasCallStack):
  error, called at test.hs:43:11 in main:Test

This is what I got so far, it throws no error when compile and the test case throws the right error, however, it doesn't include the tokens:

module Test where

data Token = OpenParen
           | CloseParen
           | Operator Char
           | PosNum Double
     deriving (Show, Eq)

data ParseTree = NumNode Double
               | OpNode Char [ParseTree]
         deriving Show

scan :: String ->[Token]
scan xs
     | null lexeme = []
     | c == '(' = OpenParen:scan rest
     | c == ')' = CloseParen:scan rest
     | elem c ['+', '-', '*', '/'] = Operator c: scan (cs ++ rest)
     | elem c ['0'..'9'] = PosNum (read (c:cs):: Double):scan rest
     | otherwise = error ("Lexical Error - invalid character: " ++ [c])
     where [(lexeme, rest)] = lex xs
           c:cs = lexeme

recognize :: [Token] -> Bool
recognize ts = let (s, r) = rexpr ts
     in s && null r

-- <expr> ->OPENPAREN OPERATOR <operands> CLOSEPAREN |  POSNUMBER
rexpr :: [Token] -> (Bool, [Token])
rexpr (OpenParen:Operator  _: rest) = let(b1,r1) = roperands rest
     in case r1 of
          CloseParen:r2 -> (b1, r2)
          _ ->  error "Parse Error: expected closing parenthesis"
          
rexpr (PosNum _:rest) = (True, rest)
rexpr _ = error "Parse Error: invalid expression"

-- <operands> ->  <expr> [<operands>]
roperands :: [Token] -> (Bool, [Token])
roperands ts = let (b1, r1) = rexpr ts
     in case isValidStart r1 of
          True -> let (b2, r2) = roperands r1 in (b1 && b2, r2)
          False     -> (b1, r1)

isValidStart :: [Token] -> Bool
isValidStart (OpenParen: _) = True
isValidStart (PosNum _:_) = True
isValidStart _ = False

check:: String -> Bool
check = recognize.scan

Any help or advice is highly appreciated!

Upvotes: 0

Views: 117

Answers (1)

amalloy
amalloy

Reputation: 92077

In the last clause of rexpr, the one producing a parse error, you are ignoring the parameter...which is a list of tokens! And since a list of tokens is what you want to produce in the case of a parse error, you can simply show those tokens and append them to the error message.

Upvotes: 3

Related Questions