Rename procedure.[ch] to dataset.[ch].
[pspp-builds.git] / src / language / control / loop.c
index ce80f665ab17e397c6ef23d33149ea75f1fc9325..e91d9438f39c131836141ee2be53d47c970d2904 100644 (file)
@@ -1,40 +1,39 @@
-/* PSPP - computes sample statistics.
-   Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
-   Written by Ben Pfaff <blp@gnu.org>.
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 1997-9, 2000, 2009-2011 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 the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   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
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-   This program is distributed in the hope that it will be useful, but
-   WITHOUT ANY WARRANTY; without even the implied warranty of
-   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   General Public License for more details.
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
    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., 51 Franklin Street, Fifth Floor, Boston, MA
-   02110-1301, USA. */
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
 
 #include <config.h>
 
-#include "control-stack.h"
-#include <data/case.h>
-#include <data/dictionary.h>
-#include <data/procedure.h>
-#include <data/settings.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/compiler.h>
-#include <libpspp/message.h>
-#include <libpspp/misc.h>
-#include <libpspp/pool.h>
-#include <libpspp/str.h>
+#include "language/control/control-stack.h"
+
+#include "data/case.h"
+#include "data/dataset.h"
+#include "data/dictionary.h"
+#include "data/settings.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/compiler.h"
+#include "libpspp/message.h"
+#include "libpspp/misc.h"
+#include "libpspp/pool.h"
+#include "libpspp/str.h"
+
+#include "gl/xalloc.h"
 
 #include "gettext.h"
 #define _(msgid) gettext (msgid)
