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        (void)hdr;
259
260        if ((iter->name_space == -1)
261            || (iter->curr->name_space == iter->name_space)) {
262            return 1;
263        }
264
265        iter->curr = iter->curr->next_with_same_name;
266    }
267
268    return 0;
269}
270
271
272/**
273 * Determine the scope "distance" of a symbol from the current scope
274 *
275 * \return
276 * A non-negative number for the number of scopes between the current scope
277 * and the scope where a symbol was defined.  A value of zero means the current
278 * scope.  A negative number if the symbol does not exist.
279 */
280int
281_mesa_symbol_table_symbol_scope(struct _mesa_symbol_table *table,
282				int name_space, const char *name)
283{
284    struct symbol_header *const hdr = find_symbol(table, name);
285    struct symbol *sym;
286
287    if (hdr != NULL) {
288       for (sym = hdr->symbols; sym != NULL; sym = sym->next_with_same_name) {
289	  assert(sym->hdr == hdr);
290
291	  if ((name_space == -1) || (sym->name_space == name_space)) {
292	     assert(sym->depth <= table->depth);
293	     return sym->depth - table->depth;
294	  }
295       }
296    }
297
298    return -1;
299}
300
301
302void *
303_mesa_symbol_table_find_symbol(struct _mesa_symbol_table *table,
304                               int name_space, const char *name)
305{
306    struct symbol_header *const hdr = find_symbol(table, name);
307
308    if (hdr != NULL) {
309        struct symbol *sym;
310
311
312        for (sym = hdr->symbols; sym != NULL; sym = sym->next_with_same_name) {
313            assert(sym->hdr == hdr);
314
315            if ((name_space == -1) || (sym->name_space == name_space)) {
316                return sym->data;
317            }
318        }
319    }
320
321    return NULL;
322}
323
324
325int
326_mesa_symbol_table_add_symbol(struct _mesa_symbol_table *table,
327                              int name_space, const char *name,
328                              void *declaration)
329{
330    struct symbol_header *hdr;
331    struct symbol *sym;
332
333    check_symbol_table(table);
334
335    hdr = find_symbol(table, name);
336
337    check_symbol_table(table);
338
339    if (hdr == NULL) {
340       hdr = calloc(1, sizeof(*hdr));
341       hdr->name = strdup(name);
342
343       hash_table_insert(table->ht, hdr, hdr->name);
344       hdr->next = table->hdr;
345       table->hdr = hdr;
346    }
347
348    check_symbol_table(table);
349
350    /* If the symbol already exists in this namespace at this scope, it cannot
351     * be added to the table.
352     */
353    for (sym = hdr->symbols
354	 ; (sym != NULL) && (sym->name_space != name_space)
355	 ; sym = sym->next_with_same_name) {
356       /* empty */
357    }
358
359    if (sym && (sym->depth == table->depth))
360       return -1;
361
362    sym = calloc(1, sizeof(*sym));
363    sym->next_with_same_name = hdr->symbols;
364    sym->next_with_same_scope = table->current_scope->symbols;
365    sym->hdr = hdr;
366    sym->name_space = name_space;
367    sym->data = declaration;
368    sym->depth = table->depth;
369
370    assert(sym->hdr == hdr);
371
372    hdr->symbols = sym;
373    table->current_scope->symbols = sym;
374
375    check_symbol_table(table);
376    return 0;
377}
378
379
380int
381_mesa_symbol_table_add_global_symbol(struct _mesa_symbol_table *table,
382				     int name_space, const char *name,
383				     void *declaration)
384{
385    struct symbol_header *hdr;
386    struct symbol *sym;
387    struct symbol *curr;
388    struct scope_level *top_scope;
389
390    check_symbol_table(table);
391
392    hdr = find_symbol(table, name);
393
394    check_symbol_table(table);
395
396    if (hdr == NULL) {
397        hdr = calloc(1, sizeof(*hdr));
398        hdr->name = strdup(name);
399
400        hash_table_insert(table->ht, hdr, hdr->name);
401        hdr->next = table->hdr;
402        table->hdr = hdr;
403    }
404
405    check_symbol_table(table);
406
407    /* If the symbol already exists in this namespace at this scope, it cannot
408     * be added to the table.
409     */
410    for (sym = hdr->symbols
411	 ; (sym != NULL) && (sym->name_space != name_space)
412	 ; sym = sym->next_with_same_name) {
413       /* empty */
414    }
415
416    if (sym && sym->depth == 0)
417       return -1;
418
419    /* Find the top-level scope */
420    for (top_scope = table->current_scope
421	 ; top_scope->next != NULL
422	 ; top_scope = top_scope->next) {
423       /* empty */
424    }
425
426    sym = calloc(1, sizeof(*sym));
427    sym->next_with_same_scope = top_scope->symbols;
428    sym->hdr = hdr;
429    sym->name_space = name_space;
430    sym->data = declaration;
431
432    assert(sym->hdr == hdr);
433
434    /* Since next_with_same_name is ordered by scope, we need to append the
435     * new symbol to the _end_ of the list.
436     */
437    if (hdr->symbols == NULL) {
438       hdr->symbols = sym;
439    } else {
440       for (curr = hdr->symbols
441	    ; curr->next_with_same_name != NULL
442	    ; curr = curr->next_with_same_name) {
443	  /* empty */
444       }
445       curr->next_with_same_name = sym;
446    }
447    top_scope->symbols = sym;
448
449    check_symbol_table(table);
450    return 0;
451}
452
453
454
455struct _mesa_symbol_table *
456_mesa_symbol_table_ctor(void)
457{
458    struct _mesa_symbol_table *table = calloc(1, sizeof(*table));
459
460    if (table != NULL) {
461       table->ht = hash_table_ctor(32, hash_table_string_hash,
462				   hash_table_string_compare);
463
464       _mesa_symbol_table_push_scope(table);
465    }
466
467    return table;
468}
469
470
471void
472_mesa_symbol_table_dtor(struct _mesa_symbol_table *table)
473{
474   struct symbol_header *hdr;
475   struct symbol_header *next;
476
477   while (table->current_scope != NULL) {
478      _mesa_symbol_table_pop_scope(table);
479   }
480
481   for (hdr = table->hdr; hdr != NULL; hdr = next) {
482       next = hdr->next;
483       free(hdr->name);
484       free(hdr);
485   }
486
487   hash_table_dtor(table->ht);
488   free(table);
489}
490