-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathEval.hs
133 lines (118 loc) · 4.73 KB
/
Eval.hs
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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
module Eval (
eval
) where
import Environment
import IError
import IOPrimitives
import LispVal
import Primitives
import Control.Monad.Error
applyFunc :: LispVal -> [LispVal] -> IOThrowsLispError LispVal
applyFunc (PrimitiveFunc func) args = liftThrows $ func args
applyFunc (Func params vargs body closure) args =
if num params /= num args && vargs == Nothing
then throwError $ NumArgs (num params) args
else bindParams >>= bindVarArgs vargs >>= evalBody
where
restArgs = drop (length params) args
num = toInteger . length
bindParams = liftIO $ bindVars closure $ zip params args
bindVarArgs arg env = case arg of
Just argn -> liftIO $ bindVars env [(argn, LispList $ restArgs)]
Nothing -> return env
evalBody env = liftM last $ mapM (eval env) body
applyFunc (IOFunc func) args = func args
evalIf :: (Environment LispVal)
-> LispVal
-> LispVal
-> LispVal
-> IOThrowsLispError LispVal
evalIf env pred conseq alt = do
result <- eval env pred
case result of
LispBool True -> eval env conseq
LispBool False -> eval env alt
otherwise -> throwError $ TypeMismatch "boolean" result
lastVal :: [LispVal] -> IOThrowsLispError LispVal
lastVal args = return $ last args
evalCond :: (Environment LispVal) -> LispVal -> IOThrowsLispError LispVal
-- This is unspecified: return false for now
evalCond env (LispList []) = return $ LispBool False
evalCond env (LispList (x : xs)) = evalClause x
where
evalClause (LispList [pred]) = do
result <- eval env pred
case result of
LispBool True -> return result
LispBool False -> evalCond env $ LispList xs
otherwise -> throwError $ TypeMismatch "boolean" result
evalClause (LispList (LispAtom "else" : rest)) =
mapM (eval env) rest >>= lastVal
evalClause (LispList (pred : rest)) = do
result <- eval env pred
case result of
LispBool True -> mapM (eval env) rest >>= lastVal
LispBool False -> evalCond env $ LispList xs
otherwise -> throwError $ TypeMismatch "boolean" result
evalClause other = throwError $ BadSpecialForm "malformed cond clause" other
checkDatum :: LispVal -> LispVal -> IOThrowsLispError LispVal
checkDatum key (LispList []) = return $ LispBool False
checkDatum key (LispList (x : xs)) = do
result <- liftThrows $ eqv [key, x]
case result of
LispBool True -> return $ LispBool True
LispBool False -> checkDatum key $ LispList xs
evalCase :: (Environment LispVal)
-> LispVal
-> LispVal
-> IOThrowsLispError LispVal
-- This is unspcified: return false for now
evalCase env (LispList []) key = return $ LispBool False
evalCase env (LispList (x : xs)) key = evalClause x
where
evalClause (LispList (LispAtom "else" : rest)) =
mapM (eval env) rest >>= lastVal
evalClause (LispList (datum : rest)) = do
result <- checkDatum key datum
case result of
LispBool True -> mapM (eval env) rest >>= lastVal
LispBool False -> evalCase env (LispList xs) key
evalClause other = throwError $ BadSpecialForm "malformed case clause" other
eval :: ((Environment LispVal)) -> LispVal -> IOThrowsLispError LispVal
eval env val@(LispChar _) = return val
eval env val@(LispString _ _) = return val
eval env val@(LispNumber _) = return val
eval env val@(LispBool _) = return val
eval env (LispAtom id) = getVar env id
eval env (LispList [LispAtom "quote", val]) = return val
-- Define / set variables
eval env (LispList [LispAtom "set!", LispAtom var, form]) =
eval env form >>= setVar env var
eval env (LispList [LispAtom "define", LispAtom var, form]) =
eval env form >>= defineVar env var
-- Conditionals
eval env (LispList [LispAtom "if", pred, conseq, alt]) = evalIf env pred conseq alt
eval env (LispList (LispAtom "cond" : clauses)) = evalCond env $ LispList clauses
eval env (LispList (LispAtom "case" : key : clauses)) =
eval env key >>= (evalCase env $ LispList clauses)
-- Function definitions
eval env (LispList (LispAtom "define" : LispList (LispAtom var : params) : body)) =
makeNormalFunc env params body >>= defineVar env var
eval env (LispList (LispAtom "define" : LispDottedList (LispAtom var : params) vargs : body)) =
makeVargsFunc vargs env params body >>= defineVar env var
eval env (LispList (LispAtom "lambda" : LispList params : body)) =
makeNormalFunc env params body
eval env (LispList (LispAtom "lambda" : LispDottedList params vargs : body)) =
makeVargsFunc vargs env params body
eval env (LispList (LispAtom "lambda" : vargs@(LispAtom _) : body)) =
makeVargsFunc vargs env [] body
-- "Special" load function
eval env (LispList [LispAtom "load", LispString filename _]) =
load filename >>= liftM last . mapM (eval env)
-- Function calls
eval env (LispList (func : args)) = do
lookup <- eval env func
argVals <- mapM (eval env) args
applyFunc lookup argVals
-- Error
eval env badForm = throwError $ BadSpecialForm "Unrecognized special form" badForm