better
authorBen Pfaff <blp@cs.stanford.edu>
Sat, 11 Dec 2021 05:02:57 +0000 (21:02 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Sat, 11 Dec 2021 05:02:57 +0000 (21:02 -0800)
src/data/transformations.c
src/language/command.c
src/language/command.def
src/language/command.h
src/language/control/do-if.c
src/language/control/loop.c
src/language/data-io/inpt-pgm.c

index 999638c52715e00544838351e8b2709ac702a8c7..f1cfaf47c9688c5d51cd273d7d623b34292d3449 100644 (file)
@@ -90,7 +90,6 @@ trns_chain_execute (const struct trns_chain *chain,
   for (size_t i = 0; i < chain->n; i++)
     {
       const struct transformation *trns = &chain->xforms[i];
-      printf ("%s\n", trns->class->name);
       int retval = trns->class->execute (trns->aux, c, case_nr);
       if (retval != TRNS_CONTINUE)
         return retval;
index 15b76519678145bd468101037671849f4652c554..09c951cae2cc7690037bffb24b392a4bc654c432 100644 (file)
@@ -86,13 +86,15 @@ cmd_result_is_failure (enum cmd_result result)
 /* Command processing states. */
 enum states
   {
-    S_INITIAL = 1 << 0,         /* Allowed before active dataset defined. */
-    S_DATA = 1 << 1,            /* Allowed after active dataset defined. */
-    S_INPUT_PROGRAM = 1 << 2,   /* Allowed in INPUT PROGRAM. */
-    S_FILE_TYPE = 1 << 3,       /* Allowed in FILE TYPE. */
-    S_NESTED = 1 << 4,          /* Allowed in LOOP and DO IF. */
-
-    S_ANY = S_INITIAL | S_DATA | S_INPUT_PROGRAM | S_FILE_TYPE | S_NESTED,
+    S_INITIAL = 1 << CMD_STATE_INITIAL,
+    S_DATA = 1 << CMD_STATE_DATA,
+    S_INPUT_PROGRAM = 1 << CMD_STATE_INPUT_PROGRAM,
+    S_FILE_TYPE = 1 << CMD_STATE_FILE_TYPE,
+    S_NESTED_DATA = 1 << CMD_STATE_NESTED_DATA,
+    S_NESTED_INPUT_PROGRAM = 1 << CMD_STATE_NESTED_INPUT_PROGRAM,
+
+    S_NESTED_ANY = S_NESTED_DATA | S_NESTED_INPUT_PROGRAM,
+    S_ANY = S_INITIAL | S_DATA | S_INPUT_PROGRAM | S_FILE_TYPE | S_NESTED_ANY,
   };
 
 /* Other command requirements. */
@@ -359,26 +361,7 @@ parse_command_name (struct lexer *lexer, int *n_tokens)
 static bool
 in_correct_state (const struct command *command, enum cmd_state state)
 {
-  switch (state)
-    {
-    case CMD_STATE_INITIAL:
-      return command->states & S_INITIAL;
-
-    case CMD_STATE_DATA:
-      return command->states & S_DATA;
-
-    case CMD_STATE_INPUT_PROGRAM:
-      return command->states & S_INPUT_PROGRAM;
-
-    case CMD_STATE_FILE_TYPE:
-      return command->states & S_FILE_TYPE;
-
-    case CMD_STATE_NESTED:
-      return command->states & S_NESTED;
-
-    default:
-      NOT_REACHED ();
-    }
+  return command->states & (1 << state);
 }
 
 /* Emits an appropriate error message for trying to invoke
@@ -469,8 +452,22 @@ report_state_mismatch (const struct command *command, enum cmd_state state)
       msg (SE, _("%s is not allowed inside %s."), command->name, "FILE TYPE");
       break;
 
-    case CMD_STATE_NESTED:
-      msg (SE, _("%s is not allowed inside DO IF or LOOP."), command->name);
+    case CMD_STATE_NESTED_DATA:
+    case CMD_STATE_NESTED_INPUT_PROGRAM:
+      switch ((int) command->states & S_NESTED_ANY)
+        {
+        case 0:
+          msg (SE, _("%s is not allowed inside DO IF or LOOP."), command->name);
+          break;
+
+        case S_NESTED_DATA:
+          msg (SE, _("In INPUT PROGRAM, "
+                     "%s is not allowed inside DO IF or LOOP."), command->name);
+          break;
+
+        default:
+          NOT_REACHED ();
+        }
       break;
     }
 }
index 53a23a2c9008066916784fd77c87e2f1ea67c5d4..b588552244bd20036f307c97aabdff598c809c1c 100644 (file)
@@ -65,46 +65,48 @@ DEF_CMD (S_INITIAL | S_DATA, 0, "DATASET NAME", cmd_dataset_name)
 DEF_CMD (S_INITIAL | S_DATA, 0, "DATASET DISPLAY", cmd_dataset_display)
 
 /* Utilities that may appear after active file definition or within INPUT PROGRAM. */
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "ADD VALUE LABELS", cmd_add_value_labels)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "ADD DOCUMENT", cmd_add_documents)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "APPLY DICTIONARY", cmd_apply_dictionary)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "DATAFILE ATTRIBUTE", cmd_datafile_attribute)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "DISPLAY", cmd_display)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "DOCUMENT", cmd_document)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "DROP DOCUMENTS", cmd_drop_documents)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "FORMATS", cmd_formats)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "LEAVE", cmd_leave)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "MISSING VALUES", cmd_missing_values)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "MRSETS", cmd_mrsets)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "PRINT FORMATS", cmd_print_formats)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "SPLIT FILE", cmd_split_file)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "VALUE LABELS", cmd_value_labels)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "VARIABLE ALIGNMENT", cmd_variable_alignment)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "VARIABLE ATTRIBUTE", cmd_variable_attribute)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "VARIABLE LABELS", cmd_variable_labels)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "VARIABLE LEVEL", cmd_variable_level)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "VARIABLE ROLE", cmd_variable_role)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "VARIABLE WIDTH", cmd_variable_width)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "VECTOR", cmd_vector)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "WEIGHT", cmd_weight)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "WRITE FORMATS", cmd_write_formats)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "ADD VALUE LABELS", cmd_add_value_labels)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "ADD DOCUMENT", cmd_add_documents)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "APPLY DICTIONARY", cmd_apply_dictionary)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "DATAFILE ATTRIBUTE", cmd_datafile_attribute)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "DISPLAY", cmd_display)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "DOCUMENT", cmd_document)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "DROP DOCUMENTS", cmd_drop_documents)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "FORMATS", cmd_formats)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "LEAVE", cmd_leave)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "MISSING VALUES", cmd_missing_values)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "MRSETS", cmd_mrsets)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "PRINT FORMATS", cmd_print_formats)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "SPLIT FILE", cmd_split_file)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "VALUE LABELS", cmd_value_labels)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "VARIABLE ALIGNMENT", cmd_variable_alignment)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "VARIABLE ATTRIBUTE", cmd_variable_attribute)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "VARIABLE LABELS", cmd_variable_labels)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "VARIABLE LEVEL", cmd_variable_level)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "VARIABLE ROLE", cmd_variable_role)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "VARIABLE WIDTH", cmd_variable_width)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "VECTOR", cmd_vector)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "WEIGHT", cmd_weight)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "WRITE FORMATS", cmd_write_formats)
 
 /* Transformations. */
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "BREAK", cmd_break)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "COMPUTE", cmd_compute)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "DO IF", cmd_do_if)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "IF", cmd_if)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "LOOP", cmd_loop)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "NUMERIC", cmd_numeric)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "PRINT EJECT", cmd_print_eject)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "PRINT SPACE", cmd_print_space)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "PRINT", cmd_print)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "RECODE", cmd_recode)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "SELECT IF", cmd_select_if)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "STRING", cmd_string)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "WRITE", cmd_write)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, F_ENHANCED, "XEXPORT", cmd_xexport)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "XSAVE", cmd_xsave)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "BREAK", cmd_break)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "COMPUTE", cmd_compute)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "DO IF", cmd_do_if)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "IF", cmd_if)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "LOOP", cmd_loop)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "NUMERIC", cmd_numeric)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "PRINT EJECT", cmd_print_eject)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "PRINT SPACE", cmd_print_space)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "PRINT", cmd_print)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "RECODE", cmd_recode)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "STRING", cmd_string)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "WRITE", cmd_write)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, F_ENHANCED, "XEXPORT", cmd_xexport)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED_ANY, 0, "XSAVE", cmd_xsave)
+
+/* Restricted transformations. */
+DEF_CMD (S_DATA | S_NESTED_DATA, 0, "SELECT IF", cmd_select_if)
 
 /* Commands that may appear after active dataset definition. */
 DEF_CMD (S_DATA, 0, "AGGREGATE", cmd_aggregate)
