/* dot_expr.c "emit" expressions to dot (graphvis). Copyright (C) 2011 Bill Currie Author: Bill Currie Date: 2011/01/20 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 2 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, write to: Free Software Foundation, Inc. 59 Temple Place - Suite 330 Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #ifdef HAVE_STRING_H # include #endif #ifdef HAVE_STRINGS_H # include #endif #include #include #include #include #include #include "expr.h" #include "symtab.h" #include "type.h" #include "qc-parse.h" #include "strpool.h" static const char *expr_names[] = { "error", "state", "bool", "label", "labelref", "block", "expr", "uexpr", "symbol", "temp", "nil", "value", }; const char * get_op_string (int op) { switch (op) { case PAS: return ".="; case OR: return "||"; case AND: return "&&"; case EQ: return "=="; case NE: return "!="; case LE: return "<="; case GE: return ">="; case LT: return "<"; case GT: return ">"; case '=': return "="; case '+': return "+"; case '-': return "-"; case '*': return "*"; case '/': return "/"; case '%': return "%"; case '&': return "&"; case '|': return "|"; case '^': return "^"; case '~': return "~"; case '!': return "!"; case SHL: return "<<"; case SHR: return ">>"; case '.': return "."; case 'i': return ""; case 'n': return ""; case IFBE: return ""; case IFB: return ""; case IFAE: return ""; case IFA: return ""; case 'g': return ""; case 'r': return ""; case 's': return ""; case 'c': return ""; case 'A': return ""; case 'C': return ""; case 'M': return ""; case 'm': return ""; default: return "unknown"; } } typedef void (*print_f) (dstring_t *dstr, expr_t *, int, int, expr_t *); static void _print_expr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next); static void print_error (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; dasprintf (dstr, "%*se_%p [label=\"(error)\\n%d\"];\n", indent, "", e, e->line); } static void print_state (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; _print_expr (dstr, e->e.state.frame, level, id, next); _print_expr (dstr, e->e.state.think, level, id, next); if (e->e.state.step) _print_expr (dstr, e->e.state.step, level, id, next); dasprintf (dstr, "%*se_%p:f -> \"e_%p\";\n", indent, "", e, e->e.state.frame); dasprintf (dstr, "%*se_%p:t -> \"e_%p\";\n", indent, "", e, e->e.state.think); if (e->e.state.step) dasprintf (dstr, "%*se_%p:s -> e_%p;\n", indent, "", e, e->e.state.step); dasprintf (dstr, "%*se_%p [label=\"state|think|step\",shape=record];\n", indent, "", e); } static void print_bool (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; int i, count; ex_bool_t *bool = &e->e.bool; dasprintf (dstr, "%*se_%p [shape=none,label=<\n", indent, "", e); dasprintf (dstr, "%*s\n", indent + 2, ""); dasprintf (dstr, "%*s\n", indent + 4, "", e->line); dasprintf (dstr, "%*s\n", indent + 4, ""); count = min (bool->true_list->size, bool->false_list->size); for (i = 0; i < count; i++) dasprintf (dstr, "%*s" "\n", indent, "", i, i); for ( ; i < bool->true_list->size; i++) dasprintf (dstr, "%*s%s\n", indent, "", i, i == count ? va ("", bool->true_list->size - count) : ""); for ( ; i < bool->false_list->size; i++) dasprintf (dstr, "%*s%s\n", indent, "", i == count ? va ("", bool->false_list->size - count) : "", i); dasprintf (dstr, "%*s
<bool>(%d)
truefalse
tf
t
f
\n", indent + 2, ""); dasprintf (dstr, "%*s>];\n", indent, ""); if (e->next) next = e->next; _print_expr (dstr, e->e.bool.e, level, id, next); for (i = 0; i < bool->true_list->size; i++) dasprintf (dstr, "%*se_%p:t%d -> e_%p;\n", indent, "", e, i, bool->true_list->e[i]); for (i = 0; i < bool->false_list->size; i++) dasprintf (dstr, "%*se_%p:f%d -> e_%p;\n", indent, "", e, i, bool->false_list->e[i]); dasprintf (dstr, "%*se_%p -> e_%p;\n", indent, "", e, e->e.bool.e); } static void print_label (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; if (e->next) next = e->next; if (next) dasprintf (dstr, "%*se_%p -> e_%p [constraint=false,style=dashed];\n", indent, "", e, next); dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, e->e.label.name, e->line); } static void print_labelref (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; if (e->next) next = e->next; if (next) dasprintf (dstr, "%*se_%p -> e_%p [constraint=false,style=dashed];\n", indent, "", e, e->next); dasprintf (dstr, "%*se_%p [label=\"&%s\\n%d\"];\n", indent, "", e, e->e.label.name, e->line); } static void print_block (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; int i; expr_t *se; dasprintf (dstr, "%*se_%p [shape=none,label=<\n", indent, "", e); dasprintf (dstr, "%*s\n", indent + 2, ""); dasprintf (dstr, "%*s" "\n", indent + 4, "", e->line, e->e.block.is_call ? "c" : ""); if (e->e.block.result) dasprintf (dstr, "%*s" "\n", indent + 4, ""); for (se = e->e.block.head, i = 0; se; se = se->next, i++) dasprintf (dstr, "%*s\n", indent + 4, "", i, i, expr_names[se->type]); dasprintf (dstr, "%*s
<block>(%d)%s
=
%d%s
\n", indent + 2, ""); dasprintf (dstr, "%*s>];\n", indent, ""); if (e->e.block.result) { _print_expr (dstr, e->e.block.result, level + 1, id, next); dasprintf (dstr, "%*se_%p:result -> e_%p;\n", indent, "", e, e->e.block.result); } if (e->next) next = e->next; for (se = e->e.block.head, i = 0; se; se = se->next, i++) { _print_expr (dstr, se, level + 1, id, next); dasprintf (dstr, "%*se_%p:b%d -> e_%p;\n", indent, "", e, i, se); } } static void print_call (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; expr_t *p; int i, count; expr_t **args; for (count = 0, p = e->e.expr.e2; p; p = p->next) count++; args = alloca (count * sizeof (expr_t *)); for (i = 0, p = e->e.expr.e2; p; p = p->next, i++) args[count - 1 - i] = p; _print_expr (dstr, e->e.expr.e1, level, id, next); dasprintf (dstr, "%*se_%p [label=\"call", indent, "", e); for (i = 0; i < count; i++) dasprintf (dstr, "|p%d", i, i); dasprintf (dstr, "\",shape=record];\n"); for (i = 0; i < count; i++) { _print_expr (dstr, args[i], level + 1, id, next); dasprintf (dstr, "%*se_%p:p%d -> e_%p;\n", indent + 2, "", e, i, args[i]); } dasprintf (dstr, "%*se_%p:c -> e_%p;\n", indent, "", e, e->e.expr.e1); } static void print_subexpr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; if (e->e.expr.op == 'c') { print_call (dstr, e, level, id, next); return; } else if (e->e.expr.op == 'i' || e->e.expr.op == 'n' || e->e.expr.op == IFB || e->e.expr.op ==IFBE || e->e.expr.op == IFA || e->e.expr.op ==IFAE) { _print_expr (dstr, e->e.expr.e1, level, id, next); dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"t\"];\n", indent, "", e, e->e.expr.e1); dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"g\"];\n", indent, "", e, e->e.expr.e2); if (e->next) next = e->next; if (next) dasprintf (dstr, "%*se_%p -> e_%p [constraint=false," "style=dashed];\n", indent, "", e, next); } else { _print_expr (dstr, e->e.expr.e1, level, id, next); _print_expr (dstr, e->e.expr.e2, level, id, next); dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"l\"];\n", indent, "", e, e->e.expr.e1); dasprintf (dstr, "%*se_%p -> \"e_%p\" [label=\"r\"];\n", indent, "", e, e->e.expr.e2); } dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, get_op_string (e->e.expr.op), e->line); } static void print_uexpr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; dstring_t *typestr = dstring_newstr(); if (e->e.expr.op != 'g' && e->e.expr.e1) _print_expr (dstr, e->e.expr.e1, level, id, next); if (e->e.expr.op == 'A') { dstring_copystr (typestr, "\\n"); print_type_str (typestr, e->e.expr.type); } if (e->e.expr.op != 'r' || e->e.expr.e1) dasprintf (dstr, "%*se_%p -> \"e_%p\";\n", indent, "", e, e->e.expr.e1); dasprintf (dstr, "%*se_%p [label=\"%s%s\\n%d\"];\n", indent, "", e, get_op_string (e->e.expr.op), typestr->str, e->line); dstring_delete (typestr); } static void print_symbol (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, e->e.symbol->name, e->line); } static void print_temp (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; dasprintf (dstr, "%*se_%p [label=\"tmp_%p\\n%d\"];\n", indent, "", e, e, e->line); } static void print_nil (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; dasprintf (dstr, "%*se_%p [label=\"nil\\n%d\"];\n", indent, "", e, e->line); } static void print_value (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { int indent = level * 2 + 2; type_t *type; const char *label = "?!?"; switch (e->e.value->type) { case ev_string: label = va ("\\\"%s\\\"", quote_string (e->e.value->v.string_val)); break; case ev_float: label = va ("%g", e->e.value->v.float_val); break; case ev_vector: label = va ("'%g %g %g'", e->e.value->v.vector_val[0], e->e.value->v.vector_val[1], e->e.value->v.vector_val[2]); break; case ev_quat: label = va ("'%g %g %g %g'", e->e.value->v.quaternion_val[0], e->e.value->v.quaternion_val[1], e->e.value->v.quaternion_val[2], e->e.value->v.quaternion_val[3]); break; case ev_pointer: type = e->e.value->v.pointer.type; if (e->e.value->v.pointer.def) label = va ("(%s)[%d]<%s>", type ? pr_type_name[type->type] : "???", e->e.value->v.pointer.val, e->e.value->v.pointer.def->name); else label = va ("(%s)[%d]", type ? pr_type_name[type->type] : "???", e->e.value->v.pointer.val); break; case ev_field: label = va ("field %d", e->e.value->v.pointer.val); break; case ev_entity: label = va ("ent %d", e->e.value->v.integer_val); break; case ev_func: label = va ("func %d", e->e.value->v.integer_val); break; case ev_integer: label = va ("%d", e->e.value->v.integer_val); break; case ev_uinteger: label = va ("%u", e->e.value->v.uinteger_val); break; case ev_short: label = va ("%d", e->e.value->v.short_val); break; case ev_void: label = ""; break; case ev_invalid: label = ""; break; case ev_type_count: label = ""; break; } dasprintf (dstr, "%*se_%p [label=\"%s\\n%d\"];\n", indent, "", e, label, e->line); } static void _print_expr (dstring_t *dstr, expr_t *e, int level, int id, expr_t *next) { static print_f print_funcs[] = { print_error, print_state, print_bool, print_label, print_labelref, print_block, print_subexpr, print_uexpr, print_symbol, print_temp, print_nil, print_value, }; int indent = level * 2 + 2; if (!e) { dasprintf (dstr, "%*s\"e_%p\" [label=\"(null)\"];\n", indent, "", e); return; } if (e->printid == id) // already printed this expression return; e->printid = id; if (e->type < 0 || e->type > ex_value) { dasprintf (dstr, "%*se_%p [label=\"(bad expr type)\\n%d\"];\n", indent, "", e, e->line); return; } print_funcs [e->type] (dstr, e, level, id, next); } void dump_dot_expr (void *_e, const char *filename) { static int id = 0; dstring_t *dstr = dstring_newstr (); expr_t *e = (expr_t *) _e; dasprintf (dstr, "digraph expr_%p {\n", e); dasprintf (dstr, " layout=dot; rankdir=TB; compound=true;\n"); _print_expr (dstr, e, 0, ++id, 0); dasprintf (dstr, "}\n"); if (filename) { QFile *file; file = Qopen (filename, "wt"); Qwrite (file, dstr->str, dstr->size - 1); Qclose (file); } else { fputs (dstr->str, stdout); } dstring_delete (dstr); } void print_expr (expr_t *e) { dump_dot_expr (e, 0); }