/Users/alexjokela/projects/lattice/src/env.c
Line | Count | Source |
1 | | #include "env.h" |
2 | | #include "memory.h" |
3 | | #include <stdlib.h> |
4 | | #include <string.h> |
5 | | |
6 | 2.57k | #define INITIAL_SCOPE_CAP 8 |
7 | | |
8 | 14.0k | static Scope scope_new(void) { |
9 | 14.0k | return lat_map_new(sizeof(LatValue)); |
10 | 14.0k | } |
11 | | |
12 | 294k | static void scope_free_values(const char *key, void *value, void *ctx) { |
13 | 294k | (void)key; (void)ctx; |
14 | 294k | LatValue *v = (LatValue *)value; |
15 | 294k | value_free(v); |
16 | 294k | } |
17 | | |
18 | 14.0k | static void scope_free(Scope *s) { |
19 | 14.0k | lat_map_iter(s, scope_free_values, NULL); |
20 | 14.0k | lat_map_free(s); |
21 | 14.0k | } |
22 | | |
23 | 2.57k | Env *env_new(void) { |
24 | 2.57k | Env *env = malloc(sizeof(Env)); |
25 | 2.57k | env->cap = INITIAL_SCOPE_CAP; |
26 | 2.57k | env->count = 1; |
27 | 2.57k | env->refcount = 1; |
28 | 2.57k | env->arena_backed = false; |
29 | 2.57k | env->scopes = malloc(env->cap * sizeof(Scope)); |
30 | 2.57k | env->scopes[0] = scope_new(); |
31 | 2.57k | return env; |
32 | 2.57k | } |
33 | | |
34 | 7.52k | void env_retain(Env *env) { |
35 | 7.52k | if (env) env->refcount++; |
36 | 7.52k | } |
37 | | |
38 | 9.42k | void env_release(Env *env) { |
39 | 9.42k | if (!env) return; |
40 | 9.42k | if (--env->refcount == 0) |
41 | 1.89k | env_free(env); |
42 | 9.42k | } |
43 | | |
44 | 4.47k | void env_free(Env *env) { |
45 | 4.47k | if (!env) return; |
46 | 4.47k | if (env->arena_backed) return; |
47 | 10.9k | for (size_t i = 0; i < env->count; i++) { |
48 | 6.44k | scope_free(&env->scopes[i]); |
49 | 6.44k | } |
50 | 4.47k | free(env->scopes); |
51 | 4.47k | free(env); |
52 | 4.47k | } |
53 | | |
54 | 7.64k | void env_push_scope(Env *env) { |
55 | 7.64k | if (env->count >= env->cap) { |
56 | 22 | env->cap *= 2; |
57 | 22 | env->scopes = realloc(env->scopes, env->cap * sizeof(Scope)); |
58 | 22 | } |
59 | 7.64k | env->scopes[env->count++] = scope_new(); |
60 | 7.64k | } |
61 | | |
62 | 7.64k | void env_pop_scope(Env *env) { |
63 | 7.64k | if (env->count <= 1) return; |
64 | 7.64k | env->count--; |
65 | 7.64k | scope_free(&env->scopes[env->count]); |
66 | 7.64k | } |
67 | | |
68 | 290k | void env_define(Env *env, const char *name, LatValue value) { |
69 | 290k | if (env->count == 0) return; |
70 | 290k | env_define_at(env, env->count - 1, name, value); |
71 | 290k | } |
72 | | |
73 | 294k | void env_define_at(Env *env, size_t scope_idx, const char *name, LatValue value) { |
74 | 294k | if (scope_idx >= env->count) return; |
75 | 294k | Scope *scope = &env->scopes[scope_idx]; |
76 | 294k | LatValue *existing = lat_map_get(scope, name); |
77 | 294k | if (existing) { |
78 | 156 | value_free(existing); |
79 | 156 | } |
80 | 294k | lat_map_set(scope, name, &value); |
81 | 294k | } |
82 | | |
83 | 26.9k | bool env_get(const Env *env, const char *name, LatValue *out) { |
84 | 26.9k | if (env->count == 1) { |
85 | 3.39k | LatValue *v = lat_map_get(&env->scopes[0], name); |
86 | 3.39k | if (v) { *out = value_deep_clone(v); return true; } |
87 | 103 | return false; |
88 | 3.39k | } |
89 | 40.5k | for (size_t i = env->count; i > 0; i--) { |
90 | 40.5k | LatValue *v = lat_map_get(&env->scopes[i - 1], name); |
91 | 40.5k | if (v) { |
92 | 23.5k | *out = value_deep_clone(v); |
93 | 23.5k | return true; |
94 | 23.5k | } |
95 | 40.5k | } |
96 | 6 | return false; |
97 | 23.5k | } |
98 | | |
99 | 813 | LatValue *env_get_ref(const Env *env, const char *name) { |
100 | 813 | if (env->count == 1) |
101 | 809 | return lat_map_get(&env->scopes[0], name); |
102 | 4 | for (size_t i = env->count; i > 0; i--) { |
103 | 4 | LatValue *v = lat_map_get(&env->scopes[i - 1], name); |
104 | 4 | if (v) return v; |
105 | 4 | } |
106 | 0 | return NULL; |
107 | 4 | } |
108 | | |
109 | 3.40k | LatValue *env_get_ref_prehashed(const Env *env, const char *name, size_t hash) { |
110 | 3.40k | if (env->count == 1) |
111 | 3.38k | return lat_map_get_prehashed(&env->scopes[0], name, hash); |
112 | 21 | for (size_t i = env->count; i > 0; i--) { |
113 | 21 | LatValue *v = lat_map_get_prehashed(&env->scopes[i - 1], name, hash); |
114 | 21 | if (v) return v; |
115 | 21 | } |
116 | 0 | return NULL; |
117 | 16 | } |
118 | | |
119 | 15.8k | bool env_set(Env *env, const char *name, LatValue value) { |
120 | 15.8k | if (env->count == 1) { |
121 | 10.0k | LatValue *existing = lat_map_get(&env->scopes[0], name); |
122 | 10.0k | if (existing) { |
123 | 8.64k | value_free(existing); |
124 | 8.64k | lat_map_set(&env->scopes[0], name, &value); |
125 | 8.64k | return true; |
126 | 8.64k | } |
127 | 1.38k | return false; |
128 | 10.0k | } |
129 | 11.4k | for (size_t i = env->count; i > 0; i--) { |
130 | 11.4k | LatValue *existing = lat_map_get(&env->scopes[i - 1], name); |
131 | 11.4k | if (existing) { |
132 | 5.78k | value_free(existing); |
133 | 5.78k | lat_map_set(&env->scopes[i - 1], name, &value); |
134 | 5.78k | return true; |
135 | 5.78k | } |
136 | 11.4k | } |
137 | 0 | return false; |
138 | 5.78k | } |
139 | | |
140 | 3 | bool env_remove(Env *env, const char *name, LatValue *out) { |
141 | 3 | for (size_t i = env->count; i > 0; i--) { |
142 | 3 | LatValue *existing = lat_map_get(&env->scopes[i - 1], name); |
143 | 3 | if (existing) { |
144 | 3 | if (out) *out = *existing; |
145 | | /* Remove without freeing the value (caller takes ownership) */ |
146 | | /* We need to copy the value out before removing */ |
147 | 3 | LatValue copy = *existing; |
148 | | /* Set to unit so map_remove doesn't free our data */ |
149 | 3 | memset(existing, 0, sizeof(LatValue)); |
150 | 3 | existing->type = VAL_UNIT; |
151 | 3 | lat_map_remove(&env->scopes[i - 1], name); |
152 | 3 | if (out) *out = copy; |
153 | 0 | else value_free(©); |
154 | 3 | return true; |
155 | 3 | } |
156 | 3 | } |
157 | 0 | return false; |
158 | 3 | } |
159 | | |
160 | | /* Deep clone helper — normal (non-arena) path */ |
161 | | typedef struct { |
162 | | Env *dest; |
163 | | size_t scope_idx; |
164 | | } CloneCtx; |
165 | | |
166 | 734 | static void clone_entry(const char *key, void *value, void *ctx) { |
167 | 734 | CloneCtx *cc = (CloneCtx *)ctx; |
168 | 734 | LatValue *v = (LatValue *)value; |
169 | 734 | LatValue cloned = value_deep_clone(v); |
170 | 734 | lat_map_set(&cc->dest->scopes[cc->scope_idx], key, &cloned); |
171 | 734 | } |
172 | | |
173 | | /* Arena-routed clone: build scope map internals directly via arena alloc */ |
174 | 13 | static Env *env_clone_arena(const Env *env) { |
175 | 13 | Env *new_env = lat_alloc_routed(sizeof(Env)); |
176 | 13 | new_env->cap = env->cap; |
177 | 13 | new_env->count = env->count; |
178 | 13 | new_env->refcount = 1; |
179 | 13 | new_env->scopes = lat_alloc_routed(new_env->cap * sizeof(Scope)); |
180 | | |
181 | 13 | new_env->arena_backed = true; |
182 | 51 | for (size_t i = 0; i < env->count; i++) { |
183 | 38 | const LatMap *src = &env->scopes[i]; |
184 | 38 | LatMap *dst = &new_env->scopes[i]; |
185 | 38 | dst->value_size = src->value_size; |
186 | 38 | dst->cap = src->cap; |
187 | 38 | dst->count = src->count; /* preserve tombstone count for probe chains */ |
188 | 38 | dst->live = src->live; |
189 | 38 | dst->entries = lat_calloc_routed(src->cap, sizeof(LatMapEntry)); |
190 | 646 | for (size_t j = 0; j < src->cap; j++) { |
191 | | /* Point value to inline buffer for all entries */ |
192 | 608 | dst->entries[j].value = dst->entries[j]._ibuf; |
193 | 608 | if (src->entries[j].state == MAP_OCCUPIED) { |
194 | 19 | dst->entries[j].state = MAP_OCCUPIED; |
195 | 19 | dst->entries[j].key = lat_strdup_routed(src->entries[j].key); |
196 | 19 | LatValue *sv = (LatValue *)src->entries[j].value; |
197 | 19 | LatValue cloned = value_deep_clone(sv); |
198 | 19 | *(LatValue *)dst->entries[j].value = cloned; |
199 | 589 | } else if (src->entries[j].state == MAP_TOMBSTONE) { |
200 | 0 | dst->entries[j].state = MAP_TOMBSTONE; |
201 | 0 | } |
202 | 608 | } |
203 | 38 | } |
204 | 13 | return new_env; |
205 | 13 | } |
206 | | |
207 | 1.91k | Env *env_clone(const Env *env) { |
208 | 1.91k | if (value_get_arena()) return env_clone_arena(env); |
209 | | |
210 | 1.90k | Env *new_env = malloc(sizeof(Env)); |
211 | 1.90k | new_env->cap = env->cap; |
212 | 1.90k | new_env->count = env->count; |
213 | 1.90k | new_env->refcount = 1; |
214 | 1.90k | new_env->arena_backed = false; |
215 | 1.90k | new_env->scopes = malloc(new_env->cap * sizeof(Scope)); |
216 | | |
217 | 1.90k | CloneCtx ctx; |
218 | 1.90k | ctx.dest = new_env; |
219 | 5.77k | for (size_t i = 0; i < env->count; i++) { |
220 | 3.87k | new_env->scopes[i] = scope_new(); |
221 | 3.87k | ctx.scope_idx = i; |
222 | 3.87k | lat_map_iter(&env->scopes[i], clone_entry, &ctx); |
223 | 3.87k | } |
224 | 1.90k | return new_env; |
225 | 1.91k | } |
226 | | |
227 | 60.7k | static void iter_scope_values(const char *key, void *value, void *ctx) { |
228 | 60.7k | (void)key; |
229 | 60.7k | void **args = (void **)ctx; |
230 | 60.7k | EnvIterFn fn = (EnvIterFn)args[0]; |
231 | 60.7k | void *user_ctx = args[1]; |
232 | 60.7k | fn((LatValue *)value, user_ctx); |
233 | 60.7k | } |
234 | | |
235 | 15.0k | void env_iter_values(Env *env, EnvIterFn fn, void *ctx) { |
236 | 15.0k | void *args[2] = { (void *)fn, ctx }; |
237 | 72.2k | for (size_t i = 0; i < env->count; i++) { |
238 | 57.1k | lat_map_iter(&env->scopes[i], iter_scope_values, args); |
239 | 57.1k | } |
240 | 15.0k | } |