@@ -148,9 +150,9 @@ DEF_CMD (S_DATA, 0, "TEMPORARY", cmd_temporary)
 DEF_CMD (S_DATA, 0, "USE", cmd_use)
 
 /* Commands valid only with INPUT PROGRAM. */
-DEF_CMD (S_INPUT_PROGRAM | S_NESTED, 0, "END CASE", cmd_end_case)
-DEF_CMD (S_INPUT_PROGRAM | S_NESTED, 0, "END FILE", cmd_end_file)
-DEF_CMD (S_INPUT_PROGRAM | S_NESTED, 0, "REREAD", cmd_reread)
+DEF_CMD (S_INPUT_PROGRAM | S_NESTED_INPUT_PROGRAM, 0, "END CASE", cmd_end_case)
+DEF_CMD (S_INPUT_PROGRAM | S_NESTED_INPUT_PROGRAM, 0, "END FILE", cmd_end_file)
+DEF_CMD (S_INPUT_PROGRAM | S_NESTED_INPUT_PROGRAM, 0, "REREAD", cmd_reread)
 
 /* Commands for testing PSPP. */
 DEF_CMD (S_ANY, F_TESTING, "DEBUG EXPAND", cmd_debug_expand)
index f38ed5cdb9275d6d938884145a80ab315cfc8341..ecd2a752659d663366c3788fe7c9f5d105a36dfe 100644 (file)
@@ -43,7 +43,10 @@ enum cmd_state
     CMD_STATE_DATA,             /* Active dataset has been defined. */
     CMD_STATE_INPUT_PROGRAM,    /* Inside INPUT PROGRAM. */
     CMD_STATE_FILE_TYPE,        /* Inside FILE TYPE. */
