1 /* PSPP - a program for statistical analysis.
2 Copyright (C) 1997-9, 2000, 2009-2011 Free Software Foundation, Inc.
4 This program is free software: you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation, either version 3 of the License, or
7 (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
21 #include "data/case.h"
22 #include "data/dataset.h"
23 #include "data/dictionary.h"
24 #include "data/settings.h"
25 #include "data/transformations.h"
26 #include "data/variable.h"
27 #include "language/command.h"
28 #include "language/commands/inpt-pgm.h"
29 #include "language/expressions/public.h"
30 #include "language/lexer/lexer.h"
31 #include "libpspp/assertion.h"
32 #include "libpspp/compiler.h"
33 #include "libpspp/message.h"
34 #include "libpspp/misc.h"
35 #include "libpspp/pool.h"
36 #include "libpspp/str.h"
38 #include "gl/xalloc.h"
41 #define _(msgid) gettext (msgid)
45 /* a=a TO b [BY c]. */
46 struct variable *index_var; /* Index variable. */
47 struct expression *first_expr; /* Starting index. */
48 struct expression *by_expr; /* Index increment (or NULL). */
49 struct expression *last_expr; /* Terminal index. */
51 /* IF condition for LOOP or END LOOP. */
52 struct expression *loop_condition;
53 struct expression *end_loop_condition;
55 /* Inner transformations. */
56 struct trns_chain xforms;
59 double cur, by, last; /* Index data. */
60 int iteration; /* For MXLOOPS. */
61 size_t resume_idx; /* For resuming after END CASE. */
64 static struct trns_class loop_trns_class;
68 static bool parse_if_clause (struct lexer *, struct dataset *,
69 struct expression **);
70 static bool parse_index_clause (struct dataset *, struct lexer *,
77 cmd_loop (struct lexer *lexer, struct dataset *ds)
79 struct loop_trns *loop = xmalloc (sizeof *loop);
80 *loop = (struct loop_trns) { .resume_idx = SIZE_MAX };
83 while (lex_token (lexer) != T_ENDCMD && ok)
85 if (lex_match_id (lexer, "IF"))
86 ok = parse_if_clause (lexer, ds, &loop->loop_condition);
88 ok = parse_index_clause (ds, lexer, loop);
91 lex_end_of_command (lexer);
92 lex_discard_rest_of_command (lexer);
94 proc_push_transformations (ds);
98 if (lex_token (lexer) == T_STOP)
100 lex_error_expecting (lexer, "END LOOP");
104 else if (lex_match_phrase (lexer, "END LOOP"))
106 if (lex_match_id (lexer, "IF"))
107 ok = parse_if_clause (lexer, ds, &loop->end_loop_condition) && ok;
111 cmd_parse_in_state (lexer, ds,
113 ? CMD_STATE_NESTED_INPUT_PROGRAM
114 : CMD_STATE_NESTED_DATA));
117 proc_pop_transformations (ds, &loop->xforms);
119 add_transformation (ds, &loop_trns_class, loop);
121 return ok ? CMD_SUCCESS : CMD_FAILURE;
125 cmd_inside_loop (struct lexer *lexer, struct dataset *ds UNUSED)
127 lex_ofs_error (lexer, 0, lex_ofs (lexer) - 1,
128 _("This command cannot appear outside LOOP...END LOOP."));
132 static enum trns_result
133 break_trns_proc (void *aux UNUSED, struct ccase **c UNUSED,
134 casenumber case_num UNUSED)
141 cmd_break (struct lexer *lexer, struct dataset *ds)
145 cmd_inside_loop (lexer, ds);
149 static const struct trns_class trns_class = {
151 .execute = break_trns_proc
153 add_transformation (ds, &trns_class, NULL);
158 /* Parses an IF clause for LOOP or END LOOP and stores the
159 resulting expression to *CONDITION.
160 Returns true if successful, false on failure. */
162 parse_if_clause (struct lexer *lexer, struct dataset *ds,
163 struct expression **condition)
165 if (*condition != NULL)
167 lex_sbc_only_once (lexer, "IF");
171 *condition = expr_parse_bool (lexer, ds);
172 return *condition != NULL;
175 /* Parses an indexing clause into LOOP. Returns true if successful, false on
178 parse_index_clause (struct dataset *ds, struct lexer *lexer,
179 struct loop_trns *loop)
181 if (loop->index_var != NULL)
183 lex_error (lexer, _("Only one index clause may be specified."));
187 if (!lex_force_id (lexer))
190 loop->index_var = dict_lookup_var (dataset_dict (ds), lex_tokcstr (lexer));
191 if (!loop->index_var)
192 loop->index_var = dict_create_var_assert (dataset_dict (ds),
193 lex_tokcstr (lexer), 0);
196 if (!lex_force_match (lexer, T_EQUALS))
199 loop->first_expr = expr_parse (lexer, ds, VAL_NUMERIC);
200 if (loop->first_expr == NULL)
205 struct expression **e;
206 if (lex_match (lexer, T_TO))
207 e = &loop->last_expr;
208 else if (lex_match (lexer, T_BY))
215 lex_sbc_only_once (lexer, e == &loop->last_expr ? "TO" : "BY");
218 *e = expr_parse (lexer, ds, VAL_NUMERIC);
222 if (loop->last_expr == NULL)
224 lex_sbc_missing (lexer, "TO");
231 /* Sets up LOOP for the first pass. */
232 static enum trns_result
233 loop_trns_proc (void *loop_, struct ccase **c, casenumber case_num)
235 struct loop_trns *loop = loop_;
237 size_t start_idx = loop->resume_idx;
238 loop->resume_idx = SIZE_MAX;
239 if (start_idx != SIZE_MAX)
244 /* Evaluate loop index expressions. */
245 loop->cur = expr_evaluate_num (loop->first_expr, *c, case_num);
246 loop->by = (loop->by_expr
247 ? expr_evaluate_num (loop->by_expr, *c, case_num)
249 loop->last = expr_evaluate_num (loop->last_expr, *c, case_num);
251 /* Even if the loop is never entered, set the index
252 variable to the initial value. */
253 *c = case_unshare (*c);
254 *case_num_rw (*c, loop->index_var) = loop->cur;
256 /* Throw out pathological cases. */
257 if (!isfinite (loop->cur)
258 || !isfinite (loop->by)
259 || !isfinite (loop->last)
261 || (loop->by > 0.0 && loop->cur > loop->last)
262 || (loop->by < 0.0 && loop->cur < loop->last))
263 return TRNS_CONTINUE;
266 for (loop->iteration = 0;
267 loop->index_var || loop->iteration < settings_get_mxloops ();
270 if (loop->loop_condition
271 && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
276 for (size_t i = start_idx; i < loop->xforms.n; i++)
278 const struct transformation *trns = &loop->xforms.xforms[i];
279 enum trns_result r = trns->class->execute (trns->aux, c, case_num);
286 return TRNS_CONTINUE;
289 loop->resume_idx = i;
290 return TRNS_END_CASE;
301 if (loop->end_loop_condition != NULL
302 && expr_evaluate_num (loop->end_loop_condition, *c, case_num) != 0.0)
307 loop->cur += loop->by;
308 if (loop->by > 0.0 ? loop->cur > loop->last : loop->cur < loop->last)
311 *c = case_unshare (*c);
312 *case_num_rw (*c, loop->index_var) = loop->cur;
315 return TRNS_CONTINUE;
320 loop_trns_free (void *loop_)
322 struct loop_trns *loop = loop_;
324 expr_free (loop->first_expr);
325 expr_free (loop->by_expr);
326 expr_free (loop->last_expr);
328 expr_free (loop->loop_condition);
329 expr_free (loop->end_loop_condition);
331 trns_chain_uninit (&loop->xforms);
337 static struct trns_class loop_trns_class = {
339 .execute = loop_trns_proc,
340 .destroy = loop_trns_free,