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 "quasiquote") :: args) ->
306 evalNoApply (evalQuasiQuote expr)
309 Eval.fail "unsupported arguments"
311 MalList ((MalSymbol "defmacro!") :: args) ->
314 MalList ((MalSymbol "macroexpand") :: args) ->
320 Eval.fail "unsupported arguments"
322 MalList ((MalSymbol "try*") :: args) ->
331 Eval.fail "can't happen"
333 (MalFunction (CoreFunc fn)) :: args ->
336 (MalFunction (UserFunc { lazyFn })) :: args ->
342 Eval.fail ((printString env True fn) ++ " is not a function")
354 (\env -> (printString env True ast) ++ " = " ++ (printString env True res))
359 evalAst : MalExpr -> Eval MalExpr
363 -- Lookup symbol in env and return value or raise error if not found.
364 Eval.withEnv (Env.get sym >> Eval.fromResult)
367 -- Return new list that is result of calling eval on each element of list.
372 evalList (Array.toList vec)
373 |> Eval.map (Array.fromList >> MalVector)
376 evalList (Dict.values map)
387 evalList : List MalExpr -> Eval (List MalExpr)
393 Eval.succeed (List.reverse acc)
399 Eval.pushRef val <| go rest (val :: acc)
402 Eval.withStack <| go list []
405 evalDef : List MalExpr -> Eval MalExpr
408 [ MalSymbol name, uneValue ] ->
412 Eval.modifyEnv (Env.set name value)
413 |> Eval.andThen (\_ -> Eval.succeed value)
417 Eval.fail "def! expected two args: name and value"
420 evalDefMacro : List MalExpr -> Eval MalExpr
423 [ MalSymbol name, uneValue ] ->
428 MalFunction (UserFunc fn) ->
431 MalFunction (UserFunc { fn | isMacro = True })
433 Eval.modifyEnv (Env.set name macroFn)
434 |> Eval.andThen (\_ -> Eval.succeed macroFn)
437 Eval.fail "defmacro! is only supported on a user function"
441 Eval.fail "defmacro! expected two args: name and value"
444 evalLet : List MalExpr -> Eval MalExpr
449 (MalSymbol name) :: expr :: rest ->
453 Eval.modifyEnv (Env.set name value)
456 if List.isEmpty rest then
464 Eval.fail "let* expected an even number of binds (symbol expr ..)"
467 Eval.modifyEnv Env.push
468 |> Eval.andThen (\_ -> evalBinds binds)
469 |> Eval.andThen (\_ -> evalNoApply body)
470 |> Eval.finally Env.pop
473 [ MalList binds, body ] ->
476 [ MalVector bindsVec, body ] ->
477 go (Array.toList bindsVec) body
480 Eval.fail "let* expected two args: binds and a body"
483 evalDo : List MalExpr -> Eval MalExpr
485 case List.reverse args of
487 evalList (List.reverse rest)
488 |> Eval.andThen (\_ -> evalNoApply last)
491 Eval.fail "do expected at least one arg"
494 evalIf : List MalExpr -> Eval MalExpr
498 expr /= MalNil && expr /= (MalBool False)
500 go condition trueExpr falseExpr =
502 |> Eval.map isThruthy
514 [ condition, trueExpr ] ->
515 go condition trueExpr MalNil
517 [ condition, trueExpr, falseExpr ] ->
518 go condition trueExpr falseExpr
521 Eval.fail "if expected at least two args"
524 evalFn : List MalExpr -> Eval MalExpr
527 {- Extract symbols from the binds list and verify their uniqueness -}
528 extractSymbols acc list =
531 Ok (List.reverse acc)
533 (MalSymbol name) :: rest ->
534 if List.member name acc then
535 Err "all binds must have unique names"
537 extractSymbols (name :: acc) rest
540 Err "all binds in fn* must be a symbol"
543 case List.reverse list of
544 var :: "&" :: rest ->
545 Ok <| bindVarArgs (List.reverse rest) var
548 if List.member "&" list then
549 Err "varargs separator '&' is used incorrectly"
554 extractSymbols [] >> Result.andThen parseBinds
556 bindArgs binds args =
561 if List.length args /= numBinds then
564 ++ (toString numBinds)
569 bindVarArgs binds var args =
575 MalList (List.drop minArgs args)
577 if List.length args < minArgs then
579 "function expected at least "
580 ++ (toString minArgs)
583 Ok <| zip binds args ++ [ ( var, varArgs ) ]
585 makeFn frameId binder body =
603 , eagerFn = lazyFn >> Eval.andThen eval
609 extractAndParse bindsList
611 -- reference the current frame.
612 |> Eval.ignore (Eval.modifyEnv Env.ref)
618 (makeFn env.currentFrameId binder body)
623 [ MalList bindsList, body ] ->
626 [ MalVector bindsVec, body ] ->
627 go (Array.toList bindsVec) body
630 Eval.fail "fn* expected two args: binds list and body"
633 evalQuote : List MalExpr -> Eval MalExpr
640 Eval.fail "unsupported arguments"
643 evalQuasiQuote : MalExpr -> MalExpr
644 evalQuasiQuote expr =
648 [ MalSymbol "unquote", ast ] ->
651 (MalList [ MalSymbol "splice-unquote", ast ]) :: rest ->
654 , evalQuasiQuote (MalList rest)
660 , evalQuasiQuote (MalList rest)
664 makeCall "quote" [ empty ]
668 apply list (MalList [])
671 apply (Array.toList vec) (MalVector Array.empty)
674 makeCall "quote" [ ast ]
677 macroexpand : MalExpr -> Eval MalExpr
682 MalList ((MalSymbol name) :: args) ->
683 case Env.get name env of
684 Ok (MalFunction (UserFunc fn)) ->
686 Left <| fn.eagerFn args
696 Eval.runLoop expand expr
699 evalTry : List MalExpr -> Eval MalExpr
704 [ body, MalList [ MalSymbol "catch*", MalSymbol sym, handler ] ] ->
708 Eval.modifyEnv Env.push
711 Eval.modifyEnv (Env.set sym ex)
713 |> Eval.andThen (\_ -> eval handler)
714 |> Eval.finally Env.pop
718 Eval.fail "try* expected a body a catch block"
721 print : Env -> MalExpr -> String
726 printError : Env -> MalExpr -> String
727 printError env expr =
728 "Error: " ++ (printString env False expr)
733 Doesn't actually run the Eval but returns the monad.
736 rep : String -> Maybe (Eval MalExpr)
738 case readString input of