1
1
mirror of https://github.com/kanaka/mal.git synced 2024-11-11 00:52:44 +03:00
mal/elm/Eval.elm

239 lines
5.1 KiB
Elm
Raw Normal View History

module Eval exposing (..)
import Types exposing (..)
import IO exposing (IO)
2017-06-14 16:49:27 +03:00
import Env
apply : Eval a -> Env -> EvalContext a
apply f env =
f env
run : Env -> Eval a -> EvalContext a
run env e =
apply e env
withEnv : (Env -> Eval a) -> Eval a
withEnv f env =
apply (f env) env
setEnv : Env -> Eval ()
setEnv env _ =
apply (succeed ()) env
modifyEnv : (Env -> Env) -> Eval ()
modifyEnv f env =
apply (succeed ()) (f env)
succeed : a -> Eval a
succeed res env =
( env, EvalOk res )
io : Cmd Msg -> (IO -> Eval a) -> Eval a
io cmd cont env =
( env, EvalIO cmd cont )
map : (a -> b) -> Eval a -> Eval b
map f e env =
case apply e env of
( env, EvalOk res ) ->
( env, EvalOk (f res) )
( env, EvalErr msg ) ->
( env, EvalErr msg )
( env, EvalIO cmd cont ) ->
( env, EvalIO cmd (cont >> map f) )
{-| Chain two Eval's together. The function f takes the result from
the left eval and generates a new Eval.
-}
andThen : (a -> Eval b) -> Eval a -> Eval b
andThen f e env =
case apply e env of
( env, EvalOk res ) ->
apply (f res) env
( env, EvalErr msg ) ->
( env, EvalErr msg )
( env, EvalIO cmd cont ) ->
( env, EvalIO cmd (cont >> andThen f) )
{-| Apply a transformation to the Env, for a Ok and a Err.
-}
finally : (Env -> Env) -> Eval a -> Eval a
finally f e env =
case apply e env of
( env, EvalOk res ) ->
( f env, EvalOk res )
( env, EvalErr msg ) ->
( f env, EvalErr msg )
( env, EvalIO cmd cont ) ->
( env, EvalIO cmd (cont >> finally f) )
gcPass : Eval MalExpr -> Eval MalExpr
gcPass e env =
let
go env t expr =
if env.gcCounter >= env.gcInterval then
2017-07-18 22:34:36 +03:00
--Debug.log
-- ("before GC: "
-- ++ (printEnv env)
-- )
-- ""
-- |> always ( Env.gc env, t expr )
( Env.gc expr env, t expr )
else
( env, t expr )
in
case apply e env of
( env, EvalOk res ) ->
go env EvalOk res
( env, EvalErr msg ) ->
go env EvalErr msg
( env, EvalIO cmd cont ) ->
( env, EvalIO cmd (cont >> gcPass) )
2017-06-23 17:56:04 +03:00
catchError : (MalExpr -> Eval a) -> Eval a -> Eval a
2017-06-20 18:23:00 +03:00
catchError f e env =
case apply e env of
( env, EvalOk res ) ->
( env, EvalOk res )
( env, EvalErr msg ) ->
apply (f msg) env
( env, EvalIO cmd cont ) ->
( env, EvalIO cmd (cont >> catchError f) )
fail : String -> Eval a
fail msg env =
2017-06-23 17:56:04 +03:00
( env, EvalErr <| MalString msg )
throw : MalExpr -> Eval a
throw ex env =
( env, EvalErr ex )
2017-06-14 16:49:27 +03:00
{-| Apply f to expr repeatedly.
Continues iterating if f returns (Left eval).
Stops if f returns (Right expr).
Tail call optimized.
-}
2017-06-20 18:23:00 +03:00
runLoop : (MalExpr -> Env -> Either (Eval MalExpr) MalExpr) -> MalExpr -> Eval MalExpr
runLoop f expr env =
2017-06-20 18:23:00 +03:00
case f expr env of
Left e ->
case apply e env of
( env, EvalOk expr ) ->
runLoop f expr env
( env, EvalErr msg ) ->
( env, EvalErr msg )
( env, EvalIO cmd cont ) ->
( env, EvalIO cmd (cont >> andThen (runLoop f)) )
Right expr ->
( env, EvalOk expr )
2017-06-18 22:38:48 +03:00
fromResult : Result String a -> Eval a
fromResult res =
case res of
Ok val ->
succeed val
Err msg ->
fail msg
2017-06-18 23:21:25 +03:00
{-| Chain the left and right Eval but ignore the right's result.
-}
ignore : Eval b -> Eval a -> Eval a
ignore right left =
left
|> andThen
(\res ->
right
|> andThen (\_ -> succeed res)
)
2017-07-18 22:34:36 +03:00
withStack : Eval a -> Eval a
withStack e =
withEnv
(\env ->
e
|> ignore
(modifyEnv
(Env.restoreRefs env.stack)
)
)
pushRef : MalExpr -> Eval a -> Eval a
pushRef ref e =
modifyEnv (Env.pushRef ref)
|> andThen (always e)
2017-07-22 22:44:25 +03:00
inGlobal : Eval a -> Eval a
inGlobal body =
let
enter env =
setEnv
{ env
| keepFrames = env.currentFrameId :: env.keepFrames
, currentFrameId = Env.globalFrameId
}
leave oldEnv newEnv =
{ newEnv
| keepFrames = oldEnv.keepFrames
, currentFrameId = oldEnv.currentFrameId
}
in
withEnv
(\env ->
if env.currentFrameId /= Env.globalFrameId then
enter env
|> andThen (always body)
|> finally (leave env)
else
body
)
runSimple : Eval a -> Result MalExpr a
runSimple e =
case run Env.global e of
( _, EvalOk res ) ->
Ok res
( _, EvalErr msg ) ->
Err msg
_ ->
Debug.crash "can't happen"