diff --git a/README b/README index a634e96..e8aca0a 100644 --- a/README +++ b/README @@ -503,6 +503,21 @@ table (x, y) assume the values (1, 2) and (3, 4). +Tables can also be used to provide information that depends on +other variables. The value of such a variable acts as a key, and a +row is only selected if all the keys in that row match the +respective variables. To mark a variable as being used as key, its +name it prefixed with a question mark. + +Example: + +loop n = 1, 2, 3 +table + { ?n, name } + { 1, "one" } + { 2, "two" } + { 3, "three" } + Expressions ----------- diff --git a/dump.c b/dump.c index e0bca5e..cf874d4 100644 --- a/dump.c +++ b/dump.c @@ -220,7 +220,8 @@ static void dump_var(FILE *file, const struct table *table, char *s; s = unparse(table->rows->values->expr); - fprintf(file, "%sset %s = %s\n\n", indent, table->vars->name, s); + fprintf(file, "%sset %s%s = %s\n\n", indent, + table->vars->key ? "?" : "", table->vars->name, s); free(s); } @@ -240,8 +241,8 @@ static void dump_table(FILE *file, const struct table *table, } fprintf(file, "%stable\n%s {", indent, indent); for (var = table->vars; var; var = var->next) - fprintf(file, "%s %s", var == table->vars ? "" : ",", - var->name); + fprintf(file, "%s %s%s", var == table->vars ? "" : ",", + var->key ? "?" : "", var->name); fprintf(file, " }\n"); for (row = table->rows; row; row = row->next) { fprintf(file, "%s {", indent); diff --git a/expr.c b/expr.c index c1753c4..62d1e18 100644 --- a/expr.c +++ b/expr.c @@ -12,6 +12,7 @@ #include +#include #include #include "util.h" @@ -95,6 +96,18 @@ int to_unit(struct num *n) } +/* ----- 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 ----------------------------------------------- */ @@ -131,7 +144,7 @@ struct num eval_var(const struct frame *frame, const char *name) value = table->curr_row ? table->curr_row->values : table->active_row->values; for (var = table->vars; var; var = var->next) { - if (var->name == name) { + if (!var->key && var->name == name) { if (var->visited) { fail("recursive evaluation through " "\"%s\"", name); @@ -141,7 +154,6 @@ struct num eval_var(const struct frame *frame, const char *name) res = eval_num(value->expr, frame); var->visited = 0; return res; - } value = value->next; } @@ -176,14 +188,13 @@ static const char *eval_string_var(const struct frame *frame, const char *name) value = table->curr_row ? table->curr_row->values : table->active_row->values; for (var = table->vars; var; var = var->next) { - if (var->name == name) { + 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; } @@ -210,6 +221,55 @@ struct num op_var(const struct expr *self, const struct frame *frame) } +/* ----- 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 -------------------------------------------------------- */ @@ -436,7 +496,6 @@ char *expand(const char *name, const struct frame *frame) { int len = strlen(name); char *buf = alloc_size(len+1); - char num_buf[100]; /* enough :-) */ const char *s, *s0; char *var; const char *var_unique, *value_string; @@ -483,18 +542,15 @@ char *expand(const char *name, const struct frame *frame) var_unique = unique(var); free(var); value_string = eval_string_var(frame, var_unique); - if (value_string) { - value_len = strlen(value_string); - } else { + if (!value_string) { value = eval_var(frame, var_unique); if (is_undef(value)) { fail("undefined variable \"%s\"", var_unique); goto fail; } - value_len = snprintf(num_buf, sizeof(num_buf), "%lg%s", - value.n, str_unit(value)); - value_string = num_buf; + value_string = num_to_string(value); } + value_len = strlen(value_string); len += value_len; buf = realloc(buf, len+1); if (!buf) diff --git a/expr.h b/expr.h index f89f210..1a97ca4 100644 --- a/expr.h +++ b/expr.h @@ -126,6 +126,9 @@ struct num op_div(const struct expr *self, const struct frame *frame); struct expr *new_op(op_type op); struct expr *binary_op(op_type op, struct expr *a, struct expr *b); +int var_eq(const struct frame *frame, const char *name, + const struct expr *expr); + struct num eval_var(const struct frame *frame, const char *name); /* diff --git a/fpd.y b/fpd.y index 8e36603..0276f2e 100644 --- a/fpd.y +++ b/fpd.y @@ -107,7 +107,7 @@ static struct var *find_var(const struct frame *frame, const char *name) for (table = frame->tables; table; table = table->next) for (var = table->vars; var; var = var->next) - if (var->name == name) + if (!var->key && var->name == name) return var; for (loop = frame->loops; loop; loop = loop->next) if (loop->var.name == name) @@ -130,7 +130,7 @@ static void set_frame(struct frame *frame) } -static void make_var(const char *id, struct expr *expr) +static void make_var(const char *id, int key, struct expr *expr) { struct table *table; @@ -139,6 +139,7 @@ static void make_var(const char *id, struct expr *expr) table->vars->name = id; table->vars->frame = curr_frame; table->vars->table = table; + table->vars->key = key; table->rows = zalloc_type(struct row); table->rows->table = table; table->rows->values = zalloc_type(struct value); @@ -425,6 +426,7 @@ static int dbg_meas(const char *name) %union { struct num num; + int flag; char *str; const char *id; struct expr *expr; @@ -474,6 +476,7 @@ static int dbg_meas(const char *name) %type object obj meas unlabeled_meas %type expr opt_expr add_expr mult_expr unary_expr primary_expr %type opt_num +%type opt_key %type frame_qualifier %type opt_string %type pad_type @@ -607,13 +610,13 @@ frame_items: frame_item: table - | TOK_SET ID '=' expr + | TOK_SET opt_key ID '=' expr { - if (find_var(curr_frame, $2)) { - yyerrorf("duplicate variable \"%s\"", $2); + if (!$2 && find_var(curr_frame, $3)) { + yyerrorf("duplicate variable \"%s\"", $3); YYABORT; } - make_var($2, $4); + make_var($3, $2, $5); } | TOK_LOOP ID '=' expr ',' expr { @@ -755,22 +758,32 @@ vars: ; var: - ID + opt_key ID { - if (find_var(curr_frame, $1)) { - yyerrorf("duplicate variable \"%s\"", $1); + if (!$1 && find_var(curr_frame, $2)) { + yyerrorf("duplicate variable \"%s\"", $2); YYABORT; } $$ = zalloc_type(struct var); - $$->name = $1; + $$->name = $2; $$->frame = curr_frame; $$->table = curr_table; + $$->key = $1; $$->next = NULL; n_vars++; } ; - +opt_key: + { + $$ = 0; + } + | '?' + { + $$ = 1; + } + ; + rows: { $$ = NULL; diff --git a/obj.c b/obj.c index ff2b328..7f3fa73 100644 --- a/obj.c +++ b/obj.c @@ -350,6 +350,30 @@ static int generate_items(struct frame *frame, struct coord base, int active) } +static int match_keys(struct frame *frame, struct coord base, int active) +{ + const struct table *table; + const struct var *var; + const struct value *value; + int res; + + for (table = frame->tables; table; table = table->next) { + value = table->curr_row->values; + for (var = table->vars; var; var = var->next) { + if (var->key) { + res = var_eq(frame, var->name, value->expr); + if (!res) + return 1; + if (res < 0) + return 0; + } + value = value->next; + } + } + return generate_items(frame, base, active); +} + + static int run_loops(struct frame *frame, struct loop *loop, struct coord base, int active) { @@ -358,7 +382,7 @@ static int run_loops(struct frame *frame, struct loop *loop, int found_before, ok; if (!loop) - return generate_items(frame, base, active); + return match_keys(frame, base, active); from = eval_num(loop->from.expr, frame); if (is_undef(from)) { fail_expr(loop->from.expr); diff --git a/obj.h b/obj.h index 7e5f81f..2515125 100644 --- a/obj.h +++ b/obj.h @@ -60,6 +60,10 @@ struct var { struct frame *frame; struct table *table; /* NULL if loop */ + + /* key: 0 if the variable is set, 1 if the variable is compared */ + int key; + /* for the GUI */ GtkWidget *widget; diff --git a/test/keys b/test/keys new file mode 100755 index 0000000..77a4314 --- /dev/null +++ b/test/keys @@ -0,0 +1,134 @@ +#!/bin/sh +. ./Common + +############################################################################### + +fped "keys: tables, master before slave" <