(root)/
bison-3.8.2/
src/
print-xml.c
       1  /* Print an xml on generated parser, for Bison,
       2  
       3     Copyright (C) 2007, 2009-2015, 2018-2021 Free Software Foundation,
       4     Inc.
       5  
       6     This file is part of Bison, the GNU Compiler Compiler.
       7  
       8     This program is free software: you can redistribute it and/or modify
       9     it under the terms of the GNU General Public License as published by
      10     the Free Software Foundation, either version 3 of the License, or
      11     (at your option) any later version.
      12  
      13     This program is distributed in the hope that it will be useful,
      14     but WITHOUT ANY WARRANTY; without even the implied warranty of
      15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      16     GNU General Public License for more details.
      17  
      18     You should have received a copy of the GNU General Public License
      19     along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
      20  
      21  #include <config.h>
      22  #include "print-xml.h"
      23  
      24  #include "system.h"
      25  
      26  #include <bitset.h>
      27  #include <stdarg.h>
      28  
      29  #include "closure.h"
      30  #include "complain.h"
      31  #include "conflicts.h"
      32  #include "execute.h"
      33  #include "files.h"
      34  #include "getargs.h"
      35  #include "gram.h"
      36  #include "lalr.h"
      37  #include "lr0.h"
      38  #include "muscle-tab.h"
      39  #include "path-join.h"
      40  #include "print.h"
      41  #include "reader.h"
      42  #include "reduce.h"
      43  #include "state.h"
      44  #include "symtab.h"
      45  #include "tables.h"
      46  
      47  static bitset no_reduce_set;
      48  struct escape_buf
      49  {
      50    char *ptr;
      51    size_t size;
      52  };
      53  enum { num_escape_bufs = 3 };
      54  static struct escape_buf escape_bufs[num_escape_bufs];
      55  
      56  
      57  /*--------------------------------.
      58  | Report information on a state.  |
      59  `--------------------------------*/
      60  
      61  static void
      62  print_core (FILE *out, int level, state *s)
      63  {
      64    item_index *sitems = s->items;
      65    size_t snritems = s->nitems;
      66  
      67    /* Output all the items of a state, not only its kernel.  */
      68    closure (sitems, snritems);
      69    sitems = itemset;
      70    snritems = nitemset;
      71  
      72    if (!snritems)
      73      {
      74        xml_puts (out, level, "<itemset/>");
      75        return;
      76      }
      77  
      78    xml_puts (out, level, "<itemset>");
      79  
      80    for (size_t i = 0; i < snritems; i++)
      81      {
      82        bool printed = false;
      83        item_number *sp1 = ritem + sitems[i];
      84        rule const *r = item_rule (sp1);
      85        item_number *sp = r->rhs;
      86  
      87        /* Display the lookahead tokens?  */
      88        if (item_number_is_rule_number (*sp1))
      89          {
      90            reductions *reds = s->reductions;
      91            int red = state_reduction_find (s, r);
      92            /* Print item with lookaheads if there are. */
      93            if (reds->lookaheads && red != -1)
      94              {
      95                xml_printf (out, level + 1,
      96                            "<item rule-number=\"%d\" dot=\"%d\">",
      97                            r->number, sp1 - sp);
      98                state_rule_lookaheads_print_xml (s, r,
      99                                                       out, level + 2);
     100                xml_puts (out, level + 1, "</item>");
     101                printed = true;
     102              }
     103          }
     104  
     105        if (!printed)
     106          xml_printf (out, level + 1,
     107                      "<item rule-number=\"%d\" dot=\"%d\"/>",
     108                      r->number,
     109                      sp1 - sp);
     110      }
     111    xml_puts (out, level, "</itemset>");
     112  }
     113  
     114  
     115  /*-----------------------------------------------------------.
     116  | Report the shifts if DISPLAY_SHIFTS_P or the gotos of S on |
     117  | OUT.                                                       |
     118  `-----------------------------------------------------------*/
     119  
     120  static void
     121  print_transitions (state *s, FILE *out, int level)
     122  {
     123    transitions *trans = s->transitions;
     124    int n = 0;
     125    int i;
     126  
     127    for (i = 0; i < trans->num; i++)
     128      if (!TRANSITION_IS_DISABLED (trans, i))
     129        {
     130          n++;
     131        }
     132  
     133    /* Nothing to report. */
     134    if (!n)
     135      {
     136        xml_puts (out, level, "<transitions/>");
     137        return;
     138      }
     139  
     140    /* Report lookahead tokens and shifts.  */
     141    xml_puts (out, level, "<transitions>");
     142  
     143    for (i = 0; i < trans->num; i++)
     144      if (!TRANSITION_IS_DISABLED (trans, i)
     145          && TRANSITION_IS_SHIFT (trans, i))
     146        {
     147          symbol *sym = symbols[TRANSITION_SYMBOL (trans, i)];
     148          char const *tag = sym->tag;
     149          state *s1 = trans->states[i];
     150  
     151          xml_printf (out, level + 1,
     152                      "<transition type=\"shift\" symbol=\"%s\" state=\"%d\"/>",
     153                      xml_escape (tag), s1->number);
     154        }
     155  
     156    for (i = 0; i < trans->num; i++)
     157      if (!TRANSITION_IS_DISABLED (trans, i)
     158          && !TRANSITION_IS_SHIFT (trans, i))
     159        {
     160          symbol *sym = symbols[TRANSITION_SYMBOL (trans, i)];
     161          char const *tag = sym->tag;
     162          state *s1 = trans->states[i];
     163  
     164          xml_printf (out, level + 1,
     165                      "<transition type=\"goto\" symbol=\"%s\" state=\"%d\"/>",
     166                      xml_escape (tag), s1->number);
     167        }
     168  
     169    xml_puts (out, level, "</transitions>");
     170  }
     171  
     172  
     173  /*--------------------------------------------------------.
     174  | Report the explicit errors of S raised from %nonassoc.  |
     175  `--------------------------------------------------------*/
     176  
     177  static void
     178  print_errs (FILE *out, int level, state *s)
     179  {
     180    errs *errp = s->errs;
     181    bool count = false;
     182    int i;
     183  
     184    for (i = 0; i < errp->num; ++i)
     185      if (errp->symbols[i])
     186        count = true;
     187  
     188    /* Nothing to report. */
     189    if (!count)
     190      {
     191        xml_puts (out, level, "<errors/>");
     192        return;
     193      }
     194  
     195    /* Report lookahead tokens and errors.  */
     196    xml_puts (out, level, "<errors>");
     197    for (i = 0; i < errp->num; ++i)
     198      if (errp->symbols[i])
     199        {
     200          char const *tag = errp->symbols[i]->tag;
     201          xml_printf (out, level + 1,
     202                      "<error symbol=\"%s\">nonassociative</error>",
     203                      xml_escape (tag));
     204        }
     205    xml_puts (out, level, "</errors>");
     206  }
     207  
     208  
     209  /*-------------------------------------------------------------------.
     210  | Report a reduction of RULE on LOOKAHEAD (which can be 'default').  |
     211  | If not ENABLED, the rule is masked by a shift or a reduce (S/R and |
     212  | R/R conflicts).                                                    |
     213  `-------------------------------------------------------------------*/
     214  
     215  static void
     216  print_reduction (FILE *out, int level, char const *lookahead,
     217                   rule *r, bool enabled)
     218  {
     219    if (rule_is_initial (r))
     220      xml_printf (out, level,
     221                  "<reduction symbol=\"%s\" rule=\"accept\" enabled=\"%s\"/>",
     222                  xml_escape (lookahead),
     223                  enabled ? "true" : "false");
     224    else
     225      xml_printf (out, level,
     226                  "<reduction symbol=\"%s\" rule=\"%d\" enabled=\"%s\"/>",
     227                  xml_escape (lookahead),
     228                  r->number,
     229                  enabled ? "true" : "false");
     230  }
     231  
     232  
     233  /*-------------------------------------------.
     234  | Report on OUT the reduction actions of S.  |
     235  `-------------------------------------------*/
     236  
     237  static void
     238  print_reductions (FILE *out, int level, state *s)
     239  {
     240    transitions *trans = s->transitions;
     241    reductions *reds = s->reductions;
     242    rule *default_reduction = NULL;
     243    int report = false;
     244    int i, j;
     245  
     246    if (reds->num == 0)
     247      {
     248        xml_puts (out, level, "<reductions/>");
     249        return;
     250      }
     251  
     252    if (yydefact[s->number] != 0)
     253      default_reduction = &rules[yydefact[s->number] - 1];
     254  
     255    bitset_zero (no_reduce_set);
     256    FOR_EACH_SHIFT (trans, i)
     257      bitset_set (no_reduce_set, TRANSITION_SYMBOL (trans, i));
     258    for (i = 0; i < s->errs->num; ++i)
     259      if (s->errs->symbols[i])
     260        bitset_set (no_reduce_set, s->errs->symbols[i]->content->number);
     261  
     262    if (default_reduction)
     263      report = true;
     264  
     265    if (reds->lookaheads)
     266      for (i = 0; i < ntokens; i++)
     267        {
     268          bool count = bitset_test (no_reduce_set, i);
     269  
     270          for (j = 0; j < reds->num; ++j)
     271            if (bitset_test (reds->lookaheads[j], i))
     272              {
     273                if (! count)
     274                  {
     275                    if (reds->rules[j] != default_reduction)
     276                      report = true;
     277                    count = true;
     278                  }
     279                else
     280                  {
     281                    report = true;
     282                  }
     283              }
     284        }
     285  
     286    /* Nothing to report. */
     287    if (!report)
     288      {
     289        xml_puts (out, level, "<reductions/>");
     290        return;
     291      }
     292  
     293    xml_puts (out, level, "<reductions>");
     294  
     295    /* Report lookahead tokens (or $default) and reductions.  */
     296    if (reds->lookaheads)
     297      for (i = 0; i < ntokens; i++)
     298        {
     299          bool defaulted = false;
     300          bool count = bitset_test (no_reduce_set, i);
     301  
     302          for (j = 0; j < reds->num; ++j)
     303            if (bitset_test (reds->lookaheads[j], i))
     304              {
     305                if (! count)
     306                  {
     307                    if (reds->rules[j] != default_reduction)
     308                      print_reduction (out, level + 1, symbols[i]->tag,
     309                                       reds->rules[j], true);
     310                    else
     311                      defaulted = true;
     312                    count = true;
     313                  }
     314                else
     315                  {
     316                    if (defaulted)
     317                      print_reduction (out, level + 1, symbols[i]->tag,
     318                                       default_reduction, true);
     319                    defaulted = false;
     320                    print_reduction (out, level + 1, symbols[i]->tag,
     321                                     reds->rules[j], false);
     322                  }
     323              }
     324        }
     325  
     326    if (default_reduction)
     327      print_reduction (out, level + 1,
     328                       "$default", default_reduction, true);
     329  
     330    xml_puts (out, level, "</reductions>");
     331  }
     332  
     333  
     334  /*--------------------------------------------------------------.
     335  | Report on OUT all the actions (shifts, gotos, reductions, and |
     336  | explicit errors from %nonassoc) of S.                         |
     337  `--------------------------------------------------------------*/
     338  
     339  static void
     340  print_actions (FILE *out, int level, state *s)
     341  {
     342    xml_puts (out, level, "<actions>");
     343    print_transitions (s, out, level + 1);
     344    print_errs (out, level + 1, s);
     345    print_reductions (out, level + 1, s);
     346    xml_puts (out, level, "</actions>");
     347  }
     348  
     349  
     350  /*----------------------------------.
     351  | Report all the data on S on OUT.  |
     352  `----------------------------------*/
     353  
     354  static void
     355  print_state (FILE *out, int level, state *s)
     356  {
     357    fputc ('\n', out);
     358    xml_printf (out, level, "<state number=\"%d\">", s->number);
     359    print_core (out, level + 1, s);
     360    print_actions (out, level + 1, s);
     361    if (s->solved_conflicts_xml)
     362      {
     363        xml_puts (out, level + 1, "<solved-conflicts>");
     364        fputs (s->solved_conflicts_xml, out);
     365        xml_puts (out, level + 1, "</solved-conflicts>");
     366      }
     367    else
     368      xml_puts (out, level + 1, "<solved-conflicts/>");
     369    xml_puts (out, level, "</state>");
     370  }
     371  
     372  
     373  /*-----------------------------------------.
     374  | Print information on the whole grammar.  |
     375  `-----------------------------------------*/
     376  
     377  static void
     378  print_grammar (FILE *out, int level)
     379  {
     380    fputc ('\n', out);
     381    xml_puts (out, level, "<grammar>");
     382    grammar_rules_print_xml (out, level);
     383  
     384    /* Terminals */
     385    xml_puts (out, level + 1, "<terminals>");
     386    for (int i = 0; i < max_code + 1; i++)
     387      if (token_translations[i] != undeftoken->content->number)
     388        {
     389          symbol const *sym = symbols[token_translations[i]];
     390          char const *tag = sym->tag;
     391          char const *type = sym->content->type_name;
     392          int precedence = sym->content->prec;
     393          assoc associativity = sym->content->assoc;
     394          xml_indent (out, level + 2);
     395          fprintf (out,
     396                   "<terminal symbol-number=\"%d\" token-number=\"%d\""
     397                   " name=\"%s\" type=\"%s\" usefulness=\"%s\"",
     398                   token_translations[i], i, xml_escape_n (0, tag),
     399                   type ? xml_escape_n (1, type) : "",
     400                   reduce_token_unused_in_grammar (token_translations[i])
     401                     ? "unused-in-grammar" : "useful");
     402          if (precedence)
     403            fprintf (out, " prec=\"%d\"", precedence);
     404          if (associativity != undef_assoc)
     405            fprintf (out, " assoc=\"%s\"", assoc_to_string (associativity) + 1);
     406          fputs ("/>\n", out);
     407        }
     408    xml_puts (out, level + 1, "</terminals>");
     409  
     410    /* Nonterminals */
     411    xml_puts (out, level + 1, "<nonterminals>");
     412    for (symbol_number i = ntokens; i < nsyms + nuseless_nonterminals; i++)
     413      {
     414        symbol const *sym = symbols[i];
     415        char const *tag = sym->tag;
     416        char const *type = sym->content->type_name;
     417        xml_printf (out, level + 2,
     418                    "<nonterminal symbol-number=\"%d\" name=\"%s\""
     419                    " type=\"%s\""
     420                    " usefulness=\"%s\"/>",
     421                    i, xml_escape_n (0, tag),
     422                    type ? xml_escape_n (1, type) : "",
     423                    reduce_nonterminal_useless_in_grammar (sym->content)
     424                      ? "useless-in-grammar" : "useful");
     425      }
     426    xml_puts (out, level + 1, "</nonterminals>");
     427    xml_puts (out, level, "</grammar>");
     428  }
     429  
     430  void
     431  xml_indent (FILE *out, int level)
     432  {
     433    for (int i = 0; i < level; i++)
     434      fputs ("  ", out);
     435  }
     436  
     437  void
     438  xml_puts (FILE *out, int level, char const *s)
     439  {
     440    xml_indent (out, level);
     441    fputs (s, out);
     442    fputc ('\n', out);
     443  }
     444  
     445  void
     446  xml_printf (FILE *out, int level, char const *fmt, ...)
     447  {
     448    va_list arglist;
     449  
     450    xml_indent (out, level);
     451  
     452    va_start (arglist, fmt);
     453    vfprintf (out, fmt, arglist);
     454    va_end (arglist);
     455  
     456    fputc ('\n', out);
     457  }
     458  
     459  static char const *
     460  xml_escape_string (struct escape_buf *buf, char const *str)
     461  {
     462    size_t len = strlen (str);
     463    size_t max_expansion = sizeof "&quot;" - 1;
     464  
     465    if (buf->size <= max_expansion * len)
     466      {
     467        buf->size = max_expansion * len + 1;
     468        buf->ptr = x2realloc (buf->ptr, &buf->size);
     469      }
     470    char *p = buf->ptr;
     471  
     472    for (; *str; str++)
     473      switch (*str)
     474        {
     475        default: *p++ = *str; break;
     476        case '&': p = stpcpy (p, "&amp;" ); break;
     477        case '<': p = stpcpy (p, "&lt;"  ); break;
     478        case '>': p = stpcpy (p, "&gt;"  ); break;
     479        case '"': p = stpcpy (p, "&quot;"); break;
     480        }
     481  
     482    *p = '\0';
     483    return buf->ptr;
     484  }
     485  
     486  char const *
     487  xml_escape_n (int n, char const *str)
     488  {
     489    return xml_escape_string (escape_bufs + n, str);
     490  }
     491  
     492  char const *
     493  xml_escape (char const *str)
     494  {
     495    return xml_escape_n (0, str);
     496  }
     497  
     498  void
     499  print_xml (void)
     500  {
     501    FILE *out = xfopen (spec_xml_file, "w");
     502  
     503    fputs ("<?xml version=\"1.0\"?>\n\n", out);
     504  
     505    int level = 0;
     506    xml_printf (out, level,
     507                "<bison-xml-report version=\"%s\" bug-report=\"%s\""
     508                " url=\"%s\">",
     509                xml_escape_n (0, VERSION),
     510                xml_escape_n (1, PACKAGE_BUGREPORT),
     511                xml_escape_n (2, PACKAGE_URL));
     512  
     513    fputc ('\n', out);
     514    xml_printf (out, level + 1, "<filename>%s</filename>",
     515                xml_escape (grammar_file));
     516  
     517    /* print grammar */
     518    print_grammar (out, level + 1);
     519  
     520    no_reduce_set = bitset_create (ntokens, BITSET_FIXED);
     521  
     522    /* print automaton */
     523    fputc ('\n', out);
     524    xml_puts (out, level + 1, "<automaton>");
     525    for (state_number i = 0; i < nstates; i++)
     526      print_state (out, level + 2, states[i]);
     527    xml_puts (out, level + 1, "</automaton>");
     528  
     529    bitset_free (no_reduce_set);
     530  
     531    xml_puts (out, 0, "</bison-xml-report>");
     532  
     533    for (int i = 0; i < num_escape_bufs; ++i)
     534      free (escape_bufs[i].ptr);
     535  
     536    xfclose (out);
     537  }
     538  
     539  
     540  void
     541  print_html (void)
     542  {
     543    assert (xml_flag);
     544  
     545    char *xml2html = xpath_join (pkgdatadir (), "xslt/xml2xhtml.xsl");
     546    char *xsltproc = muscle_percent_define_get ("tool.xsltproc");
     547    char const *argv[11];
     548    int i = 0;
     549    argv[i++] = xsltproc;
     550    argv[i++] = "-o";
     551    argv[i++] = spec_html_file;
     552    argv[i++] = xml2html;
     553    argv[i++] = spec_xml_file;
     554    argv[i++] = NULL;
     555    aver (i <= ARRAY_CARDINALITY (argv));
     556  
     557    if (trace_flag & trace_tools)
     558      {
     559        fputs ("running:", stderr);
     560        for (int j = 0; argv[j]; ++j)
     561          fprintf (stderr, " %s", argv[j]);
     562        fputc ('\n', stderr);
     563      }
     564  
     565    int status
     566      = execute (argv[0],
     567                 argv[0], argv,
     568                 /* directory */ NULL,
     569                 /* ignore_sigpipe */ false,
     570                 /* null_stdin, null_stdout, null_stderr */ true, true, true,
     571                 /* slave_process */ true, /* exit_on_error */ false,
     572                 /* termsigp */ NULL);
     573    if (status)
     574      complain (NULL, complaint, _("%s failed with status %d"), argv[0], status);
     575    free (xsltproc);
     576    free (xml2html);
     577  }