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/expressions/public.h"
29 #include "language/lexer/lexer.h"
30 #include "libpspp/compiler.h"
31 #include "libpspp/message.h"
32 #include "libpspp/misc.h"
33 #include "libpspp/pool.h"
34 #include "libpspp/str.h"
36 #include "gl/xalloc.h"
39 #define _(msgid) gettext (msgid)
43 /* a=a TO b [BY c]. */
44 struct variable *index_var; /* Index variable. */
45 struct expression *first; /* Starting index. */
46 struct expression *by; /* Index increment (or NULL). */
47 struct expression *last; /* Terminal index. */
49 /* IF condition for LOOP or END LOOP. */
50 struct expression *loop_condition;
51 struct expression *end_loop_condition;
53 /* Inner transformations. */
54 struct trns_chain xforms;
57 static struct trns_class loop_trns_class;
61 static bool parse_if_clause (struct lexer *, struct dataset *,
62 struct expression **);
63 static bool parse_index_clause (struct dataset *, struct lexer *,
70 cmd_loop (struct lexer *lexer, struct dataset *ds)
72 struct loop_trns *loop = xmalloc (sizeof *loop);
73 *loop = (struct loop_trns) { .index_var = NULL };
76 while (lex_token (lexer) != T_ENDCMD && ok)
78 if (lex_match_id (lexer, "IF"))
79 ok = parse_if_clause (lexer, ds, &loop->loop_condition);
81 ok = parse_index_clause (ds, lexer, loop);
83 lex_end_of_command (lexer);
85 proc_push_transformations (ds);
89 if (lex_token (lexer) == T_STOP)
91 lex_error (lexer, NULL);
95 else if (lex_match_phrase (lexer, "END LOOP"))
97 if (lex_match_id (lexer, "IF"))
98 ok = parse_if_clause (lexer, ds, &loop->end_loop_condition) && ok;
102 cmd_parse_in_state (lexer, ds, CMD_STATE_NESTED);
105 proc_pop_transformations (ds, &loop->xforms);
106 printf ("%zu loop transvformations\n", loop->xforms.n);
108 add_transformation (ds, &loop_trns_class, loop);
110 return ok ? CMD_SUCCESS : CMD_CASCADING_FAILURE;
113 static enum trns_result
114 break_trns_proc (void *aux UNUSED, struct ccase **c UNUSED,
115 casenumber case_num UNUSED)
122 cmd_break (struct lexer *lexer UNUSED, struct dataset *ds)
126 msg (SE, _("BREAK cannot appear outside LOOP...END LOOP."));
130 static const struct trns_class trns_class = {
132 .execute = break_trns_proc
134 add_transformation (ds, &trns_class, NULL);
139 /* Parses an IF clause for LOOP or END LOOP and stores the
140 resulting expression to *CONDITION.
141 Returns true if successful, false on failure. */
143 parse_if_clause (struct lexer *lexer, struct dataset *ds,
144 struct expression **condition)
146 if (*condition != NULL)
148 lex_sbc_only_once ("IF");
152 *condition = expr_parse_bool (lexer, ds);
153 return *condition != NULL;
156 /* Parses an indexing clause into LOOP. Returns true if successful, false on
159 parse_index_clause (struct dataset *ds, struct lexer *lexer,
160 struct loop_trns *loop)
162 if (loop->index_var != NULL)
164 msg (SE, _("Only one index clause may be specified."));
168 if (lex_token (lexer) != T_ID)
170 lex_error (lexer, NULL);
174 loop->index_var = dict_lookup_var (dataset_dict (ds), lex_tokcstr (lexer));
175 if (!loop->index_var)
176 loop->index_var = dict_create_var_assert (dataset_dict (ds),
177 lex_tokcstr (lexer), 0);
180 if (!lex_force_match (lexer, T_EQUALS))
183 loop->first = expr_parse (lexer, ds, VAL_NUMERIC);
184 if (loop->first == NULL)
189 struct expression **e;
190 if (lex_match (lexer, T_TO))
192 else if (lex_match (lexer, T_BY))
199 lex_sbc_only_once (e == &loop->last ? "TO" : "BY");
202 *e = expr_parse (lexer, ds, VAL_NUMERIC);
206 if (loop->last == NULL)
208 lex_sbc_missing ("TO");
215 /* Sets up LOOP for the first pass. */
216 static enum trns_result
217 loop_trns_proc (void *loop_, struct ccase **c, casenumber case_num)
219 struct loop_trns *loop = loop_;
221 double cur, by, last;
224 /* Evaluate loop index expressions. */
225 cur = expr_evaluate_num (loop->first, *c, case_num);
226 by = loop->by ? expr_evaluate_num (loop->by, *c, case_num) : 1.0;
227 last = expr_evaluate_num (loop->last, *c, case_num);
229 /* Even if the loop is never entered, set the index
230 variable to the initial value. */
231 *c = case_unshare (*c);
232 *case_num_rw (*c, loop->index_var) = cur;
234 /* Throw out pathological cases. */
235 if (!isfinite (cur) || !isfinite (by) || !isfinite (last)
237 || (by > 0.0 && cur > last)
238 || (by < 0.0 && cur < last))
239 return TRNS_CONTINUE;
242 cur = by = last = 0.0;
244 for (int i = 0; loop->index_var || i < settings_get_mxloops (); i++)
246 printf ("loop %g %g %g\n", cur, by, last);
247 if (loop->loop_condition
248 && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
251 enum trns_result r = trns_chain_execute (&loop->xforms, case_num, c);
252 if (r != TRNS_CONTINUE)
253 return r == TRNS_BREAK ? TRNS_CONTINUE : r;
255 if (loop->end_loop_condition != NULL
256 && expr_evaluate_num (loop->end_loop_condition, *c, case_num) != 0.0)
262 if (by > 0.0 ? cur > last : cur < last)
265 *c = case_unshare (*c);
266 *case_num_rw (*c, loop->index_var) = cur;
269 return TRNS_CONTINUE;
274 loop_trns_free (void *loop_)
276 struct loop_trns *loop = loop_;
278 expr_free (loop->first);
279 expr_free (loop->by);
280 expr_free (loop->last);
282 expr_free (loop->loop_condition);
283 expr_free (loop->end_loop_condition);
285 trns_chain_uninit (&loop->xforms);
291 static struct trns_class loop_trns_class = {
293 .execute = loop_trns_proc,
294 .destroy = loop_trns_free,