add_case_limit_trns (ds);
if (filter)
add_filter_trns (ds);
- trns_chain_finalize (ds->cur_trns_chain);
/* Make permanent_dict refer to the dictionary right before
data reaches the sink. */
dataset_transformations_changed__ (ds, true);
}
-/* Adds a transformation that processes a case with PROC and
- frees itself with FREE to the current set of transformations.
- When parsing of the block of transformations is complete,
- FINALIZE will be called.
- The functions are passed AUX as auxiliary data. */
-void
-add_transformation_with_finalizer (struct dataset *ds,
- trns_finalize_func *finalize,
- trns_proc_func *proc,
- trns_free_func *free, void *aux)
-{
- trns_chain_append (ds->cur_trns_chain, finalize, proc, free, aux);
- dataset_transformations_changed__ (ds, true);
-}
-
-/* Returns the index of the next transformation.
- This value can be returned by a transformation procedure
- function to indicate a "jump" to that transformation. */
-size_t
-next_transformation (const struct dataset *ds)
-{
- return trns_chain_next (ds->cur_trns_chain);
-}
-
/* Returns true if the next call to add_transformation() will add
a temporary transformation, false if it will add a permanent
transformation. */
ds->permanent_dict = dict_clone (ds->dict);
- trns_chain_finalize (ds->permanent_trns_chain);
ds->temporary_trns_chain = ds->cur_trns_chain = trns_chain_create ();
dataset_transformations_changed__ (ds, true);
}
{
if (proc_in_temporary_transformations (ds))
{
- trns_chain_finalize (ds->temporary_trns_chain);
trns_chain_splice (ds->permanent_trns_chain, ds->temporary_trns_chain);
ds->temporary_trns_chain = NULL;
return ok;
}
-static int
+static enum trns_result
store_case_num (void *var_, struct ccase **cc, casenumber case_num)
{
struct variable *var = var_;
perm_var = dict_clone_var_in_place_assert (ds->permanent_dict, temp_var);
trns_chain_append (ds->permanent_trns_chain, NULL, store_case_num,
NULL, perm_var);
- trns_chain_finalize (ds->permanent_trns_chain);
}
else
add_transformation (ds, store_case_num, NULL, temp_var);
/* Limits the maximum number of cases processed to
*CASES_REMAINING. */
-static int
+static enum trns_result
case_limit_trns_proc (void *cases_remaining_,
struct ccase **c UNUSED, casenumber case_nr UNUSED)
{
}
/* FILTER transformation. */
-static int
+static enum trns_result
filter_trns_proc (void *filter_var_,
struct ccase **c, casenumber case_nr UNUSED)
struct dataset;
struct dictionary;
struct session;
+struct transformation;
\f
struct dataset *dataset_create (struct session *, const char *);
struct dataset *dataset_clone (struct dataset *, const char *);
void add_transformation (struct dataset *ds,
trns_proc_func *, trns_free_func *, void *);
-void add_transformation_with_finalizer (struct dataset *ds,
- trns_finalize_func *,
- trns_proc_func *,
- trns_free_func *, void *);
-size_t next_transformation (const struct dataset *ds);
bool proc_cancel_all_transformations (struct dataset *ds);
struct trns_chain *proc_capture_transformations (struct dataset *ds);
+void proc_push_transformations (struct dataset *);
+void proc_pop_transformations (struct dataset *, struct transformation **,
+ size_t *n);
void proc_start_temporary_transformations (struct dataset *ds);
bool proc_in_temporary_transformations (const struct dataset *ds);
transformation index. Normally 0 but set to the starting
index of a spliced chain after splicing. */
int idx_ofs;
- trns_finalize_func *finalize; /* Finalize proc. */
trns_proc_func *execute; /* Executes the transformation. */
trns_free_func *free; /* Garbage collector proc. */
void *aux; /* Auxiliary data. */
struct transformation *trns; /* Array of transformations. */
size_t n_trns; /* Number of transformations. */
size_t allocated_trns; /* Allocated capacity. */
- bool finalized; /* Finalize functions called? */
};
/* Allocates and returns a new transformation chain. */
chain->trns = NULL;
chain->n_trns = 0;
chain->allocated_trns = 0;
- chain->finalized = false;
return chain;
}
-/* Finalizes all the un-finalized transformations in CHAIN.
- Any given transformation is only finalized once. */
-void
-trns_chain_finalize (struct trns_chain *chain)
-{
- while (!chain->finalized)
- {
- size_t i;
-
- chain->finalized = true;
- for (i = 0; i < chain->n_trns; i++)
- {
- struct transformation *trns = &chain->trns[i];
- trns_finalize_func *finalize = trns->finalize;
-
- trns->finalize = NULL;
- if (finalize != NULL)
- finalize (trns->aux);
- }
- }
-}
-
-/* Destroys CHAIN, finalizing it in the process if it has not
- already been finalized. */
+/* Destroys CHAIN. */
bool
trns_chain_destroy (struct trns_chain *chain)
{
- bool ok = true;
+ if (!chain)
+ return true;
- if (chain != NULL)
+ for (size_t i = 0; i < chain->n_trns; i++)
{
- size_t i;
-
- /* Needed to ensure that the control stack gets cleared. */
- trns_chain_finalize (chain);
-
- for (i = 0; i < chain->n_trns; i++)
- {
- struct transformation *trns = &chain->trns[i];
- if (trns->free != NULL)
- ok = trns->free (trns->aux) && ok;
- }
- free (chain->trns);
- free (chain);
+ struct transformation *trns = &chain->trns[i];
+ if (trns->free)
+ ok = trns->free (trns->aux) && ok;
}
-
+ free (chain->trns);
+ free (chain);
return ok;
}
return chain->n_trns == 0;
}
-/* Adds a transformation to CHAIN with finalize function
- FINALIZE, execute function EXECUTE, free function FREE, and
- auxiliary data AUX. */
+/* Adds a transformation to CHAIN with execute function EXECUTE, free function
+ FREE, and auxiliary data AUX. */
void
-trns_chain_append (struct trns_chain *chain, trns_finalize_func *finalize,
- trns_proc_func *execute, trns_free_func *free,
- void *aux)
+trns_chain_append (struct trns_chain *chain, trns_proc_func *execute,
+ trns_free_func *free, void *aux)
{
struct transformation *trns;
- chain->finalized = false;
-
if (chain->n_trns == chain->allocated_trns)
chain->trns = x2nrealloc (chain->trns, &chain->allocated_trns,
sizeof *chain->trns);
trns = &chain->trns[chain->n_trns++];
trns->idx_ofs = 0;
- trns->finalize = finalize;
trns->execute = execute;
trns->free = free;
trns->aux = aux;
}
-/* Appends the transformations in SRC to those in DST,
- and destroys SRC.
- Both DST and SRC must already be finalized. */
+/* Appends the transformations in SRC to those in DST, and destroys SRC. */
void
trns_chain_splice (struct trns_chain *dst, struct trns_chain *src)
{
- size_t i;
-
- assert (dst->finalized);
- assert (src->finalized);
-
if (dst->n_trns + src->n_trns > dst->allocated_trns)
{
dst->allocated_trns = dst->n_trns + src->n_trns;
dst->trns = xnrealloc (dst->trns, dst->allocated_trns, sizeof *dst->trns);
}
- for (i = 0; i < src->n_trns; i++)
+ for (size_t i = 0; i < src->n_trns; i++)
{
struct transformation *d = &dst->trns[i + dst->n_trns];
const struct transformation *s = &src->trns[i];
{
size_t i;
- assert (chain->finalized);
for (i = start < 0 ? 0 : start; i < chain->n_trns;)
{
struct transformation *trns = &chain->trns[i];
/* trns_proc_func return values. */
enum trns_result
{
- TRNS_CONTINUE = -1, /* Continue to next transformation. */
- TRNS_DROP_CASE = -2, /* Drop this case. */
- TRNS_ERROR = -3, /* A serious error, so stop the procedure. */
- TRNS_END_CASE = -4, /* Skip to next case. INPUT PROGRAM only. */
- TRNS_END_FILE = -5 /* End of input. INPUT PROGRAM only. */
+ TRNS_CONTINUE, /* Continue to next transformation. */
+ TRNS_BREAK, /* Break out of LOOP. */
+ TRNS_DROP_CASE, /* Drop this case. */
+ TRNS_ERROR, /* A serious error, so stop the procedure. */
+ TRNS_END_CASE, /* Skip to next case. INPUT PROGRAM only. */
+ TRNS_END_FILE /* End of input. INPUT PROGRAM only. */
};
struct ccase;
-typedef void trns_finalize_func (void *);
-typedef int trns_proc_func (void *, struct ccase **, casenumber);
+
+typedef enum trns_result trns_proc_func (void *, struct ccase **, casenumber);
typedef bool trns_free_func (void *);
+struct trns_class
+ {
+ int (*execute) (void *aux, struct ccase **, casenumber);
+ bool (*destroy) (void *aux);
+ };
\f
/* Transformation chains. */
-struct trns_chain *trns_chain_create (void);
-void trns_chain_finalize (struct trns_chain *);
-bool trns_chain_destroy (struct trns_chain *);
+struct transformation
+ {
+ const struct trns_class *class;
+ void *aux;
+ };
+enum trns_result transformations_execute (const struct transformation *,
+ size_t n,
+ struct ccase **, casenumber case_num);
+void transformations_destroy (struct transformation *, size_t n);
+
+struct trns_builder *trns_builder_create (void);
+void trns_builder_append (struct trns_builder *, trns_proc_func *, trns_free_func *, void *);
+struct trns_chain *trns_builder_finish (struct trns_builder *);
+void trns_builder_destroy (struct trns_chain *);
bool trns_chain_is_empty (const struct trns_chain *);
-void trns_chain_append (struct trns_chain *, trns_finalize_func *,
- trns_proc_func *, trns_free_func *, void *);
size_t trns_chain_next (struct trns_chain *);
enum trns_result trns_chain_execute (const struct trns_chain *,
enum trns_result, struct ccase **,
/* Command processing states. */
enum states
{
- S_INITIAL = 0x01, /* Allowed before active dataset defined. */
- S_DATA = 0x02, /* Allowed after active dataset defined. */
- S_INPUT_PROGRAM = 0x04, /* Allowed in INPUT PROGRAM. */
- S_FILE_TYPE = 0x08, /* Allowed in FILE TYPE. */
- S_ANY = 0x0f /* Allowed anywhere. */
+ 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,
};
/* Other command requirements. */
enum flags
{
- F_ENHANCED = 0x10, /* Allowed only in enhanced syntax mode. */
- F_TESTING = 0x20, /* Allowed only in testing mode. */
- F_ABBREV = 0x80 /* Not a candidate for name completion. */
+ F_ENHANCED = 1 << 0, /* Allowed only in enhanced syntax mode. */
+ F_TESTING = 1 << 1, /* Allowed only in testing mode. */
+ F_ABBREV = 1 << 2 /* Not a candidate for name completion. */
};
/* A single command. */
static const size_t n_commands = sizeof commands / sizeof *commands;
static bool in_correct_state (const struct command *, enum cmd_state);
-static bool report_state_mismatch (const struct command *, enum cmd_state);
+static void report_state_mismatch (const struct command *, enum cmd_state);
static void set_completion_state (enum cmd_state);
\f
/* Command parser. */
static bool
in_correct_state (const struct command *command, enum cmd_state state)
{
- return ((state == CMD_STATE_INITIAL && command->states & S_INITIAL)
- || (state == CMD_STATE_DATA && command->states & S_DATA)
- || (state == CMD_STATE_INPUT_PROGRAM
- && command->states & S_INPUT_PROGRAM)
- || (state == CMD_STATE_FILE_TYPE && command->states & S_FILE_TYPE));
+ 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 ();
+ }
}
/* Emits an appropriate error message for trying to invoke
COMMAND in STATE. */
-static bool
+static void
report_state_mismatch (const struct command *command, enum cmd_state state)
{
assert (!in_correct_state (command, state));
- if (state == CMD_STATE_INITIAL || state == CMD_STATE_DATA)
+
+ switch (state)
{
+ case CMD_STATE_INITIAL:
+ case CMD_STATE_DATA:
switch ((int) command->states)
{
/* One allowed state. */
default:
NOT_REACHED ();
}
- }
- else if (state == CMD_STATE_INPUT_PROGRAM)
- msg (SE, _("%s is not allowed inside %s."),
- command->name, "INPUT PROGRAM");
- else if (state == CMD_STATE_FILE_TYPE)
- msg (SE, _("%s is not allowed inside %s."), command->name, "FILE TYPE");
+ break;
+
+ case CMD_STATE_INPUT_PROGRAM:
+ msg (SE, _("%s is not allowed inside %s."),
+ command->name, "INPUT PROGRAM");
+ break;
- return false;
+ case CMD_STATE_FILE_TYPE:
+ 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);
+ break;
+ }
}
\f
/* Command name completion. */
/* Commands that define (or replace) the active dataset. */
DEF_CMD (S_INITIAL | S_DATA, 0, "ADD FILES", cmd_add_files)
-DEF_CMD (S_INITIAL | S_DATA | S_INPUT_PROGRAM | S_FILE_TYPE, 0, "DATA LIST", cmd_data_list)
+DEF_CMD (S_ANY, 0, "DATA LIST", cmd_data_list)
DEF_CMD (S_INITIAL | S_DATA, 0, "GET", cmd_get)
DEF_CMD (S_INITIAL | S_DATA, 0, "GET DATA", cmd_get_data)
DEF_CMD (S_INITIAL | S_DATA, 0, "IMPORT", cmd_import)
DEF_CMD (S_INITIAL | S_DATA, 0, "DATASET NAME", cmd_dataset_name)
DEF_CMD (S_INITIAL | S_DATA, 0, "DATASET DISPLAY", cmd_dataset_display)
-/* Transformations and utilities that may appear after active
- file definition or within INPUT PROGRAM. */
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "ADD VALUE LABELS", cmd_add_value_labels)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "ADD DOCUMENT", cmd_add_documents)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "APPLY DICTIONARY", cmd_apply_dictionary)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "BREAK", cmd_break)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "COMPUTE", cmd_compute)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "DATAFILE ATTRIBUTE", cmd_datafile_attribute)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "DISPLAY", cmd_display)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "DOCUMENT", cmd_document)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "DO IF", cmd_do_if)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "DROP DOCUMENTS", cmd_drop_documents)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "ELSE IF", cmd_else_if)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "ELSE", cmd_else)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "END IF", cmd_end_if)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "END LOOP", cmd_end_loop)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "FORMATS", cmd_formats)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "IF", cmd_if)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "LEAVE", cmd_leave)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "LOOP", cmd_loop)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "MISSING VALUES", cmd_missing_values)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "MRSETS", cmd_mrsets)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "NUMERIC", cmd_numeric)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "PRINT EJECT", cmd_print_eject)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "PRINT FORMATS", cmd_print_formats)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "PRINT SPACE", cmd_print_space)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "PRINT", cmd_print)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "RECODE", cmd_recode)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "SELECT IF", cmd_select_if)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "SPLIT FILE", cmd_split_file)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "STRING", cmd_string)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VALUE LABELS", cmd_value_labels)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VARIABLE ALIGNMENT", cmd_variable_alignment)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VARIABLE ATTRIBUTE", cmd_variable_attribute)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VARIABLE LABELS", cmd_variable_labels)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VARIABLE LEVEL", cmd_variable_level)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VARIABLE ROLE", cmd_variable_role)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VARIABLE WIDTH", cmd_variable_width)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "VECTOR", cmd_vector)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "WEIGHT", cmd_weight)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "WRITE FORMATS", cmd_write_formats)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "WRITE", cmd_write)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, F_ENHANCED, "XEXPORT", cmd_xexport)
-DEF_CMD (S_DATA | S_INPUT_PROGRAM, 0, "XSAVE", cmd_xsave)
+/* 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)
+
+/* 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, "ELSE IF", cmd_else_if)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "ELSE", cmd_else)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "END IF", cmd_end_if)
+DEF_CMD (S_DATA | S_INPUT_PROGRAM | S_NESTED, 0, "END LOOP", cmd_end_loop)
+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)
/* Commands that may appear after active dataset definition. */
DEF_CMD (S_DATA, 0, "AGGREGATE", cmd_aggregate)
DEF_CMD (S_DATA, 0, "USE", cmd_use)
/* Commands valid only with INPUT PROGRAM. */
-DEF_CMD (S_INPUT_PROGRAM, 0, "END CASE", cmd_end_case)
-DEF_CMD (S_INPUT_PROGRAM, 0, "END FILE", cmd_end_file)
-DEF_CMD (S_INPUT_PROGRAM, 0, "END INPUT PROGRAM", cmd_end_input_program)
-DEF_CMD (S_INPUT_PROGRAM, 0, "REREAD", cmd_reread)
+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, "END INPUT PROGRAM", cmd_end_input_program)
+DEF_CMD (S_INPUT_PROGRAM | S_NESTED, 0, "REREAD", cmd_reread)
/* Commands for testing PSPP. */
DEF_CMD (S_ANY, F_TESTING, "DEBUG EXPAND", cmd_debug_expand)
CMD_STATE_INITIAL, /* No active dataset yet defined. */
CMD_STATE_DATA, /* Active dataset has been defined. */
CMD_STATE_INPUT_PROGRAM, /* Inside INPUT PROGRAM. */
- CMD_STATE_FILE_TYPE /* Inside FILE TYPE. */
+ CMD_STATE_FILE_TYPE, /* Inside FILE TYPE. */
+ CMD_STATE_NESTED, /* Inside LOOP or DO IF. */
};
struct dataset;
language_control_sources = \
- src/language/control/control-stack.c \
- src/language/control/control-stack.h \
src/language/control/define.c \
src/language/control/do-if.c \
src/language/control/loop.c \
+++ /dev/null
-/*
-PSPP - a program for statistical analysis.
-Copyright (C) 2017 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 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.
-
-You should have received a copy of the GNU General Public License
-along with this program. If not, see <http://www.gnu.org/licenses/>.
-*/
-
-#include <config.h>
-
-#include "language/control/control-stack.h"
-
-#include <assert.h>
-#include <stdlib.h>
-
-#include "libpspp/compiler.h"
-#include "libpspp/message.h"
-
-#include "gl/xalloc.h"
-
-#include "gettext.h"
-#define _(msgid) gettext (msgid)
-
-struct ctl_struct
- {
- const struct ctl_class *class; /* Class of control structure. */
- struct ctl_struct *down; /* Points toward the bottom of ctl_stack. */
- void *private; /* Private data. */
- };
-
-static struct ctl_struct *ctl_stack;
-
-void
-ctl_stack_clear (void)
-{
- while (ctl_stack != NULL)
- {
- struct ctl_struct *top = ctl_stack;
- msg (SE, _("%s without %s."),
- top->class->start_name, top->class->end_name);
- ctl_stack_pop (top->private);
- }
-}
-
-void
-ctl_stack_push (const struct ctl_class *class, void *private)
-{
- struct ctl_struct *ctl;
-
- assert (private != NULL);
- ctl = xmalloc (sizeof *ctl);
- ctl->class = class;
- ctl->down = ctl_stack;
- ctl->private = private;
- ctl_stack = ctl;
-}
-
-void *
-ctl_stack_top (const struct ctl_class *class)
-{
- struct ctl_struct *top = ctl_stack;
- if (top != NULL && top->class == class)
- return top->private;
- else
- {
- if (ctl_stack_search (class) != NULL)
- msg (SE, _("This command must appear inside %s...%s, "
- "without intermediate %s...%s."),
- class->start_name, class->end_name,
- top->class->start_name, top->class->end_name);
- return NULL;
- }
-}
-
-void *
-ctl_stack_search (const struct ctl_class *class)
-{
- struct ctl_struct *ctl;
-
- for (ctl = ctl_stack; ctl != NULL; ctl = ctl->down)
- if (ctl->class == class)
- return ctl->private;
-
- msg (SE, _("This command cannot appear outside %s...%s."),
- class->start_name, class->end_name);
- return NULL;
-}
-
-void
-ctl_stack_pop (void *private)
-{
- struct ctl_struct *top = ctl_stack;
-
- assert (top != NULL);
- assert (top->private == private);
-
- top->class->close (top->private);
- ctl_stack = top->down;
- free (top);
-}
-
-bool
-ctl_stack_is_empty (void)
-{
- return ctl_stack == NULL;
-}
+++ /dev/null
-/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 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 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.
-
- You should have received a copy of the GNU General Public License
- along with this program. If not, see <http://www.gnu.org/licenses/>. */
-
-#ifndef CTL_STACK_H
-#define CTL_STACK_H 1
-
-#include <stdbool.h>
-
-/* The following #include avoids a potential problem when Gnulib substitutes
- * for close() by putting "#define close rpl_close" into <unistd.h>, by
- * ensuring that every source file that includes this one sees the #define..
- * (It would probably be better to rename the 'close' member of struct
- * ctl_class.) */
-#include <unistd.h>
-
-struct ctl_class
- {
- const char *start_name; /* e.g. LOOP. */
- const char *end_name; /* e.g. END LOOP. */
- void (*close) (void *); /* Closes the control structure. */
- };
-
-void ctl_stack_clear (void);
-void ctl_stack_push (const struct ctl_class *, void *private);
-void *ctl_stack_top (const struct ctl_class *);
-void *ctl_stack_search (const struct ctl_class *);
-void ctl_stack_pop (void *);
-bool ctl_stack_is_empty (void);
-
-#endif /* ctl_stack.h */
#include <stdlib.h>
-#include "data/case.h"
#include "data/dataset.h"
#include "data/transformations.h"
-#include "data/value.h"
#include "language/command.h"
-#include "language/control/control-stack.h"
#include "language/expressions/public.h"
#include "language/lexer/lexer.h"
#include "libpspp/compiler.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
-/* DO IF, ELSE IF, and ELSE are translated as a single
- transformation that evaluates each condition and jumps to the
- start of the appropriate block of transformations. Each block
- of transformations (except for the last) ends with a
- transformation that jumps past the remaining blocks.
-
- So, the following code:
-
- DO IF a.
- ...block 1...
- ELSE IF b.
- ...block 2...
- ELSE.
- ...block 3...
- END IF.
-
- is effectively translated like this:
-
- IF a GOTO 1, IF b GOTO 2, ELSE GOTO 3.
- 1: ...block 1...
- GOTO 4
- 2: ...block 2...
- GOTO 4
- 3: ...block 3...
- 4:
-
-*/
-
/* A conditional clause. */
struct clause
{
+ struct msg_location *location;
struct expression *condition; /* Test expression; NULL for ELSE clause. */
- int target_index; /* Transformation to jump to if true. */
+ struct transformation *xforms;
+ size_t n_xforms;
};
/* DO IF transformation. */
struct do_if_trns
{
- struct dataset *ds; /* The dataset */
struct clause *clauses; /* Clauses. */
size_t n_clauses; /* Number of clauses. */
- int past_END_IF_index; /* Transformation just past last clause. */
};
-static const struct ctl_class do_if_class;
-
-static int parse_clause (struct lexer *, struct do_if_trns *, struct dataset *ds);
-static void add_clause (struct do_if_trns *, struct expression *condition);
-static void add_else (struct do_if_trns *);
-
-static bool has_else (struct do_if_trns *);
-static bool must_not_have_else (struct do_if_trns *);
-static void close_do_if (void *do_if);
-
-static trns_finalize_func do_if_finalize_func;
-static trns_proc_func do_if_trns_proc, break_trns_proc;
+static trns_proc_func do_if_trns_proc;
static trns_free_func do_if_trns_free;
-/* Parse DO IF. */
-int
-cmd_do_if (struct lexer *lexer, struct dataset *ds)
-{
- struct do_if_trns *do_if = xmalloc (sizeof *do_if);
- do_if->clauses = NULL;
- do_if->n_clauses = 0;
- do_if->ds = ds;
-
- ctl_stack_push (&do_if_class, do_if);
- add_transformation_with_finalizer (ds, do_if_finalize_func,
- do_if_trns_proc, do_if_trns_free, do_if);
-
- return parse_clause (lexer, do_if, ds);
-}
-
-/* Parse ELSE IF. */
-int
-cmd_else_if (struct lexer *lexer, struct dataset *ds)
-{
- struct do_if_trns *do_if = ctl_stack_top (&do_if_class);
- if (do_if == NULL || !must_not_have_else (do_if))
- return CMD_CASCADING_FAILURE;
- return parse_clause (lexer, do_if, ds);
-}
-
-/* Parse ELSE. */
-int
-cmd_else (struct lexer *lexer UNUSED, struct dataset *ds)
+static void
+start_clause (struct lexer *lexer, struct dataset *ds,
+ bool condition, struct do_if_trns *do_if,
+ size_t *allocated_clauses, bool *ok)
{
- struct do_if_trns *do_if = ctl_stack_top (&do_if_class);
-
- if (do_if == NULL || !must_not_have_else (do_if))
- return CMD_CASCADING_FAILURE;
+ if (*ok && do_if->n_clauses > 0
+ && !do_if->clauses[do_if->n_clauses - 1].condition)
+ {
+ if (condition)
+ msg (SE, _("ELSE IF is not allowed following ELSE "
+ "within DO IF...END IF."));
+ else
+ msg (SE, _("Only one ELSE is allowed within DO IF...END IF."));
- assert (ds == do_if->ds);
+ msg_at (SN, do_if->clauses[do_if->n_clauses - 1].location,
+ _("This is the location of the previous ELSE clause."));
- add_else (do_if);
- return CMD_SUCCESS;
-}
+ msg_at (SN, do_if->clauses[0].location,
+ _("This is the location of the DO IF command."));
+ }
-/* Parse END IF. */
-int
-cmd_end_if (struct lexer *lexer UNUSED, struct dataset *ds)
-{
- struct do_if_trns *do_if = ctl_stack_top (&do_if_class);
+ if (do_if->n_clauses >= *allocated_clauses)
+ do_if->clauses = x2nrealloc (do_if->clauses, allocated_clauses,
+ sizeof *do_if->clauses);
+ struct clause *clause = &do_if->clauses[do_if->n_clauses++];
- if (do_if == NULL)
- return CMD_CASCADING_FAILURE;
+ *clause = (struct clause) { .n_xforms = 0 };
+ if (condition)
+ {
+ clause->condition = expr_parse_bool (lexer, ds);
+ if (!clause->condition)
+ lex_discard_rest_of_command (lexer);
+ }
+ clause->location = lex_ofs_location (lexer, 0, lex_ofs (lexer));
- assert (ds == do_if->ds);
- ctl_stack_pop (do_if);
+ lex_end_of_command (lexer);
+ lex_get (lexer);
- return CMD_SUCCESS;
+ proc_push_transformations (ds);
}
-/* Closes out DO_IF, by adding a sentinel ELSE clause if
- necessary and setting past_END_IF_index. */
static void
-close_do_if (void *do_if_)
+finish_clause (struct dataset *ds, struct do_if_trns *do_if)
{
- struct do_if_trns *do_if = do_if_;
-
- if (!has_else (do_if))
- add_else (do_if);
- do_if->past_END_IF_index = next_transformation (do_if->ds);
+ struct clause *clause = &do_if->clauses[do_if->n_clauses - 1];
+ proc_pop_transformations (ds, &clause->xforms, &clause->n_xforms);
}
-/* Adds an ELSE clause to DO_IF pointing to the next
- transformation. */
-static void
-add_else (struct do_if_trns *do_if)
+/* Parse DO IF. */
+int
+cmd_do_if (struct lexer *lexer, struct dataset *ds)
{
- assert (!has_else (do_if));
- add_clause (do_if, NULL);
-}
+ struct do_if_trns *do_if = xmalloc (sizeof *do_if);
+ *do_if = (struct do_if_trns) { .n_clauses = 0 };
-/* Returns true if DO_IF does not yet have an ELSE clause.
- Reports an error and returns false if it does already. */
-static bool
-must_not_have_else (struct do_if_trns *do_if)
-{
- if (has_else (do_if))
+ size_t allocated_clauses = 0;
+ bool ok = true;
+
+ start_clause (lexer, ds, true, do_if, &allocated_clauses, &ok);
+ while (!lex_match_phrase (lexer, "END IF"))
{
- msg (SE, _("This command may not follow %s in %s ... %s."), "ELSE", "DO IF", "END IF");
- return false;
+ if (lex_token (lexer) == T_STOP)
+ {
+ lex_error (lexer, NULL);
+ ok = false;
+ break;
+ }
+ else if (lex_match_phrase (lexer, "ELSE IF"))
+ {
+ finish_clause (ds, do_if);
+ start_clause (lexer, ds, true, do_if, &allocated_clauses, &ok);
+ }
+ else if (lex_match_id (lexer, "ELSE"))
+ {
+ finish_clause (ds, do_if);
+ start_clause (lexer, ds, false, do_if, &allocated_clauses, &ok);
+ }
+ else
+ cmd_parse_in_state (lexer, ds, CMD_STATE_NESTED);
}
- else
- return true;
-}
-
-/* Returns true if DO_IF already has an ELSE clause,
- false otherwise. */
-static bool
-has_else (struct do_if_trns *do_if)
-{
- return (do_if->n_clauses != 0
- && do_if->clauses[do_if->n_clauses - 1].condition == NULL);
-}
-
-/* Parses a DO IF or ELSE IF expression and appends the
- corresponding clause to DO_IF. Checks for end of command and
- returns a command return code. */
-static int
-parse_clause (struct lexer *lexer, struct do_if_trns *do_if, struct dataset *ds)
-{
- struct expression *condition;
-
- condition = expr_parse_bool (lexer, NULL, ds);
- if (condition == NULL)
- return CMD_CASCADING_FAILURE;
-
- add_clause (do_if, condition);
-
- return CMD_SUCCESS;
-}
+ finish_clause (ds, do_if);
-/* Adds a clause to DO_IF that tests for the given CONDITION and,
- if true, jumps to the set of transformations produced by
- following commands. */
-static void
-add_clause (struct do_if_trns *do_if, struct expression *condition)
-{
- struct clause *clause;
-
- if (do_if->n_clauses > 0)
- add_transformation (do_if->ds, break_trns_proc, NULL, do_if);
+ add_transformation (ds, do_if_trns_proc, do_if_trns_free, do_if);
- do_if->clauses = xnrealloc (do_if->clauses,
- do_if->n_clauses + 1, sizeof *do_if->clauses);
- clause = &do_if->clauses[do_if->n_clauses++];
- clause->condition = condition;
- clause->target_index = next_transformation (do_if->ds);
-}
-
-/* Finalizes DO IF by clearing the control stack, thus ensuring
- that all open DO IFs are closed. */
-static void
-do_if_finalize_func (void *do_if_ UNUSED)
-{
- /* This will be called multiple times if multiple DO IFs were
- executed, which is slightly unclean, but at least it's
- idempotent. */
- ctl_stack_clear ();
+ return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
}
/* DO IF transformation procedure.
Checks each clause and jumps to the appropriate
transformation. */
-static int
-do_if_trns_proc (void *do_if_, struct ccase **c, casenumber case_num UNUSED)
+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_;
- struct clause *clause;
- for (clause = do_if->clauses; clause < do_if->clauses + do_if->n_clauses;
- clause++)
+ for (size_t i = 0; i < do_if->n_clauses; i++)
{
+ const struct clause *clause = &do_if->clauses[i];
if (clause->condition != NULL)
{
double boolean = expr_evaluate_num (clause->condition, *c, case_num);
- if (boolean == 1.0)
- return clause->target_index;
+ if (boolean == 0.0)
+ continue;
else if (boolean == SYSMIS)
- return do_if->past_END_IF_index;
+ return TRNS_CONTINUE;
}
- else
- return clause->target_index;
+
+ return transformations_execute (clause->xforms, clause->n_xforms,
+ c, case_num);
}
- return do_if->past_END_IF_index;
+ return TRNS_CONTINUE;
}
/* Frees a DO IF transformation. */
do_if_trns_free (void *do_if_)
{
struct do_if_trns *do_if = do_if_;
- struct clause *clause;
- for (clause = do_if->clauses; clause < do_if->clauses + do_if->n_clauses;
- clause++)
- expr_free (clause->condition);
+ for (size_t i = 0; i < do_if->n_clauses; i++)
+ {
+ struct clause *clause = &do_if->clauses[i];
+
+ msg_location_destroy (clause->location);
+ expr_free (clause->condition);
+
+ transformations_destroy (clause->xforms, clause->n_xforms);
+ }
free (do_if->clauses);
free (do_if);
return true;
}
-
-/* Breaks out of a DO IF construct. */
-static int
-break_trns_proc (void *do_if_, struct ccase **c UNUSED,
- casenumber case_num UNUSED)
-{
- struct do_if_trns *do_if = do_if_;
-
- return do_if->past_END_IF_index;
-}
-
-/* DO IF control structure class definition. */
-static const struct ctl_class do_if_class =
- {
- "DO IF",
- "END IF",
- close_do_if,
- };
#include <config.h>
-#include "language/control/control-stack.h"
+#include <limits.h>
#include "data/case.h"
#include "data/dataset.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
-/* LOOP outputs a transformation that is executed only on the
- first pass through the loop. On this trip, it initializes for
- the first pass by resetting the pass number, setting up the
- indexing clause, and testing the LOOP IF clause. If the loop
- is not to be entered at all, it jumps forward just past the
- END LOOP transformation; otherwise, it continues to the
- transformation following LOOP.
-
- END LOOP outputs a transformation that executes at the end of
- each trip through the loop. It checks the END LOOP IF clause,
- then updates the pass number, increments the indexing clause,
- and tests the LOOP IF clause. If another pass through the
- loop is due, it jumps backward to just after the LOOP
- transformation; otherwise, it continues to the transformation
- following END LOOP. */
-
struct loop_trns
{
- struct pool *pool;
- struct dataset *ds;
-
- /* Iteration limit. */
- int max_pass_count; /* Maximum number of passes (-1=unlimited). */
- int pass; /* Number of passes through the loop so far. */
-
/* a=a TO b [BY c]. */
struct variable *index_var; /* Index variable. */
- struct expression *first_expr; /* Starting index. */
- struct expression *by_expr; /* Index increment (default 1.0 if null). */
- struct expression *last_expr; /* Terminal index. */
- double cur, by, last; /* Current value, increment, last value. */
+ struct expression *first; /* Starting index. */
+ struct expression *by; /* Index increment (or NULL). */
+ struct expression *last; /* Terminal index. */
/* IF condition for LOOP or END LOOP. */
struct expression *loop_condition;
struct expression *end_loop_condition;
- /* Transformation indexes. */
- int past_LOOP_index; /* Just past LOOP transformation. */
- int past_END_LOOP_index; /* Just past END LOOP transformation. */
+ /* Inner transformations. */
+ struct transformation *xforms;
+ size_t n_xforms;
};
-static const struct ctl_class loop_class;
-
-static trns_finalize_func loop_trns_finalize;
-static trns_proc_func loop_trns_proc, end_loop_trns_proc, break_trns_proc;
+static trns_proc_func loop_trns_proc;
static trns_free_func loop_trns_free;
-static struct loop_trns *create_loop_trns (struct dataset *);
-static bool parse_if_clause (struct lexer *,
- struct loop_trns *, struct expression **);
+static bool parse_if_clause (struct lexer *, struct dataset *,
+ struct expression **);
static bool parse_index_clause (struct dataset *, struct lexer *,
- struct loop_trns *, bool *created_index_var);
-static void close_loop (void *);
+ struct loop_trns *);
\f
/* LOOP. */
int
cmd_loop (struct lexer *lexer, struct dataset *ds)
{
- struct loop_trns *loop;
- bool created_index_var = false;
- bool ok = true;
+ struct loop_trns *loop = xmalloc (sizeof *loop);
+ *loop = (struct loop_trns) { .index_var = NULL };
- loop = create_loop_trns (ds);
+ bool ok = true;
while (lex_token (lexer) != T_ENDCMD && ok)
{
if (lex_match_id (lexer, "IF"))
- ok = parse_if_clause (lexer, loop, &loop->loop_condition);
+ ok = parse_if_clause (lexer, ds, &loop->loop_condition);
else
- ok = parse_index_clause (ds, lexer, loop, &created_index_var);
+ ok = parse_index_clause (ds, lexer, loop);
}
+ lex_end_of_command (lexer);
- /* Clean up if necessary. */
- if (!ok)
+ proc_push_transformations (ds);
+ for (;;)
{
- loop->max_pass_count = 0;
- if (loop->index_var != NULL && created_index_var)
+ if (lex_token (lexer) == T_STOP)
{
- dict_delete_var (dataset_dict (ds), loop->index_var);
- loop->index_var = NULL;
+ lex_error (lexer, NULL);
+ ok = false;
+ break;
}
+ else if (lex_match_phrase (lexer, "END LOOP"))
+ {
+ if (lex_match_id (lexer, "IF"))
+ ok = parse_if_clause (lexer, ds, &loop->end_loop_condition) && ok;
+ break;
+ }
+ else
+ cmd_parse_in_state (lexer, ds, CMD_STATE_NESTED);
}
+ proc_pop_transformations (ds, &loop->xforms, &loop->n_xforms);
- return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
-}
-
-/* Parses END LOOP. */
-int
-cmd_end_loop (struct lexer *lexer, struct dataset *ds)
-{
- struct loop_trns *loop;
- bool ok = true;
-
- loop = ctl_stack_top (&loop_class);
- if (loop == NULL)
- return CMD_CASCADING_FAILURE;
-
- assert (loop->ds == ds);
-
- /* Parse syntax. */
- if (lex_match_id (lexer, "IF"))
- ok = parse_if_clause (lexer, loop, &loop->end_loop_condition);
- if (ok)
- ok = lex_end_of_command (lexer) == CMD_SUCCESS;
+ add_transformation (ds, loop_trns_proc, loop_trns_free, loop);
- if (!ok)
- loop->max_pass_count = 0;
-
- ctl_stack_pop (loop);
-
- return ok ? CMD_SUCCESS : CMD_FAILURE;
+ return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
}
/* Parses BREAK. */
int
-cmd_break (struct lexer *lexer UNUSED, struct dataset *ds)
+cmd_break (struct lexer *lexer UNUSED, struct dataset *ds UNUSED)
{
- struct ctl_stmt *loop = ctl_stack_search (&loop_class);
- if (loop == NULL)
- return CMD_CASCADING_FAILURE;
-
- add_transformation (ds, break_trns_proc, NULL, loop);
+ //add_transformation (ds, break_trns_proc, NULL, NULL);
return CMD_SUCCESS;
}
-/* Closes a LOOP construct by emitting the END LOOP
- transformation and finalizing its members appropriately. */
-static void
-close_loop (void *loop_)
-{
- struct loop_trns *loop = loop_;
-
- 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. */
- if (loop->max_pass_count == -1 && loop->index_var == NULL)
- 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 lexer *lexer,
- struct loop_trns *loop, struct expression **condition)
+parse_if_clause (struct lexer *lexer, struct dataset *ds,
+ struct expression **condition)
{
if (*condition != NULL)
{
return false;
}
- *condition = expr_parse_bool (lexer, loop->pool, loop->ds);
+ *condition = expr_parse_bool (lexer, ds);
return *condition != NULL;
}
-/* Parses an indexing clause into LOOP.
- Stores true in *CREATED_INDEX_VAR if the index clause created
- a new variable, false otherwise.
- Returns true if successful, false on failure. */
+/* Parses an indexing clause into LOOP. Returns true if successful, false on
+ failure. */
static bool
parse_index_clause (struct dataset *ds, struct lexer *lexer,
- struct loop_trns *loop, bool *created_index_var)
+ struct loop_trns *loop)
{
if (loop->index_var != NULL)
{
}
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;
- }
+ if (!loop->index_var)
+ loop->index_var = dict_create_var_assert (dataset_dict (ds),
+ lex_tokcstr (lexer), 0);
lex_get (lexer);
if (!lex_force_match (lexer, T_EQUALS))
return false;
- loop->first_expr = expr_parse (lexer, loop->pool, loop->ds, VAL_NUMERIC);
- if (loop->first_expr == NULL)
+ loop->first = expr_parse (lexer, ds, VAL_NUMERIC);
+ if (loop->first == NULL)
return false;
for (;;)
{
struct expression **e;
if (lex_match (lexer, T_TO))
- e = &loop->last_expr;
+ e = &loop->last;
else if (lex_match (lexer, T_BY))
- e = &loop->by_expr;
+ e = &loop->by;
else
break;
if (*e != NULL)
{
- lex_sbc_only_once (e == &loop->last_expr ? "TO" : "BY");
+ lex_sbc_only_once (e == &loop->last ? "TO" : "BY");
return false;
}
- *e = expr_parse (lexer, loop->pool, loop->ds, VAL_NUMERIC);
+ *e = expr_parse (lexer, ds, VAL_NUMERIC);
if (*e == NULL)
return false;
}
- if (loop->last_expr == NULL)
+ if (loop->last == NULL)
{
lex_sbc_missing ("TO");
return false;
}
- if (loop->by_expr == NULL)
- loop->by = 1.0;
return true;
}
-/* Creates, initializes, and returns a new loop_trns. */
-static struct loop_trns *
-create_loop_trns (struct dataset *ds)
-{
- struct loop_trns *loop = pool_create_container (struct loop_trns, pool);
- loop->max_pass_count = -1;
- loop->pass = 0;
- 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 (ds, loop_trns_finalize,
- loop_trns_proc, loop_trns_free, loop);
- loop->past_LOOP_index = next_transformation (ds);
-
- ctl_stack_push (&loop_class, loop);
-
- return loop;
-}
-
-/* Finalizes LOOP by clearing the control stack, thus ensuring
- that all open LOOPs are closed. */
-static void
-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
- idempotent. */
- ctl_stack_clear ();
-}
-
/* Sets up LOOP for the first pass. */
-static int
+static enum trns_result
loop_trns_proc (void *loop_, struct ccase **c, casenumber case_num)
{
struct loop_trns *loop = loop_;
- if (loop->index_var != NULL)
+ double cur, by, last;
+ if (loop->index_var)
{
/* Evaluate loop index expressions. */
- 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);
+ 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);
/* 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) = loop->cur;
+ *case_num_rw (*c, loop->index_var) = cur;
/* Throw out pathological cases. */
- 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))
- goto zero_pass;
+ if (!isfinite (cur) || !isfinite (by) || !isfinite (last)
+ || by == 0.0
+ || (by > 0.0 && cur > last)
+ || (by < 0.0 && cur < last))
+ return TRNS_CONTINUE;
}
+ else
+ cur = by = last = 0.0;
+
+ int max_pass = loop->index_var ? settings_get_mxloops () : -1;
+ for (int i = 0; max_pass < 0 || i < max_pass; i++)
+ {
+ if (loop->loop_condition
+ && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
+ break;
- /* Initialize pass count. */
- loop->pass = 0;
- if (loop->max_pass_count >= 0 && loop->pass >= loop->max_pass_count)
- goto zero_pass;
+ enum trns_result r = transformations_execute (
+ loop->xforms, loop->n_xforms, c, case_num);
+ if (r != TRNS_CONTINUE)
+ return r == TRNS_BREAK ? TRNS_CONTINUE : r;
- /* Check condition. */
- if (loop->loop_condition != NULL
- && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
- goto zero_pass;
+ if (loop->end_loop_condition != NULL
+ && expr_evaluate_num (loop->end_loop_condition, *c, case_num) != 0.0)
+ break;
- return loop->past_LOOP_index;
+ if (loop->index_var)
+ {
+ cur += by;
+ if (by > 0.0 ? cur > last : cur < last)
+ break;
- zero_pass:
- return loop->past_END_LOOP_index;
+ *c = case_unshare (*c);
+ *case_num_rw (*c, loop->index_var) = cur;
+ }
+ }
+ return TRNS_CONTINUE;
}
/* Frees LOOP. */
{
struct loop_trns *loop = loop_;
- pool_destroy (loop->pool);
- return true;
-}
-
-/* Finishes a pass through the loop and starts the next. */
-static int
-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) != 0.0)
- goto break_out;
-
- /* MXLOOPS limiter. */
- if (loop->max_pass_count >= 0 && ++loop->pass >= loop->max_pass_count)
- goto break_out;
-
- /* Indexing clause limiter: counting downward. */
- 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;
- *c = case_unshare (*c);
- *case_num_rw (*c, loop->index_var) = loop->cur;
- }
-
- if (loop->loop_condition != NULL
- && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
- goto break_out;
-
- return loop->past_LOOP_index;
+ expr_free (loop->first);
+ expr_free (loop->by);
+ expr_free (loop->last);
- break_out:
- return loop->past_END_LOOP_index;
-}
+ expr_free (loop->loop_condition);
+ expr_free (loop->end_loop_condition);
-/* Executes BREAK. */
-static int
-break_trns_proc (void *loop_, struct ccase **c UNUSED,
- casenumber case_num UNUSED)
-{
- struct loop_trns *loop = loop_;
+ transformations_destroy (loop->xforms, loop->n_xforms);
- return loop->past_END_LOOP_index;
+ free (loop);
+ return true;
}
-
-/* LOOP control structure class definition. */
-static const struct ctl_class loop_class =
- {
- "LOOP",
- "END LOOP",
- close_loop,
- };
#include "data/value-labels.h"
#include "data/variable.h"
#include "language/command.h"
-#include "language/control/control-stack.h"
#include "language/lexer/lexer.h"
#include "libpspp/message.h"
#include "libpspp/str.h"
}
/* Handle DATA LIST transformation TRNS, parsing data into *C. */
-static int
+static enum trns_result
data_list_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
{
struct data_list_trns *trns = trns_;
- int retval;
+ enum trns_result retval;
*c = case_unshare (*c);
if (data_parser_parse (trns->parser, trns->reader, *c))
}
inp->trns_chain = proc_capture_transformations (inp->ds);
- trns_chain_finalize (inp->trns_chain);
inp->restart = TRNS_CONTINUE;
}
/* Outputs the current case */
-int
+enum trns_result
end_case_trns_proc (void *inp_, struct ccase **c UNUSED,
casenumber case_nr UNUSED)
{
goto error;
}
- e = expr_parse (lexer, NULL, ds, VAL_NUMERIC);
+ e = expr_parse (lexer, ds, VAL_NUMERIC);
if (!e)
goto error;
}
}
/* Executes a REREAD transformation. */
-static int
+static enum trns_result
reread_trns_proc (void *t_, struct ccase **c, casenumber case_num)
{
struct reread_trns *t = t_;
}
/* Executes an END FILE transformation. */
-static int
+static enum trns_result
end_file_trns_proc (void *trns_ UNUSED, struct ccase **c UNUSED,
casenumber case_num UNUSED)
{
if (lex_token (lexer) != T_ENDCMD)
{
- expr = expr_parse (lexer, NULL, ds, VAL_NUMERIC);
+ expr = expr_parse (lexer, ds, VAL_NUMERIC);
if (lex_token (lexer) != T_ENDCMD)
{
lex_error (lexer, _("expecting end of command"));
}
/* Executes a PRINT SPACE transformation. */
-static int
+static enum trns_result
print_space_trns_proc (void *t_, struct ccase **c,
casenumber case_num UNUSED)
{
bool *eject, int *record);
/* Performs the transformation inside print_trns T on case C. */
-static int
+static enum trns_result
print_text_trns_proc (void *trns_, struct ccase **c,
casenumber case_num UNUSED)
{
bool *eject, int *record);
/* Performs the transformation inside print_trns T on case C. */
-static int
+static enum trns_result
print_binary_trns_proc (void *trns_, struct ccase **c,
casenumber case_num UNUSED)
{
}
/* Writes case *C to the system file specified on XSAVE or XEXPORT. */
-static int
+static enum trns_result
output_trns_proc (void *trns_, struct ccase **c, casenumber case_num UNUSED)
{
struct output_trns *t = trns_;
/* Parses an expression of the given TYPE. If DS is nonnull then variables and
vectors within it may be referenced within the expression; otherwise, the
expression must not reference any variables or vectors. Returns the new
- expression if successful or a null pointer otherwise. If POOL is nonnull,
- then destroying POOL will free the expression; otherwise, the caller must
- eventually free it with expr_free(). */
+ expression if successful or a null pointer otherwise. */
struct expression *
-expr_parse (struct lexer *lexer, struct pool *pool, struct dataset *ds,
- enum val_type type)
+expr_parse (struct lexer *lexer, struct dataset *ds, enum val_type type)
{
assert (val_type_is_valid (type));
return NULL;
}
- e = finish_expression (expr_optimize (n, e), e);
- if (pool)
- pool_add_subpool (pool, e->expr_pool);
- return e;
+ return finish_expression (expr_optimize (n, e), e);
}
/* Parses a boolean expression, otherwise similar to expr_parse(). */
struct expression *
-expr_parse_bool (struct lexer *lexer, struct pool *pool, struct dataset *ds)
+expr_parse_bool (struct lexer *lexer, struct dataset *ds)
{
struct expression *e = expr_create (ds);
union any_node *n = parse_or (lexer, e);
return NULL;
}
- e = finish_expression (expr_optimize (n, e), e);
- if (pool)
- pool_add_subpool (pool, e->expr_pool);
- return e;
+ return finish_expression (expr_optimize (n, e), e);
}
/* Parses a numeric expression that is intended to be assigned to newly created
variable NEW_VAR_NAME. (This allows for a better error message if the
expression is not numeric.) Otherwise similar to expr_parse(). */
struct expression *
-expr_parse_new_variable (struct lexer *lexer, struct pool *pool, struct dataset *ds,
+expr_parse_new_variable (struct lexer *lexer, struct dataset *ds,
const char *new_var_name)
{
struct expression *e = expr_create (ds);
return NULL;
}
- e = finish_expression (expr_optimize (n, e), e);
- if (pool)
- pool_add_subpool (pool, e->expr_pool);
- return e;
+ return finish_expression (expr_optimize (n, e), e);
}
/* Free expression E. */
struct pool;
union value;
-struct expression *expr_parse (struct lexer *lexer, struct pool *,
- struct dataset *, enum val_type);
-struct expression *expr_parse_bool (struct lexer *lexer, struct pool *,
- struct dataset *);
-struct expression *expr_parse_new_variable (struct lexer *lexer, struct pool *,
- struct dataset *,
+struct expression *expr_parse (struct lexer *, struct dataset *, enum val_type);
+struct expression *expr_parse_bool (struct lexer *, struct dataset *);
+struct expression *expr_parse_new_variable (struct lexer *, struct dataset *,
const char *new_var_name);
void expr_free (struct expression *);
+struct expression *expr_create_false (void);
+
struct dataset;
double expr_evaluate_num (struct expression *, const struct ccase *,
int case_idx);
return direction == ASCENDING ? cmp : -cmp;
}
-static int
+static enum trns_result
autorecode_trns_proc (void *arc_, struct ccase **c,
casenumber case_idx UNUSED)
{
analyis. 4) any of the variables in the original analysis were missing
(either system or user-missing values that weren't included).
*/
-static int
+static enum trns_result
descriptives_trns_proc (void *trns_, struct ccase **c,
casenumber case_idx UNUSED)
{
/* A transformation function which juxtaposes the dataset with the
(pre-prepared) dataset containing membership and/or distance
values. */
-static int
+static enum trns_result
save_trans_func (void *aux, struct ccase **c, casenumber x UNUSED)
{
const struct save_trans_data *std = aux;
iv->current = casereader_read (iv->input);
}
-static int
+static enum trns_result
rank_trns_proc (void *trns_, struct ccase **c, casenumber case_idx UNUSED)
{
struct rank_trns *trns = trns_;
return true;
}
-static int
+static enum trns_result
save_trans_func (void *aux, struct ccase **c, casenumber x UNUSED)
{
struct save_trans_data *save_trans_data = aux;
return TRNS_CONTINUE;
}
-
int
cmd_regression (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_;
/* 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_;
}
/* 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_;
/* 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_;
compute = compute_trns_create ();
/* Test expression. */
- compute->test = expr_parse_bool (lexer, NULL, ds);
+ compute->test = expr_parse_bool (lexer, ds);
if (compute->test == NULL)
goto fail;
const struct lvalue *lvalue, struct dataset *ds)
{
if (lvalue->is_new_variable)
- return expr_parse_new_variable (lexer, NULL, ds, var_get_name (lvalue->variable));
+ return expr_parse_new_variable (lexer, ds, var_get_name (lvalue->variable));
else
- return expr_parse (lexer, NULL, ds, lvalue_get_type (lvalue));
+ return expr_parse (lexer, ds, lvalue_get_type (lvalue));
}
/* Returns a new struct compute_trns after initializing its fields. */
lex_get (lexer);
if (!lex_force_match (lexer, T_LPAREN))
goto lossage;
- lvalue->element = expr_parse (lexer, NULL, ds, VAL_NUMERIC);
+ lvalue->element = expr_parse (lexer, ds, VAL_NUMERIC);
if (lvalue->element == NULL)
goto lossage;
if (!lex_force_match (lexer, T_RPAREN))
}
/* Performs the COUNT transformation T on case C. */
-static int
+static enum trns_result
count_trns_proc (void *trns_, struct ccase **c,
casenumber case_num UNUSED)
{
#include "language/command.h"
#include "language/lexer/lexer.h"
#include "libpspp/message.h"
-
-static int trns_fail (void *x, struct ccase **c, casenumber n);
\f
/* A transformation which is guaranteed to fail. */
-static int
+static enum trns_result
trns_fail (void *x UNUSED, struct ccase **c UNUSED,
casenumber n UNUSED)
{
}
/* Performs RECODE transformation. */
-static int
+static enum trns_result
recode_trns_proc (void *trns_, struct ccase **c, casenumber case_idx UNUSED)
{
struct recode_trns *trns = trns_;
}
/* Executes a SAMPLE transformation. */
-static int
+static enum trns_result
sample_trns_proc (void *t_, struct ccase **c UNUSED,
casenumber case_num UNUSED)
{
struct expression *e;
struct select_if_trns *t;
- e = expr_parse_bool (lexer, NULL, ds);
+ e = expr_parse_bool (lexer, ds);
if (!e)
return CMD_CASCADING_FAILURE;
}
/* Performs the SELECT IF transformation T on case C. */
-static int
+static enum trns_result
select_if_proc (void *t_, struct ccase **c,
casenumber case_num)
{