Clean up how transformations work.
[pspp] / src / language / xforms / compute.c
index 0ab4e3a4bb5ac44cdc6aeaf5a0251b4df2914833..5c330f65a823e746a8dfefa854f79ff142529e56 100644 (file)
@@ -1,5 +1,5 @@
 /* PSPP - a program for statistical analysis.
-   Copyright (C) 1997-9, 2000, 2009, 2010, 2011 Free Software Foundation, Inc.
+   Copyright (C) 1997-9, 2000, 2009, 2010, 2011, 2014 Free Software Foundation, Inc.
 
    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
@@ -16,6 +16,7 @@
 
 #include <config.h>
 
+#include <float.h>
 #include <stdint.h>
 #include <stdlib.h>
 
 struct compute_trns;
 struct lvalue;
 
+/* 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
+  {
+    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. */
+  };
+
 /* Target of a COMPUTE or IF assignment, either a variable or a
    vector element. */
 static struct lvalue *lvalue_parse (struct lexer *lexer, struct dataset *);
@@ -72,8 +85,8 @@ static struct expression *parse_rvalue (struct lexer *lexer,
                                        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;
+static bool compute_trns_free (void *compute_);
+static const struct trns_class *get_trns_class (const struct lvalue *);
 \f
 /* COMPUTE. */
 
@@ -96,7 +109,7 @@ cmd_compute (struct lexer *lexer, struct dataset *ds)
   if (compute->rvalue == NULL)
     goto fail;
 
-  add_transformation (ds, get_proc_func (lvalue), compute_trns_free, compute);
+  add_transformation (ds, get_trns_class (lvalue), compute);
 
   lvalue_finalize (lvalue, compute, dict);
 
@@ -111,7 +124,7 @@ cmd_compute (struct lexer *lexer, struct dataset *ds)
 /* Transformation functions. */
 
 /* Handle COMPUTE or IF with numeric target variable. */
-static int
+static enum trns_result
 compute_num (void *compute_, struct ccase **c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
@@ -120,7 +133,7 @@ compute_num (void *compute_, struct ccase **c, casenumber case_num)
       || expr_evaluate_num (compute->test, *c, case_num) == 1.0)
     {
       *c = case_unshare (*c);
-      case_data_rw (*c, compute->variable)->f
+      *case_num_rw (*c, compute->variable)
         = expr_evaluate_num (compute->rvalue, *c, case_num);
     }
 
@@ -129,7 +142,7 @@ compute_num (void *compute_, struct ccase **c, casenumber case_num)
 
 /* Handle COMPUTE or IF with numeric vector element target
    variable. */
-static int
+static enum trns_result
 compute_num_vec (void *compute_, struct ccase **c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
@@ -143,21 +156,21 @@ compute_num_vec (void *compute_, struct ccase **c, casenumber case_num)
       index = expr_evaluate_num (compute->element, *c, case_num);
       rindx = floor (index + EPSILON);
       if (index == SYSMIS
-          || rindx < 1 || rindx > vector_get_var_cnt (compute->vector))
+          || rindx < 1 || rindx > vector_get_n_vars (compute->vector))
         {
           if (index == SYSMIS)
             msg (SW, _("When executing COMPUTE: SYSMIS is not a valid value "
                        "as an index into vector %s."),
                  vector_get_name (compute->vector));
           else
-            msg (SW, _("When executing COMPUTE: %g is not a valid value as "
+            msg (SW, _("When executing COMPUTE: %.*g is not a valid value as "
                        "an index into vector %s."),
-                 index, vector_get_name (compute->vector));
+                 DBL_DIG + 1, index, vector_get_name (compute->vector));
           return TRNS_CONTINUE;
         }
 
       *c = case_unshare (*c);
-      case_data_rw (*c, vector_get_var (compute->vector, rindx - 1))->f
+      *case_num_rw (*c, vector_get_var (compute->vector, rindx - 1))
         = expr_evaluate_num (compute->rvalue, *c, case_num);
     }
 
@@ -165,7 +178,7 @@ compute_num_vec (void *compute_, struct ccase **c, casenumber case_num)
 }
 
 /* Handle COMPUTE or IF with string target variable. */
-static int
+static enum trns_result
 compute_str (void *compute_, struct ccase **c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
@@ -185,7 +198,7 @@ compute_str (void *compute_, struct ccase **c, casenumber case_num)
 
 /* Handle COMPUTE or IF with string vector element target
    variable. */
-static int
+static enum trns_result
 compute_str_vec (void *compute_, struct ccase **c, casenumber case_num)
 {
   struct compute_trns *compute = compute_;
@@ -206,11 +219,11 @@ compute_str_vec (void *compute_, struct ccase **c, casenumber case_num)
                vector_get_name (compute->vector));
           return TRNS_CONTINUE;
         }
