{-# OPTIONS_GHC -fglasgow-exts #-}
-- This monadic version based on some excellent suggestions
-- and code sent to asavige by Cale Gibbard.
module Rpn (evaluate) where
import Data.Char
import Control.Monad
import Control.Monad.Error
isStrDigit :: String -> Bool
isStrDigit = all isDigit
-- Check that a string matches regex /^-?\d+$/.
isSNum :: String -> Bool
isSNum [] = False
isSNum "-" = False
isSNum ('-':xs) = isStrDigit xs
isSNum xs = isStrDigit xs
calc :: Int -> String -> Int -> Either String Int
calc x "+" y = return $ x + y
calc x "-" y = return $ x - y
calc x "*" y = return $ x * y
calc x "/" y = return $ x `div` y
calc _ tok _ = throwError $ "Invalid token:" ++ show tok
evalStack :: [Int] -> String -> Either String [Int]
evalStack xs y
| isSNum y = return $ (read y):xs
| (a:b:cs) <- xs = do c <- calc b y a
return (c:cs)
| otherwise = throwError "Stack underflow"
evaluate :: String -> Either String Int
evaluate expr =
do xs <- foldM evalStack [] $ words expr
case xs of
[x] -> return x
_ -> throwError $ "Invalid stack:" ++ show xs