-    CMD_STATE_NESTED,           /* Inside LOOP or DO IF. */
+
+    /* Inside LOOP or DO IF... */
+    CMD_STATE_NESTED_DATA,      /* ...in CMD_STATE_DATA. */
+    CMD_STATE_NESTED_INPUT_PROGRAM, /* ...in CMD_STATE_INPUT_PROGRAM. */
   };
 
 struct dataset;
index efe4667753a387fcb9e0de66d98bf0694f1c0896..f4fab4734ccadc0e191fae79c37115d3c3d07ed5 100644 (file)
@@ -21,6 +21,7 @@
 #include "data/dataset.h"
 #include "data/transformations.h"
 #include "language/command.h"
+#include "language/data-io/inpt-pgm.h"
 #include "language/expressions/public.h"
 #include "language/lexer/lexer.h"
 #include "libpspp/compiler.h"
@@ -45,6 +46,9 @@ struct do_if_trns
   {
     struct clause *clauses;     /* Clauses. */
     size_t n_clauses;           /* Number of clauses. */
+
+    const struct trns_chain *resume;
+    size_t ofs;
   };
 
 static const struct trns_class do_if_trns_class;
@@ -127,7 +131,10 @@ cmd_do_if (struct lexer *lexer, struct dataset *ds)
           start_clause (lexer, ds, false, do_if, &allocated_clauses, &ok);
         }
       else
-        cmd_parse_in_state (lexer, ds, CMD_STATE_NESTED);
+        cmd_parse_in_state (lexer, ds,
+                            (in_input_program ()
+                             ? CMD_STATE_NESTED_INPUT_PROGRAM
+                             : CMD_STATE_NESTED_DATA));
     }
   finish_clause (ds, do_if);
 
@@ -136,32 +143,69 @@ cmd_do_if (struct lexer *lexer, struct dataset *ds)
   return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
 }
 
