--
import Monad (liftM2, when)
import EnvMT
import StateMT
import OutputMT
import ExceptMT
infixl 6 :*:, :/:
infixl 5 :+:, :-:
infixr 4 :>
type Name = String
type Message = String
data E = E :+: E | E :-: E | E :*: E | E :/: E | Int Int
| Let Name E E | Var Name
| Print Message E
| ReadRef Name
| WriteRef Name E
| E :> E
eval (e1 :+: e2) = liftM2 (+) (eval e1) (eval e2)
eval (e1 :-: e2) = liftM2 (-) (eval e1) (eval e2)
eval (e1 :*: e2) = liftM2 (*) (eval e1) (eval e2)
eval (e1 :/: e2) = liftM2 div (eval e1) $
(do x <- eval e2
when (x == 0) (raise "division by 0")
return x)
eval (Int x) = return x
eval (Let x e1 e2) = do v <- eval e1; inModEnv ((x,v):) (eval e2)
eval (Var x) = maybe (raise ("undefined variable: " ++ x)) return .
lookup x =<< getEnv
eval (Print x e) = do v <- eval e
output (x ++ show v)
return v
eval (ReadRef x) = maybe (return 0) return . lookup x =<< getSt
eval (WriteRef x e) = do v <- eval e
updSt_ $ \s -> (x,v) : filter ((/= x) . fst) s
return 0
eval (e1 :> e2) = eval e1 >> eval e2
type Heap = [(Name,Int)]
type Env = [(Name,Int)]
type M = WithState Heap
( WithEnv Env
( WithOutput String
( WithExcept String IO
)))
run :: M a -> IO a
run m =
do x <- removeExcept $ listOutput $ withEnv [] $ withSt [] m
case x of
Left err -> error ("error: " ++ err)
Right (v,o) -> mapM putStrLn o >> return v
test e = print =<< run (eval e)
-- examples:
tests =
[ Let "x" (Int 1 :/: Int 0) (Int 2) -- let is strict
, ReadRef "x" -- refs are 0 by default
, WriteRef "x" (Int 8) :> ReadRef "x" -- write - read
, Let "x" (Int 7) $ WriteRef "x" (Int 2) :> Var "x" :*: ReadRef "x" -- vars & refs are different
, Print "x before = " (ReadRef "x") -- priniting
:> WriteRef "x" (Int 7)
:> Print "x after = " (ReadRef "x")
, Let "x" (Int 1) $ Var "x" :+: Let "x" (Int 2) (Var "x") -- nested lets
]
--