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
56 update : Msg -> Model -> ( Model, Cmd Msg )
73 Input (Ok (LineRead (Just line))) ->
79 ( model, readLine prompt )
81 Input (Ok LineWritten) ->
82 ( model, readLine prompt )
84 Input (Ok (LineRead Nothing)) ->
89 Debug.crash "unexpected IO received: " io
100 Debug.crash msg ( model, Cmd.none )
103 runInit : Env -> Eval MalExpr -> ( Model, Cmd Msg )
105 case Eval.run env expr of
106 ( env, EvalOk expr ) ->
107 -- Init went okay, start REPL.
108 ( ReplActive env, readLine prompt )
110 ( env, EvalErr msg ) ->
111 -- Init failed, don't start REPL.
112 ( InitError msg, writeLine ("ERR:" ++ msg) )
114 ( env, EvalIO cmd cont ) ->
116 ( InitIO env cont, cmd )
119 run : Env -> Eval MalExpr -> ( Model, Cmd Msg )
121 case Eval.run env expr of
122 ( env, EvalOk expr ) ->
123 ( ReplActive env, writeLine (print env expr) )
125 ( env, EvalErr msg ) ->
126 ( ReplActive env, writeLine ("ERR:" ++ msg) )
128 ( env, EvalIO cmd cont ) ->
129 ( ReplIO env cont, cmd )
137 {-| read can return three things:
139 Ok (Just expr) -> parsed okay
140 Ok Nothing -> empty string (only whitespace and/or comments)
141 Err msg -> parse error
144 read : String -> Result String (Maybe MalExpr)
149 debug : String -> (Env -> a) -> Eval b -> Eval b
153 Env.debug env msg (f env)
158 eval : MalExpr -> Eval MalExpr
166 (\env -> printString env True expr)
174 |> Eval.andThen (Eval.runLoop apply)
177 evalApply : ApplyRec -> Eval MalExpr
178 evalApply { frameId, bound, body } =
181 Eval.modifyEnv (Env.enter frameId bound)
182 |> Eval.andThen (\_ -> evalNoApply body)
185 Eval.modifyEnv (Env.leave env.currentFrameId)
186 |> Eval.map (\_ -> res)
191 evalNoApply : MalExpr -> Eval MalExpr
194 (\env -> printString env True ast)
199 MalList ((MalSymbol "def!") :: args) ->
202 MalList ((MalSymbol "let*") :: args) ->
205 MalList ((MalSymbol "do") :: args) ->
208 MalList ((MalSymbol "if") :: args) ->
211 MalList ((MalSymbol "fn*") :: args) ->
220 Eval.fail "can't happen"
222 (MalFunction (CoreFunc fn)) :: args ->
225 (MalFunction (UserFunc { lazyFn })) :: args ->
231 Eval.fail ((printString env True fn) ++ " is not a function")
240 evalAst : MalExpr -> Eval MalExpr
244 -- Lookup symbol in env and return value or raise error if not found.
247 case Env.get sym env of
256 -- Return new list that is result of calling eval on each element of list.
261 evalList (Array.toList vec)
262 |> Eval.map (Array.fromList >> MalVector)
265 evalList (Dict.values map)
276 evalList : List MalExpr -> Eval (List MalExpr)
282 Eval.succeed (List.reverse acc)
294 evalDef : List MalExpr -> Eval MalExpr
297 [ MalSymbol name, uneValue ] ->
301 Eval.modifyEnv (Env.set name value)
302 |> Eval.andThen (\_ -> Eval.succeed value)
306 Eval.fail "def! expected two args: name and value"
309 evalLet : List MalExpr -> Eval MalExpr
314 (MalSymbol name) :: expr :: rest ->
318 Eval.modifyEnv (Env.set name value)
321 if List.isEmpty rest then
329 Eval.fail "let* expected an even number of binds (symbol expr ..)"
332 Eval.modifyEnv Env.push
333 |> Eval.andThen (\_ -> evalBinds binds)
334 |> Eval.andThen (\_ -> evalNoApply body)
337 Eval.modifyEnv Env.pop
338 |> Eval.map (\_ -> res)
342 [ MalList binds, body ] ->
345 [ MalVector bindsVec, body ] ->
346 go (Array.toList bindsVec) body
349 Eval.fail "let* expected two args: binds and a body"
352 evalDo : List MalExpr -> Eval MalExpr
354 case List.reverse args of
356 evalList (List.reverse rest)
357 |> Eval.andThen (\_ -> evalNoApply last)
360 Eval.fail "do expected at least one arg"
363 evalIf : List MalExpr -> Eval MalExpr
367 expr /= MalNil && expr /= (MalBool False)
369 go condition trueExpr falseExpr =
371 |> Eval.map isThruthy
383 [ condition, trueExpr ] ->
384 go condition trueExpr MalNil
386 [ condition, trueExpr, falseExpr ] ->
387 go condition trueExpr falseExpr
390 Eval.fail "if expected at least two args"
393 evalFn : List MalExpr -> Eval MalExpr
396 {- Extract symbols from the binds list and verify their uniqueness -}
397 extractSymbols acc list =
400 Ok (List.reverse acc)
402 (MalSymbol name) :: rest ->
403 if List.member name acc then
404 Err "all binds must have unique names"
406 extractSymbols (name :: acc) rest
409 Err "all binds in fn* must be a symbol"
412 case List.reverse list of
413 var :: "&" :: rest ->
414 Ok <| bindVarArgs (List.reverse rest) var
417 if List.member "&" list then
418 Err "varargs separator '&' is used incorrectly"
423 extractSymbols [] >> Result.andThen parseBinds
425 bindArgs binds args =
430 if List.length args /= numBinds then
433 ++ (toString numBinds)
438 bindVarArgs binds var args =
444 MalList (List.drop minArgs args)
446 if List.length args < minArgs then
448 "function expected at least "
449 ++ (toString minArgs)
452 Ok <| zip binds args ++ [ ( var, varArgs ) ]
454 makeFn frameId binder body =
461 -- TODO : choice Env.enter prematurely?
462 -- I think it is needed by the garbage collect..
475 , eagerFn = lazyFn >> Eval.andThen eval
479 case extractAndParse bindsList of
481 Eval.modifyEnv Env.ref
482 -- reference the current frame.
488 (makeFn env.currentFrameId binder body)
496 [ MalList bindsList, body ] ->
499 [ MalVector bindsVec, body ] ->
500 go (Array.toList bindsVec) body
503 Eval.fail "fn* expected two args: binds list and body"
506 print : Env -> MalExpr -> String
513 Doesn't actually run the Eval but returns the monad.
516 rep : String -> Maybe (Eval MalExpr)
518 case readString input of