@@ -58,6 +57,7 @@
 struct loop_trns
   {
     struct pool *pool;
+    struct dataset *ds;
 
     /* Iteration limit. */
     int max_pass_count;         /* Maximum number of passes (-1=unlimited). */
@@ -85,46 +85,49 @@ static trns_finalize_func loop_trns_finalize;
 static trns_proc_func loop_trns_proc, end_loop_trns_proc, break_trns_proc;
 static trns_free_func loop_trns_free;
 
-static struct loop_trns *create_loop_trns (void);
-static bool parse_if_clause (struct loop_trns *, struct expression **);
-static bool parse_index_clause (struct loop_trns *, char index_var_name[]);
+static struct loop_trns *create_loop_trns (struct dataset *);
+static bool parse_if_clause (struct lexer *,
+                             struct loop_trns *, struct expression **);
+static bool parse_index_clause (struct dataset *, struct lexer *,
+                                struct loop_trns *, bool *created_index_var);
 static void close_loop (void *);
 \f
 /* LOOP. */
 
 /* Parses LOOP. */
 int
-cmd_loop (void)
+cmd_loop (struct lexer *lexer, struct dataset *ds)
 {
   struct loop_trns *loop;
-  char index_var_name[LONG_NAME_LEN + 1];
+  bool created_index_var = false;
   bool ok = true;
 
-  loop = create_loop_trns ();
-  while (token != '.' && ok) 
+  loop = create_loop_trns (ds);
+  while (lex_token (lexer) != T_ENDCMD && ok)
     {
-      if (lex_match_id ("IF")) 
-        ok = parse_if_clause (loop, &loop->loop_condition);
+      if (lex_match_id (lexer, "IF"))
+        ok = parse_if_clause (lexer, loop, &loop->loop_condition);
       else
-        ok = parse_index_clause (loop, index_var_name);
+        ok = parse_index_clause (ds, lexer, loop, &created_index_var);
     }
 
-  /* Find index variable and create if necessary. */
-  if (ok && index_var_name[0] != '\0')
+  /* Clean up if necessary. */
+  if (!ok)
     {
-      loop->index_var = dict_lookup_var (dataset_dict (current_dataset), index_var_name);
-      if (loop->index_var == NULL)
-        loop->index_var = dict_create_var (dataset_dict (current_dataset), index_var_name, 0);
+      loop->max_pass_count = 0;
+      if (loop->index_var != NULL && created_index_var)
+        {
+          dict_delete_var (dataset_dict (ds), loop->index_var);
+          loop->index_var = NULL;
+        }
     }
-  
-  if (!ok)
-    loop->max_pass_count = 0;
-  return ok ? CMD_SUCCESS : CMD_FAILURE;
+
+  return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
 }
 
 /* Parses END LOOP. */
 int
-cmd_end_loop (void)
+cmd_end_loop (struct lexer *lexer, struct dataset *ds)
 {
   struct loop_trns *loop;
   bool ok = true;
@@ -132,32 +135,34 @@ cmd_end_loop (void)
   loop = ctl_stack_top (&loop_class);
   if (loop == NULL)
     return CMD_CASCADING_FAILURE;
-  
+
+  assert (loop->ds == ds);
+
   /* Parse syntax. */
-  if (lex_match_id ("IF"))
-    ok = parse_if_clause (loop, &loop->end_loop_condition);
+  if (lex_match_id (lexer, "IF"))
+    ok = parse_if_clause (lexer, loop, &loop->end_loop_condition);
   if (ok)
-    ok = lex_end_of_command () == CMD_SUCCESS;
+    ok = lex_end_of_command (lexer) == CMD_SUCCESS;
 
   if (!ok)
     loop->max_pass_count = 0;
 
   ctl_stack_pop (loop);
-  
+
   return ok ? CMD_SUCCESS : CMD_FAILURE;
 }
 
 /* Parses BREAK. */
 int
-cmd_break (void)
+cmd_break (struct lexer *lexer UNUSED, struct dataset *ds)
 {
   struct ctl_stmt *loop = ctl_stack_search (&loop_class);
   if (loop == NULL)
     return CMD_CASCADING_FAILURE;
 
-  add_transformation (current_dataset, break_trns_proc, NULL, loop);
+  add_transformation (ds, break_trns_proc, NULL, loop);
 
-  return lex_end_of_command ();
+  return CMD_SUCCESS;
 }
 
 /* Closes a LOOP construct by emitting the END LOOP
@@ -166,9 +171,9 @@ static void
 close_loop (void *loop_)
 {
   struct loop_trns *loop = loop_;
-  
-  add_transformation (current_dataset, end_loop_trns_proc, NULL, loop);
-  loop->past_END_LOOP_index = next_transformation (current_dataset);
+
+  add_transformation (loop->ds, end_loop_trns_proc, NULL, loop);
+  loop->past_END_LOOP_index = next_transformation (loop->ds);
 
   /* If there's nothing else limiting the number of loops, use
      MXLOOPS as a limit. */
@@ -176,62 +181,87 @@ close_loop (void *loop_)
       && loop->index_var == NULL
       && loop->loop_condition == NULL
       && loop->end_loop_condition == NULL)
-    loop->max_pass_count = get_mxloops ();
+    loop->max_pass_count = settings_get_mxloops ();
 }
 
 /* Parses an IF clause for LOOP or END LOOP and stores the
    resulting expression to *CONDITION.
    Returns true if successful, false on failure. */
 static bool
-parse_if_clause (struct loop_trns *loop, struct expression **condition) 
+parse_if_clause (struct lexer *lexer,
+                struct loop_trns *loop, struct expression **condition)
 {
-  *condition = expr_parse_pool (loop->pool, dataset_dict (current_dataset), EXPR_BOOLEAN);
+  if (*condition != NULL)
+    {
+      lex_sbc_only_once ("IF");
+      return false;
+    }
+
+  *condition = expr_parse_pool (lexer, loop->pool, loop->ds, EXPR_BOOLEAN);
   return *condition != NULL;
 }
 
 /* Parses an indexing clause into LOOP.
-   Stores the index variable's name in INDEX_VAR_NAME[].
+   Stores true in *CREATED_INDEX_VAR if the index clause created
+   a new variable, false otherwise.
    Returns true if successful, false on failure. */
 static bool
