/* Muscle table manager for Bison. Copyright (C) 2001-2013 Free Software Foundation, Inc. This file is part of Bison, the GNU Compiler Compiler. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include "system.h" #include #include "complain.h" #include "files.h" #include "getargs.h" #include "muscle-tab.h" #include "quote.h" /* A key-value pair, along with storage that can be reclaimed when this pair is no longer needed. */ typedef struct { char const *key; char const *value; char *storage; } muscle_entry; /* An obstack used to create some entries. */ struct obstack muscle_obstack; /* Initial capacity of muscles hash table. */ #define HT_INITIAL_CAPACITY 257 static struct hash_table *muscle_table = NULL; static bool hash_compare_muscles (void const *x, void const *y) { muscle_entry const *m1 = x; muscle_entry const *m2 = y; return strcmp (m1->key, m2->key) == 0; } static size_t hash_muscle (const void *x, size_t tablesize) { muscle_entry const *m = x; return hash_string (m->key, tablesize); } /*-----------------------------------------------------------------. | Create the MUSCLE_TABLE, and initialize it with default values. | | Also set up the MUSCLE_OBSTACK. | `-----------------------------------------------------------------*/ static void muscle_entry_free (void *entry) { muscle_entry *mentry = entry; free (mentry->storage); free (mentry); } void muscle_init (void) { /* Initialize the muscle obstack. */ obstack_init (&muscle_obstack); muscle_table = hash_initialize (HT_INITIAL_CAPACITY, NULL, hash_muscle, hash_compare_muscles, muscle_entry_free); /* Version and input file. */ MUSCLE_INSERT_STRING ("version", VERSION); } /*------------------------------------------------------------. | Free all the memory consumed by the muscle machinery only. | `------------------------------------------------------------*/ void muscle_free (void) { hash_free (muscle_table); obstack_free (&muscle_obstack, NULL); } /*------------------------------------------------------------. | Insert (KEY, VALUE). If KEY already existed, overwrite the | | previous value. | `------------------------------------------------------------*/ void muscle_insert (char const *key, char const *value) { muscle_entry probe; muscle_entry *entry; probe.key = key; entry = hash_lookup (muscle_table, &probe); if (!entry) { /* First insertion in the hash. */ entry = xmalloc (sizeof *entry); entry->key = key; if (!hash_insert (muscle_table, entry)) xalloc_die (); } else free (entry->storage); entry->value = value; entry->storage = NULL; } /*-------------------------------------------------------------------. | Append VALUE to the current value of KEY. If KEY did not already | | exist, create it. Use MUSCLE_OBSTACK. De-allocate the previously | | associated value. Copy VALUE and SEPARATOR. | `-------------------------------------------------------------------*/ void muscle_grow (const char *key, const char *val, const char *separator) { muscle_entry probe; muscle_entry *entry = NULL; probe.key = key; entry = hash_lookup (muscle_table, &probe); if (!entry) { /* First insertion in the hash. */ entry = xmalloc (sizeof *entry); entry->key = key; if (!hash_insert (muscle_table, entry)) xalloc_die (); entry->value = entry->storage = xstrdup (val); } else { /* Grow the current value. */ char *new_val; obstack_sgrow (&muscle_obstack, entry->value); free (entry->storage); obstack_sgrow (&muscle_obstack, separator); obstack_sgrow (&muscle_obstack, val); obstack_1grow (&muscle_obstack, 0); new_val = obstack_finish (&muscle_obstack); entry->value = entry->storage = xstrdup (new_val); obstack_free (&muscle_obstack, new_val); } } /*------------------------------------------------------------------. | Using muscle_grow, append a synchronization line for the location | | LOC to the current value of KEY. | `------------------------------------------------------------------*/ static void muscle_syncline_grow (char const *key, location loc) { char *extension = NULL; obstack_printf (&muscle_obstack, "]b4_syncline(%d, ", loc.start.line); obstack_quote (&muscle_obstack, quotearg_style (c_quoting_style, loc.start.file)); obstack_sgrow (&muscle_obstack, ")["); obstack_1grow (&muscle_obstack, 0); extension = obstack_finish (&muscle_obstack); muscle_grow (key, extension, ""); obstack_free (&muscle_obstack, extension); } /*------------------------------------------------------------------. | Append VALUE to the current value of KEY, using muscle_grow. But | | in addition, issue a synchronization line for the location LOC | | using muscle_syncline_grow. | `------------------------------------------------------------------*/ void muscle_code_grow (const char *key, const char *val, location loc) { muscle_syncline_grow (key, loc); muscle_grow (key, val, "\n"); } void muscle_pair_list_grow (const char *muscle, const char *a1, const char *a2) { char *pair; obstack_sgrow (&muscle_obstack, "["); obstack_quote (&muscle_obstack, a1); obstack_sgrow (&muscle_obstack, ", "); obstack_quote (&muscle_obstack, a2); obstack_sgrow (&muscle_obstack, "]"); obstack_1grow (&muscle_obstack, 0); pair = obstack_finish (&muscle_obstack); muscle_grow (muscle, pair, ",\n"); obstack_free (&muscle_obstack, pair); } /*----------------------------------------------------------------------------. | Find the value of muscle KEY. Unlike MUSCLE_FIND, this is always reliable | | to determine whether KEY has a value. | `----------------------------------------------------------------------------*/ char const * muscle_find_const (char const *key) { muscle_entry probe; muscle_entry *result = NULL; probe.key = key; result = hash_lookup (muscle_table, &probe); if (result) return result->value; return NULL; } /*----------------------------------------------------------------------------. | Find the value of muscle KEY. Abort if muscle_insert was invoked more | | recently than muscle_grow for KEY since muscle_find can't return a | | char const *. | `----------------------------------------------------------------------------*/ char * muscle_find (char const *key) { muscle_entry probe; muscle_entry *result = NULL; probe.key = key; result = hash_lookup (muscle_table, &probe); if (result) { aver (result->value == result->storage); return result->storage; } return NULL; } /* In the format `file_name:line.column', append BOUND to MUSCLE. Use digraphs for special characters in the file name. */ static void muscle_boundary_grow (char const *key, boundary bound) { char *extension; obstack_sgrow (&muscle_obstack, "[["); obstack_escape (&muscle_obstack, bound.file); obstack_1grow (&muscle_obstack, ':'); obstack_printf (&muscle_obstack, "%d", bound.line); obstack_1grow (&muscle_obstack, '.'); obstack_printf (&muscle_obstack, "%d", bound.column); obstack_sgrow (&muscle_obstack, "]]"); obstack_1grow (&muscle_obstack, '\0'); extension = obstack_finish (&muscle_obstack); muscle_grow (key, extension, ""); obstack_free (&muscle_obstack, extension); } /* In the format `[[file_name:line.column]], [[file_name:line.column]]', append LOC to MUSCLE. Use digraphs for special characters in each file name. */ static void muscle_location_grow (char const *key, location loc) { muscle_boundary_grow (key, loc.start); muscle_grow (key, "", ", "); muscle_boundary_grow (key, loc.end); } #define COMMON_DECODE(Value) \ case '$': \ aver (*++(Value) == ']'); \ aver (*++(Value) == '['); \ obstack_sgrow (&muscle_obstack, "$"); \ break; \ case '@': \ switch (*++(Value)) \ { \ case '@': obstack_sgrow (&muscle_obstack, "@" ); break; \ case '{': obstack_sgrow (&muscle_obstack, "[" ); break; \ case '}': obstack_sgrow (&muscle_obstack, "]" ); break; \ default: aver (false); break; \ } \ break; \ default: \ obstack_1grow (&muscle_obstack, *(Value)); \ break; /* Reverse of obstack_escape. */ static char * string_decode (char const *key) { char const *value; char *value_decoded; char *result; value = muscle_find_const (key); if (!value) return NULL; do { switch (*value) { COMMON_DECODE (value) case '[': case ']': aver (false); break; } } while (*value++); value_decoded = obstack_finish (&muscle_obstack); result = xstrdup (value_decoded); obstack_free (&muscle_obstack, value_decoded); return result; } /* Reverse of muscle_location_grow. */ static location location_decode (char const *key) { location loc; char const *value = muscle_find_const (key); aver (value); aver (*value == '['); aver (*++value == '['); while (*++value) switch (*value) { COMMON_DECODE (value) case '[': aver (false); break; case ']': { char *boundary_str; aver (*++value == ']'); obstack_1grow (&muscle_obstack, '\0'); boundary_str = obstack_finish (&muscle_obstack); switch (*++value) { case ',': boundary_set_from_string (&loc.start, boundary_str); obstack_free (&muscle_obstack, boundary_str); aver (*++value == ' '); aver (*++value == '['); aver (*++value == '['); break; case '\0': boundary_set_from_string (&loc.end, boundary_str); obstack_free (&muscle_obstack, boundary_str); return loc; break; default: aver (false); break; } } break; } aver (false); return loc; } void muscle_user_name_list_grow (char const *key, char const *user_name, location loc) { muscle_grow (key, "[[[[", ","); muscle_grow (key, user_name, ""); muscle_grow (key, "]], ", ""); muscle_location_grow (key, loc); muscle_grow (key, "]]", ""); } /** If the \a variable name is obsolete, return the name to use, * otherwise \a variable. */ static char const * muscle_percent_variable_update (char const *variable) { typedef struct { const char *obsolete; const char *updated; } conversion_type; const conversion_type conversion[] = { { "api.push_pull", "api.push-pull", }, { "location_type", "api.location.type", }, { "lr.keep_unreachable_states", "lr.keep-unreachable-states", }, }; char const *res = variable; int i; for (i = 0; i < ARRAY_CARDINALITY (conversion); ++i) if (STREQ (conversion[i].obsolete, variable)) { res = conversion[i].updated; break; } return res; } void muscle_percent_define_insert (char const *var, location variable_loc, char const *value, muscle_percent_define_how how) { /* Backward compatibility. */ char const *variable = muscle_percent_variable_update (var); char const *name = UNIQSTR_CONCAT ("percent_define(", variable, ")"); char const *loc_name = UNIQSTR_CONCAT ("percent_define_loc(", variable, ")"); char const *syncline_name = UNIQSTR_CONCAT ("percent_define_syncline(", variable, ")"); char const *how_name = UNIQSTR_CONCAT ("percent_define_how(", variable, ")"); /* Command-line options are processed before the grammar file. */ if (how == MUSCLE_PERCENT_DEFINE_GRAMMAR_FILE && muscle_find_const (name)) { unsigned i = 0; muscle_percent_define_how how_old = atoi (muscle_find_const (how_name)); if (how_old == MUSCLE_PERCENT_DEFINE_F) return; complain_at_indent (variable_loc, &i, _("%%define variable %s redefined"), quote (variable)); i += SUB_INDENT; complain_at_indent (muscle_percent_define_get_loc (variable), &i, _("previous definition")); } MUSCLE_INSERT_STRING (name, value); muscle_insert (loc_name, ""); muscle_location_grow (loc_name, variable_loc); muscle_insert (syncline_name, ""); muscle_syncline_grow (syncline_name, variable_loc); muscle_user_name_list_grow ("percent_define_user_variables", variable, variable_loc); MUSCLE_INSERT_INT (how_name, how); } char * muscle_percent_define_get (char const *variable) { char const *name; char const *usage_name; char *value; name = UNIQSTR_CONCAT ("percent_define(", variable, ")"); usage_name = UNIQSTR_CONCAT ("percent_define_bison_variables(", variable, ")"); muscle_insert (usage_name, ""); value = string_decode (name); if (!value) value = xstrdup (""); return value; } location muscle_percent_define_get_loc (char const *variable) { char const *loc_name; loc_name = UNIQSTR_CONCAT ("percent_define_loc(", variable, ")"); if (!muscle_find_const (loc_name)) fatal(_("%s: undefined %%define variable %s"), "muscle_percent_define_get_loc", quote (variable)); return location_decode (loc_name); } char const * muscle_percent_define_get_syncline (char const *variable) { char const *syncline_name; char const *syncline; syncline_name = UNIQSTR_CONCAT ("percent_define_syncline(", variable, ")"); syncline = muscle_find_const (syncline_name); if (!syncline) fatal(_("%s: undefined %%define variable %s"), "muscle_percent_define_get_syncline", quote (variable)); return syncline; } bool muscle_percent_define_ifdef (char const *variable) { char const *name; char const *usage_name; char const *value; name = UNIQSTR_CONCAT ("percent_define(", variable, ")"); usage_name = UNIQSTR_CONCAT ("percent_define_bison_variables(", variable, ")"); value = muscle_find_const (name); if (value) { muscle_insert (usage_name, ""); return true; } return false; } bool muscle_percent_define_flag_if (char const *variable) { char const *invalid_boolean_name; bool result = false; invalid_boolean_name = UNIQSTR_CONCAT ("percent_define_invalid_boolean(", variable, ")"); if (muscle_percent_define_ifdef (variable)) { char *value = muscle_percent_define_get (variable); if (value[0] == '\0' || 0 == strcmp (value, "true")) result = true; else if (0 == strcmp (value, "false")) result = false; else if (!muscle_find_const (invalid_boolean_name)) { muscle_insert (invalid_boolean_name, ""); complain_at(muscle_percent_define_get_loc (variable), _("invalid value for %%define Boolean variable %s"), quote (variable)); } free (value); } else fatal(_("%s: undefined %%define variable %s"), "muscle_percent_define_flag", quote (variable)); return result; } void muscle_percent_define_default (char const *variable, char const *value) { char const *name; char const *loc_name; char const *syncline_name; name = UNIQSTR_CONCAT ("percent_define(", variable, ")"); loc_name = UNIQSTR_CONCAT ("percent_define_loc(", variable, ")"); syncline_name = UNIQSTR_CONCAT ("percent_define_syncline(", variable, ")"); if (!muscle_find_const (name)) { location loc; MUSCLE_INSERT_STRING (name, value); loc.start.file = loc.end.file = ""; loc.start.line = loc.end.line = -1; loc.start.column = loc.end.column = -1; muscle_insert (loc_name, ""); muscle_location_grow (loc_name, loc); muscle_insert (syncline_name, ""); } } void muscle_percent_define_check_values (char const * const *values) { for (; *values; ++values) { char const * const *variablep = values; char const *name; char *value; name = UNIQSTR_CONCAT ("percent_define(", *variablep, ")"); value = string_decode (name); if (value) { for (++values; *values; ++values) { if (0 == strcmp (value, *values)) break; } if (!*values) { unsigned i = 0; location loc = muscle_percent_define_get_loc (*variablep); complain_at_indent (loc, &i, _("invalid value for %%define variable %s: %s"), quote (*variablep), quote_n (1, value)); i += SUB_INDENT; for (values = variablep + 1; *values; ++values) complain_at_indent (loc, &i, _("accepted value: %s"), quote (*values)); } else { while (*values) ++values; } free (value); } else fatal (_("%s: undefined %%define variable %s"), "muscle_percent_define_check_values", quote (*variablep)); } } void muscle_percent_code_grow (char const *qualifier, location qualifier_loc, char const *code, location code_loc) { char const *name; name = UNIQSTR_CONCAT ("percent_code(", qualifier, ")"); muscle_code_grow (name, code, code_loc); muscle_user_name_list_grow ("percent_code_user_qualifiers", qualifier, qualifier_loc); } /*------------------------------------------------. | Output the definition of ENTRY as a m4_define. | `------------------------------------------------*/ static inline bool muscle_m4_output (muscle_entry *entry, FILE *out) { fprintf (out, "m4_define([b4_%s],\n", entry->key); fprintf (out, "[[%s]])\n\n\n", entry->value); return true; } static bool muscle_m4_output_processor (void *entry, void *out) { return muscle_m4_output (entry, out); } /*----------------------------------------------------------------. | Output the definition of all the current muscles into a list of | | m4_defines. | `----------------------------------------------------------------*/ void muscles_m4_output (FILE *out) { hash_do_for_each (muscle_table, muscle_m4_output_processor, out); }