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))
60 (\b a -> a |> Eval.andThen (\_ -> b))
63 runInit args initEnv evalMalInit
70 (if a false true)))"""
74 (str "(do " (slurp f) ")")))))"""
81 (throw "odd number of forms to cond"))
82 (cons 'cond (rest (rest xs)))))))"""
89 `(let* (or_FIXME ~(first xs))
90 (if or_FIXME or_FIXME (or ~@(rest xs))))))))"""
94 update : Msg -> Model -> ( Model, Cmd Msg )
100 InitIO args env cont ->
103 runInit args env (cont io)
111 runScriptLoop env (cont io)
118 Input (Ok (LineRead (Just line))) ->
124 ( model, readLine prompt )
126 Input (Ok LineWritten) ->
127 ( model, readLine prompt )
129 Input (Ok (LineRead Nothing)) ->
134 Debug.crash "unexpected IO received: " io
145 Debug.crash msg ( model, Cmd.none )
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 ) ->
154 -- If we got no args: start REPL.
156 ( ReplActive env, readLine prompt )
158 -- Run the script in the first argument.
159 -- Put the rest of the arguments as *ARGV*.
161 runScript filename argv env
163 ( env, EvalErr msg ) ->
164 -- Init failed, don't start REPL.
165 ( Stopped, writeLine ("ERR:" ++ msg) )
167 ( env, EvalIO cmd cont ) ->
169 ( InitIO args env cont, cmd )
172 runScript : String -> List String -> Env -> ( Model, Cmd Msg )
173 runScript filename argv env =
176 MalList (List.map MalString argv)
179 env |> Env.set "*ARGV*" malArgv
183 [ MalSymbol "load-file"
187 runScriptLoop newEnv (eval program)
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 )
196 ( env, EvalErr msg ) ->
197 ( Stopped, writeLine ("ERR:" ++ msg) )
199 ( env, EvalIO cmd cont ) ->
200 ( ScriptIO env cont, cmd )
203 run : Env -> Eval MalExpr -> ( Model, Cmd Msg )
205 case Eval.run env expr of
206 ( env, EvalOk expr ) ->
207 ( ReplActive env, writeLine (print env expr) )
209 ( env, EvalErr msg ) ->
210 ( ReplActive env, writeLine ("ERR:" ++ msg) )
212 ( env, EvalIO cmd cont ) ->
213 ( ReplIO env cont, cmd )
221 {-| read can return three things:
223 Ok (Just expr) -> parsed okay
224 Ok Nothing -> empty string (only whitespace and/or comments)
225 Err msg -> parse error
228 read : String -> Result String (Maybe MalExpr)
233 debug : String -> (Env -> a) -> Eval b -> Eval b
237 Env.debug env msg (f env)
242 eval : MalExpr -> Eval MalExpr
250 (\env -> printString env True expr)
258 |> Eval.andThen (Eval.runLoop apply)
261 malEval : List MalExpr -> Eval MalExpr
267 Eval.modifyEnv (Env.jump Env.globalFrameId)
268 |> Eval.andThen (\_ -> eval expr)
271 Eval.modifyEnv (Env.jump env.currentFrameId)
272 |> Eval.andThen (\_ -> Eval.succeed res)
277 Eval.fail "unsupported arguments"
280 evalApply : ApplyRec -> Eval MalExpr
281 evalApply { frameId, bound, body } =
284 Eval.modifyEnv (Env.enter frameId bound)
285 |> Eval.andThen (\_ -> evalNoApply body)
286 |> Eval.ignore (Eval.modifyEnv (Env.leave env.currentFrameId))
290 evalNoApply : MalExpr -> Eval MalExpr
293 (\env -> printString env True ast)
301 MalList ((MalSymbol "def!") :: args) ->
304 MalList ((MalSymbol "let*") :: args) ->
307 MalList ((MalSymbol "do") :: args) ->
310 MalList ((MalSymbol "if") :: args) ->
313 MalList ((MalSymbol "fn*") :: args) ->
316 MalList ((MalSymbol "quote") :: args) ->
319 MalList ((MalSymbol "quasiquote") :: args) ->
323 evalNoApply (evalQuasiQuote expr)
326 Eval.fail "unsupported arguments"
328 MalList ((MalSymbol "defmacro!") :: args) ->
331 MalList ((MalSymbol "macroexpand") :: args) ->
337 Eval.fail "unsupported arguments"
345 Eval.fail "can't happen"
347 (MalFunction (CoreFunc fn)) :: args ->
350 (MalFunction (UserFunc { lazyFn })) :: args ->
356 Eval.fail ((printString env True fn) ++ " is not a function")
366 evalAst : MalExpr -> Eval MalExpr
370 -- Lookup symbol in env and return value or raise error if not found.
371 Eval.withEnv (Env.get sym >> Eval.fromResult)
374 -- Return new list that is result of calling eval on each element of list.
379 evalList (Array.toList vec)
380 |> Eval.map (Array.fromList >> MalVector)
383 evalList (Dict.values map)
394 evalList : List MalExpr -> Eval (List MalExpr)
400 Eval.succeed (List.reverse acc)
412 evalDef : List MalExpr -> Eval MalExpr
415 [ MalSymbol name, uneValue ] ->
419 Eval.modifyEnv (Env.set name value)
420 |> Eval.andThen (\_ -> Eval.succeed value)
424 Eval.fail "def! expected two args: name and value"
427 evalDefMacro : List MalExpr -> Eval MalExpr
430 [ MalSymbol name, uneValue ] ->
435 MalFunction (UserFunc fn) ->
438 MalFunction (UserFunc { fn | isMacro = True })
440 Eval.modifyEnv (Env.set name macroFn)
441 |> Eval.andThen (\_ -> Eval.succeed macroFn)
444 Eval.fail "defmacro! is only supported on a user function"
448 Eval.fail "defmacro! expected two args: name and value"
451 evalLet : List MalExpr -> Eval MalExpr
456 (MalSymbol name) :: expr :: rest ->
460 Eval.modifyEnv (Env.set name value)
463 if List.isEmpty rest then
471 Eval.fail "let* expected an even number of binds (symbol expr ..)"
474 Eval.modifyEnv Env.push
475 |> Eval.andThen (\_ -> evalBinds binds)
476 |> Eval.andThen (\_ -> evalNoApply body)
477 |> Eval.ignore (Eval.modifyEnv Env.pop)
480 [ MalList binds, body ] ->
483 [ MalVector bindsVec, body ] ->
484 go (Array.toList bindsVec) body
487 Eval.fail "let* expected two args: binds and a body"
490 evalDo : List MalExpr -> Eval MalExpr
492 case List.reverse args of
494 evalList (List.reverse rest)
495 |> Eval.andThen (\_ -> evalNoApply last)
498 Eval.fail "do expected at least one arg"
501 evalIf : List MalExpr -> Eval MalExpr
505 expr /= MalNil && expr /= (MalBool False)
507 go condition trueExpr falseExpr =
509 |> Eval.map isThruthy
521 [ condition, trueExpr ] ->
522 go condition trueExpr MalNil
524 [ condition, trueExpr, falseExpr ] ->
525 go condition trueExpr falseExpr
528 Eval.fail "if expected at least two args"
531 evalFn : List MalExpr -> Eval MalExpr
534 {- Extract symbols from the binds list and verify their uniqueness -}
535 extractSymbols acc list =
538 Ok (List.reverse acc)
540 (MalSymbol name) :: rest ->
541 if List.member name acc then
542 Err "all binds must have unique names"
544 extractSymbols (name :: acc) rest
547 Err "all binds in fn* must be a symbol"
550 case List.reverse list of
551 var :: "&" :: rest ->
552 Ok <| bindVarArgs (List.reverse rest) var
555 if List.member "&" list then
556 Err "varargs separator '&' is used incorrectly"
561 extractSymbols [] >> Result.andThen parseBinds
563 bindArgs binds args =
568 if List.length args /= numBinds then
571 ++ (toString numBinds)
576 bindVarArgs binds var args =
582 MalList (List.drop minArgs args)
584 if List.length args < minArgs then
586 "function expected at least "
587 ++ (toString minArgs)
590 Ok <| zip binds args ++ [ ( var, varArgs ) ]
592 makeFn frameId binder body =
600 -- TODO : choice Env.enter prematurely?
601 -- I think it is needed by the garbage collect..
612 , eagerFn = lazyFn >> Eval.andThen eval
617 extractAndParse bindsList
619 -- reference the current frame.
620 |> Eval.ignore (Eval.modifyEnv Env.ref)
626 (makeFn env.currentFrameId binder body)
631 [ MalList bindsList, body ] ->
634 [ MalVector bindsVec, body ] ->
635 go (Array.toList bindsVec) body
638 Eval.fail "fn* expected two args: binds list and body"
641 evalQuote : List MalExpr -> Eval MalExpr
648 Eval.fail "unsupported arguments"
651 evalQuasiQuote : MalExpr -> MalExpr
652 evalQuasiQuote expr =
656 [ MalSymbol "unquote", ast ] ->
659 (MalList [ MalSymbol "splice-unquote", ast ]) :: rest ->
662 , evalQuasiQuote (MalList rest)
668 , evalQuasiQuote (MalList rest)
672 makeCall "quote" [ empty ]
676 apply list (MalList [])
679 apply (Array.toList vec) (MalVector Array.empty)
682 makeCall "quote" [ ast ]
685 macroexpand : MalExpr -> Eval MalExpr
690 MalList ((MalSymbol name) :: args) ->
691 case Env.get name env of
692 Ok (MalFunction (UserFunc fn)) ->
694 Left <| fn.eagerFn args
704 Eval.runLoop expand expr
707 print : Env -> MalExpr -> String
714 Doesn't actually run the Eval but returns the monad.
717 rep : String -> Maybe (Eval MalExpr)
719 case readString input of