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 42 static bitset no_reduce_set; 43 struct escape_buf 44 { 45 char *ptr; 46 size_t size; 47 }; 48 static struct escape_buf escape_bufs[3]; 49 50 51 /*--------------------------------. 52 | Report information on a state. | 53 `--------------------------------*/ 54 55 static void 56 print_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 124 static void 125 print_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 180 static void 181 print_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 217 static void 218 print_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 239 static void 240 print_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 340 static void 341 print_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 355 static void 356 print_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 378 static void 379 print_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 426 void 427 xml_indent (FILE *out, int level) 428 { 429 int i; 430 for (i = 0; i < level; i++) 431 fputs (" ", out); 432 } 433 434 void 435 xml_puts (FILE *out, int level, char const *s) 436 { 437 xml_indent (out, level); 438 fputs (s, out); 439 fputc ('\n', out); 440 } 441 442 void 443 xml_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 456 static char const * 457 xml_escape_string (struct escape_buf *buf, char const *str) 458 { 459 size_t len = strlen (str); 460 size_t max_expansion = sizeof """ - 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, "&" ); break; 475 case '<': p = stpcpy (p, "<" ); break; 476 case '>': p = stpcpy (p, ">" ); break; 477 case '"': p = stpcpy (p, """); break; 478 } 479 480 *p = '\0'; 481 return buf->ptr; 482 } 483 484 char const * 485 xml_escape_n (int n, char const *str) 486 { 487 return xml_escape_string (escape_bufs + n, str); 488 } 489 490 char const * 491 xml_escape (char const *str) 492 { 493 return xml_escape_n (0, str); 494 } 495 496 void 497 print_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