mirror of
git://projects.qi-hardware.com/fped.git
synced 2024-12-22 23:14:36 +02:00
f2d3986600
/ \t/, / $/, and /\t$/
678 lines
13 KiB
C
678 lines
13 KiB
C
/*
|
|
* expr.c - Expressions and values
|
|
*
|
|
* Written 2009, 2010, 2012 by Werner Almesberger
|
|
* Copyright 2009, 2010, 2012 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_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_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;
|
|
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 struct num compatible_sum(struct num *a, struct num *b)
|
|
{
|
|
struct num res;
|
|
|
|
if (a->type != b->type) {
|
|
if (a->type == nt_mil) {
|
|
a->type = nt_mm;
|
|
a->n = mil_to_mm(a->n, a->exponent);
|
|
}
|
|
if (b->type == nt_mil) {
|
|
b->type = nt_mm;
|
|
b->n = mil_to_mm(b->n, a->exponent);
|
|
}
|
|
}
|
|
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) {
|
|
if (a->type == nt_mil) {
|
|
a->type = nt_mm;
|
|
a->n = mil_to_mm(a->n, a->exponent);
|
|
}
|
|
if (b->type == nt_mil) {
|
|
b->type = nt_mm;
|
|
b->n = mil_to_mm(b->n, b->exponent);
|
|
}
|
|
}
|
|
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;
|
|
}
|
|
}
|