gnulib: update
[bison.git] / src / print-xml.c
blob78801dbc63c339bd8f0b71b80b4d0934aa0e705f
1 /* Print an xml on generated parser, for Bison,
3 Copyright (C) 2007, 2009-2015, 2018-2021 Free Software Foundation,
4 Inc.
6 This file is part of Bison, the GNU Compiler Compiler.
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.
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.
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/>. */
21 #include <config.h>
22 #include "print-xml.h"
24 #include "system.h"
26 #include <bitset.h>
27 #include <stdarg.h>
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"
47 static bitset no_reduce_set;
48 struct escape_buf
50 char *ptr;
51 size_t size;
53 enum { num_escape_bufs = 3 };
54 static struct escape_buf escape_bufs[num_escape_bufs];
57 /*--------------------------------.
58 | Report information on a state. |
59 `--------------------------------*/
61 static void
62 print_core (FILE *out, int level, state *s)
64 item_index *sitems = s->items;
65 size_t snritems = s->nitems;
67 /* Output all the items of a state, not only its kernel. */
68 closure (sitems, snritems);
69 sitems = itemset;
70 snritems = nitemset;
72 if (!snritems)
74 xml_puts (out, level, "<itemset/>");
75 return;
78 xml_puts (out, level, "<itemset>");
80 for (size_t i = 0; i < snritems; i++)
82 bool printed = false;
83 item_number *sp1 = ritem + sitems[i];
84 rule const *r = item_rule (sp1);
85 item_number *sp = r->rhs;
87 /* Display the lookahead tokens? */
88 if (item_number_is_rule_number (*sp1))
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)
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;
105 if (!printed)
106 xml_printf (out, level + 1,
107 "<item rule-number=\"%d\" dot=\"%d\"/>",
108 r->number,
109 sp1 - sp);
111 xml_puts (out, level, "</itemset>");
115 /*-----------------------------------------------------------.
116 | Report the shifts if DISPLAY_SHIFTS_P or the gotos of S on |
117 | OUT. |
118 `-----------------------------------------------------------*/
120 static void
121 print_transitions (state *s, FILE *out, int level)
123 transitions *trans = s->transitions;
124 int n = 0;
125 int i;
127 for (i = 0; i < trans->num; i++)
128 if (!TRANSITION_IS_DISABLED (trans, i))
130 n++;
133 /* Nothing to report. */
134 if (!n)
136 xml_puts (out, level, "<transitions/>");
137 return;
140 /* Report lookahead tokens and shifts. */
141 xml_puts (out, level, "<transitions>");
143 for (i = 0; i < trans->num; i++)
144 if (!TRANSITION_IS_DISABLED (trans, i)
145 && TRANSITION_IS_SHIFT (trans, i))
147 symbol *sym = symbols[TRANSITION_SYMBOL (trans, i)];
148 char const *tag = sym->tag;
149 state *s1 = trans->states[i];
151 xml_printf (out, level + 1,
152 "<transition type=\"shift\" symbol=\"%s\" state=\"%d\"/>",
153 xml_escape (tag), s1->number);
156 for (i = 0; i < trans->num; i++)
157 if (!TRANSITION_IS_DISABLED (trans, i)
158 && !TRANSITION_IS_SHIFT (trans, i))
160 symbol *sym = symbols[TRANSITION_SYMBOL (trans, i)];
161 char const *tag = sym->tag;
162 state *s1 = trans->states[i];
164 xml_printf (out, level + 1,
165 "<transition type=\"goto\" symbol=\"%s\" state=\"%d\"/>",
166 xml_escape (tag), s1->number);
169 xml_puts (out, level, "</transitions>");
173 /*--------------------------------------------------------.
174 | Report the explicit errors of S raised from %nonassoc. |
175 `--------------------------------------------------------*/
177 static void
178 print_errs (FILE *out, int level, state *s)
180 errs *errp = s->errs;
181 bool count = false;
182 int i;
184 for (i = 0; i < errp->num; ++i)
185 if (errp->symbols[i])
186 count = true;
188 /* Nothing to report. */
189 if (!count)
191 xml_puts (out, level, "<errors/>");
192 return;
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])
200 char const *tag = errp->symbols[i]->tag;
201 xml_printf (out, level + 1,
202 "<error symbol=\"%s\">nonassociative</error>",
203 xml_escape (tag));
205 xml_puts (out, level, "</errors>");
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 `-------------------------------------------------------------------*/
215 static void
216 print_reduction (FILE *out, int level, char const *lookahead,
217 rule *r, bool enabled)
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");
233 /*-------------------------------------------.
234 | Report on OUT the reduction actions of S. |
235 `-------------------------------------------*/
237 static void
238 print_reductions (FILE *out, int level, state *s)
240 transitions *trans = s->transitions;
241 reductions *reds = s->reductions;
242 rule *default_reduction = NULL;
243 int report = false;
244 int i, j;
246 if (reds->num == 0)
248 xml_puts (out, level, "<reductions/>");
249 return;
252 if (yydefact[s->number] != 0)
253 default_reduction = &rules[yydefact[s->number] - 1];
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);
262 if (default_reduction)
263 report = true;
265 if (reds->lookaheads)
266 for (i = 0; i < ntokens; i++)
268 bool count = bitset_test (no_reduce_set, i);
270 for (j = 0; j < reds->num; ++j)
271 if (bitset_test (reds->lookaheads[j], i))
273 if (! count)
275 if (reds->rules[j] != default_reduction)
276 report = true;
277 count = true;
279 else
281 report = true;
286 /* Nothing to report. */
287 if (!report)
289 xml_puts (out, level, "<reductions/>");
290 return;
293 xml_puts (out, level, "<reductions>");
295 /* Report lookahead tokens (or $default) and reductions. */
296 if (reds->lookaheads)
297 for (i = 0; i < ntokens; i++)
299 bool defaulted = false;
300 bool count = bitset_test (no_reduce_set, i);
302 for (j = 0; j < reds->num; ++j)
303 if (bitset_test (reds->lookaheads[j], i))
305 if (! count)
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;
314 else
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);
326 if (default_reduction)
327 print_reduction (out, level + 1,
328 "$default", default_reduction, true);
330 xml_puts (out, level, "</reductions>");
334 /*--------------------------------------------------------------.
335 | Report on OUT all the actions (shifts, gotos, reductions, and |
336 | explicit errors from %nonassoc) of S. |
337 `--------------------------------------------------------------*/
339 static void
340 print_actions (FILE *out, int level, state *s)
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>");
350 /*----------------------------------.
351 | Report all the data on S on OUT. |
352 `----------------------------------*/
354 static void
355 print_state (FILE *out, int level, state *s)
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)
363 xml_puts (out, level + 1, "<solved-conflicts>");
364 fputs (s->solved_conflicts_xml, out);
365 xml_puts (out, level + 1, "</solved-conflicts>");
367 else
368 xml_puts (out, level + 1, "<solved-conflicts/>");
369 xml_puts (out, level, "</state>");
373 /*-----------------------------------------.
374 | Print information on the whole grammar. |
375 `-----------------------------------------*/
377 static void
378 print_grammar (FILE *out, int level)
380 fputc ('\n', out);
381 xml_puts (out, level, "<grammar>");
382 grammar_rules_print_xml (out, level);
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)
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);
408 xml_puts (out, level + 1, "</terminals>");
410 /* Nonterminals */
411 xml_puts (out, level + 1, "<nonterminals>");
412 for (symbol_number i = ntokens; i < nsyms + nuseless_nonterminals; i++)
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");
426 xml_puts (out, level + 1, "</nonterminals>");
427 xml_puts (out, level, "</grammar>");
430 void
431 xml_indent (FILE *out, int level)
433 for (int i = 0; i < level; i++)
434 fputs (" ", out);
437 void
438 xml_puts (FILE *out, int level, char const *s)
440 xml_indent (out, level);
441 fputs (s, out);
442 fputc ('\n', out);
445 void
446 xml_printf (FILE *out, int level, char const *fmt, ...)
448 va_list arglist;
450 xml_indent (out, level);
452 va_start (arglist, fmt);
453 vfprintf (out, fmt, arglist);
454 va_end (arglist);
456 fputc ('\n', out);
459 static char const *
460 xml_escape_string (struct escape_buf *buf, char const *str)
462 size_t len = strlen (str);
463 size_t max_expansion = sizeof "&quot;" - 1;
465 if (buf->size <= max_expansion * len)
467 buf->size = max_expansion * len + 1;
468 buf->ptr = x2realloc (buf->ptr, &buf->size);
470 char *p = buf->ptr;
472 for (; *str; str++)
473 switch (*str)
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;
482 *p = '\0';
483 return buf->ptr;
486 char const *
487 xml_escape_n (int n, char const *str)
489 return xml_escape_string (escape_bufs + n, str);
492 char const *
493 xml_escape (char const *str)
495 return xml_escape_n (0, str);
498 void
499 print_xml (void)
501 FILE *out = xfopen (spec_xml_file, "w");
503 fputs ("<?xml version=\"1.0\"?>\n\n", out);
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));
513 fputc ('\n', out);
514 xml_printf (out, level + 1, "<filename>%s</filename>",
515 xml_escape (grammar_file));
517 /* print grammar */
518 print_grammar (out, level + 1);
520 no_reduce_set = bitset_create (ntokens, BITSET_FIXED);
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>");
529 bitset_free (no_reduce_set);
531 xml_puts (out, 0, "</bison-xml-report>");
533 for (int i = 0; i < num_escape_bufs; ++i)
534 free (escape_bufs[i].ptr);
536 xfclose (out);
540 void
541 print_html (void)
543 assert (xml_flag);
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));
557 if (trace_flag & trace_tools)
559 fputs ("running:", stderr);
560 for (int j = 0; argv[j]; ++j)
561 fprintf (stderr, " %s", argv[j]);
562 fputc ('\n', stderr);
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);