First step in making struct variable opaque: the boring mechanical
[pspp-builds.git] / src / language / xforms / compute.c
index e6a17553377703096ecdb5b0ca57a59c52c909c6..03e38c75f6f9afd0191e84d37e7fad5936fbcea0 100644 (file)
    02110-1301, USA. */
 
 #include <config.h>
-#include <libpspp/message.h>
+
 #include <stdlib.h>
-#include <libpspp/alloc.h>
+
 #include <data/case.h>
-#include <language/command.h>
 #include <data/dictionary.h>
-#include <libpspp/message.h>
+#include <data/procedure.h>
+#include <data/transformations.h>
+#include <data/variable.h>
+#include <language/command.h>
 #include <language/expressions/public.h>
 #include <language/lexer/lexer.h>
+#include <libpspp/alloc.h>
+#include <libpspp/message.h>
+#include <libpspp/message.h>
 #include <libpspp/misc.h>
 #include <libpspp/str.h>
-#include <data/variable.h>
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -39,12 +43,12 @@ struct lvalue;
 
 /* Target of a COMPUTE or IF assignment, either a variable or a
    vector element. */
-static struct lvalue *lvalue_parse (void);
+static struct lvalue *lvalue_parse (struct lexer *lexer, struct dataset *);
 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 *);
+                             struct compute_trns *, struct dictionary *);
+static void lvalue_destroy (struct lvalue *, struct dictionary *);
 
 /* COMPUTE and IF transformation. */
 struct compute_trns
@@ -65,7 +69,10 @@ struct compute_trns
     struct expression *rvalue;  /* Rvalue expression. */
   };
 
-static struct expression *parse_rvalue (const struct lvalue *);
+static struct expression *parse_rvalue (struct lexer *lexer, 
+                                       const struct lvalue *, 
+                                       struct dataset *);
+
 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;
@@ -73,31 +80,32 @@ static trns_free_func compute_trns_free;
 /* COMPUTE. */
 
 int
-cmd_compute (void)
+cmd_compute (struct lexer *lexer, struct dataset *ds)
 {
+  struct dictionary *dict = dataset_dict (ds);
   struct lvalue *lvalue = NULL;
   struct compute_trns *compute = NULL;
 
   compute = compute_trns_create ();
 
-  lvalue = lvalue_parse ();
+  lvalue = lvalue_parse (lexer, ds);
   if (lvalue == NULL)
     goto fail;
 
-  if (!lex_force_match ('='))
+  if (!lex_force_match (lexer, '='))
     goto fail;
-  compute->rvalue = parse_rvalue (lvalue);
+  compute->rvalue = parse_rvalue (lexer, lvalue, ds);
   if (compute->rvalue == NULL)
     goto fail;
 
-  add_transformation (get_proc_func (lvalue), compute_trns_free, compute);
+  add_transformation (ds, get_proc_func (lvalue), compute_trns_free, compute);
 
-  lvalue_finalize (lvalue, compute);
+  lvalue_finalize (lvalue, compute, dict);
 
-  return lex_end_of_command ();
+  return lex_end_of_command (lexer);
 
  fail:
-  lvalue_destroy (lvalue);
+  lvalue_destroy (lvalue, dict);
   compute_trns_free (compute);
   return CMD_CASCADING_FAILURE;
 }
@@ -106,7 +114,7 @@ cmd_compute (void)
 
 /* Handle COMPUTE or IF with numeric target variable. */
 static int
