1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
|
/*
* unparse.c - Dump an expression tree into a string
*
* Written 2009 by Werner Almesberger
* Copyright 2009 by Werner Almesberger
*
* 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 is crazily inefficient but who cares :-)
*/
#include <stdlib.h>
#include <stdio.h>
#include "util.h"
#include "expr.h"
#include "unparse.h"
enum prec {
prec_add,
prec_mult,
prec_unary,
prec_primary,
};
static int precedence(op_type op)
{
if (op == op_add || op == op_sub)
return prec_add;
if (op == op_mult || op == op_div)
return prec_mult;
if (op == op_minus)
return prec_unary;
if (op == op_num || op == op_string || op == op_var ||
op == op_sin || op == op_cos || op == op_sqrt)
return prec_primary;
abort();
}
static char *merge3(char *a, const char *op, char *b)
{
char *buf;
buf = alloc_size(strlen(op)+strlen(a)+strlen(b)+1);
sprintf(buf, "%s%s%s", a, op, b);
free(a);
free(b);
return buf;
}
static char *merge2(const char *op, char *a)
{
char *buf;
buf = alloc_size(strlen(op)+strlen(a)+1);
sprintf(buf, "%s%s", op, a);
free(a);
return buf;
}
static char *unparse_op(const struct expr *expr, enum prec prec);
static char *unparse_fn(const char *name, const struct expr *expr)
{
char *buf, *tmp;
tmp = unparse_op(expr->u.op.a, prec_add);
buf = alloc_size(strlen(name)+strlen(tmp)+3);
sprintf(buf, "%s(%s)", name, tmp);
free(tmp);
return buf;
}
static char *unparse_op(const struct expr *expr, enum prec prec)
{
char tmp[100];
char *buf, *temp;
if (prec > precedence(expr->op)) {
temp = unparse_op(expr, prec_add);
buf = alloc_size(strlen(temp)+3);
sprintf(buf, "(%s)", temp);
free(temp);
return buf;
}
if (expr->op == op_num) {
snprintf(tmp, sizeof(tmp), "%lg%s",
expr->u.num.n, str_unit(expr->u.num));
return stralloc(tmp);
}
if (expr->op == op_string)
return stralloc_printf("\"%s\"", expr->u.str);
if (expr->op == op_var)
return stralloc(expr->u.var);
if (expr->op == op_minus)
return merge2("-", unparse_op(expr->u.op.a, prec_unary));
if (expr->op == op_add)
return merge3(unparse_op(expr->u.op.a, prec_add), "+",
unparse_op(expr->u.op.b, prec_add));
if (expr->op == op_sub)
return merge3(unparse_op(expr->u.op.a, prec_add), "-",
unparse_op(expr->u.op.b, prec_mult));
if (expr->op == op_mult)
return merge3(unparse_op(expr->u.op.a, prec_mult), "*",
unparse_op(expr->u.op.b, prec_mult));
if (expr->op == op_div)
return merge3(unparse_op(expr->u.op.a, prec_mult), "/",
unparse_op(expr->u.op.b, prec_primary));
if (expr->op == op_sin)
return unparse_fn("sin", expr);
if (expr->op == op_cos)
return unparse_fn("cos", expr);
if (expr->op == op_sqrt)
return unparse_fn("sqrt", expr);
abort();
}
char *unparse(const struct expr *expr)
{
return expr ? unparse_op(expr, prec_add) : stralloc("");
}
|