123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691 |
- /*
- * expr.c - Expressions and values
- *
- * Written 2009, 2010, 2012, 2016 by Werner Almesberger
- * Copyright 2009, 2010, 2012, 2016 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.
- */
- #include <stdlib.h>
- #include <string.h>
- #include <math.h>
- #include "util.h"
- #include "error.h"
- #include "obj.h"
- #include "unparse.h"
- #include "fpd.h"
- #include "expr.h"
- struct num undef = { .type = nt_none };
- /* ----- error reporting --------------------------------------------------- */
- void fail_expr(const struct expr *expr)
- {
- char *s;
- s = unparse(expr);
- fail("in \"%s\" at line %d", s, expr->lineno);
- free(s);
- }
- /* ----- unit conversion --------------------------------------------------- */
- /*
- * If an expression contains a typo, we may get large exponents. Thus, we just
- * "sprintf" in order to be able to handle any integer. Since the number of
- * different exponents in a session will still be small, we use "unique" to
- * give us a constant string, so that we don't have to worry about memory
- * allocation.
- */
- const char *str_unit(struct num n)
- {
- const char *unit;
- char buf[20]; /* @@@ plenty */
- if (n.exponent == 0)
- return "";
- switch (n.type) {
- case nt_mm:
- unit = "mm";
- break;
- case nt_um:
- unit = "um";
- break;
- case nt_mil:
- unit = "mil";
- break;
- default:
- abort();
- }
- if (n.exponent == 1)
- return unit;
- sprintf(buf, "%s^%d", unit, n.exponent);
- return unique(buf);
- }
- int to_unit(struct num *n)
- {
- if (!is_distance(*n)) {
- fail("%s^%d is not a distance",
- n->type == nt_mm ? "mm" : n->type == nt_um ? "um" :
- n->type == nt_mil ? "mil" : "?", n->exponent);
- return 0;
- }
- switch (n->type) {
- case nt_mil:
- n->n = mil_to_units(n->n);
- break;
- case nt_mm:
- n->n = mm_to_units(n->n);
- break;
- case nt_um:
- n->n = um_to_units(n->n);
- break;
- default:
- abort();
- }
- return 1;
- }
- /* ----- number to string conversion (hackish) ----------------------------- */
- static char *num_to_string(struct num n)
- {
- static char buf[100]; /* enough :-) */
- snprintf(buf, sizeof(buf), "%lg%s", n.n, str_unit(n));
- return buf;
- }
- /* ----- primary expressions ----------------------------------------------- */
- struct num op_string(const struct expr *self, const struct frame *frame)
- {
- fail("cannot evaluate string");
- return undef;
- }
- struct num op_num(const struct expr *self, const struct frame *frame)
- {
- return self->u.num;
- }
- /*
- * We have two modes of operation: during instantiation and editing, after
- * instantiation. During instantiation, we follow curr_row and curr_parent.
- * These pointers are NULL when instantiation finishes, and we use this as a
- * signal that we're now in editing mode. In editing mode, the "active" values
- * are used instead of the "current" ones.
- */
- struct num eval_var(const struct frame *frame, const char *name)
- {
- const struct table *table;
- const struct loop *loop;
- const struct value *value;
- struct var *var;
- struct num res;
- for (table = frame->tables; table; table = table->next) {
- value = table->curr_row ? table->curr_row->values :
- table->active_row->values;
- for (var = table->vars; var; var = var->next) {
- if (!var->key && var->name == name) {
- if (var->visited) {
- fail("recursive evaluation through "
- "\"%s\"", name);
- return undef;
- }
- var->visited = 1;
- res = eval_num(value->expr, frame);
- var->visited = 0;
- return res;
- }
- value = value->next;
- }
- }
- for (loop = frame->loops; loop; loop = loop->next)
- if (loop->var.name == name) {
- if (loop->curr_value == UNDEF)
- return make_num(loop->n+loop->active);
- if (!loop->initialized) {
- fail("uninitialized loop \"%s\"", name);
- return undef;
- }
- return make_num(loop->curr_value);
- }
- if (frame->curr_parent)
- return eval_var(frame->curr_parent, name);
- if (frame->active_ref)
- return eval_var(frame->active_ref->frame, name);
- return undef;
- }
- static const char *eval_string_var(const struct frame *frame, const char *name)
- {
- const struct table *table;
- const struct loop *loop;
- const struct value *value;
- struct var *var;
- const char *res;
- for (table = frame->tables; table; table = table->next) {
- value = table->curr_row ? table->curr_row->values :
- table->active_row->values;
- for (var = table->vars; var; var = var->next) {
- if (!var->key && var->name == name) {
- if (var->visited)
- return NULL;
- var->visited = 1;
- res = eval_str(value->expr, frame);
- var->visited = 0;
- return res;
- }
- value = value->next;
- }
- }
- for (loop = frame->loops; loop; loop = loop->next)
- if (loop->var.name == name)
- return NULL;
- if (frame->curr_parent)
- return eval_string_var(frame->curr_parent, name);
- if (frame->active_ref)
- return eval_string_var(frame->active_ref->frame, name);
- return NULL;
- }
- struct num op_var(const struct expr *self, const struct frame *frame)
- {
- struct num res;
- res = eval_var(frame, self->u.var);
- if (is_undef(res))
- fail("undefined variable \"%s\"", self->u.var);
- return res;
- }
- /* ----- Variable equivalence ---------------------------------------------- */
- static int num_eq(struct num a, struct num b)
- {
- if (a.exponent != b.exponent)
- return 0;
- if (a.exponent && a.type != b.type) {
- if (a.type == nt_mil)
- return mil_to_mm(a.n, a.exponent) == b.n;
- else
- return a.n == mil_to_mm(b.n, b.exponent);
- }
- return a.n == b.n;
- }
- int var_eq(const struct frame *frame, const char *name,
- const struct expr *expr)
- {
- const char *vs, *es;
- struct num vn, en;
- vs = eval_string_var(frame, name);
- if (!vs) {
- vn = eval_var(frame, name);
- if (is_undef(vn)) {
- fail("undefined variable \"%s\"", name);
- return -1;
- }
- }
- es = eval_str(expr, frame);
- if (!es) {
- en = eval_num(expr, frame);
- if (is_undef(en))
- return -1;
- }
- if (vs || es) {
- if (!vs)
- vs = num_to_string(vn);
- if (!es)
- es = num_to_string(en);
- return !strcmp(vs, es);
- } else {
- return num_eq(vn, en);
- }
- }
- /* ----- arithmetic -------------------------------------------------------- */
- static void converge_to_mm(struct num *a)
- {
- switch (a->type) {
- case nt_mil:
- a->type = nt_mm;
- a->n = mil_to_mm(a->n, a->exponent);
- break;
- case nt_um:
- a->type = nt_mm;
- a->n = um_to_mm(a->n, a->exponent);
- break;
- case nt_mm:
- break;
- default:
- abort();
- }
- }
- static struct num compatible_sum(struct num *a, struct num *b)
- {
- struct num res;
- if (a->type != b->type) {
- converge_to_mm(a);
- converge_to_mm(b);
- }
- if (a->exponent != b->exponent) {
- fail("incompatible exponents (%d, %d)",
- a->exponent, b->exponent);
- return undef;
- }
- res.type = a->type;
- res.exponent = a->exponent;
- res.n = 0; /* keep gcc happy */
- return res;
- }
- static struct num compatible_mult(struct num *a, struct num *b,
- int exponent)
- {
- struct num res;
- if (a->type != b->type) {
- converge_to_mm(a);
- converge_to_mm(b);
- }
- res.type = a->type;
- res.exponent = exponent;
- res.n = 0; /* keep gcc happy */
- return res;
- }
- static struct num sin_cos(const struct expr *self,
- const struct frame *frame, double (*fn)(double arg))
- {
- struct num res;
- res = eval_num(self->u.op.a, frame);
- if (is_undef(res))
- return undef;
- if (!is_dimensionless(res)) {
- fail("angle must be dimensionless");
- return undef;
- }
- res.n = fn(res.n/180.0*M_PI);
- return res;
- }
- struct num op_sin(const struct expr *self, const struct frame *frame)
- {
- return sin_cos(self, frame, sin);
- }
- struct num op_cos(const struct expr *self, const struct frame *frame)
- {
- return sin_cos(self, frame, cos);
- }
- struct num op_sqrt(const struct expr *self, const struct frame *frame)
- {
- struct num res;
- res = eval_num(self->u.op.a, frame);
- if (is_undef(res))
- return undef;
- if (res.exponent & 1) {
- fail("exponent of sqrt argument must be a multiple of two");
- return undef;
- }
- if (res.n < 0) {
- fail("argument of sqrt must be positive");
- return undef;
- }
- res.n = sqrt(res.n);
- res.exponent >>= 1;
- return res;
- }
- struct num op_minus(const struct expr *self, const struct frame *frame)
- {
- struct num res;
- res = eval_num(self->u.op.a, frame);
- if (!is_undef(res))
- res.n = -res.n;
- return res;
- }
- struct num op_floor(const struct expr *self, const struct frame *frame)
- {
- struct num res;
- res = eval_num(self->u.op.a, frame);
- if (!is_undef(res))
- res.n = floor(res.n);
- return res;
- }
- #define BINARY \
- struct num a, b, res; \
- \
- a = eval_num(self->u.op.a, frame); \
- if (is_undef(a)) \
- return undef; \
- b = eval_num(self->u.op.b, frame); \
- if (is_undef(b)) \
- return undef;
- struct num op_add(const struct expr *self, const struct frame *frame)
- {
- BINARY;
- res = compatible_sum(&a, &b);
- if (is_undef(res))
- return undef;
- res.n = a.n+b.n;
- return res;
- }
- struct num op_sub(const struct expr *self, const struct frame *frame)
- {
- BINARY;
- res = compatible_sum(&a, &b);
- if (is_undef(res))
- return undef;
- res.n = a.n-b.n;
- return res;
- }
- struct num op_mult(const struct expr *self, const struct frame *frame)
- {
- BINARY;
- res = compatible_mult(&a, &b, a.exponent+b.exponent);
- res.n = a.n*b.n;
- return res;
- }
- struct num op_div(const struct expr *self, const struct frame *frame)
- {
- BINARY;
- if (!b.n) {
- fail("division by zero");
- return undef;
- }
- res = compatible_mult(&a, &b, a.exponent-b.exponent);
- res.n = a.n/b.n;
- return res;
- }
- /* ----- expression construction ------------------------------------------- */
- struct expr *new_op(op_type op)
- {
- struct expr *expr;
- expr = alloc_type(struct expr);
- expr->op = op;
- expr->lineno = lineno;
- return expr;
- }
- struct expr *binary_op(op_type op, struct expr *a, struct expr *b)
- {
- struct expr *expr;
- expr = new_op(op);
- expr->u.op.a = a;
- expr->u.op.b = b;
- return expr;
- }
- const char *eval_str(const struct expr *expr, const struct frame *frame)
- {
- if (expr->op == op_string)
- return expr->u.str;
- if (expr->op == op_var)
- return eval_string_var(frame, expr->u.var);
- return NULL;
- }
- struct num eval_num(const struct expr *expr, const struct frame *frame)
- {
- return expr->op(expr, frame);
- }
- /* ----- string expansion -------------------------------------------------- */
- char *expand(const char *name, const struct frame *frame)
- {
- int len = strlen(name);
- char *buf = alloc_size(len+1);
- const char *s, *s0;
- char *var;
- const char *var_unique, *value_string;
- struct num value;
- int i, value_len;
- i = 0;
- for (s = name; *s; s++) {
- if (*s != '$') {
- buf[i++] = *s;
- continue;
- }
- s0 = ++s;
- if (*s != '{') {
- while (is_id_char(*s, s == s0))
- s++;
- if (s == s0) {
- if (*s) {
- goto invalid;
- } else {
- fail("incomplete variable name");
- goto fail;
- }
- }
- var = strnalloc(s0, s-s0);
- len -= s-s0+1;
- s--;
- } else {
- s++;
- while (*s != '}') {
- if (!*s) {
- fail("unfinished \"${...}\"");
- goto fail;
- }
- if (!is_id_char(*s, s == s0+1))
- goto invalid;
- s++;
- }
- var = strnalloc(s0+1, s-s0-1);
- len -= s-s0+2;
- }
- if (!frame)
- continue;
- var_unique = unique(var);
- free(var);
- value_string = eval_string_var(frame, var_unique);
- if (!value_string) {
- value = eval_var(frame, var_unique);
- if (is_undef(value)) {
- fail("undefined variable \"%s\"", var_unique);
- goto fail;
- }
- value_string = num_to_string(value);
- }
- value_len = strlen(value_string);
- len += value_len;
- buf = realloc(buf, len+1);
- if (!buf)
- abort();
- strcpy(buf+i, value_string);
- i += value_len;
- }
- buf[i] = 0;
- return buf;
- invalid:
- fail("invalid character in variable name");
- fail:
- free(buf);
- return NULL;
- }
- /* ----- make a number -----------------------------------------------------*/
- struct expr *new_num(struct num num)
- {
- struct expr *expr;
- expr = new_op(op_num);
- expr->u.num = num;
- return expr;
- }
- /* ----- expression-only parser -------------------------------------------- */
- struct expr *parse_expr(const char *s)
- {
- scan_expr(s);
- return yyparse() ? NULL : expr_result;
- }
- static void vacate_op(struct expr *expr)
- {
- if (expr->op == op_num || expr->op == op_var)
- return;
- if (expr->op == op_string) {
- free(expr->u.str);
- return;
- }
- if (expr->op == op_minus || expr->op == op_floor ||
- expr->op == op_sin || expr->op == op_cos || expr->op == op_sqrt) {
- free_expr(expr->u.op.a);
- return;
- }
- if (expr->op == op_add || expr->op == op_sub ||
- expr->op == op_mult || expr->op == op_div) {
- free_expr(expr->u.op.a);
- free_expr(expr->u.op.b);
- return;
- }
- abort();
- }
- void free_expr(struct expr *expr)
- {
- vacate_op(expr);
- free(expr);
- }
- /* ----- [var =] value, ... shortcuts -------------------------------------- */
- int parse_var(const char *s, const char **id, struct value **values,
- int max_values)
- {
- const struct value *value;
- int n;
- scan_var(s);
- if (yyparse())
- return -1;
- if (id)
- *id = var_id;
- *values = var_value_list;
- n = 0;
- for (value = var_value_list; value; value = value->next)
- n++;
- if (max_values == -1 || n <= max_values)
- return n;
- free_values(var_value_list, 0);
- return -1;
- }
- int parse_values(const char *s, struct value **values)
- {
- const struct value *value;
- int n;
- scan_values(s);
- if (yyparse())
- return -1;
- *values = var_value_list;
- n = 0;
- for (value = var_value_list; value; value = value->next)
- n++;
- return n;
- }
- void free_values(struct value *values, int keep_expr)
- {
- struct value *next;
- while (values) {
- next = values->next;
- if (!keep_expr)
- free_expr(values->expr);
- free(values);
- values = next;
- }
- }
|