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 <config.h>
+#include "error.h"
#include <stdlib.h>
#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;
\f
/* 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;
}
+\f
+/* 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;
}
\f
/* 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;
}
+\f
+/* 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;
}
\f
-/* 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;
}
-\f
-/* 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