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