| /* Counterexample derivation trees | |
| Copyright (C) 2020-2021 Free Software Foundation, Inc. | |
| This file is part of Bison, the GNU Compiler Compiler. | |
| This program is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation, either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <https://www.gnu.org/licenses/>. */ | |
| #include <config.h> | |
| #include "derivation.h" | |
| #include "glyphs.h" | |
| #include <c-ctype.h> | |
| #include <gl_linked_list.h> | |
| #include <mbswidth.h> | |
| #include <vasnprintf.h> | |
| #include "system.h" | |
| #include "complain.h" | |
| struct derivation | |
| { | |
| symbol_number sym; | |
| derivation_list children; | |
| int reference_count; | |
| // The rule SYM -> CHILDREN. | |
| const rule *rule; | |
| // Color assigned for styling. Guarantees that the derivation is | |
| // always displayed with the same color, independently of the order | |
| // in which the derivations are traversed. | |
| int color; | |
| }; | |
| static derivation d_dot = { -1, NULL, -1, NULL, -1 }; | |
| derivation * | |
| derivation_dot (void) | |
| { | |
| return &d_dot; | |
| } | |
| void | |
| derivation_list_append (derivation_list dl, derivation *d) | |
| { | |
| derivation_retain (d); | |
| gl_list_add_last (dl, d); | |
| } | |
| void | |
| derivation_list_prepend (derivation_list dl, derivation *d) | |
| { | |
| derivation_retain (d); | |
| gl_list_add_first (dl, d); | |
| } | |
| void derivation_list_free (derivation_list dl) | |
| { | |
| derivation *d = NULL; | |
| for (gl_list_iterator_t it = gl_list_iterator (dl); | |
| derivation_list_next (&it, &d); | |
| ) | |
| if (d != &d_dot) | |
| derivation_free (d); | |
| gl_list_free (dl); | |
| } | |
| derivation * | |
| derivation_new (symbol_number sym, derivation_list children, | |
| const rule *r) | |
| { | |
| derivation *res = xmalloc (sizeof *res); | |
| res->sym = sym; | |
| res->children = children; | |
| res->reference_count = 0; | |
| res->rule = r; | |
| res->color = -1; | |
| return res; | |
| } | |
| void | |
| derivation_retain (derivation *d) | |
| { | |
| ++d->reference_count; | |
| } | |
| void | |
| derivation_free (derivation *d) | |
| { | |
| if (!d) | |
| return; | |
| derivation_list free_queue = | |
| gl_list_create (GL_LINKED_LIST, NULL, NULL, NULL, true, | |
| 1, (const void **)&d); | |
| while (gl_list_size (free_queue) > 0) | |
| { | |
| derivation *deriv = (derivation *) gl_list_get_at (free_queue, 0); | |
| if (--deriv->reference_count == 0) | |
| { | |
| if (deriv->children) | |
| { | |
| derivation *child = NULL; | |
| for (gl_list_iterator_t it = gl_list_iterator (deriv->children); | |
| derivation_list_next (&it, &child); | |
| ) | |
| if (child != &d_dot) | |
| gl_list_add_last (free_queue, child); | |
| gl_list_free (deriv->children); | |
| } | |
| free (deriv); | |
| } | |
| gl_list_remove_at (free_queue, 0); | |
| } | |
| gl_list_free (free_queue); | |
| } | |
| size_t | |
| derivation_size (const derivation *deriv) | |
| { | |
| if (!deriv->children) | |
| return 1; | |
| int size = 1; | |
| derivation *child = NULL; | |
| for (gl_list_iterator_t it = gl_list_iterator (deriv->children); | |
| derivation_list_next (&it, &child); | |
| ) | |
| size += derivation_size (child); | |
| return size; | |
| } | |
| // Longest distance from root to leaf. | |
| static int | |
| derivation_depth (const derivation *deriv) | |
| { | |
| if (deriv->children) | |
| { | |
| // Children's depth cannot be 0, even if there are no children | |
| // (the case of a derivation with an empty RHS). | |
| int res = 1; | |
| derivation *child; | |
| for (gl_list_iterator_t it = gl_list_iterator (deriv->children); | |
| derivation_list_next (&it, &child); | |
| ) | |
| res = max_int (res, derivation_depth (child)); | |
| return res + 1; | |
| } | |
| else | |
| return 1; | |
| } | |
| static bool | |
| all_spaces (const char *s) | |
| { | |
| while (c_isspace (*s)) | |
| s++; | |
| return *s == '\0'; | |
| } | |
| // Printing the derivation as trees without trailing spaces is | |
| // painful: we cannot simply pad one "column" before moving to the | |
| // next: | |
| // | |
| // exp | |
| // ↳ x1 e1 foo1 x1 | |
| // ↳ x2 ↳ ε ↳ foo2 ↳ x2 | |
| // ↳ x3 ↳ foo3 ↳ x3 | |
| // ↳ "X" • ↳ x1 foo4 ↳ "X" | |
| // ↳ x2 ↳ "quuux" | |
| // ↳ x3 | |
| // ↳ "X" | |
| // | |
| // It's hard for a column to know that it's "last" to decide whether | |
| // to output the right-padding or not. So when we need to pad on the | |
| // right to complete a column, we don't output the spaces, we | |
| // accumulate the width of padding in *PADDING. | |
| // | |
| // Each time we actually print something (non space), we flush that | |
| // padding. When we _don't_ print something, its width is added to | |
| // the current padding. | |
| // | |
| // This function implements this. | |
| // | |
| // When COND is true, put S on OUT, preceded by *PADDING white spaces. | |
| // Otherwise add the width to *PADDING. Return the width of S. | |
| static int | |
| fputs_if (bool cond, FILE *out, int *padding, const char *s) | |
| { | |
| int res = mbswidth (s, 0); | |
| if (cond && !all_spaces (s)) | |
| { | |
| fprintf (out, "%*s%s", *padding, "", s); | |
| *padding = 0; | |
| } | |
| else | |
| { | |
| *padding += res; | |
| } | |
| return res; | |
| } | |
| static int | |
| fprintf_if (bool cond, FILE *out, int *padding, const char *fmt, ...) | |
| { | |
| char buf[256]; | |
| size_t len = sizeof (buf); | |
| va_list args; | |
| va_start (args, fmt); | |
| char *cp = vasnprintf (buf, &len, fmt, args); | |
| va_end (args); | |
| if (!cp) | |
| xalloc_die (); | |
| int res = fputs_if (cond, out, padding, cp); | |
| if (cp != buf) | |
| free (cp); | |
| return res; | |
| } | |
| // The width taken to report this derivation recursively down to its | |
| // leaves. | |
| static int | |
| derivation_width (const derivation *deriv) | |
| { | |
| if (deriv->children) | |
| { | |
| const symbol *sym = symbols[deriv->sym]; | |
| int self_width = mbswidth (sym->tag, 0); | |
| // Arrow and space. | |
| int children_width = down_arrow_width; | |
| children_width += snprintf (NULL, 0, "%d: ", deriv->rule->number); | |
| if (gl_list_size (deriv->children) == 0) | |
| // Empty rhs. | |
| children_width += empty_width; | |
| else | |
| { | |
| if (gl_list_size (deriv->children) == 1 | |
| && gl_list_get_first (deriv->children) == &d_dot) | |
| { | |
| children_width += empty_width; | |
| children_width += derivation_separator_width; | |
| } | |
| derivation *child; | |
| for (gl_list_iterator_t it = gl_list_iterator (deriv->children); | |
| derivation_list_next (&it, &child); | |
| ) | |
| children_width | |
| += derivation_separator_width + derivation_width (child); | |
| // No separator at the beginning. | |
| children_width -= derivation_separator_width; | |
| } | |
| return max_int (self_width, children_width); | |
| } | |
| else if (deriv == &d_dot) | |
| { | |
| return dot_width; | |
| } | |
| else // leaf. | |
| { | |
| const symbol *sym = symbols[deriv->sym]; | |
| return mbswidth (sym->tag, 0); | |
| } | |
| } | |
| // Print DERIV for DEPTH. | |
| // | |
| // The tree is printed from top to bottom with DEPTH ranging from 0 to | |
| // the total depth of the tree. DERIV should only printed when we | |
| // reach its depth, i.e., then DEPTH is 0. | |
| // | |
| // When DEPTH is 1 and we're on a subderivation, then we print the RHS | |
| // of the derivation (in DEPTH 0 we printed its LHS). | |
| // | |
| // Return the "logical printed" width. We might have not have reached | |
| // that width, in which case the missing spaces are in *PADDING. | |
| static int | |
| derivation_print_tree_impl (const derivation *deriv, FILE *out, | |
| int depth, int *padding) | |
| { | |
| const int width = derivation_width (deriv); | |
| int res = 0; | |
| if (deriv->children) | |
| { | |
| const symbol *sym = symbols[deriv->sym]; | |
| char style[20]; | |
| snprintf (style, 20, "cex-%d", deriv->color); | |
| if (depth == 0 || depth == 1) | |
| { | |
| begin_use_class (style, out); | |
| begin_use_class ("cex-step", out); | |
| } | |
| if (depth == 0) | |
| { | |
| res += fputs_if (true, out, padding, sym->tag); | |
| } | |
| else | |
| { | |
| res += fputs_if (depth == 1, out, padding, down_arrow); | |
| res += fprintf_if (depth == 1, out, padding, "%d: ", deriv->rule->number); | |
| if (gl_list_size (deriv->children) == 0) | |
| // Empty rhs. | |
| res += fputs_if (depth == 1, out, padding, empty); | |
| else | |
| { | |
| if (gl_list_size (deriv->children) == 1 | |
| && gl_list_get_first (deriv->children) == &d_dot) | |
| { | |
| res += fputs_if (depth == 1, out, padding, empty); | |
| res += fputs_if (depth == 1, out, padding, derivation_separator); | |
| } | |
| bool first = true; | |
| derivation *child; | |
| for (gl_list_iterator_t it = gl_list_iterator (deriv->children); | |
| derivation_list_next (&it, &child); | |
| ) | |
| { | |
| if (!first) | |
| res += fputs_if (depth == 1, out, padding, derivation_separator); | |
| res += derivation_print_tree_impl (child, out, depth - 1, padding); | |
| first = false; | |
| } | |
| } | |
| } | |
| if (depth == 0 || depth == 1) | |
| { | |
| end_use_class ("cex-step", out); | |
| end_use_class (style, out); | |
| } | |
| *padding += width - res; | |
| res = width; | |
| } | |
| else if (deriv == &d_dot) | |
| { | |
| if (depth == 0) | |
| begin_use_class ("cex-dot", out); | |
| res += fputs_if (depth == 0, out, padding, dot); | |
| if (depth == 0) | |
| end_use_class ("cex-dot", out); | |
| } | |
| else // leaf. | |
| { | |
| const symbol *sym = symbols[deriv->sym]; | |
| if (depth == 0) | |
| begin_use_class ("cex-leaf", out); | |
| res += fputs_if (depth == 0, out, padding, sym->tag); | |
| if (depth == 0) | |
| end_use_class ("cex-leaf", out); | |
| } | |
| return res; | |
| } | |
| static void | |
| derivation_print_tree (const derivation *deriv, FILE *out, const char *prefix) | |
| { | |
| fputc ('\n', out); | |
| for (int depth = 0, max_depth = derivation_depth (deriv); | |
| depth < max_depth; ++depth) | |
| { | |
| int padding = 0; | |
| fprintf (out, " %s", prefix); | |
| derivation_print_tree_impl (deriv, out, depth, &padding); | |
| fputc ('\n', out); | |
| } | |
| } | |
| /* Print DERIV, colored according to COUNTER. | |
| Return false if nothing is printed. */ | |
| static bool | |
| derivation_print_flat_impl (derivation *deriv, FILE *out, | |
| bool leaves_only, | |
| int *counter, const char *prefix) | |
| { | |
| if (deriv->children) | |
| { | |
| const symbol *sym = symbols[deriv->sym]; | |
| deriv->color = *counter; | |
| ++*counter; | |
| char style[20]; | |
| snprintf (style, 20, "cex-%d", deriv->color); | |
| begin_use_class (style, out); | |
| if (!leaves_only) | |
| { | |
| fputs (prefix, out); | |
| begin_use_class ("cex-step", out); | |
| fprintf (out, "%s %s [ ", sym->tag, arrow); | |
| end_use_class ("cex-step", out); | |
| prefix = ""; | |
| } | |
| bool res = false; | |
| derivation *child; | |
| for (gl_list_iterator_t it = gl_list_iterator (deriv->children); | |
| derivation_list_next (&it, &child); | |
| ) | |
| { | |
| if (derivation_print_flat_impl (child, out, | |
| leaves_only, counter, prefix)) | |
| { | |
| prefix = " "; | |
| res = true; | |
| } | |
| else if (!leaves_only) | |
| prefix = " "; | |
| } | |
| if (!leaves_only) | |
| { | |
| begin_use_class ("cex-step", out); | |
| if (res) | |
| fputs (" ]", out); | |
| else | |
| fputs ("]", out); | |
| end_use_class ("cex-step", out); | |
| } | |
| end_use_class (style, out); | |
| return res; | |
| } | |
| else if (deriv == &d_dot) | |
| { | |
| fputs (prefix, out); | |
| begin_use_class ("cex-dot", out); | |
| fputs (dot, out); | |
| end_use_class ("cex-dot", out); | |
| } | |
| else // leaf. | |
| { | |
| fputs (prefix, out); | |
| const symbol *sym = symbols[deriv->sym]; | |
| begin_use_class ("cex-leaf", out); | |
| fprintf (out, "%s", sym->tag); | |
| end_use_class ("cex-leaf", out); | |
| } | |
| return true; | |
| } | |
| static void | |
| derivation_print_flat (const derivation *deriv, FILE *out, const char *prefix) | |
| { | |
| int counter = 0; | |
| fputs (prefix, out); | |
| derivation_print_flat_impl ((derivation *)deriv, out, false, &counter, ""); | |
| fputc ('\n', out); | |
| } | |
| void | |
| derivation_print_leaves (const derivation *deriv, FILE *out) | |
| { | |
| int counter = 0; | |
| derivation_print_flat_impl ((derivation *)deriv, out, true, &counter, ""); | |
| fputc ('\n', out); | |
| } | |
| void | |
| derivation_print (const derivation *deriv, FILE *out, const char *prefix) | |
| { | |
| if (getenv ("YYFLAT")) | |
| derivation_print_flat (deriv, out, prefix); | |
| else | |
| derivation_print_tree (deriv, out, prefix); | |
| } |