module Eval (eval, runReader) 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 -> Cont -> Val
eval (Const v) env k = k v
eval (Var var) env k =
  case lookupVar var env of
    Just val -> k val
    Nothing -> error $ "Unbound variable: " ++ var
eval (App e1 e2) env k =
  eval e1 env $ \v1 ->
    case v1 of
      ValFun f -> eval e2 env $ \v2 -> f v2 k
      _ -> error $ "Non-function application: " ++ show v1
eval (Lambda var e) env k = k (ValFun (\arg -> eval e ((var, arg) : env)))
eval (Cond e1 e2 e3) env k =
  eval e1 env $ \v1 ->
    case v1 of
      ValBool b -> if b then eval e2 env k else eval e3 env k
      _ -> error $ "Non-boolean in condition: " ++ show v1
eval (LetRec fun arg body e) env k =
  let recEnv = (fun, ValFun (\argVal cont -> eval body ((arg, argVal) : recEnv) cont)) : env
   in eval e recEnv k
eval (Let var e1 e2) env k =
  eval e1 env $ \v1 ->
    let newEnv = (var, v1) : env
     in eval e2 newEnv k
eval (Pair e1 e2) env k =
  eval e1 env $ \v1 ->
    eval e2 env $ \v2 ->
      k (ValPair (v1, v2))