-
Notifications
You must be signed in to change notification settings - Fork 0
/
bind.hs
277 lines (206 loc) · 7.34 KB
/
bind.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
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
{-# LANGUAGE FlexibleInstances #-}
module Main where
import Text.ParserCombinators.Parsec hiding (spaces)
import System.Environment
import Control.Monad
import Control.Monad.Except
import Numeric
import System.IO
import Data.IORef
instance Show LispVal where show _ = "<primitive>"
instance Show (IORef a) where show _= "<iORef>"
type Env = IORef [(String, IORef LispVal)]
nullEnv :: IO Env
nullEnv = newIORef []
isBound :: Env -> String -> IO Bool
isBound envRef var = readIORef envRef >>= return . maybe False (const True) . lookup var
getVar :: Env -> String -> IO LispVal
getVar envRef var = do
env <- readIORef envRef
-- maybe :: b -> (a -> b) -> Maybe a -> b
maybe undefined
readIORef
(lookup var env)
-- setVar :: Env -> String -> LispVal
-- setVar envRef var value = do
-- env <- liftIO $ readIORef envRef
-- (liftIO . (flip writeIORef value)) (lookup var env)
-- return value
-- defineVar :: Env -> String -> LispVal
-- defineVar envRef var value = do
-- alreadyDefined <- liftIO $ isBound envRef var
-- if alreadyDefined
-- then setVar envRef var value >> return value
-- else liftIO $ do
-- valueRef <- newIORef value
-- env <- readIORef envRef
-- writeIORef envRef ((var, valueRef) : env)
-- return value
defineVar :: Env -> String -> LispVal -> IO LispVal
defineVar envRef var value = do
valueRef <- newIORef value
env <- readIORef envRef
writeIORef envRef ((var, valueRef) : env)
return value
data LispVal = Atom String
| List [LispVal]
| DottedList [LispVal] LispVal
| Number Integer
| Float Float
| String String
| Bool Bool
| PrimitiveFunc ([LispVal] -> LispVal)
| Func { params :: [LispVal], vararg :: (Maybe String),
body :: [LispVal], closure :: Env }
--showing typeclass
parseString :: Parser LispVal
parseString = do
char '"'
x <- many (noneOf "\"")
char '"'
return $ String x
parseAtom :: Parser LispVal
parseAtom = do
first <- letter <|> symbol
rest <- many (letter <|> digit <|> symbol)
let atom = first:rest
return $ case atom of
"#t" -> Bool True
"#f" -> Bool False
_ -> Atom atom
parseNumber :: Parser LispVal
parseNumber = do
x <- many1 digit
return $ Number $ read x
-- parseNumber = liftM (Number . read) $ many1 digit
parseFloat :: Parser LispVal
parseFloat = do
x <- many1 digit
char '.'
y <- many1 digit
let atom = (x ++ "." ++ y)
return $ Float (fst.head$readFloat (x++"."++y))
parseList :: Parser LispVal
parseList = liftM List $ sepBy parseExpr spaces
parseDottedList :: Parser LispVal
parseDottedList = do
head <- endBy parseExpr spaces
tail <- char '.' >> spaces >> parseExpr
return $ DottedList head tail
parseQuoted :: Parser LispVal
parseQuoted = do
char '\''
x <- parseExpr
return $ List [Atom "quote", x]
parseExpr :: Parser LispVal
parseExpr = parseAtom
<|> parseString
<|> try parseFloat
<|> parseNumber
<|> parseQuoted
<|> do char '('
x <- try parseList <|> parseDottedList
char ')'
return x
symbol :: Parser Char
symbol = oneOf "!#$%&|*+-/:<=>?@^_~"
spaces :: Parser ()
spaces = skipMany1 space
readExpr :: String -> LispVal
readExpr input = case parse parseExpr "lisp" input of
Left err -> String $ "No match: " ++ show err
Right val -> val
---- evaluator
unpackNum :: LispVal -> Integer
unpackNum (Number n) = n
-- unpackNum (String n) = let parsed = reads n :: [(Integer, String)] in
-- if null parsed
-- then 0
-- else fst $ parsed !! 0
-- unpackNum (List [n]) = unpackNum n
-- unpackNum _ = 0
unpackNum (Bool True) = 1
unpackNum (Bool False) = 0
unpackStr :: LispVal -> String
unpackStr (String s) = s
unpackBool :: LispVal -> Bool
unpackBool (Bool b) = b
numericBinop :: (Integer -> Integer -> Integer) -> [LispVal] -> LispVal
numericBinop op params = Number $ foldl1 op $ map unpackNum params
compareBinop [Atom x, Atom y] = (Bool (x==y))
compareBinop _ = (Bool False)
boolBinop :: (LispVal -> a) -> (a -> a -> Bool) -> [LispVal] -> LispVal
boolBinop unpacker op [x, y] = Bool $ (unpacker x) `op` (unpacker y)
numBoolBinop = boolBinop unpackNum
strBoolBinop = boolBinop unpackStr
boolBoolBinop = boolBinop unpackBool
primitives :: [(String, [LispVal] -> LispVal)]
primitives = [("+", numericBinop (+)),
("-", numericBinop (-)),
("*", numericBinop (*)),
("/", numericBinop div),
("mod", numericBinop mod),
("quotient", numericBinop quot),
("remainder", numericBinop rem),
("=", numBoolBinop (==)),
("<", numBoolBinop (<)),
(">", numBoolBinop (>)),
("/=", numBoolBinop (/=)),
(">=", numBoolBinop (>=)),
("<=", numBoolBinop (<=)),
("&&", boolBoolBinop (&&)),
("||", boolBoolBinop (||)),
("string=?", strBoolBinop (==)),
("string<?", strBoolBinop (<)),
("string>?", strBoolBinop (>)),
("string<=?", strBoolBinop (<=)),
("string>=?", strBoolBinop (>=)),
("eq?", compareBinop)]
makePrimitiveFunc env (var, func) = defineVar env var (PrimitiveFunc func)
primitiveBindings :: IO Env
primitiveBindings = do
env <- nullEnv
mapM (makePrimitiveFunc env) primitives
return env
apply :: LispVal -> [LispVal] -> IO LispVal
apply (Atom func) args = return $ maybe (Bool False) ($ args) (lookup func primitives)
makeFunc varargs env params body = return $ Func params varargs body env
makeNormalFunc = makeFunc Nothing
eval :: Env -> LispVal -> IO LispVal
eval env val@(String _) = return val
eval env val@(Number _) = return val
eval env val@(Bool _) = return val
eval env val@(Float _) = return val
eval env (Atom id) = getVar env id
eval env (List [Atom "quote", val]) = return val
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 func : args)) = do
x <- mapM (eval env) args
apply (Atom func) x
-- eval env (List (Atom func : args)) = return $ apply func $ map (eval env) args
-- repl--
--Then, we create a function that prints out a prompt and reads in a
--line of input:
readPrompt :: String -> IO String
readPrompt prompt = putStr prompt >> hFlush stdout >> getLine
evalString :: Env -> String -> IO String
evalString env expr = liftM show $ eval env $ readExpr expr
--evaluate the string and print the result
evalAndPrint :: Env -> String -> IO ()
evalAndPrint env expr = evalString env expr >>= putStrLn
--Now it's time to tie it all together. We want to read input, perform a function,
--and print the output
-- until_ :: Monad m => (a -> Bool) -> m a -> (a -> m ()) -> m ()
until_ pred prompt action = do
result <- prompt
if pred result
then return ()
else action result >> until_ pred prompt action
main :: IO ()
main = do
env <- nullEnv
env <- primitiveBindings
until_ (== "quit") (readPrompt "Lisp>>> ") (evalAndPrint env)