/* 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 "expr.h" #include "symtab.h" #include "type.h" #include "qc-parse.h" 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 ""; default: return "unknown"; } } typedef void (*print_f) (expr_t *, int, int); static void _print_expr (expr_t *e, int level, int id); static void print_error (expr_t *e, int level, int id) { int indent = level * 2 + 2; printf ("%*s\"e_%p\" [label=\"(error)\\n%d\"];\n", indent, "", e, e->line); } static void print_state (expr_t *e, int level, int id) { int indent = level * 2 + 2; _print_expr (e->e.state.frame, level, id); _print_expr (e->e.state.think, level, id); if (e->e.state.step) _print_expr (e->e.state.step, level, id); printf ("%*s\"e_%p\":f -> \"e_%p\";\n", indent, "", e, e->e.state.frame); printf ("%*s\"e_%p\":t -> \"e_%p\";\n", indent, "", e, e->e.state.think); if (e->e.state.step) printf ("%*s\"e_%p\":s -> \"e_%p\";\n", indent, "", e, e->e.state.step); printf ("%*s\"e_%p\" [label=\"state|think|step\"," "shape=record];\n", indent, "", e); } static void print_bool (expr_t *e, int level, int id) { int indent = level * 2 + 2; _print_expr (e->e.bool.e, level, id); if (e->e.bool.e->type == ex_block && e->e.bool.e->e.block.head) { expr_t *se; printf ("%*s\"e_%p\" -> \"e_%p\";\n", indent, "", e, e->e.bool.e); se = (expr_t *) e->e.bool.e->e.block.tail; if (se && se->type == ex_label && e->next) printf ("%*s\"e_%p\" -> \"e_%p\" " "[constraint=false,style=dashed];\n", indent, "", se, e->next); } else { printf ("%*s\"e_%p\" -> \"e_%p\";\n", indent, "", e, e->e.bool.e); } printf ("%*s\"e_%p\" [label=\"\\n%d\"];\n", indent, "", e, e->line); } static void print_label (expr_t *e, int level, int id) { int indent = level * 2 + 2; if (e->next) printf ("%*s\"e_%p\" -> \"e_%p\" " "[constraint=false,style=dashed];\n", indent, "", e, e->next); printf ("%*s\"e_%p\" [label=\"%s\\n%d\"];\n", indent, "", e, e->e.label.name, e->line); } static void print_block (expr_t *e, int level, int id) { int indent = level * 2 + 2; expr_t *se; if (e->e.block.result) { _print_expr (e->e.block.result, level + 1, id); printf ("%*s\"e_%p\" -> \"e_%p\";\n", indent, "", e, e->e.block.result); } printf ("%*s\"e_%p\" -> \"e_%p\" [style=dashed];\n", indent, "", e, e->e.block.head); //printf ("%*ssubgraph cluster_%p {\n", indent, "", e); for (se = e->e.block.head; se; se = se->next) { _print_expr (se, level + 1, id); } for (se = e->e.block.head; se && se->next; se = se->next) { if ((se->type == ex_uexpr && se->e.expr.op == 'g') || se->type == ex_label || se->type == ex_bool) continue; printf ("%*s\"e_%p\" -> \"e_%p\" [constraint=false,style=dashed];\n", indent, "", se, se->next); } if (se && se->type == ex_label && e->next) printf ("%*s\"e_%p\" -> \"e_%p\" [constraint=false,style=dashed];\n", indent, "", se, e->next); //printf ("%*s}\n", indent, ""); printf ("%*s\"e_%p\" [label=\"\\n%d\"];\n", indent, "", e, e->line); } static void print_call (expr_t *e, int level, int id) { 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 (e->e.expr.e1, level, id); printf ("%*s\"e_%p\" [label=\"call", indent, "", e); for (i = 0; i < count; i++) printf ("|p%d", i, i); printf ("\",shape=record];\n"); for (i = 0; i < count; i++) { _print_expr (args[i], level + 1, id); printf ("%*s\"e_%p\":p%d -> \"e_%p\";\n", indent + 2, "", e, i, args[i]); } printf ("%*s\"e_%p\":c -> \"e_%p\";\n", indent, "", e, e->e.expr.e1); } static void print_subexpr (expr_t *e, int level, int id) { int indent = level * 2 + 2; if (e->e.expr.op == 'c') { print_call (e, level, id); 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 (e->e.expr.e1, level, id); printf ("%*s\"e_%p\" -> \"e_%p\" [label=\"t\"];\n", indent, "", e, e->e.expr.e1); printf ("%*s\"e_%p\" -> \"e_%p\" [label=\"g\"];\n", indent, "", e, e->e.expr.e2); } else { _print_expr (e->e.expr.e1, level, id); _print_expr (e->e.expr.e2, level, id); printf ("%*s\"e_%p\" -> \"e_%p\" [label=\"l\"];\n", indent, "", e, e->e.expr.e1); printf ("%*s\"e_%p\" -> \"e_%p\" [label=\"r\"];\n", indent, "", e, e->e.expr.e2); } printf ("%*s\"e_%p\" [label=\"%s\\n%d\"];\n", indent, "", e, get_op_string (e->e.expr.op), e->line); } static void print_uexpr (expr_t *e, int level, int id) { int indent = level * 2 + 2; dstring_t *dstr = dstring_newstr(); if (e->e.expr.op != 'g') _print_expr (e->e.expr.e1, level, id); if (e->e.expr.op == 'A') { dstring_copystr (dstr, "\\n"); print_type_str (dstr, e->e.expr.type); } printf ("%*s\"e_%p\" -> \"e_%p\";\n", indent, "", e, e->e.expr.e1); printf ("%*s\"e_%p\" [label=\"%s%s\\n%d\"];\n", indent, "", e, get_op_string (e->e.expr.op), dstr->str, e->line); dstring_delete (dstr); } static void print_symbol (expr_t *e, int level, int id) { int indent = level * 2 + 2; printf ("%*s\"e_%p\" [label=\"%s\\n%d\"];\n", indent, "", e, e->e.symbol->name, e->line); } static void print_temp (expr_t *e, int level, int id) { int indent = level * 2 + 2; printf ("%*s\"e_%p\" [label=\"tmp_%p\\n%d\"];\n", indent, "", e, e, e->line); } static void print_nil (expr_t *e, int level, int id) { int indent = level * 2 + 2; printf ("%*s\"e_%p\" [label=\"nil\\n%d\"];\n", indent, "", e, e->line); } static void print_value (expr_t *e, int level, int id) { int indent = level * 2 + 2; type_t *type; const char *label = "?!?"; switch (e->e.value.type) { case ev_string: label = va ("\\\"%s\\\"", 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; } printf ("%*s\"e_%p\" [label=\"%s\\n%d\"];\n", indent, "", e, label, e->line); } static void _print_expr (expr_t *e, int level, int id) { static print_f print_funcs[] = { print_error, print_state, print_bool, print_label, print_block, print_subexpr, print_uexpr, print_symbol, print_temp, print_nil, print_value, }; int indent = level * 2 + 2; if (!e) { printf ("%*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) { printf ("%*s\"e_%p\" [label=\"(bad expr type)\\n%d\"];\n", indent, "", e, e->line); return; } print_funcs [e->type] (e, level, id); } void print_expr (expr_t *e) { static int id = 0; printf ("digraph expr_%p {\n", e); printf (" layout=dot; rankdir=TB; compound=true;\n"); _print_expr (e, 0, ++id); printf ("}\n"); }