/* Print an xml on generated parser, for Bison, Copyright (C) 2007, 2009-2015, 2018-2021 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 "print-xml.h" #include "system.h" #include #include #include "closure.h" #include "complain.h" #include "conflicts.h" #include "execute.h" #include "files.h" #include "getargs.h" #include "gram.h" #include "lalr.h" #include "lr0.h" #include "muscle-tab.h" #include "path-join.h" #include "print.h" #include "reader.h" #include "reduce.h" #include "state.h" #include "symtab.h" #include "tables.h" static bitset no_reduce_set; struct escape_buf { char *ptr; size_t size; }; enum { num_escape_bufs = 3 }; static struct escape_buf escape_bufs[num_escape_bufs]; /*--------------------------------. | Report information on a state. | `--------------------------------*/ static void print_core (FILE *out, int level, state *s) { item_index *sitems = s->items; size_t snritems = s->nitems; /* Output all the items of a state, not only its kernel. */ closure (sitems, snritems); sitems = itemset; snritems = nitemset; if (!snritems) { xml_puts (out, level, ""); return; } xml_puts (out, level, ""); for (size_t i = 0; i < snritems; i++) { bool printed = false; item_number *sp1 = ritem + sitems[i]; rule const *r = item_rule (sp1); item_number *sp = r->rhs; /* Display the lookahead tokens? */ if (item_number_is_rule_number (*sp1)) { reductions *reds = s->reductions; int red = state_reduction_find (s, r); /* Print item with lookaheads if there are. */ if (reds->lookaheads && red != -1) { xml_printf (out, level + 1, "", r->number, sp1 - sp); state_rule_lookaheads_print_xml (s, r, out, level + 2); xml_puts (out, level + 1, ""); printed = true; } } if (!printed) xml_printf (out, level + 1, "", r->number, sp1 - sp); } xml_puts (out, level, ""); } /*-----------------------------------------------------------. | Report the shifts if DISPLAY_SHIFTS_P or the gotos of S on | | OUT. | `-----------------------------------------------------------*/ static void print_transitions (state *s, FILE *out, int level) { transitions *trans = s->transitions; int n = 0; int i; for (i = 0; i < trans->num; i++) if (!TRANSITION_IS_DISABLED (trans, i)) { n++; } /* Nothing to report. */ if (!n) { xml_puts (out, level, ""); return; } /* Report lookahead tokens and shifts. */ xml_puts (out, level, ""); for (i = 0; i < trans->num; i++) if (!TRANSITION_IS_DISABLED (trans, i) && TRANSITION_IS_SHIFT (trans, i)) { symbol *sym = symbols[TRANSITION_SYMBOL (trans, i)]; char const *tag = sym->tag; state *s1 = trans->states[i]; xml_printf (out, level + 1, "", xml_escape (tag), s1->number); } for (i = 0; i < trans->num; i++) if (!TRANSITION_IS_DISABLED (trans, i) && !TRANSITION_IS_SHIFT (trans, i)) { symbol *sym = symbols[TRANSITION_SYMBOL (trans, i)]; char const *tag = sym->tag; state *s1 = trans->states[i]; xml_printf (out, level + 1, "", xml_escape (tag), s1->number); } xml_puts (out, level, ""); } /*--------------------------------------------------------. | Report the explicit errors of S raised from %nonassoc. | `--------------------------------------------------------*/ static void print_errs (FILE *out, int level, state *s) { errs *errp = s->errs; bool count = false; int i; for (i = 0; i < errp->num; ++i) if (errp->symbols[i]) count = true; /* Nothing to report. */ if (!count) { xml_puts (out, level, ""); return; } /* Report lookahead tokens and errors. */ xml_puts (out, level, ""); for (i = 0; i < errp->num; ++i) if (errp->symbols[i]) { char const *tag = errp->symbols[i]->tag; xml_printf (out, level + 1, "nonassociative", xml_escape (tag)); } xml_puts (out, level, ""); } /*-------------------------------------------------------------------. | Report a reduction of RULE on LOOKAHEAD (which can be 'default'). | | If not ENABLED, the rule is masked by a shift or a reduce (S/R and | | R/R conflicts). | `-------------------------------------------------------------------*/ static void print_reduction (FILE *out, int level, char const *lookahead, rule *r, bool enabled) { if (rule_is_initial (r)) xml_printf (out, level, "", xml_escape (lookahead), enabled ? "true" : "false"); else xml_printf (out, level, "", xml_escape (lookahead), r->number, enabled ? "true" : "false"); } /*-------------------------------------------. | Report on OUT the reduction actions of S. | `-------------------------------------------*/ static void print_reductions (FILE *out, int level, state *s) { transitions *trans = s->transitions; reductions *reds = s->reductions; rule *default_reduction = NULL; int report = false; int i, j; if (reds->num == 0) { xml_puts (out, level, ""); return; } if (yydefact[s->number] != 0) default_reduction = &rules[yydefact[s->number] - 1]; bitset_zero (no_reduce_set); FOR_EACH_SHIFT (trans, i) bitset_set (no_reduce_set, TRANSITION_SYMBOL (trans, i)); for (i = 0; i < s->errs->num; ++i) if (s->errs->symbols[i]) bitset_set (no_reduce_set, s->errs->symbols[i]->content->number); if (default_reduction) report = true; if (reds->lookaheads) for (i = 0; i < ntokens; i++) { bool count = bitset_test (no_reduce_set, i); for (j = 0; j < reds->num; ++j) if (bitset_test (reds->lookaheads[j], i)) { if (! count) { if (reds->rules[j] != default_reduction) report = true; count = true; } else { report = true; } } } /* Nothing to report. */ if (!report) { xml_puts (out, level, ""); return; } xml_puts (out, level, ""); /* Report lookahead tokens (or $default) and reductions. */ if (reds->lookaheads) for (i = 0; i < ntokens; i++) { bool defaulted = false; bool count = bitset_test (no_reduce_set, i); for (j = 0; j < reds->num; ++j) if (bitset_test (reds->lookaheads[j], i)) { if (! count) { if (reds->rules[j] != default_reduction) print_reduction (out, level + 1, symbols[i]->tag, reds->rules[j], true); else defaulted = true; count = true; } else { if (defaulted) print_reduction (out, level + 1, symbols[i]->tag, default_reduction, true); defaulted = false; print_reduction (out, level + 1, symbols[i]->tag, reds->rules[j], false); } } } if (default_reduction) print_reduction (out, level + 1, "$default", default_reduction, true); xml_puts (out, level, ""); } /*--------------------------------------------------------------. | Report on OUT all the actions (shifts, gotos, reductions, and | | explicit errors from %nonassoc) of S. | `--------------------------------------------------------------*/ static void print_actions (FILE *out, int level, state *s) { xml_puts (out, level, ""); print_transitions (s, out, level + 1); print_errs (out, level + 1, s); print_reductions (out, level + 1, s); xml_puts (out, level, ""); } /*----------------------------------. | Report all the data on S on OUT. | `----------------------------------*/ static void print_state (FILE *out, int level, state *s) { fputc ('\n', out); xml_printf (out, level, "", s->number); print_core (out, level + 1, s); print_actions (out, level + 1, s); if (s->solved_conflicts_xml) { xml_puts (out, level + 1, ""); fputs (s->solved_conflicts_xml, out); xml_puts (out, level + 1, ""); } else xml_puts (out, level + 1, ""); xml_puts (out, level, ""); } /*-----------------------------------------. | Print information on the whole grammar. | `-----------------------------------------*/ static void print_grammar (FILE *out, int level) { fputc ('\n', out); xml_puts (out, level, ""); grammar_rules_print_xml (out, level); /* Terminals */ xml_puts (out, level + 1, ""); for (int i = 0; i < max_code + 1; i++) if (token_translations[i] != undeftoken->content->number) { symbol const *sym = symbols[token_translations[i]]; char const *tag = sym->tag; char const *type = sym->content->type_name; int precedence = sym->content->prec; assoc associativity = sym->content->assoc; xml_indent (out, level + 2); fprintf (out, "\n", out); } xml_puts (out, level + 1, ""); /* Nonterminals */ xml_puts (out, level + 1, ""); for (symbol_number i = ntokens; i < nsyms + nuseless_nonterminals; i++) { symbol const *sym = symbols[i]; char const *tag = sym->tag; char const *type = sym->content->type_name; xml_printf (out, level + 2, "", i, xml_escape_n (0, tag), type ? xml_escape_n (1, type) : "", reduce_nonterminal_useless_in_grammar (sym->content) ? "useless-in-grammar" : "useful"); } xml_puts (out, level + 1, ""); xml_puts (out, level, ""); } void xml_indent (FILE *out, int level) { for (int i = 0; i < level; i++) fputs (" ", out); } void xml_puts (FILE *out, int level, char const *s) { xml_indent (out, level); fputs (s, out); fputc ('\n', out); } void xml_printf (FILE *out, int level, char const *fmt, ...) { va_list arglist; xml_indent (out, level); va_start (arglist, fmt); vfprintf (out, fmt, arglist); va_end (arglist); fputc ('\n', out); } static char const * xml_escape_string (struct escape_buf *buf, char const *str) { size_t len = strlen (str); size_t max_expansion = sizeof """ - 1; if (buf->size <= max_expansion * len) { buf->size = max_expansion * len + 1; buf->ptr = x2realloc (buf->ptr, &buf->size); } char *p = buf->ptr; for (; *str; str++) switch (*str) { default: *p++ = *str; break; case '&': p = stpcpy (p, "&" ); break; case '<': p = stpcpy (p, "<" ); break; case '>': p = stpcpy (p, ">" ); break; case '"': p = stpcpy (p, """); break; } *p = '\0'; return buf->ptr; } char const * xml_escape_n (int n, char const *str) { return xml_escape_string (escape_bufs + n, str); } char const * xml_escape (char const *str) { return xml_escape_n (0, str); } void print_xml (void) { FILE *out = xfopen (spec_xml_file, "w"); fputs ("\n\n", out); int level = 0; xml_printf (out, level, "", xml_escape_n (0, VERSION), xml_escape_n (1, PACKAGE_BUGREPORT), xml_escape_n (2, PACKAGE_URL)); fputc ('\n', out); xml_printf (out, level + 1, "%s", xml_escape (grammar_file)); /* print grammar */ print_grammar (out, level + 1); no_reduce_set = bitset_create (ntokens, BITSET_FIXED); /* print automaton */ fputc ('\n', out); xml_puts (out, level + 1, ""); for (state_number i = 0; i < nstates; i++) print_state (out, level + 2, states[i]); xml_puts (out, level + 1, ""); bitset_free (no_reduce_set); xml_puts (out, 0, ""); for (int i = 0; i < num_escape_bufs; ++i) free (escape_bufs[i].ptr); xfclose (out); } void print_html (void) { assert (xml_flag); char *xml2html = xpath_join (pkgdatadir (), "xslt/xml2xhtml.xsl"); char *xsltproc = muscle_percent_define_get ("tool.xsltproc"); char const *argv[11]; int i = 0; argv[i++] = xsltproc; argv[i++] = "-o"; argv[i++] = spec_html_file; argv[i++] = xml2html; argv[i++] = spec_xml_file; argv[i++] = NULL; aver (i <= ARRAY_CARDINALITY (argv)); if (trace_flag & trace_tools) { fputs ("running:", stderr); for (int j = 0; argv[j]; ++j) fprintf (stderr, " %s", argv[j]); fputc ('\n', stderr); } int status = execute (argv[0], argv[0], argv, /* directory */ NULL, /* ignore_sigpipe */ false, /* null_stdin, null_stdout, null_stderr */ true, true, true, /* slave_process */ true, /* exit_on_error */ false, /* termsigp */ NULL); if (status) complain (NULL, complaint, _("%s failed with status %d"), argv[0], status); free (xsltproc); free (xml2html); }