+#include "symtab.h"
+
+/*------------------------.
+| Distinguished symbols. |
+`------------------------*/
+
+symbol *errtoken = NULL;
+symbol *undeftoken = NULL;
+symbol *endtoken = NULL;
+symbol *accept = NULL;
+symbol *startsymbol = NULL;
+location startsymbol_location;
+
+/*-----------------------------------.
+| Default %destructor and %printer. |
+`-----------------------------------*/
+
+static const char *default_destructor = NULL;
+static location default_destructor_location;
+static const char *default_printer = NULL;
+static location default_printer_location;
+
+/*---------------------------------.
+| Create a new symbol, named TAG. |
+`---------------------------------*/
+
+static symbol *
+symbol_new (uniqstr tag, location loc)
+{
+ symbol *res = xmalloc (sizeof *res);
+
+ uniqstr_assert (tag);
+ res->tag = tag;
+ res->location = loc;
+
+ res->type_name = NULL;
+ res->destructor = NULL;
+ res->printer = NULL;
+
+ res->number = NUMBER_UNDEFINED;
+ res->prec = 0;
+ res->assoc = undef_assoc;
+ res->user_token_number = USER_NUMBER_UNDEFINED;
+
+ res->alias = NULL;
+ res->class = unknown_sym;
+ res->declared = false;
+
+ if (nsyms == SYMBOL_NUMBER_MAXIMUM)
+ fatal (_("too many symbols in input grammar (limit is %d)"),
+ SYMBOL_NUMBER_MAXIMUM);
+ nsyms++;
+ return res;
+}
+
+/*----------------------------------------.
+| Create a new semantic type, named TAG. |
+`----------------------------------------*/
+
+static semantic_type *
+semantic_type_new (uniqstr tag)
+{
+ semantic_type *res = xmalloc (sizeof *res);
+
+ uniqstr_assert (tag);
+ res->tag = tag;
+ res->destructor = NULL;
+ res->printer = NULL;
+
+ return res;
+}
+
+
+/*-----------------.
+| Print a symbol. |
+`-----------------*/
+
+#define SYMBOL_ATTR_PRINT(Attr) \
+ if (s->Attr) \
+ fprintf (f, " %s { %s }", #Attr, s->Attr)
+
+void
+symbol_print (symbol *s, FILE *f)
+{
+ if (s)
+ {
+ fprintf (f, "\"%s\"", s->tag);
+ SYMBOL_ATTR_PRINT (type_name);
+ SYMBOL_ATTR_PRINT (destructor);
+ SYMBOL_ATTR_PRINT (printer);
+ }
+ else
+ fprintf (f, "<NULL>");
+}
+
+#undef SYMBOL_ATTR_PRINT
+
+/*------------------------------------------------------------------.
+| Complain that S's WHAT is redeclared at SECOND, and was first set |
+| at FIRST. |
+`------------------------------------------------------------------*/
+
+static void
+symbol_redeclaration (symbol *s, const char *what, location first,
+ location second)
+{
+ complain_at (second, _("%s redeclaration for %s"), what, s->tag);
+ complain_at (first, _("previous declaration"));
+}
+
+static void
+semantic_type_redeclaration (semantic_type *s, const char *what, location first,
+ location second)
+{
+ complain_at (second, _("%s redeclaration for <%s>"), what, s->tag);
+ complain_at (first, _("previous declaration"));
+}
+
+
+/*-----------------------------------------------------------------.
+| Set the TYPE_NAME associated with SYM. Does nothing if passed 0 |
+| as TYPE_NAME. |
+`-----------------------------------------------------------------*/
+
+void
+symbol_type_set (symbol *sym, uniqstr type_name, location loc)
+{
+ if (type_name)
+ {
+ if (sym->type_name)
+ symbol_redeclaration (sym, "%type", sym->type_location, loc);
+ uniqstr_assert (type_name);
+ sym->type_name = type_name;
+ sym->type_location = loc;
+ }
+}
+
+
+/*------------------------------------------------------------------.
+| Set the DESTRUCTOR associated with SYM. Do nothing if passed 0. |
+`------------------------------------------------------------------*/
+
+void
+symbol_destructor_set (symbol *sym, const char *destructor, location loc)
+{
+ if (destructor)
+ {
+ if (sym->destructor)
+ symbol_redeclaration (sym, "%destructor", sym->destructor_location,
+ loc);
+ sym->destructor = destructor;
+ sym->destructor_location = loc;
+ }
+}
+
+/*-------------------------------------------------------------------.
+| Set the DESTRUCTOR associated with TYPE. Do nothing if passed 0. |
+`-------------------------------------------------------------------*/
+
+void
+semantic_type_destructor_set (semantic_type *type, const char *destructor,
+ location loc)
+{
+ if (destructor)
+ {
+ if (type->destructor)
+ semantic_type_redeclaration (type, "%destructor",
+ type->destructor_location, loc);
+ type->destructor = destructor;
+ type->destructor_location = loc;
+ }
+}
+
+/*---------------------------------------.
+| Get the computed %destructor for SYM. |
+`---------------------------------------*/
+
+const char *
+symbol_destructor_get (symbol *sym)
+{
+ /* Per-symbol %destructor. */
+ if (sym->destructor != NULL)
+ return sym->destructor;
+
+ /* Per-type %destructor. */
+ if (sym->type_name)
+ {
+ semantic_type *type = semantic_type_get (sym->type_name);
+ if (type->destructor)
+ return type->destructor;
+ }
+
+ /* Apply the default %destructor only to user-defined symbols. */
+ if (sym->tag[0] == '$' || sym == errtoken)
+ return NULL;
+ return default_destructor;
+}
+
+/*---------------------------------------------------------------.
+| Get the grammar location of the %destructor computed for SYM. |
+`---------------------------------------------------------------*/
+
+location
+symbol_destructor_location_get (symbol *sym)
+{
+ if (sym->destructor != NULL)
+ return sym->destructor_location;
+ if (sym->type_name)
+ {
+ semantic_type *type = semantic_type_get (sym->type_name);
+ if (type->destructor)
+ return type->destructor_location;
+ }
+ return default_destructor_location;
+}