module Slp
(
    run_maxargs,
    run_interp
) where

import Data.Maybe
import Data.Map (Map)
import qualified Data.Map as Map

type Id = String

data Binop = Plus | Minus | Times | Div

data Stm = CompoundStm Stm Stm
    | AssignStm Id Exp
    | PrintStm [Exp] 

data Exp = IdExp Id
    | NumExp Int
    | OpExp Exp Binop Exp
    | EseqExp Stm Exp

-- ###################################### maxargs #####################################

maxargs :: Stm -> Int
maxargs (CompoundStm s1 s2) = max (maxargs s1) (maxargs s2)
maxargs (AssignStm _ e2)    = maxargs_exp e2
maxargs (PrintStm es)       = length es

maxargs_exp :: Exp -> Int
maxargs_exp (EseqExp s1 e1) = maxargs s1
maxargs_exp _               = 0

-- ###################################### interp ######################################

type Table = Map Id Int

interp :: Stm -> IO ()
interp s = do interp_stm s Map.empty
              return ()

interp_stm :: Stm -> Table -> IO Table
interp_stm (CompoundStm s1 s2) t1   = do t2 <- interp_stm s1 t1
                                         interp_stm s2 t2
interp_stm (AssignStm s e) t1       = do (v1, t2) <- interp_exp e t1
                                         return $ Map.insert s v1 t2 
interp_stm (PrintStm es) t1         = do print_exps es t1
                                      where
                                          print_exps [] t       = return t1
                                          print_exps (e:es) t1  = do (v, t2) <- interp_exp e t1
                                                                     print v
                                                                     print_exps es t2

interp_exp :: Exp -> Table -> IO (Int, Table)
interp_exp (IdExp s) t1         = case Map.lookup s t1 of
                                      Just v  -> return (v, t1)
                                      Nothing -> return (undefined, t1)
interp_exp (NumExp i) t1        = return (i, t1) 
interp_exp (OpExp e1 op e2) t1  = do (v1, t2) <- interp_exp e1 t1
                                     (v2, t3) <- interp_exp e2 t2
                                     return (math_op op v1 v2, t2)
interp_exp (EseqExp s1 e1) t1   = do t2 <- interp_stm s1 t1
                                     interp_exp e1 t2

math_op :: Binop -> Int -> Int -> Int
math_op Plus    = (+)
math_op Minus   = (-)
math_op Div     = div
math_op Times   = (*)

-- ###################################### run ######################################

-- a := 5 + 3 ; b := ( print ( a , a - 1 ) , 10 * a ) ; print ( b )
prog = 
    CompoundStm
        (AssignStm "a" (OpExp (NumExp 5) Plus (NumExp 3)))
        (CompoundStm
            ((AssignStm
                "b"
                (EseqExp
                    (PrintStm [(IdExp "a"), (OpExp (IdExp "a") Minus (NumExp 1))]) 
                    (OpExp (NumExp 10) Times (IdExp "a")))))
            (PrintStm [IdExp "b"]))

run_maxargs :: IO ()
run_maxargs = do print $ maxargs prog

run_interp :: IO ()
run_interp = do interp prog