18 import Types exposing (MalExpr(..), MalFunction(..), Frame, Env)
24 debug : Env -> String -> a -> a
39 { frames = Dict.singleton globalFrameId (emptyFrame Nothing)
40 , nextFrameId = globalFrameId + 1
41 , currentFrameId = globalFrameId
55 emptyFrame (Just env.currentFrameId)
58 | currentFrameId = frameId
59 , frames = Dict.insert frameId newFrame env.frames
60 , nextFrameId = env.nextFrameId + 1
70 case Dict.get frameId env.frames of
72 case currentFrame.outerId of
75 | currentFrameId = outerId
76 , frames = Dict.update frameId deref env.frames
80 Debug.crash "tried to pop global frame"
89 setBinds : List ( String, MalExpr ) -> Frame -> Frame
90 setBinds binds frame =
95 ( name, expr ) :: rest ->
97 { frame | data = Dict.insert name expr frame.data }
100 enter : Int -> List ( String, MalExpr ) -> Env -> Env
101 enter parentFrameId binds env =
104 debug env "enter #" env.nextFrameId
107 setBinds binds (emptyFrame (Just parentFrameId))
110 | currentFrameId = frameId
111 , frames = Dict.insert frameId newFrame env.frames
112 , nextFrameId = env.nextFrameId + 1
116 leave : Int -> Env -> Env
117 leave orgFrameId env =
120 debug env "leave #" env.currentFrameId
123 | currentFrameId = orgFrameId
124 , frames = Dict.update frameId deref env.frames
128 {-| Increase refCnt for the current frame
136 { frame | refCnt = frame.refCnt + 1 }
140 Dict.update env.currentFrameId incRef env.frames
142 { env | frames = newFrames }
145 deref : Maybe Frame -> Maybe Frame
149 if frame.refCnt == 1 then
152 Just { frame | refCnt = frame.refCnt - 1 }
156 {-| Given an Env see which frames are not reachable from the
157 global frame. Return a new Env without the unreachable frames.
163 List.foldl countRefs acc
165 countFrame acc { data } =
166 data |> Dict.values |> countList acc
169 debug env ("gc-visit " ++ (toString expr)) <|
171 MalFunction (UserFunc { frameId }) ->
172 if not (Set.member frameId acc) then
173 debug env "gc-counting" <|
174 case Dict.get frameId env.frames of
176 countFrame (Set.insert frameId acc) frame
179 Debug.crash ("frame " ++ (toString frameId) ++ " not found in GC")
187 countList acc (Array.toList vec)
190 countList acc (Dict.values map)
196 Set.fromList [ globalFrameId, env.currentFrameId ]
198 reportUnused frames used =
199 Dict.diff frames used
200 |> debug env "unused frames"
203 case Dict.get globalFrameId env.frames of
205 Debug.crash "global frame not found"
208 countFrame initSet globalFrame
210 |> debug env "used frames"
211 |> List.map (\frameId -> ( frameId, emptyFrame Nothing ))
213 |> reportUnused env.frames
214 |> Dict.intersect env.frames
215 |> (\frames -> { env | frames = frames })
218 emptyFrame : Maybe Int -> Frame
226 set : String -> MalExpr -> Env -> Env
232 { frame | data = Dict.insert name expr frame.data }
239 Dict.update frameId updateFrame env.frames
241 { env | frames = newFrames }
244 get : String -> Env -> Result String MalExpr
248 case Dict.get frameId env.frames of
250 Err <| "frame " ++ (toString frameId) ++ " not found"
253 case Dict.get name frame.data of
260 |> Maybe.withDefault (Err "symbol not found")
262 go env.currentFrameId
265 newAtom : MalExpr -> Env -> ( Env, Int )
273 | atoms = Dict.insert atomId value env.atoms
274 , nextAtomId = atomId + 1
280 getAtom : Int -> Env -> MalExpr
282 case Dict.get atomId env.atoms of
287 Debug.crash <| "atom " ++ (toString atomId) ++ " not found"
290 setAtom : Int -> MalExpr -> Env -> Env
291 setAtom atomId value env =
293 | atoms = Dict.insert atomId value env.atoms