aboutsummaryrefslogtreecommitdiff
path: root/src/Environment.hs
blob: 7b3f365d91cc0810b5d7c2e9d48cd5453f608294 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
module Environment ( Env
                   , setVar
                   , getVar
                   , defineVar
                   , manyBindings
                   , newEnv
                   , liftLispResult
                   , IOResult
                   ) where

import           Control.Monad        (liftM, mapM)
import           Control.Monad.Except
import           Data.IORef
import           Data.Maybe           (isJust)
import           Error.Base           (LispError (..), LispResult (..), unwrap)
import           Parser               (Expr (..))

type Env = IORef [(String, IORef Expr)]

newEnv :: IO Env
newEnv = newIORef []

type IOResult = ExceptT LispError IO

liftLispResult :: LispResult a -> IOResult a
liftLispResult (Left err)  = throwError err
liftLispResult (Right val) = return val

isBound :: Env -> String -> IO Bool
isBound env var = do
    ptr <- readIORef env
    return $ isJust $ lookup var ptr

-- env modifiers

getVar :: Env -> String -> IOResult Expr
getVar env var = do
    ptr <- liftIO $ readIORef env
    maybe (throwError $ UnboundVariable var)
          (liftIO . readIORef)
          $ lookup var ptr

setVar :: Env -> String -> Expr -> IOResult ()
setVar env var val = do
    ptr <- liftIO $ readIORef env
    maybe (throwError $ UnboundVariable var)
          (liftIO . (flip writeIORef val))
          $ lookup var ptr

defineVar :: Env -> String -> Expr -> IOResult ()
defineVar env var val = do
    alreadyBound <- liftIO $ isBound env var
    if alreadyBound
       then setVar env var val
       else liftIO $ do
           newRef <- newIORef val
           ptr <- readIORef env
           writeIORef env $ (var, newRef):ptr

makeBind :: (String, Expr) -> IO (String, IORef Expr)
makeBind (var, val) = do
    n <- newIORef val
    return (var, n)

manyBindings :: Env -> [(String, Expr)] -> IO Env
manyBindings env binds = do
    ptr <- readIORef env
    extendedEnv <- liftM (++ ptr) $ mapM makeBind binds
    newIORef extendedEnv