-      else if (rindx < 1 || rindx > vector_get_var_cnt (compute->vector))
+      else if (rindx < 1 || rindx > vector_get_n_vars (compute->vector))
         {
-          msg (SW, _("When executing COMPUTE: %g is not a valid value as "
+          msg (SW, _("When executing COMPUTE: %.*g is not a valid value as "
                      "an index into vector %s."),
-               index, vector_get_name (compute->vector));
+               DBL_DIG + 1, index, vector_get_name (compute->vector));
           return TRNS_CONTINUE;
         }
 
@@ -236,7 +249,7 @@ cmd_if (struct lexer *lexer, struct dataset *ds)
   compute = compute_trns_create ();
 
   /* Test expression. */
-  compute->test = expr_parse (lexer, ds, EXPR_BOOLEAN);
+  compute->test = expr_parse_bool (lexer, ds);
   if (compute->test == NULL)
     goto fail;
 
@@ -252,7 +265,7 @@ cmd_if (struct lexer *lexer, struct dataset *ds)
   if (compute->rvalue == NULL)
     goto fail;
 
-  add_transformation (ds, get_proc_func (lvalue), compute_trns_free, compute);
+  add_transformation (ds, get_trns_class (lvalue), compute);
 
   lvalue_finalize (lvalue, compute, dict);
 
@@ -266,15 +279,35 @@ cmd_if (struct lexer *lexer, struct dataset *ds)
 \f
 /* Code common to COMPUTE and IF. */
 
-static trns_proc_func *
-get_proc_func (const struct lvalue *lvalue)
+static const struct trns_class *
+get_trns_class (const struct lvalue *lvalue)
 {
+  static const struct trns_class classes[2][2] = {
+    [false][false] = {
+      .name = "COMPUTE",
+      .execute = compute_str,
+      .destroy = compute_trns_free
+    },
+    [false][true] = {
+      .name = "COMPUTE",
+      .execute = compute_str_vec,
+      .destroy = compute_trns_free
+    },
+    [true][false] = {
+      .name = "COMPUTE",
+      .execute = compute_num,
+      .destroy = compute_trns_free
+    },
+    [true][true] = {
+      .name = "COMPUTE",
+      .execute = compute_num_vec,
+      .destroy = compute_trns_free
+    },
+  };
+
   bool is_numeric = lvalue_get_type (lvalue) == VAL_NUMERIC;
   bool is_vector = lvalue_is_vector (lvalue);
-
-  return (is_numeric
-          ? (is_vector ? compute_num_vec : compute_num)
-          : (is_vector ? compute_str_vec : compute_str));
+  return &classes[is_numeric][is_vector];
 }
 
 /* Parses and returns an rvalue expression of the same type as
@@ -283,9 +316,10 @@ static struct expression *
 parse_rvalue (struct lexer *lexer,
              const struct lvalue *lvalue, struct dataset *ds)
 {
-  bool is_numeric = lvalue_get_type (lvalue) == VAL_NUMERIC;
-
-  return expr_parse (lexer, ds, is_numeric ? EXPR_NUMBER : EXPR_STRING);
+  if (lvalue->is_new_variable)
+    return expr_parse_new_variable (lexer, ds, var_get_name (lvalue->variable));
+  else
+    return expr_parse (lexer, ds, lvalue_get_type (lvalue));
 }
 
 /* Returns a new struct compute_trns after initializing its fields. */
@@ -317,18 +351,6 @@ compute_trns_free (void *compute_)
   return true;
 }
 \f
-/* 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
-  {
-    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. */
-  };
-
 /* Parses the target variable or vector element into a new
    `struct lvalue', which is returned. */
 static struct lvalue *
@@ -360,7 +382,7 @@ lvalue_parse (struct lexer *lexer, struct dataset *ds)
       lex_get (lexer);
       if (!lex_force_match (lexer, T_LPAREN))
        goto lossage;
-      lvalue->element = expr_parse (lexer, ds, EXPR_NUMBER);
+      lvalue->element = expr_parse (lexer, ds, VAL_NUMERIC);
       if (lvalue->element == NULL)
         goto lossage;
       if (!lex_force_match (lexer, T_RPAREN))