1bf215546Sopenharmony_ci/*
2bf215546Sopenharmony_ci * Copyright © 2008 Intel Corporation
3bf215546Sopenharmony_ci *
4bf215546Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a
5bf215546Sopenharmony_ci * copy of this software and associated documentation files (the "Software"),
6bf215546Sopenharmony_ci * to deal in the Software without restriction, including without limitation
7bf215546Sopenharmony_ci * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8bf215546Sopenharmony_ci * and/or sell copies of the Software, and to permit persons to whom the
9bf215546Sopenharmony_ci * Software is furnished to do so, subject to the following conditions:
10bf215546Sopenharmony_ci *
11bf215546Sopenharmony_ci * The above copyright notice and this permission notice (including the next
12bf215546Sopenharmony_ci * paragraph) shall be included in all copies or substantial portions of the
13bf215546Sopenharmony_ci * Software.
14bf215546Sopenharmony_ci *
15bf215546Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16bf215546Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17bf215546Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
18bf215546Sopenharmony_ci * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19bf215546Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20bf215546Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21bf215546Sopenharmony_ci * DEALINGS IN THE SOFTWARE.
22bf215546Sopenharmony_ci */
23bf215546Sopenharmony_ci
24bf215546Sopenharmony_ci
25bf215546Sopenharmony_ci#include "main/errors.h"
26bf215546Sopenharmony_ci#include "symbol_table.h"
27bf215546Sopenharmony_ci#include "util/hash_table.h"
28bf215546Sopenharmony_ci#include "util/u_string.h"
29bf215546Sopenharmony_ci
30bf215546Sopenharmony_cistruct symbol {
31bf215546Sopenharmony_ci   /** Symbol name. */
32bf215546Sopenharmony_ci   char *name;
33bf215546Sopenharmony_ci
34bf215546Sopenharmony_ci    /**
35bf215546Sopenharmony_ci     * Link to the next symbol in the table with the same name
36bf215546Sopenharmony_ci     *
37bf215546Sopenharmony_ci     * The linked list of symbols with the same name is ordered by scope
38bf215546Sopenharmony_ci     * from inner-most to outer-most.
39bf215546Sopenharmony_ci     */
40bf215546Sopenharmony_ci    struct symbol *next_with_same_name;
41bf215546Sopenharmony_ci
42bf215546Sopenharmony_ci    /**
43bf215546Sopenharmony_ci     * Link to the next symbol in the table with the same scope
44bf215546Sopenharmony_ci     *
45bf215546Sopenharmony_ci     * The linked list of symbols with the same scope is unordered.  Symbols
46bf215546Sopenharmony_ci     * in this list my have unique names.
47bf215546Sopenharmony_ci     */
48bf215546Sopenharmony_ci    struct symbol *next_with_same_scope;
49bf215546Sopenharmony_ci
50bf215546Sopenharmony_ci    /** Scope depth where this symbol was defined. */
51bf215546Sopenharmony_ci    unsigned depth;
52bf215546Sopenharmony_ci
53bf215546Sopenharmony_ci    /**
54bf215546Sopenharmony_ci     * Arbitrary user supplied data.
55bf215546Sopenharmony_ci     */
56bf215546Sopenharmony_ci    void *data;
57bf215546Sopenharmony_ci};
58bf215546Sopenharmony_ci
59bf215546Sopenharmony_ci
60bf215546Sopenharmony_ci/**
61bf215546Sopenharmony_ci * Element of the scope stack.
62bf215546Sopenharmony_ci */
63bf215546Sopenharmony_cistruct scope_level {
64bf215546Sopenharmony_ci    /** Link to next (inner) scope level. */
65bf215546Sopenharmony_ci    struct scope_level *next;
66bf215546Sopenharmony_ci
67bf215546Sopenharmony_ci    /** Linked list of symbols with the same scope. */
68bf215546Sopenharmony_ci    struct symbol *symbols;
69bf215546Sopenharmony_ci};
70bf215546Sopenharmony_ci
71bf215546Sopenharmony_ci
72bf215546Sopenharmony_ci/**
73bf215546Sopenharmony_ci *
74bf215546Sopenharmony_ci */
75bf215546Sopenharmony_cistruct _mesa_symbol_table {
76bf215546Sopenharmony_ci    /** Hash table containing all symbols in the symbol table. */
77bf215546Sopenharmony_ci    struct hash_table *ht;
78bf215546Sopenharmony_ci
79bf215546Sopenharmony_ci    /** Top of scope stack. */
80bf215546Sopenharmony_ci    struct scope_level *current_scope;
81bf215546Sopenharmony_ci
82bf215546Sopenharmony_ci    /** Current scope depth. */
83bf215546Sopenharmony_ci    unsigned depth;
84bf215546Sopenharmony_ci};
85bf215546Sopenharmony_ci
86bf215546Sopenharmony_civoid
87bf215546Sopenharmony_ci_mesa_symbol_table_pop_scope(struct _mesa_symbol_table *table)
88bf215546Sopenharmony_ci{
89bf215546Sopenharmony_ci    struct scope_level *const scope = table->current_scope;
90bf215546Sopenharmony_ci    struct symbol *sym = scope->symbols;
91bf215546Sopenharmony_ci
92bf215546Sopenharmony_ci    table->current_scope = scope->next;
93bf215546Sopenharmony_ci    table->depth--;
94bf215546Sopenharmony_ci
95bf215546Sopenharmony_ci    free(scope);
96bf215546Sopenharmony_ci
97bf215546Sopenharmony_ci    while (sym != NULL) {
98bf215546Sopenharmony_ci        struct symbol *const next = sym->next_with_same_scope;
99bf215546Sopenharmony_ci        struct hash_entry *hte = _mesa_hash_table_search(table->ht,
100bf215546Sopenharmony_ci                                                         sym->name);
101bf215546Sopenharmony_ci        if (sym->next_with_same_name) {
102bf215546Sopenharmony_ci           /* If there is a symbol with this name in an outer scope update
103bf215546Sopenharmony_ci            * the hash table to point to it.
104bf215546Sopenharmony_ci            */
105bf215546Sopenharmony_ci           hte->key = sym->next_with_same_name->name;
106bf215546Sopenharmony_ci           hte->data = sym->next_with_same_name;
107bf215546Sopenharmony_ci        } else {
108bf215546Sopenharmony_ci           _mesa_hash_table_remove(table->ht, hte);
109bf215546Sopenharmony_ci           free(sym->name);
110bf215546Sopenharmony_ci        }
111bf215546Sopenharmony_ci
112bf215546Sopenharmony_ci        free(sym);
113bf215546Sopenharmony_ci        sym = next;
114bf215546Sopenharmony_ci    }
115bf215546Sopenharmony_ci}
116bf215546Sopenharmony_ci
117bf215546Sopenharmony_ci
118bf215546Sopenharmony_civoid
119bf215546Sopenharmony_ci_mesa_symbol_table_push_scope(struct _mesa_symbol_table *table)
120bf215546Sopenharmony_ci{
121bf215546Sopenharmony_ci    struct scope_level *const scope = calloc(1, sizeof(*scope));
122bf215546Sopenharmony_ci    if (scope == NULL) {
123bf215546Sopenharmony_ci       _mesa_error_no_memory(__func__);
124bf215546Sopenharmony_ci       return;
125bf215546Sopenharmony_ci    }
126bf215546Sopenharmony_ci
127bf215546Sopenharmony_ci    scope->next = table->current_scope;
128bf215546Sopenharmony_ci    table->current_scope = scope;
129bf215546Sopenharmony_ci    table->depth++;
130bf215546Sopenharmony_ci}
131bf215546Sopenharmony_ci
132bf215546Sopenharmony_ci
133bf215546Sopenharmony_cistatic struct symbol *
134bf215546Sopenharmony_cifind_symbol(struct _mesa_symbol_table *table, const char *name)
135bf215546Sopenharmony_ci{
136bf215546Sopenharmony_ci   struct hash_entry *entry = _mesa_hash_table_search(table->ht, name);
137bf215546Sopenharmony_ci   return entry ? (struct symbol *) entry->data : NULL;
138bf215546Sopenharmony_ci}
139bf215546Sopenharmony_ci
140bf215546Sopenharmony_ci
141bf215546Sopenharmony_ci/**
142bf215546Sopenharmony_ci * Determine the scope "distance" of a symbol from the current scope
143bf215546Sopenharmony_ci *
144bf215546Sopenharmony_ci * \return
145bf215546Sopenharmony_ci * A non-negative number for the number of scopes between the current scope
146bf215546Sopenharmony_ci * and the scope where a symbol was defined.  A value of zero means the current
147bf215546Sopenharmony_ci * scope.  A negative number if the symbol does not exist.
148bf215546Sopenharmony_ci */
149bf215546Sopenharmony_ciint
150bf215546Sopenharmony_ci_mesa_symbol_table_symbol_scope(struct _mesa_symbol_table *table,
151bf215546Sopenharmony_ci                                const char *name)
152bf215546Sopenharmony_ci{
153bf215546Sopenharmony_ci   struct symbol *const sym = find_symbol(table, name);
154bf215546Sopenharmony_ci
155bf215546Sopenharmony_ci   if (sym) {
156bf215546Sopenharmony_ci      assert(sym->depth <= table->depth);
157bf215546Sopenharmony_ci      return table->depth - sym->depth;
158bf215546Sopenharmony_ci   }
159bf215546Sopenharmony_ci
160bf215546Sopenharmony_ci   return -1;
161bf215546Sopenharmony_ci}
162bf215546Sopenharmony_ci
163bf215546Sopenharmony_ci
164bf215546Sopenharmony_civoid *
165bf215546Sopenharmony_ci_mesa_symbol_table_find_symbol(struct _mesa_symbol_table *table,
166bf215546Sopenharmony_ci                               const char *name)
167bf215546Sopenharmony_ci{
168bf215546Sopenharmony_ci   struct symbol *const sym = find_symbol(table, name);
169bf215546Sopenharmony_ci   if (sym)
170bf215546Sopenharmony_ci      return sym->data;
171bf215546Sopenharmony_ci
172bf215546Sopenharmony_ci   return NULL;
173bf215546Sopenharmony_ci}
174bf215546Sopenharmony_ci
175bf215546Sopenharmony_ci
176bf215546Sopenharmony_ciint
177bf215546Sopenharmony_ci_mesa_symbol_table_add_symbol(struct _mesa_symbol_table *table,
178bf215546Sopenharmony_ci                              const char *name, void *declaration)
179bf215546Sopenharmony_ci{
180bf215546Sopenharmony_ci   struct symbol *new_sym;
181bf215546Sopenharmony_ci   struct symbol *sym = find_symbol(table, name);
182bf215546Sopenharmony_ci
183bf215546Sopenharmony_ci   if (sym && sym->depth == table->depth)
184bf215546Sopenharmony_ci      return -1;
185bf215546Sopenharmony_ci
186bf215546Sopenharmony_ci   new_sym = calloc(1, sizeof(*sym));
187bf215546Sopenharmony_ci   if (new_sym == NULL) {
188bf215546Sopenharmony_ci      _mesa_error_no_memory(__func__);
189bf215546Sopenharmony_ci      return -1;
190bf215546Sopenharmony_ci   }
191bf215546Sopenharmony_ci
192bf215546Sopenharmony_ci   if (sym) {
193bf215546Sopenharmony_ci      /* Store link to symbol in outer scope with the same name */
194bf215546Sopenharmony_ci      new_sym->next_with_same_name = sym;
195bf215546Sopenharmony_ci      new_sym->name = sym->name;
196bf215546Sopenharmony_ci   } else {
197bf215546Sopenharmony_ci      new_sym->name = strdup(name);
198bf215546Sopenharmony_ci      if (new_sym->name == NULL) {
199bf215546Sopenharmony_ci         free(new_sym);
200bf215546Sopenharmony_ci         _mesa_error_no_memory(__func__);
201bf215546Sopenharmony_ci         return -1;
202bf215546Sopenharmony_ci      }
203bf215546Sopenharmony_ci   }
204bf215546Sopenharmony_ci
205bf215546Sopenharmony_ci   new_sym->next_with_same_scope = table->current_scope->symbols;
206bf215546Sopenharmony_ci   new_sym->data = declaration;
207bf215546Sopenharmony_ci   new_sym->depth = table->depth;
208bf215546Sopenharmony_ci
209bf215546Sopenharmony_ci   table->current_scope->symbols = new_sym;
210bf215546Sopenharmony_ci
211bf215546Sopenharmony_ci   _mesa_hash_table_insert(table->ht, new_sym->name, new_sym);
212bf215546Sopenharmony_ci
213bf215546Sopenharmony_ci   return 0;
214bf215546Sopenharmony_ci}
215bf215546Sopenharmony_ci
216bf215546Sopenharmony_ciint
217bf215546Sopenharmony_ci_mesa_symbol_table_replace_symbol(struct _mesa_symbol_table *table,
218bf215546Sopenharmony_ci                                  const char *name,
219bf215546Sopenharmony_ci                                  void *declaration)
220bf215546Sopenharmony_ci{
221bf215546Sopenharmony_ci    struct symbol *sym = find_symbol(table, name);
222bf215546Sopenharmony_ci
223bf215546Sopenharmony_ci    /* If the symbol doesn't exist, it cannot be replaced. */
224bf215546Sopenharmony_ci    if (sym == NULL)
225bf215546Sopenharmony_ci       return -1;
226bf215546Sopenharmony_ci
227bf215546Sopenharmony_ci    sym->data = declaration;
228bf215546Sopenharmony_ci    return 0;
229bf215546Sopenharmony_ci}
230bf215546Sopenharmony_ci
231bf215546Sopenharmony_ciint
232bf215546Sopenharmony_ci_mesa_symbol_table_add_global_symbol(struct _mesa_symbol_table *table,
233bf215546Sopenharmony_ci                                     const char *name, void *declaration)
234bf215546Sopenharmony_ci{
235bf215546Sopenharmony_ci   struct scope_level *top_scope;
236bf215546Sopenharmony_ci   struct symbol *inner_sym = NULL;
237bf215546Sopenharmony_ci   struct symbol *sym = find_symbol(table, name);
238bf215546Sopenharmony_ci
239bf215546Sopenharmony_ci   while (sym) {
240bf215546Sopenharmony_ci      if (sym->depth == 0)
241bf215546Sopenharmony_ci         return -1;
242bf215546Sopenharmony_ci
243bf215546Sopenharmony_ci      inner_sym = sym;
244bf215546Sopenharmony_ci
245bf215546Sopenharmony_ci      /* Get symbol from the outer scope with the same name */
246bf215546Sopenharmony_ci      sym = sym->next_with_same_name;
247bf215546Sopenharmony_ci   }
248bf215546Sopenharmony_ci
249bf215546Sopenharmony_ci   /* Find the top-level scope */
250bf215546Sopenharmony_ci   for (top_scope = table->current_scope; top_scope->next != NULL;
251bf215546Sopenharmony_ci        top_scope = top_scope->next) {
252bf215546Sopenharmony_ci      /* empty */
253bf215546Sopenharmony_ci   }
254bf215546Sopenharmony_ci
255bf215546Sopenharmony_ci   sym = calloc(1, sizeof(*sym));
256bf215546Sopenharmony_ci   if (sym == NULL) {
257bf215546Sopenharmony_ci      _mesa_error_no_memory(__func__);
258bf215546Sopenharmony_ci      return -1;
259bf215546Sopenharmony_ci   }
260bf215546Sopenharmony_ci
261bf215546Sopenharmony_ci   if (inner_sym) {
262bf215546Sopenharmony_ci      /* In case we add the global out of order store a link to the global
263bf215546Sopenharmony_ci       * symbol in global.
264bf215546Sopenharmony_ci       */
265bf215546Sopenharmony_ci      inner_sym->next_with_same_name = sym;
266bf215546Sopenharmony_ci
267bf215546Sopenharmony_ci      sym->name = inner_sym->name;
268bf215546Sopenharmony_ci   } else {
269bf215546Sopenharmony_ci      sym->name = strdup(name);
270bf215546Sopenharmony_ci      if (sym->name == NULL) {
271bf215546Sopenharmony_ci         free(sym);
272bf215546Sopenharmony_ci         _mesa_error_no_memory(__func__);
273bf215546Sopenharmony_ci         return -1;
274bf215546Sopenharmony_ci      }
275bf215546Sopenharmony_ci   }
276bf215546Sopenharmony_ci
277bf215546Sopenharmony_ci   sym->next_with_same_scope = top_scope->symbols;
278bf215546Sopenharmony_ci   sym->data = declaration;
279bf215546Sopenharmony_ci
280bf215546Sopenharmony_ci   top_scope->symbols = sym;
281bf215546Sopenharmony_ci
282bf215546Sopenharmony_ci   _mesa_hash_table_insert(table->ht, sym->name, sym);
283bf215546Sopenharmony_ci
284bf215546Sopenharmony_ci   return 0;
285bf215546Sopenharmony_ci}
286bf215546Sopenharmony_ci
287bf215546Sopenharmony_ci
288bf215546Sopenharmony_ci
289bf215546Sopenharmony_cistruct _mesa_symbol_table *
290bf215546Sopenharmony_ci_mesa_symbol_table_ctor(void)
291bf215546Sopenharmony_ci{
292bf215546Sopenharmony_ci    struct _mesa_symbol_table *table = calloc(1, sizeof(*table));
293bf215546Sopenharmony_ci
294bf215546Sopenharmony_ci    if (table != NULL) {
295bf215546Sopenharmony_ci       table->ht = _mesa_hash_table_create(NULL, _mesa_hash_string,
296bf215546Sopenharmony_ci                                           _mesa_key_string_equal);
297bf215546Sopenharmony_ci
298bf215546Sopenharmony_ci       _mesa_symbol_table_push_scope(table);
299bf215546Sopenharmony_ci    }
300bf215546Sopenharmony_ci
301bf215546Sopenharmony_ci    return table;
302bf215546Sopenharmony_ci}
303bf215546Sopenharmony_ci
304bf215546Sopenharmony_ci
305bf215546Sopenharmony_civoid
306bf215546Sopenharmony_ci_mesa_symbol_table_dtor(struct _mesa_symbol_table *table)
307bf215546Sopenharmony_ci{
308bf215546Sopenharmony_ci   while (table->current_scope != NULL) {
309bf215546Sopenharmony_ci      _mesa_symbol_table_pop_scope(table);
310bf215546Sopenharmony_ci   }
311bf215546Sopenharmony_ci
312bf215546Sopenharmony_ci   _mesa_hash_table_destroy(table->ht, NULL);
313bf215546Sopenharmony_ci   free(table);
314bf215546Sopenharmony_ci}
315