-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathEval.hs
More file actions
341 lines (293 loc) · 14.4 KB
/
Copy pathEval.hs
File metadata and controls
341 lines (293 loc) · 14.4 KB
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
module Eval where
import ValueLib
import Control.Monad
import Control.Monad.Except
import Text.ParserCombinators.Parsec hiding (spaces)
import System.IO
apply :: Value -> [Value] -> IOThrowsError Value
apply (PrimitiveFunc func) args = liftThrows $ func args
apply (IOFunc func) args = func args
apply (Func params varargs body closure) args =
if num params /= num args && varargs == Nothing
then throwError $ NumArgs (num params) args
else (liftIO $ bindVars closure $ zip params args) >>= bindVarArgs varargs >>= evalBody
where
remainingArgs = drop (length params) args
num = toInteger . length
evalBody env = liftM last $ mapM (eval env) body
bindVarArgs arg env = case arg of
Just argName -> liftIO $ bindVars env [(argName, List $ remainingArgs)]
Nothing -> return env
applyProc :: [Value] -> IOThrowsError Value
applyProc [func, List args] = apply func args
applyProc (func : args) = apply func args
readExpr = readOrThrow parseExpr
readExprList = readOrThrow (endBy parseExpr spaces)
readOrThrow :: Parser a -> String -> ThrowsError a
readOrThrow parser input = case parse parser "lisp" input of
Left err -> throwError $ Parser err
Right val -> return val
primitives :: [(String, [Value] -> ThrowsError Value)]
primitives = [("+", numericBinOp (+)),
("-", numericBinOp (-)),
("*", numericBinOp (*)),
("/", numericBinOp (/)),
("mod", intBinOp (mod)),
("quotient", intBinOp (quot)),
("remainder", intBinOp (rem)),
("number?", numTest),
("string?", strTest),
("boolean?", boolTest),
("list?", listTest),
("zero?", zeroTest),
("char?", charTest),
("symbol?", symTest),
("not", valNot),
("length", valLength),
("symbol->string", symStr True),
("string->symbol", symStr False),
("list->string", liStr True),
("string->list", liStr False),
("=", numBoolBinOp (==)),
("/=", numBoolBinOp (/=)),
("<", numBoolBinOp (<)),
(">", numBoolBinOp (>)),
("<=", numBoolBinOp (<=)),
(">=", numBoolBinOp (>=)),
("&&", boolBoolBinOp (&&)),
("||", boolBoolBinOp (||)),
("string=?", strBoolBinOp (==)),
("string>?", strBoolBinOp (>)),
("string<?", strBoolBinOp (<)),
("string<=?", strBoolBinOp (<=)),
("string>=?", strBoolBinOp (>=)),
("char=?", charBoolBinOp (==)),
("char>?", charBoolBinOp (>)),
("char<?", charBoolBinOp (<)),
("char>=?", charBoolBinOp (>=)),
("char<=?", charBoolBinOp (<=)),
("cons", cons),
("car", car),
("cdr", cdr),
("eq?", eqv),
("eqv?", eqv),
("make-string", makeString),
("string", stringFromChars),
("string-length", strLength),
("string-ref", strRef),
("substring", subStr),
("string-append", stringFromStrings),
("string-copy", strCpy)]
ioPrimitives :: [(String, [Value] -> IOThrowsError Value)]
ioPrimitives = [("apply", applyProc),
("open-input-file", makePort ReadMode),
("open-output-file", makePort WriteMode),
("close-input-port", closePort),
("close-output-port", closePort),
("read", readProc),
("write", writeProc),
("read-contents", readContents),
("read-all", readAll)]
eval :: Env -> Value -> IOThrowsError Value
eval env val@(String _) = return val
eval env val@(Number _) = return val
eval env val@(Float _) = return val
eval env val@(Bool _) = return val
eval env val@(Character _) = return val
eval env (Atom id) = getVar env id
eval env (List [Atom "quote", val]) = return val
eval env (List [Atom "if", pred, conseq, alt]) = do
result <- eval env pred
case result of
Bool False -> eval env alt
otherwise -> eval env conseq
eval env (List [Atom "set!", Atom var, form]) = eval env form >>= setVar env var
eval env (List [Atom "define", Atom var, form]) = eval env form >>= defineVar env var
eval env (List (Atom "define" : List (Atom var : params) : body)) = makeNormalFunc env params body >>= defineVar env var
eval env (List (Atom "define" : DottedList (Atom var : params) varargs : body)) = makeVarargs varargs env params body >>= defineVar env var
eval env (List (Atom "lambda" : List params : body)) = makeNormalFunc env params body
eval env (List (Atom "lambda" : DottedList params varargs : body)) = makeVarargs varargs env params body
eval env (List (Atom "lambda" : varargs@(Atom _) : body)) = makeVarargs varargs env [] body
eval env (List [Atom "load", String filename]) = load filename >>= liftM last . mapM (eval env)
eval env (List (function : args)) = do
func <- eval env function
argVals <- mapM (eval env) args
apply func argVals
eval env val@(List s) = return val
eval env badForm = throwError $ BadSpecialForm "Unrecognized special form " badForm
makeFunc varargs env params body = return $ Func (map show params) varargs body env
makeNormalFunc = makeFunc Nothing
makeVarargs = makeFunc . Just . show
intBinOp :: (Integer -> Integer -> Integer) -> [Value] -> ThrowsError Value
intBinOp op singleVal@[_] = throwError $ NumArgs 2 singleVal
intBinOp op params = mapM unpackInt params >>= return . Number . foldl1 op
numericBinOp :: (Float -> Float -> Float) -> [Value] -> ThrowsError Value
numericBinOp op singleVal@[_] = throwError $ NumArgs 2 singleVal
numericBinOp op params = mapM unpackFloat params >>= return . Float . foldl1 op
boolBinOp :: (Value -> ThrowsError a) -> (a -> a -> Bool) -> [Value] -> ThrowsError Value
boolBinOp unpacker op args@[_,_] = do
left <- unpacker $ args !! 0
right <- unpacker $ args !! 1
return $ Bool $ op left right
boolBinOp _ _ any = throwError $ NumArgs 2 any
numBoolBinOp = boolBinOp unpackFloat
strBoolBinOp = boolBinOp unpackStr
boolBoolBinOp = boolBinOp unpackBool
charBoolBinOp = boolBinOp unpackChar
unpackInt :: Value -> ThrowsError Integer
unpackInt (Number n) = return n
unpackInt notNum = throwError $ TypeMismatch "Number" notNum
unpackFloat :: Value -> ThrowsError Float
unpackFloat (Float n) = return n
unpackFloat (Number n) = return $ fromIntegral n
unpackFloat notFloat = throwError $ TypeMismatch "Number or Float" notFloat
unpackStr :: Value -> ThrowsError String
unpackStr (String s) = return s
unpackStr notStr = throwError $ TypeMismatch "String" notStr
unpackBool :: Value -> ThrowsError Bool
unpackBool (Bool b) = return b
unpackBool notBool = throwError $ TypeMismatch "Boolean" notBool
unpackChar :: Value -> ThrowsError Char
unpackChar (Character c) = return c
unpackChar notChar = throwError $ TypeMismatch "Character" notChar
numTest :: [Value] -> ThrowsError Value
numTest [Number _] = return $ Bool True
numTest [Float _] = return $ Bool True
numTest [_] = return $ Bool False
numTest any = throwError $ NumArgs 1 any
strTest :: [Value] -> ThrowsError Value
strTest [String _] = return $ Bool True
strTest [_] = return $ Bool False
strTest any = throwError $ NumArgs 1 any
boolTest :: [Value] -> ThrowsError Value
boolTest [Bool _] = return $ Bool True
boolTest [_] = return $ Bool False
boolTest any = throwError $ NumArgs 1 any
listTest :: [Value] -> ThrowsError Value
listTest [List _] = return $ Bool True
listTest [_] = return $ Bool False
listTest any = throwError $ NumArgs 1 any
zeroTest :: [Value] -> ThrowsError Value
zeroTest [Number x] = return $ Bool $ x == 0
zeroTest [Float x] = return $ Bool $ x == 0
zeroTest [_] = return $ Bool False
zeroTest any = throwError $ NumArgs 1 any
charTest :: [Value] -> ThrowsError Value
charTest [Character _] = return $ Bool True
charTest [_] = return $ Bool False
charTest any = throwError $ NumArgs 1 any
symTest :: [Value] -> ThrowsError Value
symTest [Atom _] = return $ Bool True
symTest [_] = return $ Bool False
symTest any = throwError $ NumArgs 1 any
valNot :: [Value] -> ThrowsError Value
valNot [Bool False] = return $ Bool True
valNot [_] = return $ Bool False
valNot any = throwError $ NumArgs 1 any
valLength :: [Value] -> ThrowsError Value
valLength [List xs] = return $ Number $ toInteger (length xs)
valLength [notList] = throwError $ TypeMismatch "List" notList
valLength any = throwError $ NumArgs 1 any
symStr :: Bool -> [Value] -> ThrowsError Value
symStr True [Atom x] = return $ String $ x
symStr False [String s] = return $ Atom $ s
symStr True [notAtom] = throwError $ TypeMismatch "Atom" notAtom
symStr False [notStr] = throwError $ TypeMismatch "String" notStr
symStr _ any = throwError $ NumArgs 1 any
liStr :: Bool -> [Value] -> ThrowsError Value
liStr True [List chars] = stringFromChars chars
liStr False [String s] = return $ List $ map (\c -> Character c) s
liStr True [notList] = throwError $ TypeMismatch "List" notList
liStr False [notStr] = throwError $ TypeMismatch "String" notStr
liStr _ any = throwError $ NumArgs 1 any
car :: [Value] -> ThrowsError Value
car [List (x:xs)] = return x
car [DottedList (x:xs) _] = return x
car [notList] = throwError $ TypeMismatch "List" notList
car any = throwError $ NumArgs 1 any
cdr :: [Value] -> ThrowsError Value
cdr [List (x:xs)] = return $ List xs
cdr [DottedList [_] x] = return x
cdr [DottedList (_:xs) x] = return $ DottedList xs x
cdr [notList] = throwError $ TypeMismatch "List" notList
cdr any = throwError $ NumArgs 1 any
cons :: [Value] -> ThrowsError Value
cons [x, List []] = return $ List [x]
cons [x, List xs] = return $ List $ [x] ++ xs
cons [x, DottedList xs xlast] = return $ DottedList ([x] ++ xs) xlast
cons [x1, x2] = return $ DottedList [x1] x2
cons any = throwError $ NumArgs 2 any
eqv :: [Value] -> ThrowsError Value
eqv [(Bool a), (Bool b)] = return $ Bool $ a == b
eqv [(Atom a), (Atom b)] = return $ Bool $ a == b
eqv [(List a), (List b)] = return $ Bool $ listEq a b
eqv [(DottedList as a), (DottedList bs b)] = return $ Bool $ listEq (as ++ [a]) (bs ++ [b])
eqv [(Number a), (Number b)] = return $ Bool $ a == b
eqv [(Number a), (Float b)] = return $ Bool $ (fromIntegral a) == b
eqv [(Float a), (Number b)] = return $ Bool $ a == (fromIntegral b)
eqv [(Float a), (Float b)] = return $ Bool $ a == b
eqv [(String a), (String b)] = return $ Bool $ a == b
eqv [(Character a), (Character b)] = return $ Bool $ a == b
eqv [_,_] = return $ Bool False
eqv any = throwError $ NumArgs 2 any
listEq :: [Value] -> [Value] -> Bool
listEq xs ys | length xs /= length ys = False
| otherwise = and (map eqvPair $ zip xs ys)
where
eqvPair (x,y) = case eqv [x,y] of
Left err -> False
Right (Bool val) -> val
makeString :: [Value] -> ThrowsError Value
makeString [(Number n), (Character c)] = return $ String $ map (\_ -> c) [1..n]
makeString any@[_,_] = throwError $ TypeMismatch "a Number and Character" (List any)
makeString any = throwError $ NumArgs 2 any
stringFromChars :: [Value] -> ThrowsError Value
stringFromChars chars = mapM unpackChar chars >>= return . String
stringFromStrings :: [Value] -> ThrowsError Value
stringFromStrings strings = mapM unpackStr strings >>= return . String . foldl1 (++)
strLength :: [Value] -> ThrowsError Value
strLength [(String s)] = return $ Number $ toInteger (length s)
strLength [notStr] = throwError $ TypeMismatch "String" notStr
strLength any = throwError $ NumArgs 2 any
strRef :: [Value] -> ThrowsError Value
strRef [(String s), (Number n)] | n < 0 || n > (toInteger ((length s) - 1)) = throwError $ Default ("Number must be between 0 and " ++ (show ((length s) - 1)) ++ ".")
| otherwise = return $ Character $ s !! (fromIntegral n)
strRef notArgs@[_,_] = throwError $ TypeMismatch "a String and a Number" (List notArgs)
strRef any = throwError $ NumArgs 2 any
subStr :: [Value] -> ThrowsError Value
subStr [(String s), (Number start), (Number end)] | start < 0 = throwError $ Default "Start must be 0 or more."
| end > (toInteger ((length s) - 1)) = throwError $ Default ("End must be less than " ++ show ((length s) - 1) ++ ".")
| otherwise = return $ String $ drop (fromIntegral start) (take (fromIntegral end) s)
subStr any@[_,_,_] = throwError $ TypeMismatch "a String, a start Number, and an end Number" (List any)
subStr any = throwError $ NumArgs 3 any
strCpy :: [Value] -> ThrowsError Value
strCpy [val@(String s)] = return val
strCpy [notStr] = throwError $ TypeMismatch "String" notStr
strCpy any = throwError $ NumArgs 1 any
makePort :: IOMode -> [Value] -> IOThrowsError Value
makePort mode [String filename] = liftM Port $ liftIO $ openFile filename mode
makePort mode [notStr] = throwError $ TypeMismatch "String" notStr
makePort _ any = throwError $ NumArgs 1 any
closePort :: [Value] -> IOThrowsError Value
closePort [Port port] = liftIO $ hClose port >> (return $ Bool True)
closePort [_] = return $ Bool False
closePort any = throwError $ NumArgs 1 any
readProc :: [Value] -> IOThrowsError Value
readProc [] = readProc [Port stdin]
readProc [Port port] = (liftIO $ hGetLine port) >>= liftThrows . readExpr
readProc [notPort] = throwError $ TypeMismatch "Port" notPort
readProc any = throwError $ NumArgs 1 any
writeProc :: [Value] -> IOThrowsError Value
writeProc [obj] = writeProc [obj, Port stdout]
writeProc [obj, Port port] = liftIO $ hPrint port obj >> (return $ Bool True)
writeProc any = throwError $ NumArgs 2 any
readContents :: [Value] -> IOThrowsError Value
readContents [String filename] = liftM String $ liftIO $ readFile filename
readContents [notStr] = throwError $ TypeMismatch "String" notStr
readContents any = throwError $ NumArgs 1 any
load :: String -> IOThrowsError [Value]
load filename = (liftIO $ readFile filename) >>= liftThrows . readExprList
readAll :: [Value] -> IOThrowsError Value
readAll [String filename] = liftM List $ load filename
readAll [notStr] = throwError $ TypeMismatch "String" notStr
readAll any = throwError $ NumArgs 1 any