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