1/*
2 * Copyright © 2008 Intel Corporation
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice (including the next
12 * paragraph) shall be included in all copies or substantial portions of the
13 * Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 */
23
24#include "main/imports.h"
25#include "symbol_table.h"
26#include "hash_table.h"
27
28struct symbol {
29    /**
30     * Link to the next symbol in the table with the same name
31     *
32     * The linked list of symbols with the same name is ordered by scope
33     * from inner-most to outer-most.
34     */
35    struct symbol *next_with_same_name;
36
37
38    /**
39     * Link to the next symbol in the table with the same scope
40     *
41     * The linked list of symbols with the same scope is unordered.  Symbols
42     * in this list my have unique names.
43     */
44    struct symbol *next_with_same_scope;
45
46
47    /**
48     * Header information for the list of symbols with the same name.
49     */
50    struct symbol_header *hdr;
51
52
53    /**
54     * Name space of the symbol
55     *
56     * Name space are arbitrary user assigned integers.  No two symbols can
57     * exist in the same name space at the same scope level.
58     */
59    int name_space;
60
61    /** Scope depth where this symbol was defined. */
62    unsigned depth;
63
64    /**
65     * Arbitrary user supplied data.
66     */
67    void *data;
68};
69
70
71/**
72 */
73struct symbol_header {
74    /** Linkage in list of all headers in a given symbol table. */
75    struct symbol_header *next;
76
77    /** Symbol name. */
78    char *name;
79
80    /** Linked list of symbols with the same name. */
81    struct symbol *symbols;
82};
83
84
85/**
86 * Element of the scope stack.
87 */
88struct scope_level {
89    /** Link to next (inner) scope level. */
90    struct scope_level *next;
91
92    /** Linked list of symbols with the same scope. */
93    struct symbol *symbols;
94};
95
96
97/**
98 *
99 */
100struct _mesa_symbol_table {
101    /** Hash table containing all symbols in the symbol table. */
102    struct hash_table *ht;
103
104    /** Top of scope stack. */
105    struct scope_level *current_scope;
106
107    /** List of all symbol headers in the table. */
108    struct symbol_header *hdr;
109
110    /** Current scope depth. */
111    unsigned depth;
112};
113
114
115struct _mesa_symbol_table_iterator {
116    /**
117     * Name space of symbols returned by this iterator.
118     */
119    int name_space;
120
121
122    /**
123     * Currently iterated symbol
124     *
125     * The next call to \c _mesa_symbol_table_iterator_get will return this
126     * value.  It will also update this value to the value that should be
127     * returned by the next call.
128     */
129    struct symbol *curr;
130};
131
132
133static void
134check_symbol_table(struct _mesa_symbol_table *table)
135{
136#if 1
137    struct scope_level *scope;
138
139    for (scope = table->current_scope; scope != NULL; scope = scope->next) {
140        struct symbol *sym;
141
142        for (sym = scope->symbols
143             ; sym != NULL
144             ; sym = sym->next_with_same_name) {
145            const struct symbol_header *const hdr = sym->hdr;
146            struct symbol *sym2;
147
148            for (sym2 = hdr->symbols
149                 ; sym2 != NULL
150                 ; sym2 = sym2->next_with_same_name) {
151                assert(sym2->hdr == hdr);
152            }
153        }
154    }
155#endif
156}
157
158void
159_mesa_symbol_table_pop_scope(struct _mesa_symbol_table *table)
160{
161    struct scope_level *const scope = table->current_scope;
162    struct symbol *sym = scope->symbols;
163
164    table->current_scope = scope->next;
165    table->depth--;
166
167    free(scope);
168
169    while (sym != NULL) {
170        struct symbol *const next = sym->next_with_same_scope;
171        struct symbol_header *const hdr = sym->hdr;
172
173        assert(hdr->symbols == sym);
174
175        hdr->symbols = sym->next_with_same_name;
176
177        free(sym);
178
179        sym = next;
180    }
181
182    check_symbol_table(table);
183}
184
185
186void
187_mesa_symbol_table_push_scope(struct _mesa_symbol_table *table)
188{
189    struct scope_level *const scope = calloc(1, sizeof(*scope));
190
191    scope->next = table->current_scope;
192    table->current_scope = scope;
193    table->depth++;
194}
195
196
197static struct symbol_header *
198find_symbol(struct _mesa_symbol_table *table, const char *name)
199{
200    return (struct symbol_header *) hash_table_find(table->ht, name);
201}
202
203
204struct _mesa_symbol_table_iterator *
205_mesa_symbol_table_iterator_ctor(struct _mesa_symbol_table *table,
206                                 int name_space, const char *name)
207{
208    struct _mesa_symbol_table_iterator *iter = calloc(1, sizeof(*iter));
209    struct symbol_header *const hdr = find_symbol(table, name);
210
211    iter->name_space = name_space;
212
213    if (hdr != NULL) {
214        struct symbol *sym;
215
216        for (sym = hdr->symbols; sym != NULL; sym = sym->next_with_same_name) {
217            assert(sym->hdr == hdr);
218
219            if ((name_space == -1) || (sym->name_space == name_space)) {
220                iter->curr = sym;
221                break;
222            }
223        }
224    }
225
226    return iter;
227}
228
229
230void
231_mesa_symbol_table_iterator_dtor(struct _mesa_symbol_table_iterator *iter)
232{
233    free(iter);
234}
235
236
237void *
238_mesa_symbol_table_iterator_get(struct _mesa_symbol_table_iterator *iter)
239{
240    return (iter->curr == NULL) ? NULL : iter->curr->data;
241}
242
243
244int
245_mesa_symbol_table_iterator_next(struct _mesa_symbol_table_iterator *iter)
246{
247    struct symbol_header *hdr;
248
249    if (iter->curr == NULL) {
250        return 0;
251    }
252
253    hdr = iter->curr->hdr;
254    iter->curr = iter->curr->next_with_same_name;
255
256    while (iter->curr != NULL) {
257        assert(iter->curr->hdr == hdr);
258
259        if ((iter->name_space == -1)
260            || (iter->curr->name_space == iter->name_space)) {
261            return 1;
262        }
263
264        iter->curr = iter->curr->next_with_same_name;
265    }
266
267    return 0;
268}
269
270
271/**
272 * Determine the scope "distance" of a symbol from the current scope
273 *
274 * \return
275 * A non-negative number for the number of scopes between the current scope
276 * and the scope where a symbol was defined.  A value of zero means the current
277 * scope.  A negative number if the symbol does not exist.
278 */
279int
280_mesa_symbol_table_symbol_scope(struct _mesa_symbol_table *table,
281				int name_space, const char *name)
282{
283    struct symbol_header *const hdr = find_symbol(table, name);
284    struct symbol *sym;
285
286    if (hdr != NULL) {
287       for (sym = hdr->symbols; sym != NULL; sym = sym->next_with_same_name) {
288	  assert(sym->hdr == hdr);
289
290	  if ((name_space == -1) || (sym->name_space == name_space)) {
291	     assert(sym->depth <= table->depth);
292	     return sym->depth - table->depth;
293	  }
294       }
295    }
296
297    return -1;
298}
299
300
301void *
302_mesa_symbol_table_find_symbol(struct _mesa_symbol_table *table,
303                               int name_space, const char *name)
304{
305    struct symbol_header *const hdr = find_symbol(table, name);
306
307    if (hdr != NULL) {
308        struct symbol *sym;
309
310
311        for (sym = hdr->symbols; sym != NULL; sym = sym->next_with_same_name) {
312            assert(sym->hdr == hdr);
313
314            if ((name_space == -1) || (sym->name_space == name_space)) {
315                return sym->data;
316            }
317        }
318    }
319
320    return NULL;
321}
322
323
324int
325_mesa_symbol_table_add_symbol(struct _mesa_symbol_table *table,
326                              int name_space, const char *name,
327                              void *declaration)
328{
329    struct symbol_header *hdr;
330    struct symbol *sym;
331
332    check_symbol_table(table);
333
334    hdr = find_symbol(table, name);
335
336    check_symbol_table(table);
337
338    if (hdr == NULL) {
339       hdr = calloc(1, sizeof(*hdr));
340       hdr->name = strdup(name);
341
342       hash_table_insert(table->ht, hdr, hdr->name);
343       hdr->next = table->hdr;
344       table->hdr = hdr;
345    }
346
347    check_symbol_table(table);
348
349    /* If the symbol already exists in this namespace at this scope, it cannot
350     * be added to the table.
351     */
352    for (sym = hdr->symbols
353	 ; (sym != NULL) && (sym->name_space != name_space)
354	 ; sym = sym->next_with_same_name) {
355       /* empty */
356    }
357
358    if (sym && (sym->depth == table->depth))
359       return -1;
360
361    sym = calloc(1, sizeof(*sym));
362    sym->next_with_same_name = hdr->symbols;
363    sym->next_with_same_scope = table->current_scope->symbols;
364    sym->hdr = hdr;
365    sym->name_space = name_space;
366    sym->data = declaration;
367    sym->depth = table->depth;
368
369    assert(sym->hdr == hdr);
370
371    hdr->symbols = sym;
372    table->current_scope->symbols = sym;
373
374    check_symbol_table(table);
375    return 0;
376}
377
378
379int
380_mesa_symbol_table_add_global_symbol(struct _mesa_symbol_table *table,
381				     int name_space, const char *name,
382				     void *declaration)
383{
384    struct symbol_header *hdr;
385    struct symbol *sym;
386    struct symbol *curr;
387    struct scope_level *top_scope;
388
389    check_symbol_table(table);
390
391    hdr = find_symbol(table, name);
392
393    check_symbol_table(table);
394
395    if (hdr == NULL) {
396        hdr = calloc(1, sizeof(*hdr));
397        hdr->name = strdup(name);
398
399        hash_table_insert(table->ht, hdr, hdr->name);
400        hdr->next = table->hdr;
401        table->hdr = hdr;
402    }
403
404    check_symbol_table(table);
405
406    /* If the symbol already exists in this namespace at this scope, it cannot
407     * be added to the table.
408     */
409    for (sym = hdr->symbols
410	 ; (sym != NULL) && (sym->name_space != name_space)
411	 ; sym = sym->next_with_same_name) {
412       /* empty */
413    }
414
415    if (sym && sym->depth == 0)
416       return -1;
417
418    /* Find the top-level scope */
419    for (top_scope = table->current_scope
420	 ; top_scope->next != NULL
421	 ; top_scope = top_scope->next) {
422       /* empty */
423    }
424
425    sym = calloc(1, sizeof(*sym));
426    sym->next_with_same_scope = top_scope->symbols;
427    sym->hdr = hdr;
428    sym->name_space = name_space;
429    sym->data = declaration;
430
431    assert(sym->hdr == hdr);
432
433    /* Since next_with_same_name is ordered by scope, we need to append the
434     * new symbol to the _end_ of the list.
435     */
436    if (hdr->symbols == NULL) {
437       hdr->symbols = sym;
438    } else {
439       for (curr = hdr->symbols
440	    ; curr->next_with_same_name != NULL
441	    ; curr = curr->next_with_same_name) {
442	  /* empty */
443       }
444       curr->next_with_same_name = sym;
445    }
446    top_scope->symbols = sym;
447
448    check_symbol_table(table);
449    return 0;
450}
451
452
453
454struct _mesa_symbol_table *
455_mesa_symbol_table_ctor(void)
456{
457    struct _mesa_symbol_table *table = calloc(1, sizeof(*table));
458
459    if (table != NULL) {
460       table->ht = hash_table_ctor(32, hash_table_string_hash,
461				   hash_table_string_compare);
462
463       _mesa_symbol_table_push_scope(table);
464    }
465
466    return table;
467}
468
469
470void
471_mesa_symbol_table_dtor(struct _mesa_symbol_table *table)
472{
473   struct symbol_header *hdr;
474   struct symbol_header *next;
475
476   while (table->current_scope != NULL) {
477      _mesa_symbol_table_pop_scope(table);
478   }
479
480   for (hdr = table->hdr; hdr != NULL; hdr = next) {
481       next = hdr->next;
482       free(hdr->name);
483       free(hdr);
484   }
485
486   hash_table_dtor(table->ht);
487   free(table);
488}
489