module Env
exposing
- ( global
+ ( debug
+ , globalFrameId
+ , global
, push
, pop
+ , jump
, enter
, leave
, ref
import Set
+debug : Env -> String -> a -> a
+debug env msg value =
+ if env.debug then
+ Debug.log msg value
+ else
+ value
+
+
globalFrameId : Int
globalFrameId =
0
, currentFrameId = globalFrameId
, atoms = Dict.empty
, nextAtomId = 0
+ , debug = False
}
+jump : Int -> Env -> Env
+jump frameId env =
+ { env | currentFrameId = frameId }
+
+
push : Env -> Env
push env =
let
enter parentFrameId binds env =
let
frameId =
- Debug.log "enter #"
- env.nextFrameId
+ debug env "enter #" env.nextFrameId
newFrame =
setBinds binds (emptyFrame (Just parentFrameId))
leave orgFrameId env =
let
frameId =
- Debug.log "leave #"
- env.currentFrameId
+ debug env "leave #" env.currentFrameId
in
{ env
| currentFrameId = orgFrameId
{ env | frames = newFrames }
-
--- TODO: when disposing, deref all function's frames?
--- TODO: is that enough instead of a GC? no: don't know how often the function is referenced.
--- TODO: consideration: keep refCnt for MalFunction, or implement a light GC.
-
-
deref : Maybe Frame -> Maybe Frame
deref =
Maybe.andThen
)
-
--- TODO need a GC.
--- given a Env, see which frames are not reachable.
--- in MalFunction need to refer to the frameId.
-
-
{-| Given an Env see which frames are not reachable from the
global frame. Return a new Env without the unreachable frames.
-}
data |> Dict.values |> countList acc
countRefs expr acc =
- Debug.log (toString expr) <|
+ debug env ("gc-visit " ++ (toString expr)) <|
case expr of
MalFunction (UserFunc { frameId }) ->
if not (Set.member frameId acc) then
- Debug.log "counting" <|
+ debug env "gc-counting" <|
case Dict.get frameId env.frames of
Just frame ->
countFrame (Set.insert frameId acc) frame
initSet =
Set.fromList [ globalFrameId, env.currentFrameId ]
+
+ reportUnused frames used =
+ Dict.diff frames used
+ |> debug env "unused frames"
+ |> (\_ -> frames)
in
case Dict.get globalFrameId env.frames of
Nothing ->
Just globalFrame ->
countFrame initSet globalFrame
|> Set.toList
- |> Debug.log "used frames"
+ |> debug env "used frames"
|> List.map (\frameId -> ( frameId, emptyFrame Nothing ))
|> Dict.fromList
- |> Dict.intersect (Debug.log "cur frames" env.frames)
+ |> reportUnused env.frames
+ |> Dict.intersect env.frames
|> (\frames -> { env | frames = frames })
}
-set : String -> MalExpr -> Env -> Env
-set name expr env =
+setInFrame : Int -> String -> MalExpr -> Env -> Env
+setInFrame frameId name expr env =
let
updateFrame =
Maybe.map
{ frame | data = Dict.insert name expr frame.data }
)
- frameId =
- env.currentFrameId
-
newFrames =
Dict.update frameId updateFrame env.frames
in
{ env | frames = newFrames }
+set : String -> MalExpr -> Env -> Env
+set name expr env =
+ setInFrame env.currentFrameId name expr env
+
+
get : String -> Env -> Result String MalExpr
get name env =
let
Nothing ->
frame.outerId
|> Maybe.map go
- |> Maybe.withDefault (Err "symbol not found")
+ |> Maybe.withDefault (Err <| "'" ++ name ++ "' not found")
in
go env.currentFrameId