module Eval (eval) where

import AST
import Control.Monad.Reader (Reader, ask, asks, local, runReader)

lookupVar :: Var -> Env -> Maybe Val
lookupVar var [] = Nothing
lookupVar var ((var', val) : env)
  | var == var' = Just val
  | otherwise = lookupVar var env

eval :: Exp -> Env -> Val
eval (Const v) env = v
eval (Var var) env =
  case lookupVar var env of
    Just val -> val
    Nothing -> error $ "Unbound variable: " ++ var
eval (App e1 e2) env =
  let v1 = eval e1 env
  in case v1 of
      ValFun f -> let v2 = eval e2 env in f v2
      _ -> error $ "Non-function application: " ++ show v1
eval (Lambda var e) env = ValFun (\arg -> eval e ((var, arg) : env))
eval (Cond e1 e2 e3) env =
  let v1 = eval e1 env
  in case v1 of
      ValBool b -> if b then eval e2 env else eval e3 env
      _ -> error $ "Non-boolean in condition: " ++ show v1
eval (LetRec fun arg body e) env =
  let recEnv = (fun, ValFun (\argVal -> eval body ((arg, argVal) : recEnv))) : env
  in eval e recEnv