Start working to eliminate VFM dependence on static variables.
[pspp] / 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 trns_proc_func goto_trns_proc, do_if_trns_proc;
80 static trns_free_func do_if_trns_free;
81
82 /* Parse DO IF. */
83 int
84 cmd_do_if (void)
85 {
86   struct do_if_trns *t;
87
88   /* Parse the transformation. */
89   t = parse_do_if ();
90   if (!t)
91     return CMD_FAILURE;
92
93   /* Finish up the transformation, add to control stack, add to
94      transformation list. */
95   t->brk = NULL;
96   t->ctl.type = CST_DO_IF;
97   t->ctl.down = ctl_stack;
98   t->ctl.trns = (struct trns_header *) t;
99   t->ctl.brk = NULL;
100   t->has_else = 0;
101   ctl_stack = &t->ctl;
102   add_transformation ((struct trns_header *) t);
103
104   return CMD_SUCCESS;
105 }
106
107 /* Parse ELSE IF. */
108 int
109 cmd_else_if (void)
110 {
111   /* Transformation created. */
112   struct do_if_trns *t;
113
114   /* Check that we're in a pleasing situation. */
115   if (!ctl_stack || ctl_stack->type != CST_DO_IF)
116     {
117       msg (SE, _("There is no DO IF to match with this ELSE IF."));
118       return CMD_FAILURE;
119     }
120   if (((struct do_if_trns *) ctl_stack->trns)->has_else)
121     {
122       msg (SE, _("The ELSE command must follow all ELSE IF commands "
123                  "in a DO IF structure."));
124       return CMD_FAILURE;
125     }
126
127   /* Parse the transformation. */
128   t = parse_do_if ();
129   if (!t)
130     return CMD_FAILURE;
131
132   /* Stick in the breakout transformation. */
133   t->brk = xmalloc (sizeof *t->brk);
134   t->brk->h.proc = goto_trns_proc;
135   t->brk->h.free = NULL;
136
137   /* Add to list of transformations, add to string of ELSE IFs. */
138   add_transformation ((struct trns_header *) t->brk);
139   add_transformation ((struct trns_header *) t);
140
141   add_ELSE_IF (t);
142
143   if (token != '.')
144     {
145       msg (SE, _("End of command expected."));
146       return CMD_TRAILING_GARBAGE;
147     }
148
149   return CMD_SUCCESS;
150 }
151
152 /* Parse ELSE. */
153 int
154 cmd_else (void)
155 {
156   struct do_if_trns *t;
157
158   lex_match_id ("ELSE");
159
160   /* Check that we're in a pleasing situation. */
161   if (!ctl_stack || ctl_stack->type != CST_DO_IF)
162     {
163       msg (SE, _("There is no DO IF to match with this ELSE."));
164       return CMD_FAILURE;
165     }
166   
167   if (((struct do_if_trns *) ctl_stack->trns)->has_else)
168     {
169       msg (SE, _("There may be at most one ELSE clause in each DO IF "
170                  "structure.  It must be the last clause."));
171       return CMD_FAILURE;
172     }
173
174   /* Note that the ELSE transformation is *not* added to the list of
175      transformations.  That's because it doesn't need to do anything.
176      Its goto transformation *is* added, because that's necessary.
177      The main DO IF do_if_trns is the destructor for this ELSE
178      do_if_trns. */
179   t = xmalloc (sizeof *t);
180   t->next = NULL;
181   t->brk = xmalloc (sizeof *t->brk);
182   t->brk->h.proc = goto_trns_proc;
183   t->brk->h.free = NULL;
184   t->cond = NULL;
185   add_transformation ((struct trns_header *) t->brk);
186   t->h.index = t->brk->h.index + 1;
187
188   /* Add to string of ELSE IFs. */
189   add_ELSE_IF (t);
190
191   return lex_end_of_command ();
192 }
193
194 /* Parse END IF. */
195 int
196 cmd_end_if (void)
197 {
198   /* List iterator. */
199   struct do_if_trns *iter;
200
201   lex_match_id ("IF");
202
203   /* Check that we're in a pleasing situation. */
204   if (!ctl_stack || ctl_stack->type != CST_DO_IF)
205     {
206       msg (SE, _("There is no DO IF to match with this END IF."));
207       return CMD_FAILURE;
208     }
209
210   /* Chain down the list, backpatching destinations for gotos. */
211   iter = (struct do_if_trns *) ctl_stack->trns;
212   for (;;)
213     {
214       if (iter->brk)
215         iter->brk->dest = n_trns;
216       iter->missing_jump = n_trns;
217       if (iter->next)
218         iter = iter->next;
219       else
220         break;
221     }
222   iter->false_jump = n_trns;
223
224   /* Pop control stack. */
225   ctl_stack = ctl_stack->down;
226
227   return lex_end_of_command ();
228 }
229
230 /* Adds an ELSE IF or ELSE to the chain of them that hangs off the
231    main DO IF. */
232 static void
233 add_ELSE_IF (struct do_if_trns * t)
234 {
235   /* List iterator. */
236   struct do_if_trns *iter;
237
238   iter = (struct do_if_trns *) ctl_stack->trns;
239   while (iter->next)
240     iter = iter->next;
241   assert (iter != NULL);
242
243   iter->next = t;
244   iter->false_jump = t->h.index;
245 }
246
247 /* Parses a DO IF or ELSE IF command and returns a pointer to a mostly
248    filled in transformation. */
249 static struct do_if_trns *
250 parse_do_if (void)
251 {
252   struct do_if_trns *t;
253   struct expression *e;
254
255   lex_match_id ("IF");
256
257   e = expr_parse (PXP_BOOLEAN);
258   if (!e)
259     return NULL;
260   if (token != '.')
261     {
262       expr_free (e);
263       lex_error (_("expecting end of command"));
264       return NULL;
265     }
266
267   t = xmalloc (sizeof *t);
268   t->h.proc = do_if_trns_proc;
269   t->h.free = do_if_trns_free;
270   t->next = NULL;
271   t->cond = e;
272
273   return t;
274 }
275
276 /* Executes a goto transformation. */
277 static int 
278 goto_trns_proc (struct trns_header * t, struct ccase * c UNUSED,
279                 int case_num 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                  int case_num UNUSED)
287 {
288   struct do_if_trns *t = (struct do_if_trns *) trns;
289   union value bool;
290
291   expr_evaluate (t->cond, c, case_num, &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 }