1 port module Main exposing (..)
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)
17 main : Program Flags Model Msg
23 \model -> input (decodeValue decodeIO >> Input)
37 = InitIO Args Env (IO -> Eval MalExpr)
38 | ScriptIO Env (IO -> Eval MalExpr)
40 | ReplIO Env (IO -> Eval MalExpr)
44 init : Flags -> ( Model, Cmd Msg )
48 CoreFunc >> MalFunction
52 |> Env.set "eval" (makeFn malEval)
53 |> Env.set "*ARGV*" (MalList (args |> List.map MalString))
54 |> Env.set "*host-language*" (MalString "elm")
61 (\b a -> a |> Eval.andThen (\_ -> b))
64 runInit args initEnv evalMalInit
71 (if a false true)))"""
75 (str "(do " (slurp f) "\nnil)")))))"""
82 (throw "odd number of forms to cond"))
83 (cons 'cond (rest (rest xs)))))))"""
87 update : Msg -> Model -> ( Model, Cmd Msg )
93 InitIO args env cont ->
96 runInit args env (cont io)
104 runScriptLoop env (cont io)
111 Input (Ok (LineRead (Just line))) ->
117 ( model, readLine prompt )
119 Input (Ok LineWritten) ->
120 ( model, readLine prompt )
122 Input (Ok (LineRead Nothing)) ->
127 Debug.crash "unexpected IO received: " io
138 Debug.crash msg ( model, Cmd.none )
141 runInit : Args -> Env -> Eval MalExpr -> ( Model, Cmd Msg )
142 runInit args env expr =
143 case Eval.run env expr of
144 ( env, EvalOk expr ) ->
147 -- If we got no args: start REPL.
149 ( ReplActive env, readLine prompt )
151 -- Run the script in the first argument.
152 -- Put the rest of the arguments as *ARGV*.
154 runScript filename argv env
156 ( env, EvalErr msg ) ->
157 -- Init failed, don't start REPL.
158 ( Stopped, writeLine (printError env msg) )
160 ( env, EvalIO cmd cont ) ->
162 ( InitIO args env cont, cmd )
165 runScript : String -> List String -> Env -> ( Model, Cmd Msg )
166 runScript filename argv env =
169 MalList (List.map MalString argv)
172 env |> Env.set "*ARGV*" malArgv
176 [ MalSymbol "load-file"
180 runScriptLoop newEnv (eval program)
183 runScriptLoop : Env -> Eval MalExpr -> ( Model, Cmd Msg )
184 runScriptLoop env expr =
185 case Eval.run env expr of
186 ( env, EvalOk expr ) ->
187 ( Stopped, Cmd.none )
189 ( env, EvalErr msg ) ->
190 ( Stopped, writeLine (printError env msg) )
192 ( env, EvalIO cmd cont ) ->
193 ( ScriptIO env cont, cmd )
196 run : Env -> Eval MalExpr -> ( Model, Cmd Msg )
198 case Eval.run env expr of
199 ( env, EvalOk expr ) ->
200 ( ReplActive env, writeLine (print env expr) )
202 ( env, EvalErr msg ) ->
203 ( ReplActive env, writeLine (printError env msg) )
205 ( env, EvalIO cmd cont ) ->
206 ( ReplIO env cont, cmd )
214 {-| read can return three things:
216 Ok (Just expr) -> parsed okay
217 Ok Nothing -> empty string (only whitespace and/or comments)
218 Err msg -> parse error
221 read : String -> Result String (Maybe MalExpr)
226 debug : String -> (Env -> a) -> Eval b -> Eval b
230 Env.debug env msg (f env)
235 eval : MalExpr -> Eval MalExpr
243 (\env -> printString env True expr)
251 |> Eval.andThen (Eval.runLoop apply)
255 malEval : List MalExpr -> Eval MalExpr
259 Eval.inGlobal (eval expr)
262 Eval.fail "unsupported arguments"
265 evalApply : ApplyRec -> Eval MalExpr
266 evalApply { frameId, bound, body } =
269 Eval.modifyEnv (Env.enter frameId bound)
270 |> Eval.andThen (\_ -> evalNoApply body)
271 |> Eval.finally Env.leave
276 evalNoApply : MalExpr -> Eval MalExpr
284 MalList ((MalSymbol "def!") :: args) ->
287 MalList ((MalSymbol "let*") :: args) ->
290 MalList ((MalSymbol "do") :: args) ->
293 MalList ((MalSymbol "if") :: args) ->
296 MalList ((MalSymbol "fn*") :: args) ->
299 MalList ((MalSymbol "quote") :: args) ->
302 MalList [MalSymbol "quasiquoteexpand", expr] ->
303 Eval.succeed <| evalQuasiQuote expr
304 MalList (MalSymbol "quasiquoteexpand" :: _) ->
305 Eval.fail "quasiquoteexpand: arg count"
307 MalList ((MalSymbol "quasiquote") :: args) ->
311 evalNoApply (evalQuasiQuote expr)
314 Eval.fail "unsupported arguments"
316 MalList ((MalSymbol "defmacro!") :: args) ->
319 MalList ((MalSymbol "macroexpand") :: args) ->
325 Eval.fail "unsupported arguments"
327 MalList ((MalSymbol "try*") :: args) ->
336 Eval.fail "can't happen"
338 (MalFunction (CoreFunc fn)) :: args ->
341 (MalFunction (UserFunc { lazyFn })) :: args ->
347 Eval.fail ((printString env True fn) ++ " is not a function")
359 (\env -> (printString env True ast) ++ " = " ++ (printString env True res))
364 evalAst : MalExpr -> Eval MalExpr
368 -- Lookup symbol in env and return value or raise error if not found.
369 Eval.withEnv (Env.get sym >> Eval.fromResult)
372 -- Return new list that is result of calling eval on each element of list.
377 evalList (Array.toList vec)
378 |> Eval.map (Array.fromList >> MalVector)
381 evalList (Dict.values map)
392 evalList : List MalExpr -> Eval (List MalExpr)
398 Eval.succeed (List.reverse acc)
404 Eval.pushRef val <| go rest (val :: acc)
407 Eval.withStack <| go list []
410 evalDef : List MalExpr -> Eval MalExpr
413 [ MalSymbol name, uneValue ] ->
417 Eval.modifyEnv (Env.set name value)
418 |> Eval.andThen (\_ -> Eval.succeed value)
422 Eval.fail "def! expected two args: name and value"
425 evalDefMacro : List MalExpr -> Eval MalExpr
428 [ MalSymbol name, uneValue ] ->
433 MalFunction (UserFunc fn) ->
436 MalFunction (UserFunc { fn | isMacro = True })
438 Eval.modifyEnv (Env.set name macroFn)
439 |> Eval.andThen (\_ -> Eval.succeed macroFn)
442 Eval.fail "defmacro! is only supported on a user function"
446 Eval.fail "defmacro! expected two args: name and value"
449 evalLet : List MalExpr -> Eval MalExpr
454 (MalSymbol name) :: expr :: rest ->
458 Eval.modifyEnv (Env.set name value)
461 if List.isEmpty rest then
469 Eval.fail "let* expected an even number of binds (symbol expr ..)"
472 Eval.modifyEnv Env.push
473 |> Eval.andThen (\_ -> evalBinds binds)
474 |> Eval.andThen (\_ -> evalNoApply body)
475 |> Eval.finally Env.pop
478 [ MalList binds, body ] ->
481 [ MalVector bindsVec, body ] ->
482 go (Array.toList bindsVec) body
485 Eval.fail "let* expected two args: binds and a body"
488 evalDo : List MalExpr -> Eval MalExpr
490 case List.reverse args of
492 evalList (List.reverse rest)
493 |> Eval.andThen (\_ -> evalNoApply last)
496 Eval.fail "do expected at least one arg"
499 evalIf : List MalExpr -> Eval MalExpr
503 expr /= MalNil && expr /= (MalBool False)
505 go condition trueExpr falseExpr =
507 |> Eval.map isThruthy
519 [ condition, trueExpr ] ->
520 go condition trueExpr MalNil
522 [ condition, trueExpr, falseExpr ] ->
523 go condition trueExpr falseExpr
526 Eval.fail "if expected at least two args"
529 evalFn : List MalExpr -> Eval MalExpr
532 {- Extract symbols from the binds list and verify their uniqueness -}
533 extractSymbols acc list =
536 Ok (List.reverse acc)
538 (MalSymbol name) :: rest ->
539 if List.member name acc then
540 Err "all binds must have unique names"
542 extractSymbols (name :: acc) rest
545 Err "all binds in fn* must be a symbol"
548 case List.reverse list of
549 var :: "&" :: rest ->
550 Ok <| bindVarArgs (List.reverse rest) var
553 if List.member "&" list then
554 Err "varargs separator '&' is used incorrectly"
559 extractSymbols [] >> Result.andThen parseBinds
561 bindArgs binds args =
566 if List.length args /= numBinds then
569 ++ (toString numBinds)
574 bindVarArgs binds var args =
580 MalList (List.drop minArgs args)
582 if List.length args < minArgs then
584 "function expected at least "
585 ++ (toString minArgs)
588 Ok <| zip binds args ++ [ ( var, varArgs ) ]
590 makeFn frameId binder body =
608 , eagerFn = lazyFn >> Eval.andThen eval
614 extractAndParse bindsList
616 -- reference the current frame.
617 |> Eval.ignore (Eval.modifyEnv Env.ref)
623 (makeFn env.currentFrameId binder body)
628 [ MalList bindsList, body ] ->
631 [ MalVector bindsVec, body ] ->
632 go (Array.toList bindsVec) body
635 Eval.fail "fn* expected two args: binds list and body"
638 evalQuote : List MalExpr -> Eval MalExpr
645 Eval.fail "unsupported arguments"
648 evalQuasiQuote : MalExpr -> MalExpr
649 evalQuasiQuote expr =
651 qq_loop : MalExpr -> MalExpr -> MalExpr
654 (MalList [MalSymbol "splice-unquote", form]) ->
655 MalList <| [MalSymbol "concat", form, acc ]
657 MalList <| [MalSymbol "cons", evalQuasiQuote elt, acc ]
660 (MalList [MalSymbol "unquote", form]) ->
663 List.foldr qq_loop (MalList []) xs
665 MalList <| (\x -> [MalSymbol "vec", x]) <| Array.foldr qq_loop (MalList []) xs
667 MalList <| [MalSymbol "quote", expr]
669 MalList <| [MalSymbol "quote", expr]
674 macroexpand : MalExpr -> Eval MalExpr
679 MalList ((MalSymbol name) :: args) ->
680 case Env.get name env of
681 Ok (MalFunction (UserFunc fn)) ->
683 Left <| fn.eagerFn args
693 Eval.runLoop expand expr
696 evalTry : List MalExpr -> Eval MalExpr
701 [ body, MalList [ MalSymbol "catch*", MalSymbol sym, handler ] ] ->
705 Eval.modifyEnv Env.push
708 Eval.modifyEnv (Env.set sym ex)
710 |> Eval.andThen (\_ -> eval handler)
711 |> Eval.finally Env.pop
715 Eval.fail "try* expected a body a catch block"
718 print : Env -> MalExpr -> String
723 printError : Env -> MalExpr -> String
724 printError env expr =
725 "Error: " ++ (printString env False expr)
730 Doesn't actually run the Eval but returns the monad.
733 rep : String -> Maybe (Eval MalExpr)
735 case readString input of