symbol_table.c revision af69d88d
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
115static void
116check_symbol_table(struct _mesa_symbol_table *table)
117{
118#if !defined(NDEBUG)
119    struct scope_level *scope;
120
121    for (scope = table->current_scope; scope != NULL; scope = scope->next) {
122        struct symbol *sym;
123
124        for (sym = scope->symbols
125             ; sym != NULL
126             ; sym = sym->next_with_same_name) {
127            const struct symbol_header *const hdr = sym->hdr;
128            struct symbol *sym2;
129
130            for (sym2 = hdr->symbols
131                 ; sym2 != NULL
132                 ; sym2 = sym2->next_with_same_name) {
133                assert(sym2->hdr == hdr);
134            }
135        }
136    }
137#else
138    (void) table;
139#endif /* !defined(NDEBUG) */
140}
141
142void
143_mesa_symbol_table_pop_scope(struct _mesa_symbol_table *table)
144{
145    struct scope_level *const scope = table->current_scope;
146    struct symbol *sym = scope->symbols;
147
148    table->current_scope = scope->next;
149    table->depth--;
150
151    free(scope);
152
153    while (sym != NULL) {
154        struct symbol *const next = sym->next_with_same_scope;
155        struct symbol_header *const hdr = sym->hdr;
156
157        assert(hdr->symbols == sym);
158
159        hdr->symbols = sym->next_with_same_name;
160
161        free(sym);
162
163        sym = next;
164    }
165
166    check_symbol_table(table);
167}
168
169
170void
171_mesa_symbol_table_push_scope(struct _mesa_symbol_table *table)
172{
173    struct scope_level *const scope = calloc(1, sizeof(*scope));
174
175    scope->next = table->current_scope;
176    table->current_scope = scope;
177    table->depth++;
178}
179
180
181static struct symbol_header *
182find_symbol(struct _mesa_symbol_table *table, const char *name)
183{
184    return (struct symbol_header *) hash_table_find(table->ht, name);
185}
186
187
188/**
189 * Determine the scope "distance" of a symbol from the current scope
190 *
191 * \return
192 * A non-negative number for the number of scopes between the current scope
193 * and the scope where a symbol was defined.  A value of zero means the current
194 * scope.  A negative number if the symbol does not exist.
195 */
196int
197_mesa_symbol_table_symbol_scope(struct _mesa_symbol_table *table,
198				int name_space, const char *name)
199{
200    struct symbol_header *const hdr = find_symbol(table, name);
201    struct symbol *sym;
202
203    if (hdr != NULL) {
204       for (sym = hdr->symbols; sym != NULL; sym = sym->next_with_same_name) {
205	  assert(sym->hdr == hdr);
206
207	  if ((name_space == -1) || (sym->name_space == name_space)) {
208	     assert(sym->depth <= table->depth);
209	     return sym->depth - table->depth;
210	  }
211       }
212    }
213
214    return -1;
215}
216
217
218void *
219_mesa_symbol_table_find_symbol(struct _mesa_symbol_table *table,
220                               int name_space, const char *name)
221{
222    struct symbol_header *const hdr = find_symbol(table, name);
223
224    if (hdr != NULL) {
225        struct symbol *sym;
226
227
228        for (sym = hdr->symbols; sym != NULL; sym = sym->next_with_same_name) {
229            assert(sym->hdr == hdr);
230
231            if ((name_space == -1) || (sym->name_space == name_space)) {
232                return sym->data;
233            }
234        }
235    }
236
237    return NULL;
238}
239
240
241int
242_mesa_symbol_table_add_symbol(struct _mesa_symbol_table *table,
243                              int name_space, const char *name,
244                              void *declaration)
245{
246    struct symbol_header *hdr;
247    struct symbol *sym;
248
249    check_symbol_table(table);
250
251    hdr = find_symbol(table, name);
252
253    check_symbol_table(table);
254
255    if (hdr == NULL) {
256       hdr = calloc(1, sizeof(*hdr));
257       hdr->name = strdup(name);
258
259       hash_table_insert(table->ht, hdr, hdr->name);
260       hdr->next = table->hdr;
261       table->hdr = hdr;
262    }
263
264    check_symbol_table(table);
265
266    /* If the symbol already exists in this namespace at this scope, it cannot
267     * be added to the table.
268     */
269    for (sym = hdr->symbols
270	 ; (sym != NULL) && (sym->name_space != name_space)
271	 ; sym = sym->next_with_same_name) {
272       /* empty */
273    }
274
275    if (sym && (sym->depth == table->depth))
276       return -1;
277
278    sym = calloc(1, sizeof(*sym));
279    sym->next_with_same_name = hdr->symbols;
280    sym->next_with_same_scope = table->current_scope->symbols;
281    sym->hdr = hdr;
282    sym->name_space = name_space;
283    sym->data = declaration;
284    sym->depth = table->depth;
285
286    assert(sym->hdr == hdr);
287
288    hdr->symbols = sym;
289    table->current_scope->symbols = sym;
290
291    check_symbol_table(table);
292    return 0;
293}
294
295
296int
297_mesa_symbol_table_add_global_symbol(struct _mesa_symbol_table *table,
298				     int name_space, const char *name,
299				     void *declaration)
300{
301    struct symbol_header *hdr;
302    struct symbol *sym;
303    struct symbol *curr;
304    struct scope_level *top_scope;
305
306    check_symbol_table(table);
307
308    hdr = find_symbol(table, name);
309
310    check_symbol_table(table);
311
312    if (hdr == NULL) {
313        hdr = calloc(1, sizeof(*hdr));
314        hdr->name = strdup(name);
315
316        hash_table_insert(table->ht, hdr, hdr->name);
317        hdr->next = table->hdr;
318        table->hdr = hdr;
319    }
320
321    check_symbol_table(table);
322
323    /* If the symbol already exists in this namespace at this scope, it cannot
324     * be added to the table.
325     */
326    for (sym = hdr->symbols
327	 ; (sym != NULL) && (sym->name_space != name_space)
328	 ; sym = sym->next_with_same_name) {
329       /* empty */
330    }
331
332    if (sym && sym->depth == 0)
333       return -1;
334
335    /* Find the top-level scope */
336    for (top_scope = table->current_scope
337	 ; top_scope->next != NULL
338	 ; top_scope = top_scope->next) {
339       /* empty */
340    }
341
342    sym = calloc(1, sizeof(*sym));
343    sym->next_with_same_scope = top_scope->symbols;
344    sym->hdr = hdr;
345    sym->name_space = name_space;
346    sym->data = declaration;
347
348    assert(sym->hdr == hdr);
349
350    /* Since next_with_same_name is ordered by scope, we need to append the
351     * new symbol to the _end_ of the list.
352     */
353    if (hdr->symbols == NULL) {
354       hdr->symbols = sym;
355    } else {
356       for (curr = hdr->symbols
357	    ; curr->next_with_same_name != NULL
358	    ; curr = curr->next_with_same_name) {
359	  /* empty */
360       }
361       curr->next_with_same_name = sym;
362    }
363    top_scope->symbols = sym;
364
365    check_symbol_table(table);
366    return 0;
367}
368
369
370
371struct _mesa_symbol_table *
372_mesa_symbol_table_ctor(void)
373{
374    struct _mesa_symbol_table *table = calloc(1, sizeof(*table));
375
376    if (table != NULL) {
377       table->ht = hash_table_ctor(32, hash_table_string_hash,
378				   hash_table_string_compare);
379
380       _mesa_symbol_table_push_scope(table);
381    }
382
383    return table;
384}
385
386
387void
388_mesa_symbol_table_dtor(struct _mesa_symbol_table *table)
389{
390   struct symbol_header *hdr;
391   struct symbol_header *next;
392
393   while (table->current_scope != NULL) {
394      _mesa_symbol_table_pop_scope(table);
395   }
396
397   for (hdr = table->hdr; hdr != NULL; hdr = next) {
398       next = hdr->next;
399       free(hdr->name);
400       free(hdr);
401   }
402
403   hash_table_dtor(table->ht);
404   free(table);
405}
406