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