21 import Types exposing (MalExpr(..), MalFunction(..), Frame, Env)
27 debug : Env -> String -> a -> a
40 defaultGcInterval : Int
47 { frames = Dict.singleton globalFrameId (emptyFrame Nothing Nothing)
48 , nextFrameId = globalFrameId + 1
49 , currentFrameId = globalFrameId
53 , gcInterval = defaultGcInterval
60 getFrame : Env -> Int -> Frame
61 getFrame env frameId =
62 case Dict.get frameId env.frames of
67 Debug.crash <| "frame #" ++ (toString frameId) ++ " not found"
70 emptyFrame : Maybe Int -> Maybe Int -> Frame
71 emptyFrame outerId exitId =
79 set : String -> MalExpr -> Env -> Env
88 { frame | data = Dict.insert name expr frame.data }
92 Dict.update frameId updateFrame env.frames
94 { env | frames = newFrames }
97 get : String -> Env -> Result String MalExpr
105 case Dict.get name frame.data of
112 |> Maybe.withDefault (Err <| "'" ++ name ++ "' not found")
114 go env.currentFrameId
117 newAtom : MalExpr -> Env -> ( Env, Int )
125 | atoms = Dict.insert atomId value env.atoms
126 , nextAtomId = atomId + 1
132 getAtom : Int -> Env -> MalExpr
134 case Dict.get atomId env.atoms of
139 Debug.crash <| "atom " ++ (toString atomId) ++ " not found"
142 setAtom : Int -> MalExpr -> Env -> Env
143 setAtom atomId value env =
145 | atoms = Dict.insert atomId value env.atoms
156 emptyFrame (Just env.currentFrameId) Nothing
159 debug env "push" frameId
162 | currentFrameId = frameId
163 , frames = Dict.insert frameId newFrame env.frames
164 , nextFrameId = env.nextFrameId + 1
178 debug env "pop" frameId
180 case frame.outerId of
183 | currentFrameId = outerId
184 , frames = Dict.update frameId free env.frames
188 Debug.crash "tried to pop global frame"
191 setBinds : List ( String, MalExpr ) -> Frame -> Frame
192 setBinds binds frame =
197 ( name, expr ) :: rest ->
199 { frame | data = Dict.insert name expr frame.data }
202 {-| Enter a new frame with a set of binds
204 enter : Int -> List ( String, MalExpr ) -> Env -> Env
205 enter outerId binds env =
208 debug env "enter #" env.nextFrameId
214 setBinds binds (emptyFrame (Just outerId) (Just exitId))
217 | currentFrameId = frameId
218 , frames = Dict.insert frameId newFrame env.frames
219 , nextFrameId = env.nextFrameId + 1
227 debug env "leave #" env.currentFrameId
240 ++ (toString frameId)
241 ++ " doesn't have an exitId"
244 | currentFrameId = exitId
247 |> Dict.insert frameId { frame | exitId = Nothing }
248 |> Dict.update frameId free
252 {-| Increase refCnt for the current frame,
253 and all it's parent frames.
264 { frame | refCnt = frame.refCnt + 1 }
267 { env | frames = Dict.insert frameId newFrame env.frames }
269 case frame.outerId of
277 go env.currentFrameId env
279 { newEnv | gcCounter = newEnv.gcCounter + 1 }
282 free : Maybe Frame -> Maybe Frame
286 if frame.refCnt == 1 then
289 Just { frame | refCnt = frame.refCnt - 1 }
293 pushRef : MalExpr -> Env -> Env
295 { env | stack = ref :: env.stack }
298 restoreRefs : List MalExpr -> Env -> Env
299 restoreRefs refs env =
300 { env | stack = refs }
303 {-| Given an Env see which frames are not reachable from the
304 global frame, or from the current expression.
306 Return a new Env with the unreachable frames removed.
309 gc : MalExpr -> Env -> Env
313 List.foldl countExpr acc
315 countFrame { data } acc =
316 data |> Dict.values |> countList acc
319 if not (Set.member frameId acc) then
325 Set.insert frameId acc
327 countFrame frame newAcc
331 countBound bound acc =
333 |> List.map Tuple.second
338 MalFunction (UserFunc { frameId }) ->
341 MalApply { frameId, bound } ->
349 countList acc (Array.toList vec)
352 countList acc (Dict.values map)
366 ([ globalFrameId, env.currentFrameId ]
370 countFrames frames acc =
372 |> List.map (getFrame env)
373 |> List.foldl countFrame acc
375 expand frameId frame fn acc =
381 Set.insert parentId acc
388 expand frameId frame .outerId
389 >> expand frameId frame .exitId
391 expandParents frames =
392 Set.foldl expandBoth frames frames
402 if Set.isEmpty newParents then
405 loop <| countFrames newParents newAcc
407 makeNewEnv newFrames =
413 keepFilter keep frameId _ =
414 Set.member frameId keep
416 filterFrames frames keep =
417 Dict.filter (keepFilter keep) frames
419 countFrames initSet initSet
421 |> (flip countList) env.stack
423 |> filterFrames env.frames