X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Flanguage%2Fcontrol%2Floop.c;h=dfc4db2e8977505743466189ef5ebfde18b1bd49;hb=1a73ef6db75f41bf209b9aff8d42c0cb579338ad;hp=242c936a159dc5b651b719b08cd85620ff2d5607;hpb=a37754c18dee3d5941e5fe041b2c1fa1a3370157;p=pspp diff --git a/src/language/control/loop.c b/src/language/control/loop.c index 242c936a15..dfc4db2e89 100644 --- a/src/language/control/loop.c +++ b/src/language/control/loop.c @@ -1,316 +1,320 @@ -/* PSPP - computes sample statistics. - Copyright (C) 1997-9, 2000 Free Software Foundation, Inc. - Written by Ben Pfaff . +/* PSPP - a program for statistical analysis. + Copyright (C) 1997-9, 2000, 2009-2011 Free Software Foundation, Inc. - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License as - published by the Free Software Foundation; either version 2 of the - License, or (at your option) any later version. + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. - This program is distributed in the hope that it will be useful, but - WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - General Public License for more details. + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA - 02110-1301, USA. */ + along with this program. If not, see . */ #include -#include "control-stack.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#include + +#include "data/case.h" +#include "data/dataset.h" +#include "data/dictionary.h" +#include "data/settings.h" +#include "data/transformations.h" +#include "data/variable.h" +#include "language/command.h" +#include "language/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" +#include "libpspp/pool.h" +#include "libpspp/str.h" + +#include "gl/xalloc.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; - - /* Iteration limit. */ - int max_pass_count; /* Maximum number of passes (-1=unlimited). */ - int pass; /* Number of passes thru the loop so far. */ - /* a=a TO b [BY c]. */ - struct variable *index_var; /* Index variable. */ + 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 *by_expr; /* Index increment (or NULL). */ + struct expression *last_expr; /* 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 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 ctl_class loop_class; +static struct trns_class loop_trns_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 int in_loop; -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 void close_loop (void *); +static bool parse_if_clause (struct lexer *, struct dataset *, + struct expression **); +static bool parse_index_clause (struct dataset *, struct lexer *, + struct loop_trns *); /* 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 ok = true; + struct loop_trns *loop = xmalloc (sizeof *loop); + *loop = (struct loop_trns) { .resume_idx = SIZE_MAX }; - loop = create_loop_trns (); - while (token != '.' && ok) + bool ok = true; + while (lex_token (lexer) != T_ENDCMD && ok) { - if (lex_match_id ("IF")) - ok = parse_if_clause (loop, &loop->loop_condition); + if (lex_match_id (lexer, "IF")) + ok = parse_if_clause (lexer, ds, &loop->loop_condition); else - ok = parse_index_clause (loop, index_var_name); + ok = parse_index_clause (ds, lexer, loop); } + if (ok) + lex_end_of_command (lexer); + lex_discard_rest_of_command (lexer); - /* Find index variable and create if necessary. */ - if (ok && index_var_name[0] != '\0') + proc_push_transformations (ds); + in_loop++; + for (;;) { - 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); + if (lex_token (lexer) == T_STOP) + { + 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, + (in_input_program () + ? CMD_STATE_NESTED_INPUT_PROGRAM + : CMD_STATE_NESTED_DATA)); } - - if (!ok) - loop->max_pass_count = 0; + in_loop--; + proc_pop_transformations (ds, &loop->xforms); + + add_transformation (ds, &loop_trns_class, loop); + return ok ? CMD_SUCCESS : CMD_FAILURE; } -/* Parses END LOOP. */ int -cmd_end_loop (void) +cmd_inside_loop (struct lexer *lexer UNUSED, struct dataset *ds UNUSED) { - struct loop_trns *loop; - bool ok = true; - - loop = ctl_stack_top (&loop_class); - if (loop == NULL) - return CMD_CASCADING_FAILURE; - - /* Parse syntax. */ - if (lex_match_id ("IF")) - ok = parse_if_clause (loop, &loop->end_loop_condition); - if (ok) - ok = lex_end_of_command () == CMD_SUCCESS; - - if (!ok) - loop->max_pass_count = 0; + msg (SE, _("This command cannot appear outside LOOP...END LOOP.")); + return CMD_FAILURE; +} - ctl_stack_pop (loop); - - return ok ? CMD_SUCCESS : CMD_FAILURE; +static enum trns_result +break_trns_proc (void *aux UNUSED, struct ccase **c UNUSED, + casenumber case_num UNUSED) +{ + return TRNS_BREAK; } /* 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); + if (!in_loop) + { + cmd_inside_loop (lexer, ds); + return CMD_FAILURE; + } - return lex_end_of_command (); -} + static const struct trns_class trns_class = { + .name = "BREAK", + .execute = break_trns_proc + }; + add_transformation (ds, &trns_class, NULL); -/* 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 (end_loop_trns_proc, NULL, loop); - loop->past_END_LOOP_index = next_transformation (); - - /* 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->loop_condition == NULL - && loop->end_loop_condition == NULL) - loop->max_pass_count = get_mxloops (); + return CMD_SUCCESS; } /* 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 dataset *ds, + 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_bool (lexer, ds); return *condition != NULL; } -/* Parses an indexing clause into LOOP. - Stores the index variable's name in INDEX_VAR_NAME[]. - 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 loop_trns *loop, char index_var_name[]) +parse_index_clause (struct dataset *ds, struct lexer *lexer, + struct loop_trns *loop) { - if (token != T_ID) + if (loop->index_var != NULL) + { + msg (SE, _("Only one index clause may be specified.")); + return false; + } + + if (lex_token (lexer) != T_ID) { - lex_error (NULL); + lex_error (lexer, NULL); return false; } - strcpy (index_var_name, tokid); - lex_get (); - if (!lex_force_match ('=')) + loop->index_var = dict_lookup_var (dataset_dict (ds), lex_tokcstr (lexer)); + if (!loop->index_var) + 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_pool (loop->pool, default_dict, EXPR_NUMBER); + loop->first_expr = expr_parse (lexer, ds, VAL_NUMERIC); 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 (lexer, ds, VAL_NUMERIC); if (*e == NULL) return false; } - if (loop->last_expr == NULL) + if (loop->last_expr == 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 (void) -{ - 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; - - add_transformation_with_finalizer (loop_trns_finalize, - loop_trns_proc, loop_trns_free, loop); - loop->past_LOOP_index = next_transformation (); - - 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, casenum_t case_num) +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) + 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. */ - loop->cur = expr_evaluate_num (loop->first_expr, c, case_num); - if (loop->by_expr != NULL) - loop->by = expr_evaluate_num (loop->by_expr, c, case_num); - loop->last = expr_evaluate_num (loop->last_expr, c, case_num); + loop->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. */ - case_data_rw (c, loop->index_var->fv)->f = loop->cur; + *c = case_unshare (*c); + *case_num_rw (*c, loop->index_var) = 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)) - goto zero_pass; + return TRNS_CONTINUE; } - /* Initialize pass count. */ - loop->pass = 0; - if (loop->max_pass_count >= 0 && loop->pass >= loop->max_pass_count) - goto zero_pass; + for (loop->iteration = 0; + loop->index_var || loop->iteration < settings_get_mxloops (); + loop->iteration++) + { + if (loop->loop_condition + && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0) + break; + + 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 (); + } + } - /* 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) + { + loop->cur += loop->by; + if (loop->by > 0.0 ? loop->cur > loop->last : loop->cur < loop->last) + break; - zero_pass: - return loop->past_END_LOOP_index; + *c = case_unshare (*c); + *case_num_rw (*c, loop->index_var) = loop->cur; + } + } + return TRNS_CONTINUE; } /* Frees LOOP. */ @@ -319,61 +323,21 @@ loop_trns_free (void *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, casenum_t 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) - goto break_out; - - /* MXLOOPS limiter. */ - if (loop->max_pass_count >= 0) - { - if (loop->pass >= loop->max_pass_count) - goto break_out; - loop->pass++; - } - - /* 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; - case_data_rw (c, loop->index_var->fv)->f = loop->cur; - } - - if (loop->loop_condition != NULL - && expr_evaluate_num (loop->loop_condition, c, case_num) != 1.0) - goto break_out; + expr_free (loop->first_expr); + expr_free (loop->by_expr); + expr_free (loop->last_expr); - return loop->past_LOOP_index; - - 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, casenum_t case_num UNUSED) -{ - struct loop_trns *loop = loop_; + trns_chain_uninit (&loop->xforms); - return loop->past_END_LOOP_index; + free (loop); + return true; } -/* LOOP control structure class definition. */ -static struct ctl_class loop_class = - { - "LOOP", - "END LOOP", - close_loop, - }; +static struct trns_class loop_trns_class = { + .name = "LOOP", + .execute = loop_trns_proc, + .destroy = loop_trns_free, +};