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 msg, writeLine ("ERR:" ++ 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 ("ERR:" ++ 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 eval : MalExpr -> Eval MalExpr
163 MalList ((MalSymbol "def!") :: args) ->
166 MalList ((MalSymbol "let*") :: args) ->
169 MalList ((MalSymbol "do") :: args) ->
172 MalList ((MalSymbol "if") :: args) ->
175 MalList ((MalSymbol "fn*") :: args) ->
184 Eval.fail "can't happen"
186 (MalFunction (CoreFunc fn)) :: args ->
189 (MalFunction (UserFunc { eagerFn })) :: args ->
195 Eval.fail ((printString env True fn) ++ " is not a function")
203 evalAst : MalExpr -> Eval MalExpr
207 -- Lookup symbol in env and return value or raise error if not found.
210 case Env.get sym env of
219 -- Return new list that is result of calling eval on each element of list.
224 evalList (Array.toList vec)
225 |> Eval.map (Array.fromList >> MalVector)
228 evalList (Dict.values map)
239 evalList : List MalExpr -> Eval (List MalExpr)
245 Eval.succeed (List.reverse acc)
257 evalDef : List MalExpr -> Eval MalExpr
260 [ MalSymbol name, uneValue ] ->
264 Eval.modifyEnv (Env.set name value)
265 |> Eval.andThen (\_ -> Eval.succeed value)
269 Eval.fail "def! expected two args: name and value"
272 evalLet : List MalExpr -> Eval MalExpr
277 (MalSymbol name) :: expr :: rest ->
281 Eval.modifyEnv (Env.set name value)
284 if List.isEmpty rest then
292 Eval.fail "let* expected an even number of binds (symbol expr ..)"
295 Eval.modifyEnv Env.push
296 |> Eval.andThen (\_ -> evalBinds binds)
297 |> Eval.andThen (\_ -> eval body)
300 Eval.modifyEnv Env.pop
301 |> Eval.map (\_ -> res)
305 [ MalList binds, body ] ->
308 [ MalVector bindsVec, body ] ->
309 go (Array.toList bindsVec) body
312 Eval.fail "let* expected two args: binds and a body"
315 evalDo : List MalExpr -> Eval MalExpr
324 Eval.fail "do expected at least one arg"
327 |> Eval.andThen returnLast
330 evalIf : List MalExpr -> Eval MalExpr
334 expr /= MalNil && expr /= (MalBool False)
336 go condition trueExpr falseExpr =
338 |> Eval.map isThruthy
350 [ condition, trueExpr ] ->
351 go condition trueExpr MalNil
353 [ condition, trueExpr, falseExpr ] ->
354 go condition trueExpr falseExpr
357 Eval.fail "if expected at least two args"
360 evalFn : List MalExpr -> Eval MalExpr
363 {- Extract symbols from the binds list and verify their uniqueness -}
364 extractSymbols acc list =
367 Ok (List.reverse acc)
369 (MalSymbol name) :: rest ->
370 if List.member name acc then
371 Err "all binds must have unique names"
373 extractSymbols (name :: acc) rest
376 Err "all binds in fn* must be a symbol"
379 case List.reverse list of
380 var :: "&" :: rest ->
381 Ok <| bindVarArgs (List.reverse rest) var
384 if List.member "&" list then
385 Err "varargs separator '&' is used incorrectly"
390 extractSymbols [] >> Result.andThen parseBinds
392 bindArgs binds args =
397 if List.length args /= numBinds then
400 ++ (toString numBinds)
405 bindVarArgs binds var args =
411 MalList (List.drop minArgs args)
413 if List.length args < minArgs then
415 "function expected at least "
416 ++ (toString minArgs)
419 Ok <| zip binds args ++ [ ( var, varArgs ) ]
421 makeFn frameId binder body =
426 Eval.enter frameId bound (eval body)
440 case extractAndParse bindsList of
442 Eval.modifyEnv Env.ref
443 -- reference the current frame.
449 (makeFn env.currentFrameId binder body)
457 [ MalList bindsList, body ] ->
460 [ MalVector bindsVec, body ] ->
461 go (Array.toList bindsVec) body
464 Eval.fail "fn* expected two args: binds list and body"
467 print : Env -> MalExpr -> String
474 Doesn't actually run the Eval but returns the monad.
477 rep : String -> Maybe (Eval MalExpr)
479 case readString input of