Move GCC attribute declarations from pref.h.orig to new file
[pspp-builds.git] / src / language / control / 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., 51 Franklin Street, Fifth Floor, Boston, MA
18    02110-1301, USA. */
19
20 #include <config.h>
21 #include "control-stack.h"
22 #include "message.h"
23 #include <stdlib.h>
24 #include "alloc.h"
25 #include "command.h"
26 #include "compiler.h"
27 #include "message.h"
28 #include "expressions/public.h"
29 #include "lexer.h"
30 #include "str.h"
31 #include "variable.h"
32
33 #include "gettext.h"
34 #define _(msgid) gettext (msgid)
35
36 /* DO IF, ELSE IF, and ELSE are translated as a single
37    transformation that evaluates each condition and jumps to the
38    start of the appropriate block of transformations.  Each block
39    of transformations (except for the last) ends with a
40    transformation that jumps past the remaining blocks.
41
42    So, the following code:
43
44        DO IF a.             
45        ...block 1...
46        ELSE IF b.
47        ...block 2...
48        ELSE.
49        ...block 3...
50        END IF.
51
52    is effectively translated like this:
53
54        IF a GOTO 1, IF b GOTO 2, ELSE GOTO 3.
55        1: ...block 1...
56           GOTO 4
57        2: ...block 2...
58           GOTO 4
59        3: ...block 3...
60        4:
61
62 */
63
64 /* A conditional clause. */
65 struct clause 
66   {
67     struct expression *condition; /* Test expression; NULL for ELSE clause. */
68     int target_index;           /* Transformation to jump to if true. */
69   };
70
71 /* DO IF transformation. */
72 struct do_if_trns
73   {
74     struct clause *clauses;     /* Clauses. */
75     size_t clause_cnt;          /* Number of clauses. */
76     int past_END_IF_index;      /* Transformation just past last clause. */
77   };
78
79 static struct ctl_class do_if_class;
80
81 static int parse_clause (struct do_if_trns *);
82 static void add_clause (struct do_if_trns *,
83                         struct expression *condition, int target_index);
84 static void add_else (struct do_if_trns *);
85
86 static bool has_else (struct do_if_trns *);
87 static bool must_not_have_else (struct do_if_trns *);
88 static void close_do_if (void *do_if);
89
90 static trns_proc_func do_if_trns_proc, break_trns_proc;
91 static trns_free_func do_if_trns_free;
92
93 /* Parse DO IF. */
94 int
95 cmd_do_if (void)
96 {
97   struct do_if_trns *do_if = xmalloc (sizeof *do_if);
98   do_if->clauses = NULL;
99   do_if->clause_cnt = 0;
100
101   ctl_stack_push (&do_if_class, do_if);
102   add_transformation (do_if_trns_proc, do_if_trns_free, do_if);
103
104   return parse_clause (do_if);
105 }
106
107 /* Parse ELSE IF. */
108 int
109 cmd_else_if (void)
110 {
111   struct do_if_trns *do_if = ctl_stack_top (&do_if_class);
112   if (do_if == NULL || !must_not_have_else (do_if))
113     return CMD_CASCADING_FAILURE;
114   return parse_clause (do_if);
115 }
116
117 /* Parse ELSE. */
118 int
119 cmd_else (void)
120 {
121   struct do_if_trns *do_if = ctl_stack_top (&do_if_class);
122   if (do_if == NULL || !must_not_have_else (do_if))
123     return CMD_CASCADING_FAILURE;
124   add_else (do_if);
125   return lex_end_of_command ();
126 }
127
128 /* Parse END IF. */
129 int
130 cmd_end_if (void)
131 {
132   struct do_if_trns *do_if = ctl_stack_top (&do_if_class);
133   if (do_if == NULL)
134     return CMD_CASCADING_FAILURE;
135
136   ctl_stack_pop (do_if);
137
138   return lex_end_of_command ();
139 }
140
141 /* Closes out DO_IF, by adding a sentinel ELSE clause if
142    necessary and setting past_END_IF_index. */
143 static void
144 close_do_if (void *do_if_) 
145 {
146   struct do_if_trns *do_if = do_if_;
147   
148   if (!has_else (do_if)) 
149     add_else (do_if);
150   do_if->past_END_IF_index = next_transformation ();
151 }
152
153 /* Adds an ELSE clause to DO_IF pointing to the next
154    transformation. */
155 static void
156 add_else (struct do_if_trns *do_if) 
157 {
158   assert (!has_else (do_if));
159   add_clause (do_if, NULL, next_transformation ());
160 }
161
162 /* Returns true if DO_IF does not yet have an ELSE clause.
163    Reports an error and returns false if it does already. */
164 static bool
165 must_not_have_else (struct do_if_trns *do_if) 
166 {
167   if (has_else (do_if))
168     {
169       msg (SE, _("This command may not follow ELSE in DO IF...END IF."));
170       return false;
171     }
172   else
173     return true;
174 }
175
176 /* Returns true if DO_IF already has an ELSE clause,
177    false otherwise. */
178 static bool
179 has_else (struct do_if_trns *do_if) 
180 {
181   return (do_if->clause_cnt != 0
182           && do_if->clauses[do_if->clause_cnt - 1].condition == NULL);
183 }
184
185 /* Parses a DO IF or ELSE IF expression and appends the
186    corresponding clause to DO_IF.  Checks for end of command and
187    returns a command return code. */
188 static int
189 parse_clause (struct do_if_trns *do_if)
190 {
191   struct expression *condition;
192
193   condition = expr_parse (default_dict, EXPR_BOOLEAN);
194   if (condition == NULL)
195     return CMD_CASCADING_FAILURE;
196
197   add_clause (do_if, condition, next_transformation ());
198
199   return lex_end_of_command ();
200 }
201
202 /* Adds a clause to DO_IF that tests for the given CONDITION and,
203    if true, jumps to TARGET_INDEX. */
204 static void
205 add_clause (struct do_if_trns *do_if,
206             struct expression *condition, int target_index) 
207 {
208   struct clause *clause;
209
210   if (do_if->clause_cnt > 0)
211     add_transformation (break_trns_proc, NULL, do_if);
212
213   do_if->clauses = xnrealloc (do_if->clauses,
214                               do_if->clause_cnt + 1, sizeof *do_if->clauses);
215   clause = &do_if->clauses[do_if->clause_cnt++];
216   clause->condition = condition;
217   clause->target_index = target_index;
218 }
219
220 /* DO IF transformation procedure.
221    Checks each clause and jumps to the appropriate
222    transformation. */
223 static int 
224 do_if_trns_proc (void *do_if_, struct ccase *c, int case_num UNUSED)
225 {
226   struct do_if_trns *do_if = do_if_;
227   struct clause *clause;
228
229   for (clause = do_if->clauses; clause < do_if->clauses + do_if->clause_cnt;
230        clause++) 
231     {
232       if (clause->condition != NULL)
233         {
234           double boolean = expr_evaluate_num (clause->condition, c, case_num);
235           if (boolean == 1.0)
236             return clause->target_index;
237           else if (boolean == SYSMIS)
238             return do_if->past_END_IF_index;
239         }
240       else 
241         return clause->target_index;
242     }
243   return do_if->past_END_IF_index;
244 }
245
246 /* Frees a DO IF transformation. */
247 static bool
248 do_if_trns_free (void *do_if_)
249 {
250   struct do_if_trns *do_if = do_if_;
251   struct clause *clause;
252
253   for (clause = do_if->clauses; clause < do_if->clauses + do_if->clause_cnt;
254        clause++)
255     expr_free (clause->condition);
256   free (do_if->clauses);
257   free (do_if);
258   return true;
259 }
260
261 /* Breaks out of a DO IF construct. */
262 static int 
263 break_trns_proc (void *do_if_, struct ccase *c UNUSED, int case_num UNUSED)
264 {
265   struct do_if_trns *do_if = do_if_;
266
267   return do_if->past_END_IF_index;
268 }
269
270 /* DO IF control structure class definition. */
271 static struct ctl_class do_if_class = 
272   {
273     "DO IF",
274     "END IF",
275     close_do_if,
276   };