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