-parse_index_clause (struct loop_trns *loop, char index_var_name[]) 
+parse_index_clause (struct dataset *ds, struct lexer *lexer,
+                    struct loop_trns *loop, bool *created_index_var)
 {
-  if (token != T_ID) 
+  if (loop->index_var != NULL)
+    {
+      msg (SE, _("Only one index clause may be specified."));
+      return false;
+    }
+
+  if (lex_token (lexer) != T_ID)
     {
-      lex_error (NULL);
+      lex_error (lexer, NULL);
       return false;
     }
-  strcpy (index_var_name, tokid);
-  lex_get ();
 
-  if (!lex_force_match ('='))
+  loop->index_var = dict_lookup_var (dataset_dict (ds), lex_tokcstr (lexer));
+  if (loop->index_var != NULL)
+    *created_index_var = false;
+  else
+    {
+      loop->index_var = dict_create_var_assert (dataset_dict (ds),
+                                                lex_tokcstr (lexer), 0);
+      *created_index_var = true;
+    }
+  lex_get (lexer);
+
+  if (!lex_force_match (lexer, T_EQUALS))
     return false;
 
-  loop->first_expr = expr_parse_pool (loop->pool, dataset_dict (current_dataset), EXPR_NUMBER);
+  loop->first_expr = expr_parse_pool (lexer, loop->pool,
+                                     loop->ds, EXPR_NUMBER);
   if (loop->first_expr == NULL)
     return false;
 
   for (;;)
     {
       struct expression **e;
-      if (lex_match (T_TO)) 
+      if (lex_match (lexer, T_TO))
         e = &loop->last_expr;
-      else if (lex_match (T_BY)) 
+      else if (lex_match (lexer, T_BY))
         e = &loop->by_expr;
       else
         break;
 
-      if (*e != NULL) 
+      if (*e != NULL)
         {
           lex_sbc_only_once (e == &loop->last_expr ? "TO" : "BY");
           return false;
         }
-      *e = expr_parse_pool (loop->pool, dataset_dict (current_dataset), EXPR_NUMBER);
+      *e = expr_parse_pool (lexer, loop->pool, loop->ds, EXPR_NUMBER);
       if (*e == NULL)
         return false;
     }
-  if (loop->last_expr == NULL) 
+  if (loop->last_expr == NULL)
     {
-      lex_sbc_missing ("TO");
+      lex_sbc_missing (lexer, "TO");
       return false;
     }
   if (loop->by_expr == NULL)
@@ -242,7 +272,7 @@ parse_index_clause (struct loop_trns *loop, char index_var_name[])
 
 /* Creates, initializes, and returns a new loop_trns. */
 static struct loop_trns *
-create_loop_trns (void) 
+create_loop_trns (struct dataset *ds)
 {
   struct loop_trns *loop = pool_create_container (struct loop_trns, pool);
   loop->max_pass_count = -1;
@@ -250,10 +280,11 @@ create_loop_trns (void)
   loop->index_var = NULL;
   loop->first_expr = loop->by_expr = loop->last_expr = NULL;
   loop->loop_condition = loop->end_loop_condition = NULL;
+  loop->ds = ds;
 
-  add_transformation_with_finalizer (current_dataset, loop_trns_finalize,
+  add_transformation_with_finalizer (ds, loop_trns_finalize,
                                      loop_trns_proc, loop_trns_free, loop);
-  loop->past_LOOP_index = next_transformation (current_dataset);
+  loop->past_LOOP_index = next_transformation (ds);
 
   ctl_stack_push (&loop_class, loop);
 
@@ -261,9 +292,9 @@ create_loop_trns (void)
 }
 
 /* Finalizes LOOP by clearing the control stack, thus ensuring
-   that all open LOOPs are closed. */ 
+   that all open LOOPs are closed. */
 static void
-loop_trns_finalize (void *do_if_ UNUSED) 
+loop_trns_finalize (void *do_if_ UNUSED)
 {
   /* This will be called multiple times if multiple LOOPs were
      executed, which is slightly unclean, but at least it's
@@ -273,24 +304,26 @@ loop_trns_finalize (void *do_if_ UNUSED)
 
 /* Sets up LOOP for the first pass. */
 static int
-loop_trns_proc (void *loop_, struct ccase *c, casenum_t case_num)
+loop_trns_proc (void *loop_, struct ccase **c, casenumber case_num)
 {
   struct loop_trns *loop = loop_;
 
   if (loop->index_var != NULL)
     {
       /* Evaluate loop index expressions. */
-      loop->cur = expr_evaluate_num (loop->first_expr, c, case_num);
+      loop->cur = expr_evaluate_num (loop->first_expr, *c, case_num);
       if (loop->by_expr != NULL)
-       loop->by = expr_evaluate_num (loop->by_expr, c, case_num);
-      loop->last = expr_evaluate_num (loop->last_expr, c, case_num);
+       loop->by = expr_evaluate_num (loop->by_expr, *c, case_num);
+      loop->last = expr_evaluate_num (loop->last_expr, *c, case_num);
 
       /* Even if the loop is never entered, set the index
          variable to the initial value. */
-      case_data_rw (c, loop->index_var->fv)->f = loop->cur;
+      *c = case_unshare (*c);
+      case_data_rw (*c, loop->index_var)->f = loop->cur;
 
       /* Throw out pathological cases. */
-      if (!finite (loop->cur) || !finite (loop->by) || !finite (loop->last)
+      if (!isfinite (loop->cur) || !isfinite (loop->by)
+          || !isfinite (loop->last)
           || loop->by == 0.0
           || (loop->by > 0.0 && loop->cur > loop->last)
           || (loop->by < 0.0 && loop->cur < loop->last))
@@ -304,7 +337,7 @@ loop_trns_proc (void *loop_, struct ccase *c, casenum_t case_num)
 
   /* Check condition. */
   if (loop->loop_condition != NULL
-      && expr_evaluate_num (loop->loop_condition, c, case_num) != 1.0)
+      && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
     goto zero_pass;
 
   return loop->past_LOOP_index;
@@ -325,12 +358,12 @@ loop_trns_free (void *loop_)
 
 /* Finishes a pass through the loop and starts the next. */
 static int
-end_loop_trns_proc (void *loop_, struct ccase *c, casenum_t case_num UNUSED)
+end_loop_trns_proc (void *loop_, struct ccase **c, casenumber case_num UNUSED)
 {
   struct loop_trns *loop = loop_;
 
   if (loop->end_loop_condition != NULL
-      && expr_evaluate_num (loop->end_loop_condition, c, case_num) != 1.0)
+      && expr_evaluate_num (loop->end_loop_condition, *c, case_num) != 0.0)
     goto break_out;
 
   /* MXLOOPS limiter. */
@@ -342,17 +375,18 @@ end_loop_trns_proc (void *loop_, struct ccase *c, casenum_t case_num UNUSED)
     }
 
   /* Indexing clause limiter: counting downward. */
-  if (loop->index_var != NULL) 
+  if (loop->index_var != NULL)
     {
       loop->cur += loop->by;
       if ((loop->by > 0.0 && loop->cur > loop->last)
           || (loop->by < 0.0 && loop->cur < loop->last))
         goto break_out;
-      case_data_rw (c, loop->index_var->fv)->f = loop->cur;
+      *c = case_unshare (*c);
+      case_data_rw (*c, loop->index_var)->f = loop->cur;
     }
 
   if (loop->loop_condition != NULL
-      && expr_evaluate_num (loop->loop_condition, c, case_num) != 1.0)
+      && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
     goto break_out;
 
   return loop->past_LOOP_index;
@@ -363,7 +397,8 @@ end_loop_trns_proc (void *loop_, struct ccase *c, casenum_t case_num UNUSED)
 
 /* Executes BREAK. */
 static int
-break_trns_proc (void *loop_, struct ccase *c UNUSED, casenum_t case_num UNUSED)
+break_trns_proc (void *loop_, struct ccase **c UNUSED,
+                 casenumber case_num UNUSED)
 {
   struct loop_trns *loop = loop_;