module Language.ArrayForth.Parse (ParseError (..), isNumber, readWord) where

import           Text.Printf                       (printf)

-- | Possible ways the input string can be malformed.
data ParseError = BadOpcode String
                | NotSlot3 String
                | NotJump String
                | NoAddr String
                | BadNumber String

instance Show ParseError where
  show (BadOpcode op) = printf "Invalid opcode `%s'." op
  show (NotSlot3 op)  = printf "`%s' cannot go into the last slot." op
  show (NotJump op)   =
    printf "`%s' is not a jump instruction and cannot get an address." op
  show (NoAddr op)    = printf "Missing a jump address for `%s'" op
  show (BadNumber n)  = printf "`%s' is not a valid number." n

-- | Is the given string a valid number with no other tokens?
isNumber :: String -> Bool
isNumber str = let asNumber = reads str :: [(Integer, String)] in
          not (null asNumber) && (null . snd $ head asNumber)

-- | Tries to read a word, giving an error if it fails.
readWord :: Read a => String -> Either ParseError a
readWord str = case reads str of
          (x, _) : _ -> Right x
          []         -> Left $ BadNumber str