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/data-io/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 (lexer, NULL);
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 UNUSED, struct dataset *ds UNUSED)
127 msg (SE, _("This command cannot appear outside LOOP...END LOOP."));
131 static enum trns_result
132 break_trns_proc (void *aux UNUSED, struct ccase **c UNUSED,
133 casenumber case_num UNUSED)
140 cmd_break (struct lexer *lexer, struct dataset *ds)
144 cmd_inside_loop (lexer, ds);
148 static const struct trns_class trns_class = {
150 .execute = break_trns_proc
152 add_transformation (ds, &trns_class, NULL);
157 /* Parses an IF clause for LOOP or END LOOP and stores the
158 resulting expression to *CONDITION.
159 Returns true if successful, false on failure. */
161 parse_if_clause (struct lexer *lexer, struct dataset *ds,
162 struct expression **condition)
164 if (*condition != NULL)
166 lex_sbc_only_once (lexer, "IF");
170 *condition = expr_parse_bool (lexer, ds);
171 return *condition != NULL;
174 /* Parses an indexing clause into LOOP. Returns true if successful, false on
177 parse_index_clause (struct dataset *ds, struct lexer *lexer,
178 struct loop_trns *loop)
180 if (loop->index_var != NULL)
182 msg (SE, _("Only one index clause may be specified."));
186 if (lex_token (lexer) != T_ID)
188 lex_error (lexer, NULL);
192 loop->index_var = dict_lookup_var (dataset_dict (ds), lex_tokcstr (lexer));
193 if (!loop->index_var)
194 loop->index_var = dict_create_var_assert (dataset_dict (ds),
195 lex_tokcstr (lexer), 0);
198 if (!lex_force_match (lexer, T_EQUALS))
201 loop->first_expr = expr_parse (lexer, ds, VAL_NUMERIC);
202 if (loop->first_expr == NULL)
207 struct expression **e;
208 if (lex_match (lexer, T_TO))
209 e = &loop->last_expr;
210 else if (lex_match (lexer, T_BY))
217 lex_sbc_only_once (lexer, e == &loop->last_expr ? "TO" : "BY");
220 *e = expr_parse (lexer, ds, VAL_NUMERIC);
224 if (loop->last_expr == NULL)
226 lex_sbc_missing ("TO");
233 /* Sets up LOOP for the first pass. */
234 static enum trns_result
235 loop_trns_proc (void *loop_, struct ccase **c, casenumber case_num)
237 struct loop_trns *loop = loop_;
239 size_t start_idx = loop->resume_idx;
240 loop->resume_idx = SIZE_MAX;
241 if (start_idx != SIZE_MAX)
246 /* Evaluate loop index expressions. */
247 loop->cur = expr_evaluate_num (loop->first_expr, *c, case_num);
248 loop->by = (loop->by_expr
249 ? expr_evaluate_num (loop->by_expr, *c, case_num)
251 loop->last = expr_evaluate_num (loop->last_expr, *c, case_num);
253 /* Even if the loop is never entered, set the index
254 variable to the initial value. */
255 *c = case_unshare (*c);
256 *case_num_rw (*c, loop->index_var) = loop->cur;
258 /* Throw out pathological cases. */
259 if (!isfinite (loop->cur)
260 || !isfinite (loop->by)
261 || !isfinite (loop->last)
263 || (loop->by > 0.0 && loop->cur > loop->last)
264 || (loop->by < 0.0 && loop->cur < loop->last))
265 return TRNS_CONTINUE;
268 for (loop->iteration = 0;
269 loop->index_var || loop->iteration < settings_get_mxloops ();
272 if (loop->loop_condition
273 && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
278 for (size_t i = start_idx; i < loop->xforms.n; i++)
280 const struct transformation *trns = &loop->xforms.xforms[i];
281 enum trns_result r = trns->class->execute (trns->aux, c, case_num);
288 return TRNS_CONTINUE;
291 loop->resume_idx = i;
292 return TRNS_END_CASE;
303 if (loop->end_loop_condition != NULL
304 && expr_evaluate_num (loop->end_loop_condition, *c, case_num) != 0.0)
309 loop->cur += loop->by;
310 if (loop->by > 0.0 ? loop->cur > loop->last : loop->cur < loop->last)
313 *c = case_unshare (*c);
314 *case_num_rw (*c, loop->index_var) = loop->cur;
317 return TRNS_CONTINUE;
322 loop_trns_free (void *loop_)
324 struct loop_trns *loop = loop_;
326 expr_free (loop->first_expr);
327 expr_free (loop->by_expr);
328 expr_free (loop->last_expr);
330 expr_free (loop->loop_condition);
331 expr_free (loop->end_loop_condition);
333 trns_chain_uninit (&loop->xforms);
339 static struct trns_class loop_trns_class = {
341 .execute = loop_trns_proc,
342 .destroy = loop_trns_free,