4dbc7c90bd7ff0c3f62bf444177d50efed3974f8
[pspp] / src / language / control / loop.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2009-2011 Free Software Foundation, Inc.
3
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.
8
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.
13
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/>. */
16
17 #include <config.h>
18
19 #include <limits.h>
20
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"
37
38 #include "gl/xalloc.h"
39
40 #include "gettext.h"
41 #define _(msgid) gettext (msgid)
42
43 struct loop_trns
44   {
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. */
50
51     /* IF condition for LOOP or END LOOP. */
52     struct expression *loop_condition;
53     struct expression *end_loop_condition;
54
55     /* Inner transformations. */
56     struct trns_chain xforms;
57
58     /* State. */
59     double cur, by, last;       /* Index data. */
60     int iteration;              /* For MXLOOPS. */
61     size_t resume_idx;          /* For resuming after END CASE. */
62   };
63
64 static struct trns_class loop_trns_class;
65
66 static int in_loop;
67
68 static bool parse_if_clause (struct lexer *, struct dataset *,
69                              struct expression **);
70 static bool parse_index_clause (struct dataset *, struct lexer *,
71                                 struct loop_trns *);
72 \f
73 /* LOOP. */
74
75 /* Parses LOOP. */
76 int
77 cmd_loop (struct lexer *lexer, struct dataset *ds)
78 {
79   struct loop_trns *loop = xmalloc (sizeof *loop);
80   *loop = (struct loop_trns) { .resume_idx = SIZE_MAX };
81
82   bool ok = true;
83   while (lex_token (lexer) != T_ENDCMD && ok)
84     {
85       if (lex_match_id (lexer, "IF"))
86         ok = parse_if_clause (lexer, ds, &loop->loop_condition);
87       else
88         ok = parse_index_clause (ds, lexer, loop);
89     }
90   if (ok)
91     lex_end_of_command (lexer);
92   lex_discard_rest_of_command (lexer);
93
94   proc_push_transformations (ds);
95   in_loop++;
96   for (;;)
97     {
98       if (lex_token (lexer) == T_STOP)
99         {
100           lex_error (lexer, NULL);
101           ok = false;
102           break;
103         }
104       else if (lex_match_phrase (lexer, "END LOOP"))
105         {
106           if (lex_match_id (lexer, "IF"))
107             ok = parse_if_clause (lexer, ds, &loop->end_loop_condition) && ok;
108           break;
109         }
110       else
111         cmd_parse_in_state (lexer, ds,
112                             (in_input_program ()
113                              ? CMD_STATE_NESTED_INPUT_PROGRAM
114                              : CMD_STATE_NESTED_DATA));
115     }
116   in_loop--;
117   proc_pop_transformations (ds, &loop->xforms);
118
119   add_transformation (ds, &loop_trns_class, loop);
120
121   return ok ? CMD_SUCCESS : CMD_FAILURE;
122 }
123
124 int
125 cmd_inside_loop (struct lexer *lexer, struct dataset *ds UNUSED)
126 {
127   lex_ofs_error (lexer, 0, lex_ofs (lexer) - 1,
128                  _("This command cannot appear outside LOOP...END LOOP."));
129   return CMD_FAILURE;
130 }
131
132 static enum trns_result
133 break_trns_proc (void *aux UNUSED, struct ccase **c UNUSED,
134                  casenumber case_num UNUSED)
135 {
136   return TRNS_BREAK;
137 }
138
139 /* Parses BREAK. */
140 int
141 cmd_break (struct lexer *lexer, struct dataset *ds)
142 {
143   if (!in_loop)
144     {
145       cmd_inside_loop (lexer, ds);
146       return CMD_FAILURE;
147     }
148
149   static const struct trns_class trns_class = {
150     .name = "BREAK",
151     .execute = break_trns_proc
152   };
153   add_transformation (ds, &trns_class, NULL);
154
155   return CMD_SUCCESS;
156 }
157
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. */
161 static bool
162 parse_if_clause (struct lexer *lexer, struct dataset *ds,
163                  struct expression **condition)
164 {
165   if (*condition != NULL)
166     {
167       lex_sbc_only_once (lexer, "IF");
168       return false;
169     }
170
171   *condition = expr_parse_bool (lexer, ds);
172   return *condition != NULL;
173 }
174
175 /* Parses an indexing clause into LOOP.  Returns true if successful, false on
176    failure. */
177 static bool
178 parse_index_clause (struct dataset *ds, struct lexer *lexer,
179                     struct loop_trns *loop)
180 {
181   if (loop->index_var != NULL)
182     {
183       msg (SE, _("Only one index clause may be specified."));
184       return false;
185     }
186
187   if (lex_token (lexer) != T_ID)
188     {
189       lex_error (lexer, NULL);
190       return false;
191     }
192
193   loop->index_var = dict_lookup_var (dataset_dict (ds), lex_tokcstr (lexer));
194   if (!loop->index_var)
195     loop->index_var = dict_create_var_assert (dataset_dict (ds),
196                                               lex_tokcstr (lexer), 0);
197   lex_get (lexer);
198
199   if (!lex_force_match (lexer, T_EQUALS))
200     return false;
201
202   loop->first_expr = expr_parse (lexer, ds, VAL_NUMERIC);
203   if (loop->first_expr == NULL)
204     return false;
205
206   for (;;)
207     {
208       struct expression **e;
209       if (lex_match (lexer, T_TO))
210         e = &loop->last_expr;
211       else if (lex_match (lexer, T_BY))
212         e = &loop->by_expr;
213       else
214         break;
215
216       if (*e != NULL)
217         {
218           lex_sbc_only_once (lexer, e == &loop->last_expr ? "TO" : "BY");
219           return false;
220         }
221       *e = expr_parse (lexer, ds, VAL_NUMERIC);
222       if (*e == NULL)
223         return false;
224     }
225   if (loop->last_expr == NULL)
226     {
227       lex_sbc_missing (lexer, "TO");
228       return false;
229     }
230
231   return true;
232 }
233
234 /* Sets up LOOP for the first pass. */
235 static enum trns_result
236 loop_trns_proc (void *loop_, struct ccase **c, casenumber case_num)
237 {
238   struct loop_trns *loop = loop_;
239
240   size_t start_idx = loop->resume_idx;
241   loop->resume_idx = SIZE_MAX;
242   if (start_idx != SIZE_MAX)
243     goto resume;
244
245   if (loop->index_var)
246     {
247       /* Evaluate loop index expressions. */
248       loop->cur = expr_evaluate_num (loop->first_expr, *c, case_num);
249       loop->by = (loop->by_expr
250                   ? expr_evaluate_num (loop->by_expr, *c, case_num)
251                   : 1.0);
252       loop->last = expr_evaluate_num (loop->last_expr, *c, case_num);
253
254       /* Even if the loop is never entered, set the index
255          variable to the initial value. */
256       *c = case_unshare (*c);
257       *case_num_rw (*c, loop->index_var) = loop->cur;
258
259       /* Throw out pathological cases. */
260       if (!isfinite (loop->cur)
261           || !isfinite (loop->by)
262           || !isfinite (loop->last)
263           || loop->by == 0.0
264           || (loop->by > 0.0 && loop->cur > loop->last)
265           || (loop->by < 0.0 && loop->cur < loop->last))
266         return TRNS_CONTINUE;
267     }
268
269   for (loop->iteration = 0;
270        loop->index_var || loop->iteration < settings_get_mxloops ();
271        loop->iteration++)
272     {
273       if (loop->loop_condition
274           && expr_evaluate_num (loop->loop_condition, *c, case_num) != 1.0)
275         break;
276
277       start_idx = 0;
278     resume:
279       for (size_t i = start_idx; i < loop->xforms.n; i++)
280         {
281           const struct transformation *trns = &loop->xforms.xforms[i];
282           enum trns_result r = trns->class->execute (trns->aux, c, case_num);
283           switch (r)
284             {
285             case TRNS_CONTINUE:
286               break;
287
288             case TRNS_BREAK:
289               return TRNS_CONTINUE;
290
291             case TRNS_END_CASE:
292               loop->resume_idx = i;
293               return TRNS_END_CASE;
294
295             case TRNS_ERROR:
296             case TRNS_END_FILE:
297               return r;
298
299             case TRNS_DROP_CASE:
300               NOT_REACHED ();
301             }
302         }
303
304       if (loop->end_loop_condition != NULL
305           && expr_evaluate_num (loop->end_loop_condition, *c, case_num) != 0.0)
306         break;
307
308       if (loop->index_var)
309         {
310           loop->cur += loop->by;
311           if (loop->by > 0.0 ? loop->cur > loop->last : loop->cur < loop->last)
312             break;
313
314           *c = case_unshare (*c);
315           *case_num_rw (*c, loop->index_var) = loop->cur;
316         }
317     }
318   return TRNS_CONTINUE;
319 }
320
321 /* Frees LOOP. */
322 static bool
323 loop_trns_free (void *loop_)
324 {
325   struct loop_trns *loop = loop_;
326
327   expr_free (loop->first_expr);
328   expr_free (loop->by_expr);
329   expr_free (loop->last_expr);
330
331   expr_free (loop->loop_condition);
332   expr_free (loop->end_loop_condition);
333
334   trns_chain_uninit (&loop->xforms);
335
336   free (loop);
337   return true;
338 }
339
340 static struct trns_class loop_trns_class = {
341   .name = "LOOP",
342   .execute = loop_trns_proc,
343   .destroy = loop_trns_free,
344 };