vb: add seq and string?
[jackhill/mal.git] / haskell / Core.hs
CommitLineData
fa9a9758
JM
1module Core
2( ns )
3where
4
5400d4bf 5import System.IO (hFlush, stdout)
c150ec41 6import Control.Exception (catch)
5400d4bf 7import Control.Monad.Trans (liftIO)
fa9a9758 8import qualified Data.Map as Map
c150ec41
JM
9import Data.Time.Clock.POSIX (getPOSIXTime)
10import Data.IORef (IORef, newIORef, readIORef, writeIORef)
fa9a9758 11
c150ec41 12import Readline (readline)
fa9a9758
JM
13import Reader (read_str)
14import Types
15import Printer (_pr_str, _pr_list)
16
17-- General functions
18
c150ec41 19equal_Q [a, b] = return $ if a == b then MalTrue else MalFalse
5400d4bf 20equal_Q _ = throwStr "illegal arguments to ="
fa9a9758 21
5400d4bf 22run_1 :: (MalVal -> MalVal) -> [MalVal] -> IOThrows MalVal
c150ec41 23run_1 f (x:[]) = return $ f x
5400d4bf 24run_1 _ _ = throwStr "function takes a single argument"
fa9a9758 25
5400d4bf 26run_2 :: (MalVal -> MalVal -> MalVal) -> [MalVal] -> IOThrows MalVal
c150ec41 27run_2 f (x:y:[]) = return $ f x y
5400d4bf
JM
28run_2 _ _ = throwStr "function takes a two arguments"
29
30-- Error/Exception functions
31
32throw (mv:[]) = throwMalVal mv
33throw _ = throwStr "illegal arguments to throw"
c150ec41
JM
34
35-- Scalar functions
36
5400d4bf
JM
37symbol (MalString str:[]) = return $ MalSymbol str
38symbol _ = throwStr "symbol called with non-string"
c150ec41 39
dbac60df 40keyword (MalString ('\x029e':str):[]) = return $ MalString $ "\x029e" ++ str
5400d4bf
JM
41keyword (MalString str:[]) = return $ MalString $ "\x029e" ++ str
42keyword _ = throwStr "keyword called with non-string"
2988d38e 43
fa9a9758
JM
44
45-- String functions
46
47pr_str args = do
48 return $ MalString $ _pr_list True " " args
49
50str args = do
51 return $ MalString $ _pr_list False "" args
52
53prn args = do
5400d4bf
JM
54 liftIO $ putStrLn $ _pr_list True " " args
55 liftIO $ hFlush stdout
fa9a9758
JM
56 return Nil
57
58println args = do
5400d4bf
JM
59 liftIO $ putStrLn $ _pr_list False " " args
60 liftIO $ hFlush stdout
fa9a9758
JM
61 return Nil
62
c150ec41 63slurp ([MalString path]) = do
5400d4bf 64 str <- liftIO $ readFile path
c150ec41 65 return $ MalString str
5400d4bf 66slurp _ = throwStr "invalid arguments to slurp"
c150ec41
JM
67
68do_readline ([MalString prompt]) = do
5400d4bf 69 str <- liftIO $ readline prompt
c150ec41 70 case str of
5400d4bf 71 Nothing -> throwStr "readline failed"
c150ec41 72 Just str -> return $ MalString str
5400d4bf 73do_readline _ = throwStr "invalid arguments to readline"
fa9a9758
JM
74
75-- Numeric functions
76
c150ec41
JM
77num_op op [MalNumber a, MalNumber b] = do
78 return $ MalNumber $ op a b
5400d4bf 79num_op _ _ = throwStr "illegal arguments to number operation"
c150ec41
JM
80
81cmp_op op [MalNumber a, MalNumber b] = do
82 return $ if op a b then MalTrue else MalFalse
5400d4bf 83cmp_op _ _ = throwStr "illegal arguments to comparison operation"
fa9a9758 84
c150ec41 85time_ms _ = do
5400d4bf 86 t <- liftIO $ getPOSIXTime
c150ec41 87 return $ MalNumber $ round (t * 1000)
fa9a9758
JM
88
89
90-- List functions
91
c150ec41 92list args = return $ MalList args Nil
fa9a9758
JM
93
94-- Vector functions
95
c150ec41 96vector args = return $ MalVector args Nil
fa9a9758
JM
97
98-- Hash Map functions
99
5400d4bf 100_pairup [x] = throwStr "Odd number of elements to _pairup"
c150ec41
JM
101_pairup [] = return []
102_pairup (MalString x:y:xs) = do
103 rest <- _pairup xs
104 return $ (x,y):rest
105
fa9a9758 106hash_map args = do
c150ec41
JM
107 pairs <- _pairup args
108 return $ MalHashMap (Map.fromList pairs) Nil
109
110assoc (MalHashMap hm _:kvs) = do
111 pairs <- _pairup kvs
112 return $ MalHashMap (Map.union (Map.fromList pairs) hm) Nil
5400d4bf 113assoc _ = throwStr "invalid call to assoc"
c150ec41
JM
114
115dissoc (MalHashMap hm _:ks) = do
116 let remover = (\hm (MalString k) -> Map.delete k hm) in
117 return $ MalHashMap (foldl remover hm ks) Nil
5400d4bf 118dissoc _ = throwStr "invalid call to dissoc"
c150ec41
JM
119
120get (MalHashMap hm _:MalString k:[]) = do
121 case Map.lookup k hm of
122 Just mv -> return mv
123 Nothing -> return Nil
124get (Nil:MalString k:[]) = return Nil
5400d4bf 125get _ = throwStr "invalid call to get"
c150ec41
JM
126
127contains_Q (MalHashMap hm _:MalString k:[]) = do
128 if Map.member k hm then return MalTrue
129 else return MalFalse
130contains_Q (Nil:MalString k:[]) = return MalFalse
5400d4bf 131contains_Q _ = throwStr "invalid call to contains?"
c150ec41
JM
132
133keys (MalHashMap hm _:[]) = do
134 return $ MalList (map MalString (Map.keys hm)) Nil
5400d4bf 135keys _ = throwStr "invalid call to keys"
c150ec41
JM
136
137vals (MalHashMap hm _:[]) = do
138 return $ MalList (Map.elems hm) Nil
5400d4bf 139vals _ = throwStr "invalid call to vals"
c150ec41 140
fa9a9758
JM
141
142-- Sequence functions
143
c150ec41
JM
144_sequential_Q (MalList _ _) = MalTrue
145_sequential_Q (MalVector _ _) = MalTrue
146_sequential_Q _ = MalFalse
147
148cons x Nil = MalList [x] Nil
149cons x (MalList lst _) = MalList (x:lst) Nil
150cons x (MalVector lst _) = MalList (x:lst) Nil
151
152concat1 a (MalList lst _) = a ++ lst
153concat1 a (MalVector lst _) = a ++ lst
154do_concat args = return $ MalList (foldl concat1 [] args) Nil
155
156nth ((MalList lst _):(MalNumber idx):[]) = do
157 if idx < length lst then return $ lst !! idx
5400d4bf 158 else throwStr "nth: index out of range"
c150ec41
JM
159nth ((MalVector lst _):(MalNumber idx):[]) = do
160 if idx < length lst then return $ lst !! idx
5400d4bf
JM
161 else throwStr "nth: index out of range"
162nth _ = throwStr "invalid call to nth"
c150ec41 163
6832696b 164first Nil = Nil
c150ec41
JM
165first (MalList lst _) = if length lst > 0 then lst !! 0 else Nil
166first (MalVector lst _) = if length lst > 0 then lst !! 0 else Nil
167
6832696b 168rest Nil = MalList [] Nil
c150ec41
JM
169rest (MalList lst _) = MalList (drop 1 lst) Nil
170rest (MalVector lst _) = MalList (drop 1 lst) Nil
171
172empty_Q Nil = MalTrue
173empty_Q (MalList [] _) = MalTrue
174empty_Q (MalVector [] _) = MalTrue
175empty_Q _ = MalFalse
176
5400d4bf
JM
177count (Nil:[]) = return $ MalNumber 0
178count (MalList lst _:[]) = return $ MalNumber $ length lst
179count (MalVector lst _:[]) = return $ MalNumber $ length lst
180count _ = throwStr $ "non-sequence passed to count"
fa9a9758 181
c150ec41
JM
182conj ((MalList lst _):args) = return $ MalList ((reverse args) ++ lst) Nil
183conj ((MalVector lst _):args) = return $ MalVector (lst ++ args) Nil
5400d4bf 184conj _ = throwStr $ "illegal arguments to conj"
c150ec41
JM
185
186apply args = do
187 f <- _get_call args
188 lst <- _to_list (last args)
189 f $ (init (drop 1 args)) ++ lst
190
191do_map args = do
192 f <- _get_call args
193 lst <- _to_list (args !! 1)
194 do new_lst <- mapM (\x -> f [x]) lst
195 return $ MalList new_lst Nil
196
197-- Metadata functions
198
199with_meta ((MalList lst _):m:[]) = return $ MalList lst m
200with_meta ((MalVector lst _):m:[]) = return $ MalVector lst m
201with_meta ((MalHashMap hm _):m:[]) = return $ MalHashMap hm m
202with_meta ((MalAtom atm _):m:[]) = return $ MalAtom atm m
203with_meta ((Func f _):m:[]) = return $ Func f m
204with_meta ((MalFunc {fn=f, ast=a, env=e, params=p, macro=mc}):m:[]) = do
205 return $ MalFunc {fn=f, ast=a, env=e, params=p, macro=mc, meta=m}
5400d4bf 206with_meta _ = throwStr $ "invalid with-meta call"
c150ec41
JM
207
208do_meta ((MalList _ m):[]) = return m
209do_meta ((MalVector _ m):[]) = return m
210do_meta ((MalHashMap _ m):[]) = return m
211do_meta ((MalAtom _ m):[]) = return m
212do_meta ((Func _ m):[]) = return m
213do_meta ((MalFunc {meta=m}):[]) = return m
5400d4bf 214do_meta _ = throwStr $ "invalid meta call"
c150ec41
JM
215
216-- Atom functions
217
218atom (val:[]) = do
5400d4bf 219 ref <- liftIO $ newIORef val
c150ec41 220 return $ MalAtom ref Nil
5400d4bf 221atom _ = throwStr "invalid atom call"
c150ec41
JM
222
223deref (MalAtom ref _:[]) = do
5400d4bf 224 val <- liftIO $ readIORef ref
c150ec41 225 return val
5400d4bf 226deref _ = throwStr "invalid deref call"
c150ec41
JM
227
228reset_BANG (MalAtom ref _:val:[]) = do
5400d4bf 229 liftIO $ writeIORef ref $ val
c150ec41 230 return val
5400d4bf 231reset_BANG _ = throwStr "invalid deref call"
c150ec41
JM
232
233swap_BANG (MalAtom ref _:args) = do
5400d4bf 234 val <- liftIO $ readIORef ref
c150ec41
JM
235 f <- _get_call args
236 new_val <- f $ [val] ++ (tail args)
5400d4bf 237 _ <- liftIO $ writeIORef ref $ new_val
c150ec41 238 return new_val
fa9a9758
JM
239
240ns = [
241 ("=", _func equal_Q),
5400d4bf 242 ("throw", _func throw),
c150ec41
JM
243 ("nil?", _func $ run_1 $ _nil_Q),
244 ("true?", _func $ run_1 $ _true_Q),
245 ("false?", _func $ run_1 $ _false_Q),
5400d4bf 246 ("symbol", _func $ symbol),
c150ec41 247 ("symbol?", _func $ run_1 $ _symbol_Q),
5400d4bf 248 ("keyword", _func $ keyword),
c150ec41 249 ("keyword?", _func $ run_1 $ _keyword_Q),
fa9a9758
JM
250
251 ("pr-str", _func pr_str),
252 ("str", _func str),
253 ("prn", _func prn),
254 ("println", _func println),
c150ec41 255 ("readline", _func do_readline),
fa9a9758
JM
256 ("read-string", _func (\[(MalString s)] -> read_str s)),
257 ("slurp", _func slurp),
c150ec41 258
fa9a9758
JM
259 ("<", _func $ cmp_op (<)),
260 ("<=", _func $ cmp_op (<=)),
261 (">", _func $ cmp_op (>)),
262 (">=", _func $ cmp_op (>=)),
263 ("+", _func $ num_op (+)),
264 ("-", _func $ num_op (-)),
265 ("*", _func $ num_op (*)),
266 ("/", _func $ num_op (div)),
c150ec41 267 ("time-ms", _func $ time_ms),
fa9a9758
JM
268
269 ("list", _func $ list),
270 ("list?", _func $ run_1 _list_Q),
271 ("vector", _func $ vector),
c150ec41 272 ("vector?", _func $ run_1 _vector_Q),
fa9a9758 273 ("hash-map", _func $ hash_map),
c150ec41
JM
274 ("map?", _func $ run_1 _hash_map_Q),
275 ("assoc", _func $ assoc),
276 ("dissoc", _func $ dissoc),
277 ("get", _func $ get),
278 ("contains?",_func $ contains_Q),
279 ("keys", _func $ keys),
280 ("vals", _func $ vals),
281
282 ("sequential?", _func $ run_1 _sequential_Q),
2988d38e
JM
283 ("cons", _func $ run_2 $ cons),
284 ("concat", _func $ do_concat),
285 ("nth", _func nth),
286 ("first", _func $ run_1 $ first),
287 ("rest", _func $ run_1 $ rest),
c150ec41 288 ("empty?", _func $ run_1 $ empty_Q),
5400d4bf 289 ("count", _func $ count),
c150ec41
JM
290 ("conj", _func $ conj),
291 ("apply", _func $ apply),
292 ("map", _func $ do_map),
293
294 ("with-meta", _func $ with_meta),
295 ("meta", _func $ do_meta),
296 ("atom", _func $ atom),
297 ("atom?", _func $ run_1 _atom_Q),
298 ("deref", _func $ deref),
299 ("reset!", _func $ reset_BANG),
300 ("swap!", _func $ swap_BANG)]