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