import Types exposing (..)
import Reader exposing (readString)
import Printer exposing (printString)
-import Utils exposing (maybeToList, zip, last, justValues)
+import Utils exposing (maybeToList, zip, last, justValues, makeCall)
import Env
import Core
import Eval
|> Env.set "*ARGV*" (MalList (args |> List.map MalString))
evalMalInit =
- Core.malInit
+ malInit
|> List.map rep
|> justValues
|> List.foldl
runInit args initEnv evalMalInit
+malInit : List String
+malInit =
+ [ """(def! not
+ (fn* (a)
+ (if a false true)))"""
+ , """(def! load-file
+ (fn* (f)
+ (eval (read-string
+ (str "(do " (slurp f) ")")))))"""
+ ]
+
+
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case model of
( env, EvalErr msg ) ->
-- Init failed, don't start REPL.
- ( Stopped, writeLine ("ERR:" ++ msg) )
+ ( Stopped, writeLine (printError env msg) )
( env, EvalIO cmd cont ) ->
-- IO in init.
( Stopped, Cmd.none )
( env, EvalErr msg ) ->
- ( Stopped, writeLine ("ERR:" ++ msg) )
+ ( Stopped, writeLine (printError env msg) )
( env, EvalIO cmd cont ) ->
( ScriptIO env cont, cmd )
( ReplActive env, writeLine (print env expr) )
( env, EvalErr msg ) ->
- ( ReplActive env, writeLine ("ERR:" ++ msg) )
+ ( ReplActive env, writeLine (printError env msg) )
( env, EvalIO cmd cont ) ->
( ReplIO env cont, cmd )
eval : MalExpr -> Eval MalExpr
eval ast =
let
- apply expr =
+ apply expr env =
case expr of
MalApply app ->
Left
malEval args =
case args of
[ expr ] ->
- eval expr
+ Eval.withEnv
+ (\env ->
+ Eval.modifyEnv (Env.jump Env.globalFrameId)
+ |> Eval.andThen (\_ -> eval expr)
+ |> Eval.finally Env.leave
+ )
_ ->
Eval.fail "unsupported arguments"
(\env ->
Eval.modifyEnv (Env.enter frameId bound)
|> Eval.andThen (\_ -> evalNoApply body)
- |> Eval.andThen
- (\res ->
- Eval.modifyEnv (Env.leave env.currentFrameId)
- |> Eval.map (\_ -> res)
- )
+ |> Eval.finally Env.leave
+ |> Eval.gcPass []
)
MalList ((MalSymbol "fn*") :: args) ->
evalFn args
+ MalList ((MalSymbol "quote") :: args) ->
+ evalQuote args
+
+ MalList ((MalSymbol "quasiquote") :: args) ->
+ case args of
+ [ expr ] ->
+ -- TCO.
+ evalNoApply (evalQuasiQuote expr)
+
+ _ ->
+ Eval.fail "unsupported arguments"
+
MalList list ->
evalList list
|> Eval.andThen
Eval.modifyEnv Env.push
|> Eval.andThen (\_ -> evalBinds binds)
|> Eval.andThen (\_ -> evalNoApply body)
- |> Eval.andThen
- (\res ->
- Eval.modifyEnv Env.pop
- |> Eval.map (\_ -> res)
- )
+ |> Eval.finally Env.pop
in
case args of
[ MalList binds, body ] ->
>> Eval.fromResult
>> Eval.map
(\bound ->
- -- TODO : choice Env.enter prematurely?
- -- I think it is needed by the garbage collect..
MalApply
{ frameId = frameId
, bound = bound
UserFunc
{ frameId = frameId
, lazyFn = lazyFn
- , eagerFn = lazyFn >> Eval.andThen eval
+ , eagerFn = \_ -> lazyFn >> Eval.andThen eval
+ , isMacro = False
+ , meta = Nothing
}
go bindsList body =
extractAndParse bindsList
|> Eval.fromResult
+ -- reference the current frame.
+ |> Eval.ignore (Eval.modifyEnv Env.ref)
|> Eval.andThen
(\binder ->
- Eval.modifyEnv Env.ref
- -- reference the current frame.
- |> Eval.andThen
- (\_ ->
- Eval.withEnv
- (\env ->
- Eval.succeed
- (makeFn env.currentFrameId binder body)
- )
- )
+ Eval.withEnv
+ (\env ->
+ Eval.succeed
+ (makeFn env.currentFrameId binder body)
+ )
)
in
case args of
Eval.fail "fn* expected two args: binds list and body"
+evalQuote : List MalExpr -> Eval MalExpr
+evalQuote args =
+ case args of
+ [ expr ] ->
+ Eval.succeed expr
+
+ _ ->
+ Eval.fail "unsupported arguments"
+
+
+evalQuasiQuote : MalExpr -> MalExpr
+evalQuasiQuote expr =
+ let
+ apply list empty =
+ case list of
+ [ MalSymbol "unquote", ast ] ->
+ ast
+
+ (MalList [ MalSymbol "splice-unquote", ast ]) :: rest ->
+ makeCall "concat"
+ [ ast
+ , evalQuasiQuote (MalList rest)
+ ]
+
+ ast :: rest ->
+ makeCall "cons"
+ [ evalQuasiQuote ast
+ , evalQuasiQuote (MalList rest)
+ ]
+
+ _ ->
+ makeCall "quote" [ empty ]
+ in
+ case expr of
+ MalList list ->
+ apply list (MalList [])
+
+ MalVector vec ->
+ apply (Array.toList vec) (MalVector Array.empty)
+
+ ast ->
+ makeCall "quote" [ ast ]
+
+
print : Env -> MalExpr -> String
print env =
printString env True
+printError : Env -> MalExpr -> String
+printError env expr =
+ "ERR:" ++ (printString env False expr)
+
+
{-| Read-Eval-Print.
Doesn't actually run the Eval but returns the monad.