-/* DO IF transformation procedure.
-   Checks each clause and jumps to the appropriate
-   transformation. */
+static const struct trns_chain *
+do_if_find_clause (const struct do_if_trns *do_if,
+                   struct ccase *c, casenumber case_num)
+{
+  for (size_t i = 0; i < do_if->n_clauses; i++)
+    {
+      const struct clause *clause = &do_if->clauses[i];
+      if (!clause->condition)
+        return &clause->xforms;
+
+      double boolean = expr_evaluate_num (clause->condition, c, case_num);
+      if (boolean != 0.0)
+        return boolean == SYSMIS ? NULL : &clause->xforms;
+    }
+  return NULL;
+}
+
 static enum trns_result
 do_if_trns_proc (void *do_if_, struct ccase **c, casenumber case_num)
 {
   struct do_if_trns *do_if = do_if_;
 
-  for (size_t i = 0; i < do_if->n_clauses; i++)
+  const struct trns_chain *chain;
+  size_t start;
+  if (do_if->resume)
     {
-      const struct clause *clause = &do_if->clauses[i];
-      if (clause->condition != NULL)
+      chain = do_if->resume;
+      start = do_if->ofs;
+      do_if->resume = NULL;
+      do_if->ofs = 0;
+    }
+  else
+    {
+      chain = do_if_find_clause (do_if, *c, case_num);
+      if (!chain)
+        return TRNS_CONTINUE;
+      start = 0;
+    }
+
+  for (size_t i = start; i < chain->n; i++)
+    {
+      const struct transformation *trns = &chain->xforms[i];
+      enum trns_result r = trns->class->execute (trns->aux, c, case_num);
+      switch (r)
         {
-          double boolean = expr_evaluate_num (clause->condition, *c, case_num);
-          if (boolean == 0.0)
-            continue;
-          else if (boolean == SYSMIS)
-            return TRNS_CONTINUE;
-        }
+        case TRNS_CONTINUE:
+          break;
 
-      return trns_chain_execute (&clause->xforms, case_num, c);
+        case TRNS_BREAK:
+        case TRNS_DROP_CASE:
+        case TRNS_ERROR:
+        case TRNS_END_FILE:
+          return r;
+
+        case TRNS_END_CASE:
+          do_if->resume = chain;
+          do_if->ofs = i;
+          return r;
+        }
     }
   return TRNS_CONTINUE;
 }
 
-/* Frees a DO IF transformation. */
 static bool
 do_if_trns_free (void *do_if_)
 {
index f081c761c78f01e2b596194eb9810930567fd166..22c73519de3caa70f48b8b2e25f0cd98bdb9510e 100644 (file)
 #include "data/transformations.h"
 #include "data/variable.h"
 #include "language/command.h"
+#include "language/data-io/inpt-pgm.h"
 #include "language/expressions/public.h"
 #include "language/lexer/lexer.h"
+#include "libpspp/assertion.h"
 #include "libpspp/compiler.h"
 #include "libpspp/message.h"
 #include "libpspp/misc.h"
 struct loop_trns
   {
     /* a=a TO b [BY c]. */
-    struct variable *index_var; /* Index variable. */
-    struct expression *first;   /* Starting index. */
-    struct expression *by    /* Index increment (or NULL). */
-    struct expression *last;    /* Terminal index. */
+    struct variable *index_var;    /* Index variable. */
+    struct expression *first_expr; /* Starting index. */
+    struct expression *by_expr;    /* Index increment (or NULL). */
+    struct expression *last_expr;  /* Terminal index. */
 
     /* IF condition for LOOP or END LOOP. */
     struct expression *loop_condition;
@@ -52,6 +54,11 @@ struct loop_trns
 
     /* Inner transformations. */
     struct trns_chain xforms;
+
+    /* State. */
+    double cur, by, last;       /* Index data. */
+    int iteration;              /* For MXLOOPS. */
+    size_t resume_idx;          /* For resuming after END CASE. */
   };
 
 static struct trns_class loop_trns_class;
@@ -70,7 +77,7 @@ int
 cmd_loop (struct lexer *lexer, struct dataset *ds)
 {
   struct loop_trns *loop = xmalloc (sizeof *loop);
-  *loop = (struct loop_trns) { .index_var = NULL };
+  *loop = (struct loop_trns) { .resume_idx = SIZE_MAX };
 
   bool ok = true;
   while (lex_token (lexer) != T_ENDCMD && ok)
@@ -99,11 +106,13 @@ cmd_loop (struct lexer *lexer, struct dataset *ds)
           break;
         }
       else
-        cmd_parse_in_state (lexer, ds, CMD_STATE_NESTED);
+        cmd_parse_in_state (lexer, ds,
+                            (in_input_program ()
+                             ? CMD_STATE_NESTED_INPUT_PROGRAM
+                             : CMD_STATE_NESTED_DATA));
     }
   in_loop--;
   proc_pop_transformations (ds, &loop->xforms);
