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)
17 main : Program Flags Model Msg
23 \model -> input (decodeValue decodeIO >> Input)
33 = InitIO Env (IO -> Eval MalExpr)
36 | ReplIO Env (IO -> Eval MalExpr)
39 init : Flags -> ( Model, Cmd Msg )
50 (\b a -> a |> Eval.andThen (\_ -> b))
53 runInit initEnv evalMalInit
60 (if a false true)))"""
64 update : Msg -> Model -> ( Model, Cmd Msg )
81 Input (Ok (LineRead (Just line))) ->
87 ( model, readLine prompt )
89 Input (Ok LineWritten) ->
90 ( model, readLine prompt )
92 Input (Ok (LineRead Nothing)) ->
97 Debug.crash "unexpected IO received: " io
108 Debug.crash msg ( model, Cmd.none )
111 runInit : Env -> Eval MalExpr -> ( Model, Cmd Msg )
113 case Eval.run env expr of
114 ( env, EvalOk expr ) ->
115 -- Init went okay, start REPL.
116 ( ReplActive env, readLine prompt )
118 ( env, EvalErr msg ) ->
119 -- Init failed, don't start REPL.
120 ( InitError, writeLine (printError env msg) )
122 ( env, EvalIO cmd cont ) ->
124 ( InitIO env cont, cmd )
127 run : Env -> Eval MalExpr -> ( Model, Cmd Msg )
129 case Eval.run env expr of
130 ( env, EvalOk expr ) ->
131 ( ReplActive env, writeLine (print env expr) )
133 ( env, EvalErr msg ) ->
134 ( ReplActive env, writeLine (printError env msg) )
136 ( env, EvalIO cmd cont ) ->
137 ( ReplIO env cont, cmd )
145 {-| read can return three things:
147 Ok (Just expr) -> parsed okay
148 Ok Nothing -> empty string (only whitespace and/or comments)
149 Err msg -> parse error
152 read : String -> Result String (Maybe MalExpr)
157 debug : String -> (Env -> a) -> Eval b -> Eval b
161 Env.debug env msg (f env)
166 eval : MalExpr -> Eval MalExpr
174 (\env -> printString env True expr)
182 |> Eval.andThen (Eval.runLoop apply)
185 evalApply : ApplyRec -> Eval MalExpr
186 evalApply { frameId, bound, body } =
189 Eval.modifyEnv (Env.enter frameId bound)
190 |> Eval.andThen (\_ -> evalNoApply body)
191 |> Eval.finally Env.leave
196 evalNoApply : MalExpr -> Eval MalExpr
199 (\env -> printString env True ast)
204 MalList ((MalSymbol "def!") :: args) ->
207 MalList ((MalSymbol "let*") :: args) ->
210 MalList ((MalSymbol "do") :: args) ->
213 MalList ((MalSymbol "if") :: args) ->
216 MalList ((MalSymbol "fn*") :: args) ->
225 Eval.fail "can't happen"
227 (MalFunction (CoreFunc fn)) :: args ->
230 (MalFunction (UserFunc { lazyFn })) :: args ->
236 Eval.fail ((printString env True fn) ++ " is not a function")
245 evalAst : MalExpr -> Eval MalExpr
249 -- Lookup symbol in env and return value or raise error if not found.
252 case Env.get sym env of
261 -- Return new list that is result of calling eval on each element of list.
266 evalList (Array.toList vec)
267 |> Eval.map (Array.fromList >> MalVector)
270 evalList (Dict.values map)
281 evalList : List MalExpr -> Eval (List MalExpr)
287 Eval.succeed (List.reverse acc)
299 evalDef : List MalExpr -> Eval MalExpr
302 [ MalSymbol name, uneValue ] ->
306 Eval.modifyEnv (Env.set name value)
307 |> Eval.andThen (\_ -> Eval.succeed value)
311 Eval.fail "def! expected two args: name and value"
314 evalLet : List MalExpr -> Eval MalExpr
319 (MalSymbol name) :: expr :: rest ->
323 Eval.modifyEnv (Env.set name value)
326 if List.isEmpty rest then
334 Eval.fail "let* expected an even number of binds (symbol expr ..)"
337 Eval.modifyEnv Env.push
338 |> Eval.andThen (\_ -> evalBinds binds)
339 |> Eval.andThen (\_ -> evalNoApply body)
342 Eval.modifyEnv Env.pop
343 |> Eval.map (\_ -> res)
347 [ MalList binds, body ] ->
350 [ MalVector bindsVec, body ] ->
351 go (Array.toList bindsVec) body
354 Eval.fail "let* expected two args: binds and a body"
357 evalDo : List MalExpr -> Eval MalExpr
359 case List.reverse args of
361 evalList (List.reverse rest)
362 |> Eval.andThen (\_ -> evalNoApply last)
365 Eval.fail "do expected at least one arg"
368 evalIf : List MalExpr -> Eval MalExpr
372 expr /= MalNil && expr /= (MalBool False)
374 go condition trueExpr falseExpr =
376 |> Eval.map isThruthy
388 [ condition, trueExpr ] ->
389 go condition trueExpr MalNil
391 [ condition, trueExpr, falseExpr ] ->
392 go condition trueExpr falseExpr
395 Eval.fail "if expected at least two args"
398 evalFn : List MalExpr -> Eval MalExpr
401 {- Extract symbols from the binds list and verify their uniqueness -}
402 extractSymbols acc list =
405 Ok (List.reverse acc)
407 (MalSymbol name) :: rest ->
408 if List.member name acc then
409 Err "all binds must have unique names"
411 extractSymbols (name :: acc) rest
414 Err "all binds in fn* must be a symbol"
417 case List.reverse list of
418 var :: "&" :: rest ->
419 Ok <| bindVarArgs (List.reverse rest) var
422 if List.member "&" list then
423 Err "varargs separator '&' is used incorrectly"
428 extractSymbols [] >> Result.andThen parseBinds
430 bindArgs binds args =
435 if List.length args /= numBinds then
438 ++ (toString numBinds)
443 bindVarArgs binds var args =
449 MalList (List.drop minArgs args)
451 if List.length args < minArgs then
453 "function expected at least "
454 ++ (toString minArgs)
457 Ok <| zip binds args ++ [ ( var, varArgs ) ]
459 makeFn frameId binder body =
478 , eagerFn = lazyFn >> Eval.andThen eval
484 case extractAndParse bindsList of
486 Eval.modifyEnv Env.ref
487 -- reference the current frame.
493 (makeFn env.currentFrameId binder body)
501 [ MalList bindsList, body ] ->
504 [ MalVector bindsVec, body ] ->
505 go (Array.toList bindsVec) body
508 Eval.fail "fn* expected two args: binds list and body"
511 print : Env -> MalExpr -> String
516 printError : Env -> MalExpr -> String
517 printError env expr =
518 "Error: " ++ (printString env False expr)
523 Doesn't actually run the Eval but returns the monad.
526 rep : String -> Maybe (Eval MalExpr)
528 case readString input of