fped/expr.c

691 lines
13 KiB
C

/*
* 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;
}
}