1 /* pkl-env.c - Compile-time lexical environments for Poke. */
2
3 /* Copyright (C) 2019, 2020, 2021 Jose E. Marchesi */
4
5 /* This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include <config.h>
20
21 #include <stdlib.h>
22 #include <xalloc.h>
23 #include <string.h>
24 #include <assert.h>
25
26 #include "pkl.h"
27 #include "pk-utils.h"
28
29 #include "pkl-ast.h"
30 #include "pkl-env.h"
31
32 /* The declarations are organized in a hash table, chained in their
33 buckes through CHAIN2.
34
35 There are two namespaces in Poke:
36
37 - A main namespace, shared by types, variables and functions.
38 HASH_TABLE is used to store declarations for these entities.
39
40 - A separated namespace for offset units. UNITS_HASH_TABLE is used
41 to store declarations for these.
42
43 UP is a link to the immediately enclosing frame. This is NULL for
44 the top-level frame. */
45
46 #define HASH_TABLE_SIZE 1008
47 typedef pkl_ast_node pkl_hash[HASH_TABLE_SIZE];
48
49 struct pkl_env
50 {
51 pkl_hash hash_table;
52 pkl_hash units_hash_table;
53
54 int num_types;
55 int num_vars;
56 int num_units;
57
58 struct pkl_env *up;
59 };
60
61 /* The hash tables above are handled using the following
62 functions. */
63 #ifdef __clang__
64 __attribute__ ((no_sanitize ("integer")))
65 #endif
66 static int
hash_string(const char * name)67 hash_string (const char *name)
68 {
69 size_t len;
70 int hash;
71 int i;
72
73 len = strlen (name);
74 hash = len;
75 for (i = 0; i < len; i++)
76 hash = ((hash * (size_t)613) + (unsigned)(name[i]));
77
78 #define HASHBITS 30
79 hash &= (1 << HASHBITS) - 1;
80 hash %= HASH_TABLE_SIZE;
81 #undef HASHBITS
82
83 return hash;
84 }
85
86 static void
free_hash_table(pkl_hash hash_table)87 free_hash_table (pkl_hash hash_table)
88 {
89 size_t i;
90 pkl_ast_node t, n;
91
92 for (i = 0; i < HASH_TABLE_SIZE; ++i)
93 if (hash_table[i])
94 for (t = hash_table[i]; t; t = n)
95 {
96 n = PKL_AST_CHAIN2 (t);
97 pkl_ast_node_free (t);
98 }
99 }
100
101 static pkl_ast_node
get_registered(pkl_hash hash_table,const char * name)102 get_registered (pkl_hash hash_table, const char *name)
103 {
104 pkl_ast_node t;
105 int hash;
106
107 if (STREQ (name, ""))
108 return NULL;
109
110 hash = hash_string (name);
111 for (t = hash_table[hash]; t != NULL; t = PKL_AST_CHAIN2 (t))
112 {
113 pkl_ast_node t_name = PKL_AST_DECL_NAME (t);
114
115 if (STREQ (PKL_AST_IDENTIFIER_POINTER (t_name),
116 name))
117 return t;
118 }
119
120 return NULL;
121 }
122
123 static int
register_decl(int top_level_p,pkl_hash hash_table,const char * name,pkl_ast_node decl)124 register_decl (int top_level_p,
125 pkl_hash hash_table,
126 const char *name,
127 pkl_ast_node decl)
128 {
129 int hash;
130 pkl_ast_node found_decl;
131
132 /* Check if DECL is already registered in the given hash table.
133
134 If we are in the global environment and the declaration is for a
135 variable, funcion, or an unit, then we allow "redefining" by
136 changing the name of the previous declaration to "".
137
138 Otherwise we don't register DECL, as it is already defined. */
139
140 found_decl = get_registered (hash_table, name);
141 if (found_decl != NULL)
142 {
143 int decl_kind = PKL_AST_DECL_KIND (decl);
144
145 if (top_level_p
146 && (decl_kind == PKL_AST_DECL_KIND_VAR
147 || decl_kind == PKL_AST_DECL_KIND_FUNC
148 || decl_kind == PKL_AST_DECL_KIND_UNIT))
149 {
150 pkl_ast_node decl_name = PKL_AST_DECL_NAME (found_decl);
151
152 free (PKL_AST_IDENTIFIER_POINTER (decl_name));
153 PKL_AST_IDENTIFIER_POINTER (decl_name) = strdup ("");
154 }
155 else
156 return 0;
157 }
158
159 /* Add the declaration to the hash table. */
160 hash = hash_string (name);
161 PKL_AST_CHAIN2 (decl) = hash_table[hash];
162 hash_table[hash] = ASTREF (decl);
163
164 return 1;
165 }
166
167 static pkl_hash *
get_ns_table(pkl_env env,int namespace)168 get_ns_table (pkl_env env, int namespace)
169 {
170 pkl_hash *table = NULL;
171
172 switch (namespace)
173 {
174 case PKL_ENV_NS_MAIN:
175 table = &env->hash_table;
176 break;
177 case PKL_ENV_NS_UNITS:
178 table = &env->units_hash_table;
179 break;
180 default:
181 assert (0);
182 }
183
184 return table;
185 }
186
187 /* The following functions are documented in pkl-env.h. */
188
189 pkl_env
pkl_env_new()190 pkl_env_new ()
191 {
192 return xzalloc (sizeof (struct pkl_env));
193 }
194
195 void
pkl_env_free(pkl_env env)196 pkl_env_free (pkl_env env)
197 {
198 if (env)
199 {
200 pkl_env_free (env->up);
201 free_hash_table (env->hash_table);
202 free_hash_table (env->units_hash_table);
203 free (env);
204 }
205 }
206
207 pkl_env
pkl_env_push_frame(pkl_env env)208 pkl_env_push_frame (pkl_env env)
209 {
210 pkl_env frame = pkl_env_new ();
211
212 frame->up = env;
213 return frame;
214 }
215
216 pkl_env
pkl_env_pop_frame(pkl_env env)217 pkl_env_pop_frame (pkl_env env)
218 {
219 pkl_env up;
220
221 assert (env->up != NULL);
222
223 up = env->up;
224 env->up = NULL;
225 pkl_env_free (env);
226 return up;
227 }
228
229 int
pkl_env_register(pkl_env env,int namespace,const char * name,pkl_ast_node decl)230 pkl_env_register (pkl_env env,
231 int namespace,
232 const char *name,
233 pkl_ast_node decl)
234 {
235 pkl_hash *table = get_ns_table (env, namespace);
236
237 if (register_decl (env->up == NULL, *table, name, decl))
238 {
239 switch (PKL_AST_DECL_KIND (decl))
240 {
241 case PKL_AST_DECL_KIND_TYPE:
242 PKL_AST_DECL_ORDER (decl) = env->num_types++;
243 break;
244 case PKL_AST_DECL_KIND_VAR:
245 case PKL_AST_DECL_KIND_FUNC:
246 PKL_AST_DECL_ORDER (decl) = env->num_vars++;
247 break;
248 case PKL_AST_DECL_KIND_UNIT:
249 PKL_AST_DECL_ORDER (decl) = env->num_units++;
250 break;
251 default:
252 assert (0);
253 }
254 return 1;
255 }
256
257 return 0;
258 }
259
260 static pkl_ast_node
pkl_env_lookup_1(pkl_env env,int namespace,const char * name,int * back,int * over,int num_frame)261 pkl_env_lookup_1 (pkl_env env, int namespace, const char *name,
262 int *back, int *over, int num_frame)
263 {
264 if (env == NULL)
265 return NULL;
266 else
267 {
268 pkl_hash *table = get_ns_table (env, namespace);
269 pkl_ast_node decl = get_registered (*table, name);
270
271 if (decl)
272 {
273 if (back)
274 *back = num_frame;
275 if (over)
276 *over = PKL_AST_DECL_ORDER (decl);
277 return decl;
278 }
279 }
280
281 return pkl_env_lookup_1 (env->up, namespace, name, back, over,
282 num_frame + 1);
283 }
284
285 pkl_ast_node
pkl_env_lookup(pkl_env env,int namespace,const char * name,int * back,int * over)286 pkl_env_lookup (pkl_env env, int namespace, const char *name,
287 int *back, int *over)
288 {
289 return pkl_env_lookup_1 (env, namespace, name, back, over, 0);
290 }
291
292 int
pkl_env_toplevel_p(pkl_env env)293 pkl_env_toplevel_p (pkl_env env)
294 {
295 return env->up == NULL;
296 }
297
298 void
pkl_env_iter_begin(pkl_env env,struct pkl_ast_node_iter * iter)299 pkl_env_iter_begin (pkl_env env, struct pkl_ast_node_iter *iter)
300 {
301 iter->bucket = 0;
302 iter->node = env->hash_table[iter->bucket];
303 while (iter->node == NULL)
304 {
305 iter->bucket++;
306 if (iter->bucket >= HASH_TABLE_SIZE)
307 break;
308 iter->node = env->hash_table[iter->bucket];
309 }
310 }
311
312 void
pkl_env_iter_next(pkl_env env,struct pkl_ast_node_iter * iter)313 pkl_env_iter_next (pkl_env env, struct pkl_ast_node_iter *iter)
314 {
315 assert (iter->node != NULL);
316
317 iter->node = PKL_AST_CHAIN2 (iter->node);
318 while (iter->node == NULL)
319 {
320 iter->bucket++;
321 if (iter->bucket >= HASH_TABLE_SIZE)
322 break;
323 iter->node = env->hash_table[iter->bucket];
324 }
325 }
326
327 bool
pkl_env_iter_end(pkl_env env,const struct pkl_ast_node_iter * iter)328 pkl_env_iter_end (pkl_env env, const struct pkl_ast_node_iter *iter)
329 {
330 return iter->bucket >= HASH_TABLE_SIZE;
331 }
332
333 void
pkl_env_map_decls(pkl_env env,int what,pkl_map_decl_fn cb,void * data)334 pkl_env_map_decls (pkl_env env,
335 int what,
336 pkl_map_decl_fn cb,
337 void *data)
338 {
339 struct pkl_ast_node_iter iter;
340 for (pkl_env_iter_begin (env, &iter); !pkl_env_iter_end (env, &iter);
341 pkl_env_iter_next (env, &iter))
342 {
343 if ((what == PKL_AST_DECL_KIND_ANY
344 || what == PKL_AST_DECL_KIND (iter.node)))
345 cb (iter.node, data);
346 }
347 }
348
349 pkl_env
pkl_env_dup_toplevel(pkl_env env)350 pkl_env_dup_toplevel (pkl_env env)
351 {
352 pkl_env new;
353 int i;
354
355 assert (pkl_env_toplevel_p (env));
356
357 new = pkl_env_new ();
358
359 for (i = 0; i < HASH_TABLE_SIZE; ++i)
360 {
361 pkl_ast_node t;
362 pkl_ast_node decl = env->hash_table[i];
363
364 for (t = decl; t; t = PKL_AST_CHAIN2 (t))
365 t = ASTREF (t);
366 new->hash_table[i] = decl;
367 }
368
369 for (i = 0; i < HASH_TABLE_SIZE; ++i)
370 {
371 pkl_ast_node t;
372 pkl_ast_node decl = env->units_hash_table[i];
373
374 for (t = decl; t; t = PKL_AST_CHAIN2 (t))
375 t = ASTREF (t);
376 new->units_hash_table[i] = decl;
377 }
378
379 new->num_types = env->num_types;
380 new->num_vars = env->num_vars;
381 new->num_units = env->num_units;
382
383 return new;
384 }
385
386
387 /* Return the name of the next decl that is currently
388 in context of ENV and matches NAME,LEN. ITER is an iterator
389 into the set of matches. Returns the name of the next
390 command in the set, or NULL if there are no more.
391 The returned value must be freed by the caller. */
392 char *
pkl_env_get_next_matching_decl(pkl_env env,struct pkl_ast_node_iter * iter,const char * name,size_t len)393 pkl_env_get_next_matching_decl (pkl_env env, struct pkl_ast_node_iter *iter,
394 const char *name, size_t len)
395 {
396 /* "Normal" commands. */
397 for (;;)
398 {
399 if (pkl_env_iter_end (env, iter))
400 break;
401
402 pkl_ast_node decl_name = PKL_AST_DECL_NAME (iter->node);
403 const char *cmdname = PKL_AST_IDENTIFIER_POINTER (decl_name);
404 if (strncmp (cmdname, name, len) == 0)
405 return strdup (cmdname);
406
407 pkl_env_iter_next (env, iter);
408 }
409 return NULL;
410 }
411