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) ")")))))"""
78 update : Msg -> Model -> ( Model, Cmd Msg )
84 InitIO args env cont ->
87 runInit args env (cont io)
95 runScriptLoop env (cont io)
102 Input (Ok (LineRead (Just line))) ->
108 ( model, readLine prompt )
110 Input (Ok LineWritten) ->
111 ( model, readLine prompt )
113 Input (Ok (LineRead Nothing)) ->
118 Debug.crash "unexpected IO received: " io
129 Debug.crash msg ( model, Cmd.none )
132 runInit : Args -> Env -> Eval MalExpr -> ( Model, Cmd Msg )
133 runInit args env expr =
134 case Eval.run env expr of
135 ( env, EvalOk expr ) ->
138 -- If we got no args: start REPL.
140 ( ReplActive env, readLine prompt )
142 -- Run the script in the first argument.
143 -- Put the rest of the arguments as *ARGV*.
145 runScript filename argv env
147 ( env, EvalErr msg ) ->
148 -- Init failed, don't start REPL.
149 ( Stopped, writeLine (printError env msg) )
151 ( env, EvalIO cmd cont ) ->
153 ( InitIO args env cont, cmd )
156 runScript : String -> List String -> Env -> ( Model, Cmd Msg )
157 runScript filename argv env =
160 MalList (List.map MalString argv)
163 env |> Env.set "*ARGV*" malArgv
167 [ MalSymbol "load-file"
171 runScriptLoop newEnv (eval program)
174 runScriptLoop : Env -> Eval MalExpr -> ( Model, Cmd Msg )
175 runScriptLoop env expr =
176 case Eval.run env expr of
177 ( env, EvalOk expr ) ->
178 ( Stopped, Cmd.none )
180 ( env, EvalErr msg ) ->
181 ( Stopped, writeLine (printError env msg) )
183 ( env, EvalIO cmd cont ) ->
184 ( ScriptIO env cont, cmd )
187 run : Env -> Eval MalExpr -> ( Model, Cmd Msg )
189 case Eval.run env expr of
190 ( env, EvalOk expr ) ->
191 ( ReplActive env, writeLine (print env expr) )
193 ( env, EvalErr msg ) ->
194 ( ReplActive env, writeLine (printError env msg) )
196 ( env, EvalIO cmd cont ) ->
197 ( ReplIO env cont, cmd )
205 {-| read can return three things:
207 Ok (Just expr) -> parsed okay
208 Ok Nothing -> empty string (only whitespace and/or comments)
209 Err msg -> parse error
212 read : String -> Result String (Maybe MalExpr)
217 debug : String -> (Env -> a) -> Eval b -> Eval b
221 Env.debug env msg (f env)
226 eval : MalExpr -> Eval MalExpr
234 (\env -> printString env True expr)
242 |> Eval.andThen (Eval.runLoop apply)
245 malEval : List MalExpr -> Eval MalExpr
249 Eval.inGlobal (eval expr)
252 Eval.fail "unsupported arguments"
255 evalApply : ApplyRec -> Eval MalExpr
256 evalApply { frameId, bound, body } =
259 Eval.modifyEnv (Env.enter frameId bound)
260 |> Eval.andThen (\_ -> evalNoApply body)
261 |> Eval.finally Env.leave
266 evalNoApply : MalExpr -> Eval MalExpr
269 (\env -> printString env True ast)
274 MalList ((MalSymbol "def!") :: args) ->
277 MalList ((MalSymbol "let*") :: args) ->
280 MalList ((MalSymbol "do") :: args) ->
283 MalList ((MalSymbol "if") :: args) ->
286 MalList ((MalSymbol "fn*") :: args) ->
289 MalList ((MalSymbol "quote") :: args) ->
292 MalList ((MalSymbol "quasiquote") :: args) ->
296 evalNoApply (evalQuasiQuote expr)
299 Eval.fail "unsupported arguments"
307 Eval.fail "can't happen"
309 (MalFunction (CoreFunc fn)) :: args ->
312 (MalFunction (UserFunc { lazyFn })) :: args ->
318 Eval.fail ((printString env True fn) ++ " is not a function")
327 evalAst : MalExpr -> Eval MalExpr
331 -- Lookup symbol in env and return value or raise error if not found.
332 Eval.withEnv (Env.get sym >> Eval.fromResult)
335 -- Return new list that is result of calling eval on each element of list.
340 evalList (Array.toList vec)
341 |> Eval.map (Array.fromList >> MalVector)
344 evalList (Dict.values map)
355 evalList : List MalExpr -> Eval (List MalExpr)
361 Eval.succeed (List.reverse acc)
373 evalDef : List MalExpr -> Eval MalExpr
376 [ MalSymbol name, uneValue ] ->
380 Eval.modifyEnv (Env.set name value)
381 |> Eval.andThen (\_ -> Eval.succeed value)
385 Eval.fail "def! expected two args: name and value"
388 evalLet : List MalExpr -> Eval MalExpr
393 (MalSymbol name) :: expr :: rest ->
397 Eval.modifyEnv (Env.set name value)
400 if List.isEmpty rest then
408 Eval.fail "let* expected an even number of binds (symbol expr ..)"
411 Eval.modifyEnv Env.push
412 |> Eval.andThen (\_ -> evalBinds binds)
413 |> Eval.andThen (\_ -> evalNoApply body)
414 |> Eval.finally Env.pop
417 [ MalList binds, body ] ->
420 [ MalVector bindsVec, body ] ->
421 go (Array.toList bindsVec) body
424 Eval.fail "let* expected two args: binds and a body"
427 evalDo : List MalExpr -> Eval MalExpr
429 case List.reverse args of
431 evalList (List.reverse rest)
432 |> Eval.andThen (\_ -> evalNoApply last)
435 Eval.fail "do expected at least one arg"
438 evalIf : List MalExpr -> Eval MalExpr
442 expr /= MalNil && expr /= (MalBool False)
444 go condition trueExpr falseExpr =
446 |> Eval.map isThruthy
458 [ condition, trueExpr ] ->
459 go condition trueExpr MalNil
461 [ condition, trueExpr, falseExpr ] ->
462 go condition trueExpr falseExpr
465 Eval.fail "if expected at least two args"
468 evalFn : List MalExpr -> Eval MalExpr
471 {- Extract symbols from the binds list and verify their uniqueness -}
472 extractSymbols acc list =
475 Ok (List.reverse acc)
477 (MalSymbol name) :: rest ->
478 if List.member name acc then
479 Err "all binds must have unique names"
481 extractSymbols (name :: acc) rest
484 Err "all binds in fn* must be a symbol"
487 case List.reverse list of
488 var :: "&" :: rest ->
489 Ok <| bindVarArgs (List.reverse rest) var
492 if List.member "&" list then
493 Err "varargs separator '&' is used incorrectly"
498 extractSymbols [] >> Result.andThen parseBinds
500 bindArgs binds args =
505 if List.length args /= numBinds then
508 ++ (toString numBinds)
513 bindVarArgs binds var args =
519 MalList (List.drop minArgs args)
521 if List.length args < minArgs then
523 "function expected at least "
524 ++ (toString minArgs)
527 Ok <| zip binds args ++ [ ( var, varArgs ) ]
529 makeFn frameId binder body =
547 , eagerFn = lazyFn >> Eval.andThen eval
553 extractAndParse bindsList
555 -- reference the current frame.
556 |> Eval.ignore (Eval.modifyEnv Env.ref)
562 (makeFn env.currentFrameId binder body)
567 [ MalList bindsList, body ] ->
570 [ MalVector bindsVec, body ] ->
571 go (Array.toList bindsVec) body
574 Eval.fail "fn* expected two args: binds list and body"
577 evalQuote : List MalExpr -> Eval MalExpr
584 Eval.fail "unsupported arguments"
587 evalQuasiQuote : MalExpr -> MalExpr
588 evalQuasiQuote expr =
592 [ MalSymbol "unquote", ast ] ->
595 (MalList [ MalSymbol "splice-unquote", ast ]) :: rest ->
598 , evalQuasiQuote (MalList rest)
604 , evalQuasiQuote (MalList rest)
608 makeCall "quote" [ empty ]
612 apply list (MalList [])
615 apply (Array.toList vec) (MalVector Array.empty)
618 makeCall "quote" [ ast ]
621 print : Env -> MalExpr -> String
626 printError : Env -> MalExpr -> String
627 printError env expr =
628 "Error: " ++ (printString env False expr)
633 Doesn't actually run the Eval but returns the monad.
636 rep : String -> Maybe (Eval MalExpr)
638 case readString input of