-compute_num (void *compute_, struct ccase *c, int case_num)
+compute_num (void *compute_, struct ccase *c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
 
@@ -121,7 +129,7 @@ compute_num (void *compute_, struct ccase *c, int case_num)
 /* Handle COMPUTE or IF with numeric vector element target
    variable. */
 static int
-compute_num_vec (void *compute_, struct ccase *c, int case_num)
+compute_num_vec (void *compute_, struct ccase *c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
 
@@ -153,7 +161,7 @@ compute_num_vec (void *compute_, struct ccase *c, int case_num)
 
 /* Handle COMPUTE or IF with string target variable. */
 static int
-compute_str (void *compute_, struct ccase *c, int case_num)
+compute_str (void *compute_, struct ccase *c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
 
@@ -168,7 +176,7 @@ compute_str (void *compute_, struct ccase *c, int case_num)
 /* Handle COMPUTE or IF with string vector element target
    variable. */
 static int
-compute_str_vec (void *compute_, struct ccase *c, int case_num)
+compute_str_vec (void *compute_, struct ccase *c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
 
@@ -198,7 +206,7 @@ compute_str_vec (void *compute_, struct ccase *c, int case_num)
 
       vr = compute->vector->var[rindx - 1];
       expr_evaluate_str (compute->rvalue, c, case_num,
-                         case_data_rw (c, vr->fv)->s, vr->width);
+                         case_data_rw (c, vr->fv)->s, var_get_width (vr));
     }
   
   return TRNS_CONTINUE;
@@ -207,38 +215,39 @@ compute_str_vec (void *compute_, struct ccase *c, int case_num)
 /* IF. */
 
 int
-cmd_if (void)
+cmd_if (struct lexer *lexer, struct dataset *ds)
 {
+  struct dictionary *dict = dataset_dict (ds);
   struct compute_trns *compute = NULL;
   struct lvalue *lvalue = NULL;
 
   compute = compute_trns_create ();
 
   /* Test expression. */
-  compute->test = expr_parse (default_dict, EXPR_BOOLEAN);
+  compute->test = expr_parse (lexer, ds, EXPR_BOOLEAN);
   if (compute->test == NULL)
     goto fail;
 
   /* Lvalue variable. */
-  lvalue = lvalue_parse ();
+  lvalue = lvalue_parse (lexer, ds);
   if (lvalue == NULL)
     goto fail;
 
   /* Rvalue expression. */
-  if (!lex_force_match ('='))
+  if (!lex_force_match (lexer, '='))
     goto fail;
-  compute->rvalue = parse_rvalue (lvalue);
+  compute->rvalue = parse_rvalue (lexer, lvalue, ds);
   if (compute->rvalue == NULL)
     goto fail;
 
-  add_transformation (get_proc_func (lvalue), compute_trns_free, compute);
+  add_transformation (ds, get_proc_func (lvalue), compute_trns_free, compute);
 
-  lvalue_finalize (lvalue, compute);
+  lvalue_finalize (lvalue, compute, dict);
 
-  return lex_end_of_command ();
+  return lex_end_of_command (lexer);
 
  fail:
-  lvalue_destroy (lvalue);
+  lvalue_destroy (lvalue, dict);
   compute_trns_free (compute);
   return CMD_CASCADING_FAILURE;
 }
@@ -259,11 +268,12 @@ get_proc_func (const struct lvalue *lvalue)
 /* 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)
+parse_rvalue (struct lexer *lexer, 
+             const struct lvalue *lvalue, struct dataset *ds)
 {
   bool is_numeric = lvalue_get_type (lvalue) == NUMERIC;
 
-  return expr_parse (default_dict, is_numeric ? EXPR_NUMBER : EXPR_STRING);
+  return expr_parse (lexer, ds, is_numeric ? EXPR_NUMBER : EXPR_STRING);
 }
 
 /* Returns a new struct compute_trns after initializing its fields. */
@@ -295,10 +305,14 @@ compute_trns_free (void *compute_)
   return true;
 }
 \f
-/* COMPUTE or IF target variable or vector element. */
+/* COMPUTE or IF target variable or vector element.
+   For a variable, the `variable' member is non-null.
+   For a vector element, the `vector' member is non-null. */
 struct lvalue
   {
-    char var_name[LONG_NAME_LEN + 1];   /* Destination variable name, or "". */
+    struct variable *variable;   /* Destination variable. */
+    bool is_new_variable;        /* Did we create the variable? */
+
     const struct vector *vector; /* Destination vector, if any, or NULL. */
     struct expression *element;  /* Destination vector element, or NULL. */
   };
@@ -306,48 +320,56 @@ struct lvalue
 /* Parses the target variable or vector element into a new
    `struct lvalue', which is returned. */
 static struct lvalue *
-lvalue_parse (void
+lvalue_parse (struct lexer *lexer, struct dataset *ds
 {
+  struct dictionary *dict = dataset_dict (ds);
   struct lvalue *lvalue;
 
   lvalue = xmalloc (sizeof *lvalue);
-  lvalue->var_name[0] = '\0';
+  lvalue->variable = NULL;
+  lvalue->is_new_variable = false;
   lvalue->vector = NULL;
   lvalue->element = NULL;
 
-  if (!lex_force_id ())
+  if (!lex_force_id (lexer))
     goto lossage;
   
-  if (lex_look_ahead () == '(')
+  if (lex_look_ahead (lexer) == '(')
     {
       /* Vector. */
-      lvalue->vector = dict_lookup_vector (default_dict, tokid);
+      lvalue->vector = dict_lookup_vector (dict, lex_tokid (lexer));
       if (lvalue->vector == NULL)
        {
-         msg (SE, _("There is no vector named %s."), tokid);
+         msg (SE, _("There is no vector named %s."), lex_tokid (lexer));
           goto lossage;
        }
 
       /* Vector element. */
-      lex_get ();
-      if (!lex_force_match ('('))
+      lex_get (lexer);
+      if (!lex_force_match (lexer, '('))
        goto lossage;
-      lvalue->element = expr_parse (default_dict, EXPR_NUMBER);
+      lvalue->element = expr_parse (lexer, ds, EXPR_NUMBER);
       if (lvalue->element == NULL)
         goto lossage;
-      if (!lex_force_match (')'))
+      if (!lex_force_match (lexer, ')'))
         goto lossage;
     }
   else
     {
       /* Variable name. */
-      str_copy_trunc (lvalue->var_name, sizeof lvalue->var_name, tokid);
-      lex_get ();
+      const char *var_name = lex_tokid (lexer);
+      lvalue->variable = dict_lookup_var (dict, var_name);
+      if (lvalue->variable == NULL) 
+        {
+         lvalue->variable = dict_create_var_assert (dict, var_name, 0);
+          lvalue->is_new_variable = true; 
+        }
+      lex_get (lexer);
     }
   return lvalue;
 
  lossage:
-  lvalue_destroy (lvalue);
+  lvalue_destroy (lvalue, dict);
   return NULL;
 }
 
@@ -356,19 +378,12 @@ lvalue_parse (void)
 static int
 lvalue_get_type (const struct lvalue *lvalue) 
 {
-  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;
+  return (lvalue->variable != NULL
+          ? var_get_type (lvalue->variable)
+          : var_get_type (lvalue->vector->var[0]));
 }
 
-/* Returns nonzero if LVALUE has a vector as its target. */
+/* Returns true if LVALUE has a vector as its target. */
 static bool
 lvalue_is_vector (const struct lvalue *lvalue) 
 {
@@ -378,21 +393,22 @@ lvalue_is_vector (const struct lvalue *lvalue)
 /* 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) 
+lvalue_finalize (struct lvalue *lvalue, 
+                struct compute_trns *compute, 
+                struct dictionary *dict) 
 {
   if (lvalue->vector == NULL)
     {
-      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->variable = lvalue->variable;
       compute->fv = compute->variable->fv;
-      compute->width = compute->variable->width;
+      compute->width = var_get_width (compute->variable);
 
       /* Goofy behavior, but compatible: Turn off LEAVE. */
-      if (dict_class_from_id (compute->variable->name) != DC_SCRATCH)
+      if (dict_class_from_id (var_get_name (compute->variable)) != DC_SCRATCH)
         compute->variable->leave = false;
+
+      /* Prevent lvalue_destroy from deleting variable. */
+      lvalue->is_new_variable = false;
     }
   else 
     {
@@ -401,16 +417,18 @@ lvalue_finalize (struct lvalue *lvalue, struct compute_trns *compute)
       lvalue->element = NULL;
     }
 
-  lvalue_destroy (lvalue);
+  lvalue_destroy (lvalue, dict);
 }
 
 /* Destroys LVALUE. */
 static void 
-lvalue_destroy (struct lvalue *lvalue) 
+lvalue_destroy (struct lvalue *lvalue, struct dictionary *dict
 {
   if (lvalue == NULL) 
      return;
 
+  if (lvalue->is_new_variable)
+    dict_delete_var (dict, lvalue->variable);
   expr_free (lvalue->element);
   free (lvalue);
 }