Commit | Line | Data |
---|---|---|
16fbc20a JB |
1 | port module Main exposing (..) |
2 | ||
3 | import Array | |
4 | import Dict exposing (Dict) | |
5 | import IO exposing (..) | |
6 | import Json.Decode exposing (decodeValue) | |
7 | import Platform exposing (programWithFlags) | |
8 | import Types exposing (..) | |
9 | import Reader exposing (readString) | |
10 | import Printer exposing (printString) | |
11 | import Utils exposing (maybeToList, zip, last, justValues, makeCall) | |
12 | import Env | |
13 | import Core | |
14 | import Eval | |
15 | ||
16 | ||
17 | main : Program Flags Model Msg | |
18 | main = | |
19 | programWithFlags | |
20 | { init = init | |
21 | , update = update | |
22 | , subscriptions = | |
23 | \model -> input (decodeValue decodeIO >> Input) | |
24 | } | |
25 | ||
26 | ||
27 | type alias Args = | |
28 | List String | |
29 | ||
30 | ||
31 | type alias Flags = | |
32 | { args : Args | |
33 | } | |
34 | ||
35 | ||
36 | type Model | |
37 | = InitIO Args Env (IO -> Eval MalExpr) | |
38 | | ScriptIO Env (IO -> Eval MalExpr) | |
39 | | ReplActive Env | |
40 | | ReplIO Env (IO -> Eval MalExpr) | |
41 | | Stopped | |
42 | ||
43 | ||
44 | init : Flags -> ( Model, Cmd Msg ) | |
45 | init { args } = | |
46 | let | |
47 | makeFn = | |
48 | CoreFunc >> MalFunction | |
49 | ||
50 | initEnv = | |
51 | Core.ns | |
52 | |> Env.set "eval" (makeFn malEval) | |
53 | |> Env.set "*ARGV*" (MalList (args |> List.map MalString)) | |
54 | ||
55 | evalMalInit = | |
56 | malInit | |
57 | |> List.map rep | |
58 | |> justValues | |
59 | |> List.foldl | |
60 | (\b a -> a |> Eval.andThen (\_ -> b)) | |
61 | (Eval.succeed MalNil) | |
62 | in | |
63 | runInit args initEnv evalMalInit | |
64 | ||
65 | ||
66 | malInit : List String | |
67 | malInit = | |
68 | [ """(def! not | |
69 | (fn* (a) | |
70 | (if a false true)))""" | |
71 | , """(def! load-file | |
72 | (fn* (f) | |
73 | (eval (read-string | |
74 | (str "(do " (slurp f) ")")))))""" | |
75 | , """(defmacro! cond | |
76 | (fn* (& xs) | |
77 | (if (> (count xs) 0) | |
78 | (list 'if (first xs) | |
79 | (if (> (count xs) 1) | |
80 | (nth xs 1) | |
81 | (throw "odd number of forms to cond")) | |
82 | (cons 'cond (rest (rest xs)))))))""" | |
83 | , """(defmacro! or | |
84 | (fn* (& xs) | |
85 | (if (empty? xs) | |
86 | nil | |
87 | (if (= 1 (count xs)) | |
88 | (first xs) | |
89 | `(let* (or_FIXME ~(first xs)) | |
90 | (if or_FIXME or_FIXME (or ~@(rest xs))))))))""" | |
91 | ] | |
92 | ||
93 | ||
94 | update : Msg -> Model -> ( Model, Cmd Msg ) | |
95 | update msg model = | |
96 | case model of | |
97 | Stopped -> | |
98 | ( model, Cmd.none ) | |
99 | ||
100 | InitIO args env cont -> | |
101 | case msg of | |
102 | Input (Ok io) -> | |
103 | runInit args env (cont io) | |
104 | ||
105 | Input (Err msg) -> | |
106 | Debug.crash msg | |
107 | ||
108 | ScriptIO env cont -> | |
109 | case msg of | |
110 | Input (Ok io) -> | |
111 | runScriptLoop env (cont io) | |
112 | ||
113 | Input (Err msg) -> | |
114 | Debug.crash msg | |
115 | ||
116 | ReplActive env -> | |
117 | case msg of | |
118 | Input (Ok (LineRead (Just line))) -> | |
119 | case rep line of | |
120 | Just expr -> | |
121 | run env expr | |
122 | ||
123 | Nothing -> | |
124 | ( model, readLine prompt ) | |
125 | ||
126 | Input (Ok LineWritten) -> | |
127 | ( model, readLine prompt ) | |
128 | ||
129 | Input (Ok (LineRead Nothing)) -> | |
130 | -- Ctrl+D = The End. | |
131 | ( model, Cmd.none ) | |
132 | ||
133 | Input (Ok io) -> | |
134 | Debug.crash "unexpected IO received: " io | |
135 | ||
136 | Input (Err msg) -> | |
137 | Debug.crash msg | |
138 | ||
139 | ReplIO env cont -> | |
140 | case msg of | |
141 | Input (Ok io) -> | |
142 | run env (cont io) | |
143 | ||
144 | Input (Err msg) -> | |
145 | Debug.crash msg ( model, Cmd.none ) | |
146 | ||
147 | ||
148 | runInit : Args -> Env -> Eval MalExpr -> ( Model, Cmd Msg ) | |
149 | runInit args env expr = | |
150 | case Eval.run env expr of | |
151 | ( env, EvalOk expr ) -> | |
152 | -- Init went okay. | |
153 | case args of | |
154 | -- If we got no args: start REPL. | |
155 | [] -> | |
156 | ( ReplActive env, readLine prompt ) | |
157 | ||
158 | -- Run the script in the first argument. | |
159 | -- Put the rest of the arguments as *ARGV*. | |
160 | filename :: argv -> | |
161 | runScript filename argv env | |
162 | ||
163 | ( env, EvalErr msg ) -> | |
164 | -- Init failed, don't start REPL. | |
165 | ( Stopped, writeLine ("ERR:" ++ msg) ) | |
166 | ||
167 | ( env, EvalIO cmd cont ) -> | |
168 | -- IO in init. | |
169 | ( InitIO args env cont, cmd ) | |
170 | ||
171 | ||
172 | runScript : String -> List String -> Env -> ( Model, Cmd Msg ) | |
173 | runScript filename argv env = | |
174 | let | |
175 | malArgv = | |
176 | MalList (List.map MalString argv) | |
177 | ||
178 | newEnv = | |
179 | env |> Env.set "*ARGV*" malArgv | |
180 | ||
181 | program = | |
182 | MalList | |
183 | [ MalSymbol "load-file" | |
184 | , MalString filename | |
185 | ] | |
186 | in | |
187 | runScriptLoop newEnv (eval program) | |
188 | ||
189 | ||
190 | runScriptLoop : Env -> Eval MalExpr -> ( Model, Cmd Msg ) | |
191 | runScriptLoop env expr = | |
192 | case Eval.run env expr of | |
193 | ( env, EvalOk expr ) -> | |
194 | ( Stopped, Cmd.none ) | |
195 | ||
196 | ( env, EvalErr msg ) -> | |
197 | ( Stopped, writeLine ("ERR:" ++ msg) ) | |
198 | ||
199 | ( env, EvalIO cmd cont ) -> | |
200 | ( ScriptIO env cont, cmd ) | |
201 | ||
202 | ||
203 | run : Env -> Eval MalExpr -> ( Model, Cmd Msg ) | |
204 | run env expr = | |
205 | case Eval.run env expr of | |
206 | ( env, EvalOk expr ) -> | |
207 | ( ReplActive env, writeLine (print env expr) ) | |
208 | ||
209 | ( env, EvalErr msg ) -> | |
210 | ( ReplActive env, writeLine ("ERR:" ++ msg) ) | |
211 | ||
212 | ( env, EvalIO cmd cont ) -> | |
213 | ( ReplIO env cont, cmd ) | |
214 | ||
215 | ||
216 | prompt : String | |
217 | prompt = | |
218 | "user> " | |
219 | ||
220 | ||
221 | {-| read can return three things: | |
222 | ||
223 | Ok (Just expr) -> parsed okay | |
224 | Ok Nothing -> empty string (only whitespace and/or comments) | |
225 | Err msg -> parse error | |
226 | ||
227 | -} | |
228 | read : String -> Result String (Maybe MalExpr) | |
229 | read = | |
230 | readString | |
231 | ||
232 | ||
233 | debug : String -> (Env -> a) -> Eval b -> Eval b | |
234 | debug msg f e = | |
235 | Eval.withEnv | |
236 | (\env -> | |
237 | Env.debug env msg (f env) | |
238 | |> always e | |
239 | ) | |
240 | ||
241 | ||
242 | eval : MalExpr -> Eval MalExpr | |
243 | eval ast = | |
244 | let | |
245 | apply expr env = | |
246 | case expr of | |
247 | MalApply app -> | |
248 | Left | |
249 | (debug "evalApply" | |
250 | (\env -> printString env True expr) | |
251 | (evalApply app) | |
252 | ) | |
253 | ||
254 | _ -> | |
255 | Right expr | |
256 | in | |
257 | evalNoApply ast | |
258 | |> Eval.andThen (Eval.runLoop apply) | |
259 | ||
260 | ||
261 | malEval : List MalExpr -> Eval MalExpr | |
262 | malEval args = | |
263 | case args of | |
264 | [ expr ] -> | |
265 | Eval.withEnv | |
266 | (\env -> | |
267 | Eval.modifyEnv (Env.jump Env.globalFrameId) | |
268 | |> Eval.andThen (\_ -> eval expr) | |
269 | |> Eval.andThen | |
270 | (\res -> | |
271 | Eval.modifyEnv (Env.jump env.currentFrameId) | |
272 | |> Eval.andThen (\_ -> Eval.succeed res) | |
273 | ) | |
274 | ) | |
275 | ||
276 | _ -> | |
277 | Eval.fail "unsupported arguments" | |
278 | ||
279 | ||
280 | evalApply : ApplyRec -> Eval MalExpr | |
281 | evalApply { frameId, bound, body } = | |
282 | Eval.withEnv | |
283 | (\env -> | |
284 | Eval.modifyEnv (Env.enter frameId bound) | |
285 | |> Eval.andThen (\_ -> evalNoApply body) | |
286 | |> Eval.ignore (Eval.modifyEnv (Env.leave env.currentFrameId)) | |
287 | ) | |
288 | ||
289 | ||
290 | evalNoApply : MalExpr -> Eval MalExpr | |
291 | evalNoApply ast = | |
292 | let | |
293 | go ast = | |
294 | case ast of | |
295 | MalList [] -> | |
296 | Eval.succeed ast | |
297 | ||
298 | MalList ((MalSymbol "def!") :: args) -> | |
299 | evalDef args | |
300 | ||
301 | MalList ((MalSymbol "let*") :: args) -> | |
302 | evalLet args | |
303 | ||
304 | MalList ((MalSymbol "do") :: args) -> | |
305 | evalDo args | |
306 | ||
307 | MalList ((MalSymbol "if") :: args) -> | |
308 | evalIf args | |
309 | ||
310 | MalList ((MalSymbol "fn*") :: args) -> | |
311 | evalFn args | |
312 | ||
313 | MalList ((MalSymbol "quote") :: args) -> | |
314 | evalQuote args | |
315 | ||
316 | MalList ((MalSymbol "quasiquote") :: args) -> | |
317 | case args of | |
318 | [ expr ] -> | |
319 | -- TCO. | |
320 | evalNoApply (evalQuasiQuote expr) | |
321 | ||
322 | _ -> | |
323 | Eval.fail "unsupported arguments" | |
324 | ||
325 | MalList ((MalSymbol "defmacro!") :: args) -> | |
326 | evalDefMacro args | |
327 | ||
328 | MalList ((MalSymbol "macroexpand") :: args) -> | |
329 | case args of | |
330 | [ expr ] -> | |
331 | macroexpand expr | |
332 | ||
333 | _ -> | |
334 | Eval.fail "unsupported arguments" | |
335 | ||
336 | MalList ((MalSymbol "try*") :: args) -> | |
337 | evalTry args | |
338 | ||
339 | MalList list -> | |
340 | evalList list | |
341 | |> Eval.andThen | |
342 | (\newList -> | |
343 | case newList of | |
344 | [] -> | |
345 | Eval.fail "can't happen" | |
346 | ||
347 | (MalFunction (CoreFunc fn)) :: args -> | |
348 | fn args | |
349 | ||
350 | (MalFunction (UserFunc { lazyFn })) :: args -> | |
351 | lazyFn args | |
352 | ||
353 | fn :: _ -> | |
354 | Eval.withEnv | |
355 | (\env -> | |
356 | Eval.fail ((printString env True fn) ++ " is not a function") | |
357 | ) | |
358 | ) | |
359 | ||
360 | _ -> | |
361 | evalAst ast | |
362 | in | |
363 | debug "evalNoApply" | |
364 | (\env -> printString env True ast) | |
365 | (macroexpand ast |> Eval.andThen go) | |
366 | ||
367 | ||
368 | evalAst : MalExpr -> Eval MalExpr | |
369 | evalAst ast = | |
370 | case ast of | |
371 | MalSymbol sym -> | |
372 | -- Lookup symbol in env and return value or raise error if not found. | |
373 | Eval.withEnv (Env.get sym >> Eval.fromResult) | |
374 | ||
375 | MalList list -> | |
376 | -- Return new list that is result of calling eval on each element of list. | |
377 | evalList list | |
378 | |> Eval.map MalList | |
379 | ||
380 | MalVector vec -> | |
381 | evalList (Array.toList vec) | |
382 | |> Eval.map (Array.fromList >> MalVector) | |
383 | ||
384 | MalMap map -> | |
385 | evalList (Dict.values map) | |
386 | |> Eval.map | |
387 | (zip (Dict.keys map) | |
388 | >> Dict.fromList | |
389 | >> MalMap | |
390 | ) | |
391 | ||
392 | _ -> | |
393 | Eval.succeed ast | |
394 | ||
395 | ||
396 | evalList : List MalExpr -> Eval (List MalExpr) | |
397 | evalList list = | |
398 | let | |
399 | go list acc = | |
400 | case list of | |
401 | [] -> | |
402 | Eval.succeed (List.reverse acc) | |
403 | ||
404 | x :: rest -> | |
405 | eval x | |
406 | |> Eval.andThen | |
407 | (\val -> | |
408 | go rest (val :: acc) | |
409 | ) | |
410 | in | |
411 | go list [] | |
412 | ||
413 | ||
414 | evalDef : List MalExpr -> Eval MalExpr | |
415 | evalDef args = | |
416 | case args of | |
417 | [ MalSymbol name, uneValue ] -> | |
418 | eval uneValue | |
419 | |> Eval.andThen | |
420 | (\value -> | |
421 | Eval.modifyEnv (Env.set name value) | |
422 | |> Eval.andThen (\_ -> Eval.succeed value) | |
423 | ) | |
424 | ||
425 | _ -> | |
426 | Eval.fail "def! expected two args: name and value" | |
427 | ||
428 | ||
429 | evalDefMacro : List MalExpr -> Eval MalExpr | |
430 | evalDefMacro args = | |
431 | case args of | |
432 | [ MalSymbol name, uneValue ] -> | |
433 | eval uneValue | |
434 | |> Eval.andThen | |
435 | (\value -> | |
436 | case value of | |
437 | MalFunction (UserFunc fn) -> | |
438 | let | |
439 | macroFn = | |
440 | MalFunction (UserFunc { fn | isMacro = True }) | |
441 | in | |
442 | Eval.modifyEnv (Env.set name macroFn) | |
443 | |> Eval.andThen (\_ -> Eval.succeed macroFn) | |
444 | ||
445 | _ -> | |
446 | Eval.fail "defmacro! is only supported on a user function" | |
447 | ) | |
448 | ||
449 | _ -> | |
450 | Eval.fail "defmacro! expected two args: name and value" | |
451 | ||
452 | ||
453 | evalLet : List MalExpr -> Eval MalExpr | |
454 | evalLet args = | |
455 | let | |
456 | evalBinds binds = | |
457 | case binds of | |
458 | (MalSymbol name) :: expr :: rest -> | |
459 | eval expr | |
460 | |> Eval.andThen | |
461 | (\value -> | |
462 | Eval.modifyEnv (Env.set name value) | |
463 | |> Eval.andThen | |
464 | (\_ -> | |
465 | if List.isEmpty rest then | |
466 | Eval.succeed () | |
467 | else | |
468 | evalBinds rest | |
469 | ) | |
470 | ) | |
471 | ||
472 | _ -> | |
473 | Eval.fail "let* expected an even number of binds (symbol expr ..)" | |
474 | ||
475 | go binds body = | |
476 | Eval.modifyEnv Env.push | |
477 | |> Eval.andThen (\_ -> evalBinds binds) | |
478 | |> Eval.andThen (\_ -> evalNoApply body) | |
479 | |> Eval.ignore (Eval.modifyEnv Env.pop) | |
480 | in | |
481 | case args of | |
482 | [ MalList binds, body ] -> | |
483 | go binds body | |
484 | ||
485 | [ MalVector bindsVec, body ] -> | |
486 | go (Array.toList bindsVec) body | |
487 | ||
488 | _ -> | |
489 | Eval.fail "let* expected two args: binds and a body" | |
490 | ||
491 | ||
492 | evalDo : List MalExpr -> Eval MalExpr | |
493 | evalDo args = | |
494 | case List.reverse args of | |
495 | last :: rest -> | |
496 | evalList (List.reverse rest) | |
497 | |> Eval.andThen (\_ -> evalNoApply last) | |
498 | ||
499 | [] -> | |
500 | Eval.fail "do expected at least one arg" | |
501 | ||
502 | ||
503 | evalIf : List MalExpr -> Eval MalExpr | |
504 | evalIf args = | |
505 | let | |
506 | isThruthy expr = | |
507 | expr /= MalNil && expr /= (MalBool False) | |
508 | ||
509 | go condition trueExpr falseExpr = | |
510 | eval condition | |
511 | |> Eval.map isThruthy | |
512 | |> Eval.andThen | |
513 | (\cond -> | |
514 | evalNoApply | |
515 | (if cond then | |
516 | trueExpr | |
517 | else | |
518 | falseExpr | |
519 | ) | |
520 | ) | |
521 | in | |
522 | case args of | |
523 | [ condition, trueExpr ] -> | |
524 | go condition trueExpr MalNil | |
525 | ||
526 | [ condition, trueExpr, falseExpr ] -> | |
527 | go condition trueExpr falseExpr | |
528 | ||
529 | _ -> | |
530 | Eval.fail "if expected at least two args" | |
531 | ||
532 | ||
533 | evalFn : List MalExpr -> Eval MalExpr | |
534 | evalFn args = | |
535 | let | |
536 | {- Extract symbols from the binds list and verify their uniqueness -} | |
537 | extractSymbols acc list = | |
538 | case list of | |
539 | [] -> | |
540 | Ok (List.reverse acc) | |
541 | ||
542 | (MalSymbol name) :: rest -> | |
543 | if List.member name acc then | |
544 | Err "all binds must have unique names" | |
545 | else | |
546 | extractSymbols (name :: acc) rest | |
547 | ||
548 | _ -> | |
549 | Err "all binds in fn* must be a symbol" | |
550 | ||
551 | parseBinds list = | |
552 | case List.reverse list of | |
553 | var :: "&" :: rest -> | |
554 | Ok <| bindVarArgs (List.reverse rest) var | |
555 | ||
556 | _ -> | |
557 | if List.member "&" list then | |
558 | Err "varargs separator '&' is used incorrectly" | |
559 | else | |
560 | Ok <| bindArgs list | |
561 | ||
562 | extractAndParse = | |
563 | extractSymbols [] >> Result.andThen parseBinds | |
564 | ||
565 | bindArgs binds args = | |
566 | let | |
567 | numBinds = | |
568 | List.length binds | |
569 | in | |
570 | if List.length args /= numBinds then | |
571 | Err <| | |
572 | "function expected " | |
573 | ++ (toString numBinds) | |
574 | ++ " arguments" | |
575 | else | |
576 | Ok <| zip binds args | |
577 | ||
578 | bindVarArgs binds var args = | |
579 | let | |
580 | minArgs = | |
581 | List.length binds | |
582 | ||
583 | varArgs = | |
584 | MalList (List.drop minArgs args) | |
585 | in | |
586 | if List.length args < minArgs then | |
587 | Err <| | |
588 | "function expected at least " | |
589 | ++ (toString minArgs) | |
590 | ++ " arguments" | |
591 | else | |
592 | Ok <| zip binds args ++ [ ( var, varArgs ) ] | |
593 | ||
594 | makeFn frameId binder body = | |
595 | MalFunction <| | |
596 | let | |
597 | lazyFn = | |
598 | binder | |
599 | >> Eval.fromResult | |
600 | >> Eval.map | |
601 | (\bound -> | |
602 | -- TODO : choice Env.enter prematurely? | |
603 | -- I think it is needed by the garbage collect.. | |
604 | MalApply | |
605 | { frameId = frameId | |
606 | , bound = bound | |
607 | , body = body | |
608 | } | |
609 | ) | |
610 | in | |
611 | UserFunc | |
612 | { frameId = frameId | |
613 | , lazyFn = lazyFn | |
614 | , eagerFn = lazyFn >> Eval.andThen eval | |
615 | , isMacro = False | |
616 | } | |
617 | ||
618 | go bindsList body = | |
619 | extractAndParse bindsList | |
620 | |> Eval.fromResult | |
621 | -- reference the current frame. | |
622 | |> Eval.ignore (Eval.modifyEnv Env.ref) | |
623 | |> Eval.andThen | |
624 | (\binder -> | |
625 | Eval.withEnv | |
626 | (\env -> | |
627 | Eval.succeed | |
628 | (makeFn env.currentFrameId binder body) | |
629 | ) | |
630 | ) | |
631 | in | |
632 | case args of | |
633 | [ MalList bindsList, body ] -> | |
634 | go bindsList body | |
635 | ||
636 | [ MalVector bindsVec, body ] -> | |
637 | go (Array.toList bindsVec) body | |
638 | ||
639 | _ -> | |
640 | Eval.fail "fn* expected two args: binds list and body" | |
641 | ||
642 | ||
643 | evalQuote : List MalExpr -> Eval MalExpr | |
644 | evalQuote args = | |
645 | case args of | |
646 | [ expr ] -> | |
647 | Eval.succeed expr | |
648 | ||
649 | _ -> | |
650 | Eval.fail "unsupported arguments" | |
651 | ||
652 | ||
653 | evalQuasiQuote : MalExpr -> MalExpr | |
654 | evalQuasiQuote expr = | |
655 | let | |
656 | apply list empty = | |
657 | case list of | |
658 | [ MalSymbol "unquote", ast ] -> | |
659 | ast | |
660 | ||
661 | (MalList [ MalSymbol "splice-unquote", ast ]) :: rest -> | |
662 | makeCall "concat" | |
663 | [ ast | |
664 | , evalQuasiQuote (MalList rest) | |
665 | ] | |
666 | ||
667 | ast :: rest -> | |
668 | makeCall "cons" | |
669 | [ evalQuasiQuote ast | |
670 | , evalQuasiQuote (MalList rest) | |
671 | ] | |
672 | ||
673 | _ -> | |
674 | makeCall "quote" [ empty ] | |
675 | in | |
676 | case expr of | |
677 | MalList list -> | |
678 | apply list (MalList []) | |
679 | ||
680 | MalVector vec -> | |
681 | apply (Array.toList vec) (MalVector Array.empty) | |
682 | ||
683 | ast -> | |
684 | makeCall "quote" [ ast ] | |
685 | ||
686 | ||
687 | macroexpand : MalExpr -> Eval MalExpr | |
688 | macroexpand expr = | |
689 | let | |
690 | expand expr env = | |
691 | case expr of | |
692 | MalList ((MalSymbol name) :: args) -> | |
693 | case Env.get name env of | |
694 | Ok (MalFunction (UserFunc fn)) -> | |
695 | if fn.isMacro then | |
696 | Left <| fn.eagerFn args | |
697 | else | |
698 | Right expr | |
699 | ||
700 | _ -> | |
701 | Right expr | |
702 | ||
703 | _ -> | |
704 | Right expr | |
705 | in | |
706 | Eval.runLoop expand expr | |
707 | ||
708 | ||
709 | evalTry : List MalExpr -> Eval MalExpr | |
710 | evalTry args = | |
711 | case args of | |
712 | [ body, MalList [ MalSymbol "catch*", MalSymbol sym, handler ] ] -> | |
713 | eval body | |
714 | |> Eval.catchError | |
715 | (\msg -> | |
716 | Eval.modifyEnv Env.push | |
717 | |> Eval.andThen | |
718 | (\_ -> | |
719 | Eval.modifyEnv (Env.set sym (MalString msg)) | |
720 | ) | |
721 | |> Eval.andThen (\_ -> evalNoApply handler) | |
722 | |> Eval.ignore (Eval.modifyEnv Env.pop) | |
723 | ) | |
724 | ||
725 | _ -> | |
726 | Eval.fail "try* expected a body a catch block" | |
727 | ||
728 | ||
729 | print : Env -> MalExpr -> String | |
730 | print env = | |
731 | printString env True | |
732 | ||
733 | ||
734 | {-| Read-Eval-Print. | |
735 | ||
736 | Doesn't actually run the Eval but returns the monad. | |
737 | ||
738 | -} | |
739 | rep : String -> Maybe (Eval MalExpr) | |
740 | rep input = | |
741 | case readString input of | |
742 | Ok Nothing -> | |
743 | Nothing | |
744 | ||
745 | Err msg -> | |
746 | Just (Eval.fail msg) | |
747 | ||
748 | Ok (Just ast) -> | |
749 | eval ast |> Just |