-  printf ("%zu loop transvformations\n", loop->xforms.n);
 
   add_transformation (ds, &loop_trns_class, loop);
 
@@ -180,30 +189,30 @@ parse_index_clause (struct dataset *ds, struct lexer *lexer,
   if (!lex_force_match (lexer, T_EQUALS))
     return false;
 
-  loop->first = expr_parse (lexer, ds, VAL_NUMERIC);
-  if (loop->first == NULL)
+  loop->first_expr = expr_parse (lexer, ds, VAL_NUMERIC);
+  if (loop->first_expr == NULL)
     return false;
 
   for (;;)
     {
       struct expression **e;
       if (lex_match (lexer, T_TO))
-        e = &loop->last;
+        e = &loop->last_expr;
       else if (lex_match (lexer, T_BY))
-        e = &loop->by;
+        e = &loop->by_expr;
       else
         break;
 
       if (*e != NULL)
         {
-          lex_sbc_only_once (e == &loop->last ? "TO" : "BY");
+          lex_sbc_only_once (e == &loop->last_expr ? "TO" : "BY");
           return false;
         }
       *e = expr_parse (lexer, ds, VAL_NUMERIC);
       if (*e == NULL)
         return false;
     }
-  if (loop->last == NULL)
+  if (loop->last_expr == NULL)
     {
       lex_sbc_missing ("TO");
       return false;
@@ -218,39 +227,69 @@ loop_trns_proc (void *loop_, struct ccase **c, casenumber case_num)
 {
   struct loop_trns *loop = loop_;
 
-  double cur, by, last;
+  size_t start_idx = loop->resume_idx;
+  loop->resume_idx = SIZE_MAX;
+  if (start_idx != SIZE_MAX)
+    goto resume;
+
   if (loop->index_var)
     {
       /* Evaluate loop index expressions. */
-      cur = expr_evaluate_num (loop->first, *c, case_num);
-      by = loop->by ? expr_evaluate_num (loop->by, *c, case_num) : 1.0;
-      last = expr_evaluate_num (loop->last, *c, case_num);
+      loop->cur = expr_evaluate_num (loop->first_expr, *c, case_num);
+      loop->by = (loop->by_expr
+                  ? expr_evaluate_num (loop->by_expr, *c, case_num)
+                  : 1.0);
+      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. */
       *c = case_unshare (*c);
-      *case_num_rw (*c, loop->index_var) = cur;
+      *case_num_rw (*c, loop->index_var) = loop->cur;
 
       /* Throw out pathological cases. */
-      if (!isfinite (cur) || !isfinite (by) || !isfinite (last)
-          || by == 0.0
-          || (by > 0.0 && cur > last)
-          || (by < 0.0 && cur < 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))
         return TRNS_CONTINUE;
     }
-  else
-    cur = by = last = 0.0;
 
-  for (int i = 0; loop->index_var || i < settings_get_mxloops (); i++)
+  for (loop->iteration = 0;
+       loop->index_var || loop->iteration < settings_get_mxloops ();
+       loop->iteration++)
     {
-      printf ("loop %g %g %g\n", cur, by, last);
       if (loop->loop_condition
           && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
         break;
 
-      enum trns_result r = trns_chain_execute (&loop->xforms, case_num, c);
-      if (r != TRNS_CONTINUE)
-        return r == TRNS_BREAK ? TRNS_CONTINUE : r;
+      start_idx = 0;
+    resume:
+      for (size_t i = start_idx; i < loop->xforms.n; i++)
+        {
+          const struct transformation *trns = &loop->xforms.xforms[i];
+          enum trns_result r = trns->class->execute (trns->aux, c, case_num);
+          switch (r)
+            {
+            case TRNS_CONTINUE:
+              break;
+
+            case TRNS_BREAK:
+              return TRNS_CONTINUE;
+
+            case TRNS_END_CASE:
+              loop->resume_idx = i;
+              return TRNS_END_CASE;
+
+            case TRNS_ERROR:
+            case TRNS_END_FILE:
+              return r;
+
+            case TRNS_DROP_CASE:
+              NOT_REACHED ();
+            }
+        }
 
       if (loop->end_loop_condition != NULL
           && expr_evaluate_num (loop->end_loop_condition, *c, case_num) != 0.0)
@@ -258,12 +297,12 @@ loop_trns_proc (void *loop_, struct ccase **c, casenumber case_num)
 
       if (loop->index_var)
         {
-          cur += by;
-          if (by > 0.0 ? cur > last : cur < last)
+          loop->cur += loop->by;
+          if (loop->by > 0.0 ? loop->cur > loop->last : loop->cur < loop->last)
             break;
 
           *c = case_unshare (*c);
-          *case_num_rw (*c, loop->index_var) = cur;
+          *case_num_rw (*c, loop->index_var) = loop->cur;
         }
     }
   return TRNS_CONTINUE;
@@ -275,9 +314,9 @@ loop_trns_free (void *loop_)
 {
   struct loop_trns *loop = loop_;
 
-  expr_free (loop->first);
-  expr_free (loop->by);
-  expr_free (loop->last);
+  expr_free (loop->first_expr);
+  expr_free (loop->by_expr);
+  expr_free (loop->last_expr);
 
   expr_free (loop->loop_condition);
   expr_free (loop->end_loop_condition);
index 55b19cbf5e2bb144c7421e54d84360e55f0fcd8f..03345003b701d9d6ba6fd339ce49c44d1fe611b8 100644 (file)
@@ -84,7 +84,7 @@ in_input_program (void)
 static void
 emit_END_CASE (struct dataset *ds)
 {
-  add_transformation (ds, &end_case_trns_class, NULL);
+  add_transformation (ds, &end_case_trns_class, xzalloc (sizeof (bool)));
 }
 
 int
@@ -165,10 +165,8 @@ input_program_casereader_read (struct casereader *reader UNUSED, void *inp_)
   struct ccase *c = case_create (inp->proto);
   caseinit_init_vars (inp->init, c);
 
-  printf ("%s:%d\n", __FILE__, __LINE__);
   for (size_t i = inp->idx < inp->xforms.n ? inp->idx : 0; ; i++)
     {
-      printf ("%s:%d %zu\n", __FILE__, __LINE__, i);
       if (i >= inp->xforms.n)
         {
           i = 0;
@@ -181,23 +179,19 @@ input_program_casereader_read (struct casereader *reader UNUSED, void *inp_)
       switch (trns->class->execute (trns->aux, &c, inp->case_nr))
         {
         case TRNS_END_CASE:
-          printf ("END CASE\n");
           inp->case_nr++;
-          inp->idx = i + 1;
+          inp->idx = i;
           return c;
 
         case TRNS_ERROR:
-          printf ("ERROR\n");
           casereader_force_error (reader);
           /* Fall through. */
         case TRNS_END_FILE:
-          printf ("END FILE\n");
           inp->eof = true;
           case_unref (c);
           return NULL;
 
         case TRNS_CONTINUE:
-          printf ("CONTINUE\n");
           break;
 
         default:
@@ -246,16 +240,26 @@ cmd_end_case (struct lexer *lexer UNUSED, struct dataset *ds)
 
 /* Outputs the current case */
 static enum trns_result
-end_case_trns_proc (void *aux UNUSED, struct ccase **c UNUSED,
+end_case_trns_proc (void *resume_, struct ccase **c UNUSED,
                     casenumber case_nr UNUSED)
 {
-  printf ("%s:%d\n", __FILE__, __LINE__);
-  return TRNS_END_CASE;
+  bool *resume = resume_;
+  enum trns_result retval = *resume ? TRNS_CONTINUE : TRNS_END_CASE;
+  *resume = !*resume;
+  return retval;
+}
+
+static bool
+end_case_trns_free (void *resume)
+{
+  free (resume);
+  return true;
 }
 
 static const struct trns_class end_case_trns_class = {
   .name = "END CASE",
   .execute = end_case_trns_proc,
+  .destroy = end_case_trns_free,
 };
 
 /* REREAD transformation. */