0

My question relates to the simple interpreter written in in this answer

How could one add IO capabilities to this interpreter (the first non monadic version)? By this I simply mean adding a statement that uses putStrLn. I'm not that well versed in Haskell yet, but I'm guessing you can just combine the IO monad somehow. Can somebody point me in the right direction?

data Stmt
  = Var := Exp                                   
  | While Exp Stmt                                               
  | Seq [Stmt]      
  | Print Exp       -- a print statement
John Smith
  • 2,282
  • 1
  • 14
  • 22

2 Answers2

2

You can make your interpreter have a result in the IO monad:

exec :: Stmt -> Store -> IO Store

Now the exec function can do anything in IO. The existing operations will need to be modified accordingly.

Paul Johnson
  • 17,438
  • 3
  • 42
  • 59
1

I have extended your interpreter with a Print statement that prints an expression to the console. the signature of exec and run had to be extended accordingly:

infixl 6 :+:, :-:
infixl 7 :*:, :/:

data Exp
    = C Int        -- constant                                                     
    | V Var        -- variable                                                     
    | Exp :+: Exp  -- addition                                                     
    | Exp :-: Exp  -- subtraction                                                  
    | Exp :*: Exp  -- multiplication                                               
    | Exp :/: Exp  -- division
    deriving (Show)

infix 1 :=

data Stmt
    = Var := Exp      -- assignment                                                
    | While Exp Stmt  -- loop                                                      
    | Seq [Stmt]      -- sequence
    | Print Exp


type Prog = Stmt

type Var = String
type Val = Int
type Store = [(Var, Val)]

eval :: Exp -> Store -> Val
eval (C n) r       = n
eval (V x) r       = case lookup x r of
                    Nothing -> error ("unbound variable `" ++ x ++ "'")
                    Just v  -> v
eval (e1 :+: e2) r = eval e1 r + eval e2 r
eval (e1 :-: e2) r = eval e1 r - eval e2 r
eval (e1 :*: e2) r = eval e1 r * eval e2 r
eval (e1 :/: e2) r = eval e1 r `div` eval e2 r

exec :: Stmt -> Store -> IO Store
exec (x := e) r                    = return $ (x, eval e r) : r
exec (While e s) r | eval e r /= 0 = exec (Seq [s, While e s]) r
                | otherwise     = return r
exec (Seq []) r                    = return r
exec (Seq (s : ss)) r              = do
                                        r' <- exec s r
                                        exec (Seq ss) r'
exec (Print x) r                   = print (eval x r) >> return r

run :: Prog -> Store -> IO Store
run p r = nubBy ((==) `on` fst) <$> (exec p r)

fib :: Prog
fib = Seq
[ "x" := C 0
, "y" := C 1
, While (V "n") $ Seq
    [ "z" := V "x" :+: V "y"
    , "x" := V "y"
    , "y" := V "z"
    , "n" := V "n" :-: C 1
    , Print (V "x")
    ]
]

main = lookup "x" <$> run fib [("n", 25)]    
Thomas Mahler
  • 180
  • 10