2017-06-08 20:19:27 +03:00
|
|
|
module Eval exposing (..)
|
|
|
|
|
|
|
|
import Types exposing (..)
|
|
|
|
import IO exposing (IO)
|
2017-06-14 16:49:27 +03:00
|
|
|
import Env
|
2017-06-08 20:19:27 +03:00
|
|
|
|
|
|
|
|
2017-06-11 23:44:49 +03:00
|
|
|
apply : Eval a -> Env -> EvalContext a
|
2017-06-15 22:06:49 +03:00
|
|
|
apply f env =
|
|
|
|
f env
|
2017-06-08 20:19:27 +03:00
|
|
|
|
|
|
|
|
2017-06-11 23:44:49 +03:00
|
|
|
run : Env -> Eval a -> EvalContext a
|
2017-06-15 22:06:49 +03:00
|
|
|
run env e =
|
|
|
|
apply e env
|
2017-06-08 20:19:27 +03:00
|
|
|
|
|
|
|
|
2017-06-11 23:44:49 +03:00
|
|
|
withEnv : (Env -> Eval a) -> Eval a
|
2017-06-15 22:06:49 +03:00
|
|
|
withEnv f env =
|
|
|
|
apply (f env) env
|
2017-06-08 20:19:27 +03:00
|
|
|
|
|
|
|
|
2017-06-11 23:44:49 +03:00
|
|
|
setEnv : Env -> Eval ()
|
2017-06-15 22:06:49 +03:00
|
|
|
setEnv env _ =
|
|
|
|
apply (succeed ()) env
|
2017-06-08 20:19:27 +03:00
|
|
|
|
|
|
|
|
2017-06-11 23:44:49 +03:00
|
|
|
modifyEnv : (Env -> Env) -> Eval ()
|
2017-06-15 22:06:49 +03:00
|
|
|
modifyEnv f env =
|
|
|
|
apply (succeed ()) (f env)
|
2017-06-08 20:19:27 +03:00
|
|
|
|
|
|
|
|
|
|
|
succeed : a -> Eval a
|
2017-06-15 22:06:49 +03:00
|
|
|
succeed res env =
|
|
|
|
( env, EvalOk res )
|
2017-06-08 20:19:27 +03:00
|
|
|
|
|
|
|
|
|
|
|
io : Cmd Msg -> (IO -> Eval a) -> Eval a
|
2017-06-15 22:06:49 +03:00
|
|
|
io cmd cont env =
|
|
|
|
( env, EvalIO cmd cont )
|
2017-06-08 20:19:27 +03:00
|
|
|
|
|
|
|
|
|
|
|
map : (a -> b) -> Eval a -> Eval b
|
2017-06-15 22:06:49 +03:00
|
|
|
map f e env =
|
|
|
|
case apply e env of
|
|
|
|
( env, EvalOk res ) ->
|
|
|
|
( env, EvalOk (f res) )
|
2017-06-08 20:19:27 +03:00
|
|
|
|
2017-06-15 22:06:49 +03:00
|
|
|
( env, EvalErr msg ) ->
|
|
|
|
( env, EvalErr msg )
|
2017-06-08 20:19:27 +03:00
|
|
|
|
2017-06-15 22:06:49 +03:00
|
|
|
( env, EvalIO cmd cont ) ->
|
|
|
|
( env, EvalIO cmd (cont >> map f) )
|
2017-06-08 20:19:27 +03:00
|
|
|
|
|
|
|
|
2017-07-08 19:41:39 +03:00
|
|
|
{-| Chain two Eval's together. The function f takes the result from
|
|
|
|
the left eval and generates a new Eval.
|
|
|
|
-}
|
2017-06-08 20:19:27 +03:00
|
|
|
andThen : (a -> Eval b) -> Eval a -> Eval b
|
2017-06-15 22:06:49 +03:00
|
|
|
andThen f e env =
|
|
|
|
case apply e env of
|
|
|
|
( env, EvalOk res ) ->
|
|
|
|
apply (f res) env
|
2017-06-08 20:19:27 +03:00
|
|
|
|
2017-06-15 22:06:49 +03:00
|
|
|
( env, EvalErr msg ) ->
|
|
|
|
( env, EvalErr msg )
|
2017-06-08 20:19:27 +03:00
|
|
|
|
2017-06-15 22:06:49 +03:00
|
|
|
( env, EvalIO cmd cont ) ->
|
|
|
|
( env, EvalIO cmd (cont >> andThen f) )
|
2017-06-08 20:19:27 +03:00
|
|
|
|
|
|
|
|
2017-07-08 19:41:39 +03:00
|
|
|
{-| 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) )
|
|
|
|
|
|
|
|
|
2017-07-09 13:34:58 +03:00
|
|
|
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 )
|
2017-07-09 13:34:58 +03:00
|
|
|
( 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) )
|
|
|
|
|
|
|
|
|
2017-06-08 20:19:27 +03:00
|
|
|
fail : String -> Eval a
|
2017-06-15 22:06:49 +03:00
|
|
|
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
|
|
|
|
|
|
|
|
2017-06-15 22:06:49 +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
|
2017-06-15 22:06:49 +03:00
|
|
|
runLoop f expr env =
|
2017-06-20 18:23:00 +03:00
|
|
|
case f expr env of
|
2017-06-15 22:06:49 +03:00
|
|
|
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
|
|
|
|
)
|
2017-07-25 23:19:56 +03:00
|
|
|
|
|
|
|
|
|
|
|
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"
|