88022b3b4ccf257b163e058f7e19768699c9f229
[pspp-builds.git] / src / do-if.c
1 /* PSPP - computes sample statistics.
2    Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3    Written by Ben Pfaff <blp@gnu.org>.
4
5    This program is free software; you can redistribute it and/or
6    modify it under the terms of the GNU General Public License as
7    published by the Free Software Foundation; either version 2 of the
8    License, or (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful, but
11    WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    General Public License for more details.
14
15    You should have received a copy of the GNU General Public License
16    along with this program; if not, write to the Free Software
17    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
18    02111-1307, USA. */
19
20 #include <config.h>
21 #include <assert.h>
22 #include <stdlib.h>
23 #include "alloc.h"
24 #include "command.h"
25 #include "error.h"
26 #include "expr.h"
27 #include "lexer.h"
28 #include "str.h"
29 #include "var.h"
30
31 #include "debug-print.h"
32
33 #if DEBUGGING
34 #include <stdio.h>
35 #endif
36
37 /* *INDENT-OFF* */
38 /* Description of DO IF transformations:
39
40    DO IF has two transformations.  One is a conditional jump around
41    a false condition.  The second is an unconditional jump around
42    the rest of the code after a true condition.  Both of these types
43    have their destinations backpatched in by the next clause (ELSE IF,
44    END IF).
45
46    The characters `^V<>' are meant to represent arrows.
47
48    1. DO IF
49  V<<<<if false
50  V
51  V *. Transformations executed when the condition on DO IF is true.
52  V
53  V 2. GOTO>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>V
54  V                                                                     V
55  >>1. ELSE IF                                                          V
56  V<<<<if false                                                         V
57  V                                                                     V
58  V *. Transformations executed when condition on 1st ELSE IF is true.  V
59  V                                                                     V
60  V 2. GOTO>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>V
61  V                                                                     V
62  >>1. ELSE IF                                                          V
63  V<<<<if false                                                         V
64  V                                                                     V
65  V *. Transformations executed when condition on 2nd ELSE IF is true.  V
66  V                                                                     V
67  V 2. GOTO>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>V
68  V                                                                     V
69  >>*. Transformations executed when no condition is true. (ELSE)       V
70                                                                        V
71    *. Transformations after DO IF structure.<<<<<<<<<<<<<<<<<<<<<<<<<<<<
72
73 */
74 /* *INDENT-ON* */
75
76 #include "do-ifP.h"
77
78 static struct do_if_trns *parse_do_if (void);
79 static void add_ELSE_IF (struct do_if_trns *);
80 static int goto_trns_proc (struct trns_header *, struct ccase *);
81 static int do_if_trns_proc (struct trns_header *, struct ccase *);
82 static void do_if_trns_free (struct trns_header *);
83
84 /* Parse DO IF. */
85 int
86 cmd_do_if (void)
87 {
88   struct do_if_trns *t;
89
90   /* Parse the transformation. */
91   t = parse_do_if ();
92   if (!t)
93     return CMD_FAILURE;
94
95   /* Finish up the transformation, add to control stack, add to
96      transformation list. */
97   t->brk = NULL;
98   t->ctl.type = CST_DO_IF;
99   t->ctl.down = ctl_stack;
100   t->ctl.trns = (struct trns_header *) t;
101   t->ctl.brk = NULL;
102   t->has_else = 0;
103   ctl_stack = &t->ctl;
104   add_transformation ((struct trns_header *) t);
105
106   return CMD_SUCCESS;
107 }
108
109 /* Parse ELSE IF. */
110 int
111 cmd_else_if (void)
112 {
113   /* Transformation created. */
114   struct do_if_trns *t;
115
116   /* Check that we're in a pleasing situation. */
117   if (!ctl_stack || ctl_stack->type != CST_DO_IF)
118     {
119       msg (SE, _("There is no DO IF to match with this ELSE IF."));
120       return CMD_FAILURE;
121     }
122   if (((struct do_if_trns *) ctl_stack->trns)->has_else)
123     {
124       msg (SE, _("The ELSE command must follow all ELSE IF commands "
125                  "in a DO IF structure."));
126       return CMD_FAILURE;
127     }
128
129   /* Parse the transformation. */
130   t = parse_do_if ();
131   if (!t)
132     return CMD_FAILURE;
133
134   /* Stick in the breakout transformation. */
135   t->brk = xmalloc (sizeof *t->brk);
136   t->brk->h.proc = goto_trns_proc;
137   t->brk->h.free = NULL;
138
139   /* Add to list of transformations, add to string of ELSE IFs. */
140   add_transformation ((struct trns_header *) t->brk);
141   add_transformation ((struct trns_header *) t);
142
143   add_ELSE_IF (t);
144
145   if (token != '.')
146     {
147       msg (SE, _("End of command expected."));
148       return CMD_TRAILING_GARBAGE;
149     }
150
151   return CMD_SUCCESS;
152 }
153
154 /* Parse ELSE. */
155 int
156 cmd_else (void)
157 {
158   struct do_if_trns *t;
159
160   lex_match_id ("ELSE");
161
162   /* Check that we're in a pleasing situation. */
163   if (!ctl_stack || ctl_stack->type != CST_DO_IF)
164     {
165       msg (SE, _("There is no DO IF to match with this ELSE."));
166       return CMD_FAILURE;
167     }
168   
169   if (((struct do_if_trns *) ctl_stack->trns)->has_else)
170     {
171       msg (SE, _("There may be at most one ELSE clause in each DO IF "
172                  "structure.  It must be the last clause."));
173       return CMD_FAILURE;
174     }
175
176   /* Note that the ELSE transformation is *not* added to the list of
177      transformations.  That's because it doesn't need to do anything.
178      Its goto transformation *is* added, because that's necessary.
179      The main DO IF do_if_trns is the destructor for this ELSE
180      do_if_trns. */
181   t = xmalloc (sizeof *t);
182   t->next = NULL;
183   t->brk = xmalloc (sizeof *t->brk);
184   t->brk->h.proc = goto_trns_proc;
185   t->brk->h.free = NULL;
186   t->cond = NULL;
187   add_transformation ((struct trns_header *) t->brk);
188   t->h.index = t->brk->h.index + 1;
189
190   /* Add to string of ELSE IFs. */
191   add_ELSE_IF (t);
192
193   return lex_end_of_command ();
194 }
195
196 /* Parse END IF. */
197 int
198 cmd_end_if (void)
199 {
200   /* List iterator. */
201   struct do_if_trns *iter;
202
203   lex_match_id ("IF");
204
205   /* Check that we're in a pleasing situation. */
206   if (!ctl_stack || ctl_stack->type != CST_DO_IF)
207     {
208       msg (SE, _("There is no DO IF to match with this END IF."));
209       return CMD_FAILURE;
210     }
211
212   /* Chain down the list, backpatching destinations for gotos. */
213   iter = (struct do_if_trns *) ctl_stack->trns;
214   for (;;)
215     {
216       if (iter->brk)
217         iter->brk->dest = n_trns;
218       iter->missing_jump = n_trns;
219       if (iter->next)
220         iter = iter->next;
221       else
222         break;
223     }
224   iter->false_jump = n_trns;
225
226   /* Pop control stack. */
227   ctl_stack = ctl_stack->down;
228
229   return lex_end_of_command ();
230 }
231
232 /* Adds an ELSE IF or ELSE to the chain of them that hangs off the
233    main DO IF. */
234 static void
235 add_ELSE_IF (struct do_if_trns * t)
236 {
237   /* List iterator. */
238   struct do_if_trns *iter;
239
240   iter = (struct do_if_trns *) ctl_stack->trns;
241   while (iter->next)
242     iter = iter->next;
243   assert (iter != NULL);
244
245   iter->next = t;
246   iter->false_jump = t->h.index;
247 }
248
249 /* Parses a DO IF or ELSE IF command and returns a pointer to a mostly
250    filled in transformation. */
251 static struct do_if_trns *
252 parse_do_if (void)
253 {
254   struct do_if_trns *t;
255   struct expression *e;
256
257   lex_match_id ("IF");
258
259   e = expr_parse (PXP_BOOLEAN);
260   if (!e)
261     return NULL;
262   if (token != '.')
263     {
264       expr_free (e);
265       lex_error (_("expecting end of command"));
266       return NULL;
267     }
268
269   t = xmalloc (sizeof *t);
270   t->h.proc = do_if_trns_proc;
271   t->h.free = do_if_trns_free;
272   t->next = NULL;
273   t->cond = e;
274
275   return t;
276 }
277
278 /* Executes a goto transformation. */
279 static int 
280 goto_trns_proc (struct trns_header * t, struct ccase * c unused)
281 {
282   return ((struct goto_trns *) t)->dest;
283 }
284
285 static int 
286 do_if_trns_proc (struct trns_header * trns, struct ccase * c)
287 {
288   struct do_if_trns *t = (struct do_if_trns *) trns;
289   union value bool;
290
291   expr_evaluate (t->cond, c, &bool);
292   if (bool.f == 1.0)
293     {
294       debug_printf ((_("DO IF %d: true\n"), t->h.index));
295       return -1;
296     }
297   else if (bool.f == 0.0)
298     {
299       debug_printf ((_("DO IF %d: false\n"), t->h.index));
300       return t->false_jump;
301     }
302   else
303     {
304       debug_printf ((_("DO IF %d: missing\n"), t->h.index));
305       return t->missing_jump;
306     }
307 }
308
309 static void 
310 do_if_trns_free (struct trns_header * trns)
311 {
312   struct do_if_trns *t = (struct do_if_trns *) trns;
313   expr_free (t->cond);
314
315   /* If brk is NULL then this is the main DO IF; therefore we
316      need to chain down to the ELSE and delete it. */
317   if (t->brk == NULL)
318     {
319       struct do_if_trns *iter = t->next;
320       while (iter)
321         {
322           if (!iter->cond)
323             {
324               /* This is the ELSE. */
325               free (iter);
326               break;
327             }
328           iter = iter->next;
329         }
330     }
331 }