-/* PSPP - computes sample statistics.
+/* PSPP - a program for statistical analysis.
Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
- Written by Ben Pfaff <blp@gnu.org>.
- 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 "message.h"
-#include "alloc.h"
-#include "case.h"
-#include "command.h"
-#include "compiler.h"
-#include "dictionary.h"
+
#include "control-stack.h"
-#include "message.h"
-#include "expressions/public.h"
-#include "lexer.h"
-#include "misc.h"
-#include "pool.h"
-#include "settings.h"
-#include "str.h"
-#include "variable.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/compiler.h>
+#include <libpspp/message.h>
+#include <libpspp/misc.h>
+#include <libpspp/pool.h>
+#include <libpspp/str.h>
+
+#include "xalloc.h"
#include "gettext.h"
#define _(msgid) gettext (msgid)
struct loop_trns
{
struct pool *pool;
+ struct dataset *ds;
/* Iteration limit. */
int max_pass_count; /* Maximum number of passes (-1=unlimited). */
int past_END_LOOP_index; /* Just past END LOOP transformation. */
};
-static struct ctl_class loop_class;
+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_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) != '.' && 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 (default_dict, index_var_name);
- if (loop->index_var == NULL)
- loop->index_var = dict_create_var (default_dict, 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_PART_SUCCESS;
+
+ 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;
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_PART_SUCCESS;
+
+ return ok ? CMD_SUCCESS : CMD_FAILURE;
}
/* Parses BREAK. */
int
-cmd_break (void)
+cmd_break (struct lexer *lexer, struct dataset *ds)
{
struct ctl_stmt *loop = ctl_stack_search (&loop_class);
if (loop == NULL)
return CMD_CASCADING_FAILURE;
- add_transformation (break_trns_proc, NULL, loop);
+ add_transformation (ds, break_trns_proc, NULL, loop);
- return lex_end_of_command ();
+ return lex_end_of_command (lexer);
}
/* Closes a LOOP construct by emitting the END LOOP
close_loop (void *loop_)
{
struct loop_trns *loop = loop_;
-
- add_transformation (end_loop_trns_proc, NULL, loop);
- loop->past_END_LOOP_index = next_transformation ();
+
+ 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. */
&& 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, default_dict, 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_tokid (lexer));
+ if (loop->index_var != NULL)
+ *created_index_var = false;
+ else
+ {
+ loop->index_var = dict_create_var_assert (dataset_dict (ds),
+ lex_tokid (lexer), 0);
+ *created_index_var = true;
+ }
+ lex_get (lexer);
+
+ if (!lex_force_match (lexer, '='))
return false;
- loop->first_expr = expr_parse_pool (loop->pool, default_dict, 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, default_dict, 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)
/* 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;
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 (loop_trns_proc, loop_trns_free, loop);
- loop->past_LOOP_index = next_transformation ();
+ 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
-loop_trns_proc (void *loop_, struct ccase *c, int case_num)
+loop_trns_proc (void *loop_, struct ccase *c, casenumber case_num)
{
struct loop_trns *loop = loop_;
/* 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;
+ 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))
/* Finishes a pass through the loop and starts the next. */
static int
-end_loop_trns_proc (void *loop_, struct ccase *c, int 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. */
}
/* 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;
+ case_data_rw (c, loop->index_var)->f = loop->cur;
}
if (loop->loop_condition != NULL
/* Executes BREAK. */
static int
-break_trns_proc (void *loop_, struct ccase *c UNUSED, int case_num UNUSED)
+break_trns_proc (void *loop_, struct ccase *c UNUSED, casenumber case_num UNUSED)
{
struct loop_trns *loop = loop_;
}
/* LOOP control structure class definition. */
-static struct ctl_class loop_class =
+static const struct ctl_class loop_class =
{
"LOOP",
"END LOOP",