X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fcompute.c;h=37592c885862c4b4d64064e251411ea5b3a06f9b;hb=92fb12eb06716d14c05b781f5d9dcde956d77c30;hp=d07bcb6e21d0983b61b3e5e8ad30e907ff2591c7;hpb=fcb9e49b2a2d57af7c001ae5d2eda9ac443ba36b;p=pspp diff --git a/src/compute.c b/src/compute.c index d07bcb6e21..37592c8858 100644 --- a/src/compute.c +++ b/src/compute.c @@ -14,462 +14,402 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA - 02111-1307, USA. */ + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + 02110-1301, USA. */ #include +#include "error.h" #include #include "alloc.h" -#include "approx.h" -#include "cases.h" +#include "case.h" #include "command.h" +#include "dictionary.h" #include "error.h" -#include "expr.h" +#include "expressions/public.h" #include "lexer.h" +#include "misc.h" #include "str.h" #include "var.h" -#include "vector.h" -/* I can't think of any really good reason to disable debugging for - this module. */ -#include "debug-print.h" +#include "gettext.h" +#define _(msgid) gettext (msgid) + +struct compute_trns; +struct lvalue; + +/* Target of a COMPUTE or IF assignment, either a variable or a + vector element. */ +static struct lvalue *lvalue_parse (void); +static int lvalue_get_type (const struct lvalue *); +static bool lvalue_is_vector (const struct lvalue *); +static void lvalue_finalize (struct lvalue *, + struct compute_trns *); +static void lvalue_destroy (struct lvalue *); /* COMPUTE and IF transformation. */ struct compute_trns { - struct trns_header h; - - /* Destination. (Used only during parsing.) */ - struct variable *v; /* Destvar, if dest isn't a vector elem. */ - int created; /* Whether we created the destvar (used only during - parsing). */ - - /* Destination. (Used during execution.) */ - struct vector *vec; /* Destination vector, if dest is a vector elem. */ - int fv; /* `value' index of destination variable. */ - int width; /* Target variable width (string vars only). */ - - /* Expressions. */ - struct expression *vec_elem; /* Destination vector element expr. */ - struct expression *target; /* Target expression. */ - struct expression *test; /* Test expression (IF only). */ + /* Test expression (IF only). */ + struct expression *test; /* Test expression. */ + + /* Variable lvalue, if variable != NULL. */ + struct variable *variable; /* Destination variable, if any. */ + int fv; /* `value' index of destination variable. */ + int width; /* Lvalue string width; 0=numeric. */ + + /* Vector lvalue, if vector != NULL. */ + const struct vector *vector; /* Destination vector, if any. */ + struct expression *element; /* Destination vector element expr. */ + + /* Rvalue. */ + struct expression *rvalue; /* Rvalue expression. */ }; -static int parse_target_expression (struct compute_trns *, - int (*func_tab[4]) (struct trns_header *, struct ccase *)); -static struct compute_trns *new_trns (void); -static void delete_trns (struct compute_trns *); -static void free_trns (struct trns_header *); -static int parse_var_or_vec (struct compute_trns *); +static struct expression *parse_rvalue (const struct lvalue *); +static struct compute_trns *compute_trns_create (void); +static trns_proc_func *get_proc_func (const struct lvalue *); +static trns_free_func compute_trns_free; /* COMPUTE. */ -static int compute_num (struct trns_header *, struct ccase *); -static int compute_str (struct trns_header *, struct ccase *); -static int compute_num_vec (struct trns_header *, struct ccase *); -static int compute_str_vec (struct trns_header *, struct ccase *); - int cmd_compute (void) { - /* Table of functions to process data. */ - static int (*func_tab[4]) (struct trns_header *, struct ccase *) = - { - compute_num, - compute_str, - compute_num_vec, - compute_str_vec, - }; + struct lvalue *lvalue = NULL; + struct compute_trns *compute = NULL; - /* Transformation being constructed. */ - struct compute_trns *c; + compute = compute_trns_create (); - lex_match_id ("COMPUTE"); - - c = new_trns (); - if (!parse_var_or_vec (c)) + lvalue = lvalue_parse (); + if (lvalue == NULL) goto fail; - if (!lex_force_match ('=') - || !parse_target_expression (c, func_tab)) + if (!lex_force_match ('=')) + goto fail; + compute->rvalue = parse_rvalue (lvalue); + if (compute->rvalue == NULL) goto fail; - /* Goofy behavior, but compatible: Turn off LEAVE on the destvar. */ - if (c->v && c->v->left && c->v->name[0] != '#') - { - devector (c->v); - c->v->left = 0; - envector (c->v); - } + add_transformation (get_proc_func (lvalue), compute_trns_free, compute); - add_transformation ((struct trns_header *) c); + lvalue_finalize (lvalue, compute); - return CMD_SUCCESS; + return lex_end_of_command (); -fail: - delete_trns (c); + fail: + lvalue_destroy (lvalue); + compute_trns_free (compute); return CMD_FAILURE; } + +/* Transformation functions. */ +/* Handle COMPUTE or IF with numeric target variable. */ static int -compute_num (struct trns_header * pt, struct ccase * c) +compute_num (void *compute_, struct ccase *c, int case_num) { - struct compute_trns *t = (struct compute_trns *) pt; - expr_evaluate (t->target, c, &c->data[t->fv]); + struct compute_trns *compute = compute_; + + if (compute->test == NULL + || expr_evaluate_num (compute->test, c, case_num) == 1.0) + case_data_rw (c, compute->fv)->f = expr_evaluate_num (compute->rvalue, c, + case_num); + return -1; } +/* Handle COMPUTE or IF with numeric vector element target + variable. */ static int -compute_num_vec (struct trns_header * pt, struct ccase * c) +compute_num_vec (void *compute_, struct ccase *c, int case_num) { - struct compute_trns *t = (struct compute_trns *) pt; + struct compute_trns *compute = compute_; - /* Index into the vector. */ - union value index; - - /* Rounded index value. */ - int rindx; - - expr_evaluate (t->vec_elem, c, &index); - rindx = floor (index.f + EPSILON); - if (index.f == SYSMIS || rindx < 1 || rindx > t->vec->nv) + if (compute->test == NULL + || expr_evaluate_num (compute->test, c, case_num) == 1.0) { - if (index.f == SYSMIS) - msg (SW, _("When executing COMPUTE: SYSMIS is not a valid value as " - "an index into vector %s."), t->vec->name); - else - msg (SW, _("When executing COMPUTE: %g is not a valid value as " - "an index into vector %s."), index.f, t->vec->name); - return -1; + double index; /* Index into the vector. */ + int rindx; /* Rounded index value. */ + + index = expr_evaluate_num (compute->element, c, case_num); + rindx = floor (index + EPSILON); + if (index == SYSMIS || rindx < 1 || rindx > compute->vector->cnt) + { + if (index == SYSMIS) + msg (SW, _("When executing COMPUTE: SYSMIS is not a valid value as " + "an index into vector %s."), compute->vector->name); + else + msg (SW, _("When executing COMPUTE: %g is not a valid value as " + "an index into vector %s."), + index, compute->vector->name); + return -1; + } + case_data_rw (c, compute->vector->var[rindx - 1]->fv)->f + = expr_evaluate_num (compute->rvalue, c, case_num); } - expr_evaluate (t->target, c, &c->data[t->vec->v[rindx - 1]->fv]); + return -1; } +/* Handle COMPUTE or IF with string target variable. */ static int -compute_str (struct trns_header * pt, struct ccase * c) +compute_str (void *compute_, struct ccase *c, int case_num) { - struct compute_trns *t = (struct compute_trns *) pt; + struct compute_trns *compute = compute_; - /* Temporary storage for string expression return value. */ - union value v; - - expr_evaluate (t->target, c, &v); - st_bare_pad_len_copy (c->data[t->fv].s, &v.c[1], t->width, v.c[0]); + if (compute->test == NULL + || expr_evaluate_num (compute->test, c, case_num) == 1.0) + expr_evaluate_str (compute->rvalue, c, case_num, + case_data_rw (c, compute->fv)->s, compute->width); + return -1; } +/* Handle COMPUTE or IF with string vector element target + variable. */ static int -compute_str_vec (struct trns_header * pt, struct ccase * c) +compute_str_vec (void *compute_, struct ccase *c, int case_num) { - struct compute_trns *t = (struct compute_trns *) pt; - - /* Temporary storage for string expression return value. */ - union value v; - - /* Index into the vector. */ - union value index; + struct compute_trns *compute = compute_; - /* Rounded index value. */ - int rindx; - - /* Variable reference by indexed vector. */ - struct variable *vr; - - expr_evaluate (t->vec_elem, c, &index); - rindx = floor (index.f + EPSILON); - if (index.f == SYSMIS || rindx < 1 || rindx > t->vec->nv) + if (compute->test == NULL + || expr_evaluate_num (compute->test, c, case_num) == 1.0) { - if (index.f == SYSMIS) - msg (SW, _("When executing COMPUTE: SYSMIS is not a valid value as " - "an index into vector %s."), t->vec->name); - else - msg (SW, _("When executing COMPUTE: %g is not a valid value as " - "an index into vector %s."), index.f, t->vec->name); - return -1; + double index; /* Index into the vector. */ + int rindx; /* Rounded index value. */ + struct variable *vr; /* Variable reference by indexed vector. */ + + index = expr_evaluate_num (compute->element, c, case_num); + rindx = floor (index + EPSILON); + if (index == SYSMIS) + { + msg (SW, _("When executing COMPUTE: SYSMIS is not a valid " + "value as an index into vector %s."), + compute->vector->name); + return -1; + } + else if (rindx < 1 || rindx > compute->vector->cnt) + { + msg (SW, _("When executing COMPUTE: %g is not a valid value as " + "an index into vector %s."), + index, compute->vector->name); + return -1; + } + + vr = compute->vector->var[rindx - 1]; + expr_evaluate_str (compute->rvalue, c, case_num, + case_data_rw (c, vr->fv)->s, vr->width); } - - expr_evaluate (t->target, c, &v); - vr = t->vec->v[rindx - 1]; - st_bare_pad_len_copy (c->data[vr->fv].s, &v.c[1], vr->width, v.c[0]); + return -1; } /* IF. */ -static int if_num (struct trns_header *, struct ccase *); -static int if_str (struct trns_header *, struct ccase *); -static int if_num_vec (struct trns_header *, struct ccase *); -static int if_str_vec (struct trns_header *, struct ccase *); - int cmd_if (void) { - /* Table of functions to process data. */ - static int (*func_tab[4]) (struct trns_header *, struct ccase *) = - { - if_num, - if_str, - if_num_vec, - if_str_vec, - }; + struct compute_trns *compute = NULL; + struct lvalue *lvalue = NULL; - /* Transformation being constructed. */ - struct compute_trns *c; - - lex_match_id ("IF"); - c = new_trns (); + compute = compute_trns_create (); /* Test expression. */ - c->test = expr_parse (PXP_BOOLEAN); - if (!c->test) + compute->test = expr_parse (default_dict, EXPR_BOOLEAN); + if (compute->test == NULL) goto fail; - /* Target variable. */ - if (!parse_var_or_vec (c)) + /* Lvalue variable. */ + lvalue = lvalue_parse (); + if (lvalue == NULL) goto fail; - /* Target expression. */ - - if (!lex_force_match ('=') - || !parse_target_expression (c, func_tab)) + /* Rvalue expression. */ + if (!lex_force_match ('=')) goto fail; + compute->rvalue = parse_rvalue (lvalue); + if (compute->rvalue == NULL) + goto fail; + + add_transformation (get_proc_func (lvalue), compute_trns_free, compute); - add_transformation ((struct trns_header *) c); + lvalue_finalize (lvalue, compute); - return CMD_SUCCESS; + return lex_end_of_command (); -fail: - delete_trns (c); + fail: + lvalue_destroy (lvalue); + compute_trns_free (compute); return CMD_FAILURE; } + +/* Code common to COMPUTE and IF. */ -static int -if_num (struct trns_header * pt, struct ccase * c) +static trns_proc_func * +get_proc_func (const struct lvalue *lvalue) { - struct compute_trns *t = (struct compute_trns *) pt; + bool is_numeric = lvalue_get_type (lvalue) == NUMERIC; + bool is_vector = lvalue_is_vector (lvalue); - if (expr_evaluate (t->test, c, NULL) == 1.0) - expr_evaluate (t->target, c, &c->data[t->fv]); - return -1; + return (is_numeric + ? (is_vector ? compute_num_vec : compute_num) + : (is_vector ? compute_str_vec : compute_str)); } -static int -if_str (struct trns_header * pt, struct ccase * c) +/* Parses and returns an rvalue expression of the same type as + LVALUE, or a null pointer on failure. */ +static struct expression * +parse_rvalue (const struct lvalue *lvalue) { - struct compute_trns *t = (struct compute_trns *) pt; - - if (expr_evaluate (t->test, c, NULL) == 1.0) - { - union value v; + bool is_numeric = lvalue_get_type (lvalue) == NUMERIC; - expr_evaluate (t->target, c, &v); - st_bare_pad_len_copy (c->data[t->fv].s, &v.c[1], t->width, v.c[0]); - } - return -1; + return expr_parse (default_dict, is_numeric ? EXPR_NUMBER : EXPR_STRING); } -static int -if_num_vec (struct trns_header * pt, struct ccase * c) +/* Returns a new struct compute_trns after initializing its fields. */ +static struct compute_trns * +compute_trns_create (void) { - struct compute_trns *t = (struct compute_trns *) pt; - - if (expr_evaluate (t->test, c, NULL) == 1.0) - { - /* Index into the vector. */ - union value index; - - /* Rounded index value. */ - int rindx; - - expr_evaluate (t->vec_elem, c, &index); - rindx = floor (index.f + EPSILON); - if (index.f == SYSMIS || rindx < 1 || rindx > t->vec->nv) - { - if (index.f == SYSMIS) - msg (SW, _("When executing COMPUTE: SYSMIS is not a valid value as " - "an index into vector %s."), t->vec->name); - else - msg (SW, _("When executing COMPUTE: %g is not a valid value as " - "an index into vector %s."), index.f, t->vec->name); - return -1; - } - expr_evaluate (t->target, c, - &c->data[t->vec->v[rindx]->fv]); - } - return -1; + struct compute_trns *compute = xmalloc (sizeof *compute); + compute->test = NULL; + compute->variable = NULL; + compute->vector = NULL; + compute->element = NULL; + compute->rvalue = NULL; + return compute; } -static int -if_str_vec (struct trns_header * pt, struct ccase * c) +/* Deletes all the fields in COMPUTE. */ +static void +compute_trns_free (void *compute_) { - struct compute_trns *t = (struct compute_trns *) pt; + struct compute_trns *compute = compute_; - if (expr_evaluate (t->test, c, NULL) == 1.0) + if (compute != NULL) { - /* Index into the vector. */ - union value index; - - /* Rounded index value. */ - int rindx; - - /* Temporary storage for result of target expression. */ - union value v2; - - /* Variable reference by indexed vector. */ - struct variable *vr; - - expr_evaluate (t->vec_elem, c, &index); - rindx = floor (index.f + EPSILON); - if (index.f == SYSMIS || rindx < 1 || rindx > t->vec->nv) - { - if (index.f == SYSMIS) - msg (SW, _("When executing COMPUTE: SYSMIS is not a valid value as " - "an index into vector %s."), t->vec->name); - else - msg (SW, _("When executing COMPUTE: %g is not a valid value as " - "an index into vector %s."), index.f, t->vec->name); - return -1; - } - expr_evaluate (t->target, c, &v2); - vr = t->vec->v[rindx - 1]; - st_bare_pad_len_copy (c->data[vr->fv].s, &v2.c[1], vr->width, v2.c[0]); + expr_free (compute->test); + expr_free (compute->element); + expr_free (compute->rvalue); + free (compute); } - return -1; } -/* Code common to COMPUTE and IF. */ - -/* Checks for type mismatches on transformation C. Also checks for - command terminator, sets the case-handling proc from the array - passed. */ -static int -parse_target_expression (struct compute_trns *c, - int (*proc_list[4]) (struct trns_header *, struct ccase *)) -{ - int dest_type = c->v ? c->v->type : c->vec->v[0]->type; - c->target = expr_parse (dest_type == ALPHA ? PXP_STRING : PXP_NUMERIC); - if (!c->target) - return 0; - - c->h.proc = proc_list[(dest_type == ALPHA) + 2 * (c->vec != NULL)]; - - if (token != '.') - { - lex_error (_("expecting end of command")); - return 0; - } - - return 1; -} - -/* Returns a new struct compute_trns after initializing its fields. */ -static struct compute_trns * -new_trns (void) -{ - struct compute_trns *c = xmalloc (sizeof *c); - c->h.proc = NULL; - c->h.free = free_trns; - c->v = NULL; - c->created = 0; - c->vec = NULL; - c->fv = 0; - c->width = 0; - c->vec_elem = NULL; - c->target = NULL; - c->test = NULL; - return c; -} - -/* Deletes all the fields in C, the variable C->v if we created it, - and C itself. */ -static void -delete_trns (struct compute_trns * c) -{ - free_trns ((struct trns_header *) c); - if (c->created) - delete_variable (&default_dict, c->v); - free (c); -} +/* COMPUTE or IF target variable or vector element. */ +struct lvalue + { + char var_name[LONG_NAME_LEN + 1]; /* Destination variable name, or "". */ + const struct vector *vector; /* Destination vector, if any, or NULL. */ + struct expression *element; /* Destination vector element, or NULL. */ + }; -/* Deletes all the fields in C. */ -static void -free_trns (struct trns_header * pt) +/* Parses the target variable or vector element into a new + `struct lvalue', which is returned. */ +static struct lvalue * +lvalue_parse (void) { - struct compute_trns *t = (struct compute_trns *) pt; + struct lvalue *lvalue; - expr_free (t->vec_elem); - expr_free (t->target); - expr_free (t->test); -} + lvalue = xmalloc (sizeof *lvalue); + lvalue->var_name[0] = '\0'; + lvalue->vector = NULL; + lvalue->element = NULL; -/* Parses a variable name or a vector element into C. If the - variable does not exist, it is created. Returns success. */ -static int -parse_var_or_vec (struct compute_trns * c) -{ if (!lex_force_id ()) - return 0; + goto lossage; if (lex_look_ahead () == '(') { - /* Vector element. */ - c->vec = find_vector (tokid); - if (!c->vec) + /* Vector. */ + lvalue->vector = dict_lookup_vector (default_dict, tokid); + if (lvalue->vector == NULL) { msg (SE, _("There is no vector named %s."), tokid); - return 0; + goto lossage; } - + + /* Vector element. */ lex_get (); if (!lex_force_match ('(')) - return 0; - c->vec_elem = expr_parse (PXP_NUMERIC); - if (!c->vec_elem) - return 0; + goto lossage; + lvalue->element = expr_parse (default_dict, EXPR_NUMBER); + if (lvalue->element == NULL) + goto lossage; if (!lex_force_match (')')) - { - expr_free (c->vec_elem); - return 0; - } + goto lossage; } else { /* Variable name. */ - c->v = find_variable (tokid); - if (!c->v) - { - c->v = force_create_variable (&default_dict, tokid, NUMERIC, 0); - envector (c->v); - c->created = 1; - } - c->fv = c->v->fv; - c->width = c->v->width; + str_copy_trunc (lvalue->var_name, sizeof lvalue->var_name, tokid); lex_get (); } - return 1; + return lvalue; + + lossage: + lvalue_destroy (lvalue); + return NULL; } - -/* EVALUATE. */ -#if GLOBAL_DEBUGGING -int -cmd_evaluate (void) +/* Returns the type (NUMERIC or ALPHA) of the target variable or + vector in LVALUE. */ +static int +lvalue_get_type (const struct lvalue *lvalue) { - struct expression *expr; + if (lvalue->vector == NULL) + { + struct variable *var = dict_lookup_var (default_dict, lvalue->var_name); + if (var == NULL) + return NUMERIC; + else + return var->type; + } + else + return lvalue->vector->var[0]->type; +} - lex_match_id ("EVALUATE"); - expr = expr_parse (PXP_DUMP); - if (!expr) - return CMD_FAILURE; +/* Returns nonzero if LVALUE has a vector as its target. */ +static bool +lvalue_is_vector (const struct lvalue *lvalue) +{ + return lvalue->vector != NULL; +} - expr_free (expr); - if (token != '.') +/* Finalizes making LVALUE the target of COMPUTE, by creating the + target variable if necessary and setting fields in COMPUTE. */ +static void +lvalue_finalize (struct lvalue *lvalue, struct compute_trns *compute) +{ + if (lvalue->vector == NULL) { - msg (SE, _("Extra characters after expression.")); - return CMD_FAILURE; + compute->variable = dict_lookup_var (default_dict, lvalue->var_name); + if (compute->variable == NULL) + compute->variable = dict_create_var_assert (default_dict, + lvalue->var_name, 0); + + compute->fv = compute->variable->fv; + compute->width = compute->variable->width; + + /* Goofy behavior, but compatible: Turn off LEAVE. */ + if (dict_class_from_id (compute->variable->name) != DC_SCRATCH) + compute->variable->reinit = 1; } - - return CMD_SUCCESS; + else + { + compute->vector = lvalue->vector; + compute->element = lvalue->element; + lvalue->element = NULL; + } + + lvalue_destroy (lvalue); +} + +/* Destroys LVALUE. */ +static void +lvalue_destroy (struct lvalue *lvalue) +{ + if (lvalue == NULL) + return; + + expr_free (lvalue->element); + free (lvalue); } -#endif