a2a7e7d2a430c108cae7170888ebabea2f7fafd8
[pspp-builds.git] / src / language / xforms / compute.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
22 #include <stdlib.h>
23
24 #include <data/case.h>
25 #include <data/dictionary.h>
26 #include <data/procedure.h>
27 #include <data/transformations.h>
28 #include <data/variable.h>
29 #include <language/command.h>
30 #include <language/expressions/public.h>
31 #include <language/lexer/lexer.h>
32 #include <libpspp/alloc.h>
33 #include <libpspp/message.h>
34 #include <libpspp/message.h>
35 #include <libpspp/misc.h>
36 #include <libpspp/str.h>
37
38 #include "gettext.h"
39 #define _(msgid) gettext (msgid)
40
41 struct compute_trns;
42 struct lvalue;
43
44 /* Target of a COMPUTE or IF assignment, either a variable or a
45    vector element. */
46 static struct lvalue *lvalue_parse (struct lexer *lexer, struct dataset *);
47 static int lvalue_get_type (const struct lvalue *);
48 static bool lvalue_is_vector (const struct lvalue *);
49 static void lvalue_finalize (struct lvalue *,
50                              struct compute_trns *, struct dictionary *);
51 static void lvalue_destroy (struct lvalue *, struct dictionary *);
52
53 /* COMPUTE and IF transformation. */
54 struct compute_trns
55   {
56     /* Test expression (IF only). */
57     struct expression *test;     /* Test expression. */
58
59     /* Variable lvalue, if variable != NULL. */
60     struct variable *variable;   /* Destination variable, if any. */
61     int fv;                      /* `value' index of destination variable. */
62     int width;                   /* Lvalue string width; 0=numeric. */
63
64     /* Vector lvalue, if vector != NULL. */
65     const struct vector *vector; /* Destination vector, if any. */
66     struct expression *element;  /* Destination vector element expr. */
67
68     /* Rvalue. */
69     struct expression *rvalue;   /* Rvalue expression. */
70   };
71
72 static struct expression *parse_rvalue (struct lexer *lexer, 
73                                         const struct lvalue *, 
74                                         struct dataset *);
75
76 static struct compute_trns *compute_trns_create (void);
77 static trns_proc_func *get_proc_func (const struct lvalue *);
78 static trns_free_func compute_trns_free;
79 \f
80 /* COMPUTE. */
81
82 int
83 cmd_compute (struct lexer *lexer, struct dataset *ds)
84 {
85   struct dictionary *dict = dataset_dict (ds);
86   struct lvalue *lvalue = NULL;
87   struct compute_trns *compute = NULL;
88
89   compute = compute_trns_create ();
90
91   lvalue = lvalue_parse (lexer, ds);
92   if (lvalue == NULL)
93     goto fail;
94
95   if (!lex_force_match (lexer, '='))
96     goto fail;
97   compute->rvalue = parse_rvalue (lexer, lvalue, ds);
98   if (compute->rvalue == NULL)
99     goto fail;
100
101   add_transformation (ds, get_proc_func (lvalue), compute_trns_free, compute);
102
103   lvalue_finalize (lvalue, compute, dict);
104
105   return lex_end_of_command (lexer);
106
107  fail:
108   lvalue_destroy (lvalue, dict);
109   compute_trns_free (compute);
110   return CMD_CASCADING_FAILURE;
111 }
112 \f
113 /* Transformation functions. */
114
115 /* Handle COMPUTE or IF with numeric target variable. */
116 static int
117 compute_num (void *compute_, struct ccase *c, casenumber case_num)
118 {
119   struct compute_trns *compute = compute_;
120
121   if (compute->test == NULL
122       || expr_evaluate_num (compute->test, c, case_num) == 1.0) 
123     case_data_rw (c, compute->fv)->f = expr_evaluate_num (compute->rvalue, c,
124                                                           case_num); 
125   
126   return TRNS_CONTINUE;
127 }
128
129 /* Handle COMPUTE or IF with numeric vector element target
130    variable. */
131 static int
132 compute_num_vec (void *compute_, struct ccase *c, casenumber case_num)
133 {
134   struct compute_trns *compute = compute_;
135
136   if (compute->test == NULL
137       || expr_evaluate_num (compute->test, c, case_num) == 1.0) 
138     {
139       double index;     /* Index into the vector. */
140       int rindx;        /* Rounded index value. */
141
142       index = expr_evaluate_num (compute->element, c, case_num);
143       rindx = floor (index + EPSILON);
144       if (index == SYSMIS || rindx < 1 || rindx > compute->vector->cnt)
145         {
146           if (index == SYSMIS)
147             msg (SW, _("When executing COMPUTE: SYSMIS is not a valid value as "
148                        "an index into vector %s."), compute->vector->name);
149           else
150             msg (SW, _("When executing COMPUTE: %g is not a valid value as "
151                        "an index into vector %s."),
152                  index, compute->vector->name);
153           return TRNS_CONTINUE;
154         }
155       case_data_rw (c, compute->vector->var[rindx - 1]->fv)->f
156         = expr_evaluate_num (compute->rvalue, c, case_num);
157     }
158   
159   return TRNS_CONTINUE;
160 }
161
162 /* Handle COMPUTE or IF with string target variable. */
163 static int
164 compute_str (void *compute_, struct ccase *c, casenumber case_num)
165 {
166   struct compute_trns *compute = compute_;
167
168   if (compute->test == NULL
169       || expr_evaluate_num (compute->test, c, case_num) == 1.0) 
170     expr_evaluate_str (compute->rvalue, c, case_num,
171                        case_data_rw (c, compute->fv)->s, compute->width);
172   
173   return TRNS_CONTINUE;
174 }
175
176 /* Handle COMPUTE or IF with string vector element target
177    variable. */
178 static int
179 compute_str_vec (void *compute_, struct ccase *c, casenumber case_num)
180 {
181   struct compute_trns *compute = compute_;
182
183   if (compute->test == NULL
184       || expr_evaluate_num (compute->test, c, case_num) == 1.0) 
185     {
186       double index;             /* Index into the vector. */
187       int rindx;                /* Rounded index value. */
188       struct variable *vr;      /* Variable reference by indexed vector. */
189
190       index = expr_evaluate_num (compute->element, c, case_num);
191       rindx = floor (index + EPSILON);
192       if (index == SYSMIS) 
193         {
194           msg (SW, _("When executing COMPUTE: SYSMIS is not a valid "
195                      "value as an index into vector %s."),
196                compute->vector->name);
197           return TRNS_CONTINUE; 
198         }
199       else if (rindx < 1 || rindx > compute->vector->cnt)
200         {
201           msg (SW, _("When executing COMPUTE: %g is not a valid value as "
202                      "an index into vector %s."),
203                index, compute->vector->name);
204           return TRNS_CONTINUE;
205         }
206
207       vr = compute->vector->var[rindx - 1];
208       expr_evaluate_str (compute->rvalue, c, case_num,
209                          case_data_rw (c, vr->fv)->s, vr->width);
210     }
211   
212   return TRNS_CONTINUE;
213 }
214 \f
215 /* IF. */
216
217 int
218 cmd_if (struct lexer *lexer, struct dataset *ds)
219 {
220   struct dictionary *dict = dataset_dict (ds);
221   struct compute_trns *compute = NULL;
222   struct lvalue *lvalue = NULL;
223
224   compute = compute_trns_create ();
225
226   /* Test expression. */
227   compute->test = expr_parse (lexer, ds, EXPR_BOOLEAN);
228   if (compute->test == NULL)
229     goto fail;
230
231   /* Lvalue variable. */
232   lvalue = lvalue_parse (lexer, ds);
233   if (lvalue == NULL)
234     goto fail;
235
236   /* Rvalue expression. */
237   if (!lex_force_match (lexer, '='))
238     goto fail;
239   compute->rvalue = parse_rvalue (lexer, lvalue, ds);
240   if (compute->rvalue == NULL)
241     goto fail;
242
243   add_transformation (ds, get_proc_func (lvalue), compute_trns_free, compute);
244
245   lvalue_finalize (lvalue, compute, dict);
246
247   return lex_end_of_command (lexer);
248
249  fail:
250   lvalue_destroy (lvalue, dict);
251   compute_trns_free (compute);
252   return CMD_CASCADING_FAILURE;
253 }
254 \f
255 /* Code common to COMPUTE and IF. */
256
257 static trns_proc_func *
258 get_proc_func (const struct lvalue *lvalue) 
259 {
260   bool is_numeric = lvalue_get_type (lvalue) == NUMERIC;
261   bool is_vector = lvalue_is_vector (lvalue);
262
263   return (is_numeric
264           ? (is_vector ? compute_num_vec : compute_num)
265           : (is_vector ? compute_str_vec : compute_str));
266 }
267
268 /* Parses and returns an rvalue expression of the same type as
269    LVALUE, or a null pointer on failure. */
270 static struct expression *
271 parse_rvalue (struct lexer *lexer, 
272               const struct lvalue *lvalue, struct dataset *ds)
273 {
274   bool is_numeric = lvalue_get_type (lvalue) == NUMERIC;
275
276   return expr_parse (lexer, ds, is_numeric ? EXPR_NUMBER : EXPR_STRING);
277 }
278
279 /* Returns a new struct compute_trns after initializing its fields. */
280 static struct compute_trns *
281 compute_trns_create (void)
282 {
283   struct compute_trns *compute = xmalloc (sizeof *compute);
284   compute->test = NULL;
285   compute->variable = NULL;
286   compute->vector = NULL;
287   compute->element = NULL;
288   compute->rvalue = NULL;
289   return compute;
290 }
291
292 /* Deletes all the fields in COMPUTE. */
293 static bool
294 compute_trns_free (void *compute_)
295 {
296   struct compute_trns *compute = compute_;
297
298   if (compute != NULL) 
299     {
300       expr_free (compute->test);
301       expr_free (compute->element);
302       expr_free (compute->rvalue);
303       free (compute);
304     }
305   return true;
306 }
307 \f
308 /* COMPUTE or IF target variable or vector element.
309    For a variable, the `variable' member is non-null.
310    For a vector element, the `vector' member is non-null. */
311 struct lvalue
312   {
313     struct variable *variable;   /* Destination variable. */
314     bool is_new_variable;        /* Did we create the variable? */
315
316     const struct vector *vector; /* Destination vector, if any, or NULL. */
317     struct expression *element;  /* Destination vector element, or NULL. */
318   };
319
320 /* Parses the target variable or vector element into a new
321    `struct lvalue', which is returned. */
322 static struct lvalue *
323 lvalue_parse (struct lexer *lexer, struct dataset *ds) 
324 {
325   struct dictionary *dict = dataset_dict (ds);
326   struct lvalue *lvalue;
327
328   lvalue = xmalloc (sizeof *lvalue);
329   lvalue->variable = NULL;
330   lvalue->is_new_variable = false;
331   lvalue->vector = NULL;
332   lvalue->element = NULL;
333
334   if (!lex_force_id (lexer))
335     goto lossage;
336   
337   if (lex_look_ahead (lexer) == '(')
338     {
339       /* Vector. */
340       lvalue->vector = dict_lookup_vector (dict, lex_tokid (lexer));
341       if (lvalue->vector == NULL)
342         {
343           msg (SE, _("There is no vector named %s."), lex_tokid (lexer));
344           goto lossage;
345         }
346
347       /* Vector element. */
348       lex_get (lexer);
349       if (!lex_force_match (lexer, '('))
350         goto lossage;
351       lvalue->element = expr_parse (lexer, ds, EXPR_NUMBER);
352       if (lvalue->element == NULL)
353         goto lossage;
354       if (!lex_force_match (lexer, ')'))
355         goto lossage;
356     }
357   else
358     {
359       /* Variable name. */
360       const char *var_name = lex_tokid (lexer);
361       lvalue->variable = dict_lookup_var (dict, var_name);
362       if (lvalue->variable == NULL) 
363         {
364           lvalue->variable = dict_create_var_assert (dict, var_name, 0);
365           lvalue->is_new_variable = true; 
366         }
367       lex_get (lexer);
368     }
369   return lvalue;
370
371  lossage:
372   lvalue_destroy (lvalue, dict);
373   return NULL;
374 }
375
376 /* Returns the type (NUMERIC or ALPHA) of the target variable or
377    vector in LVALUE. */
378 static int
379 lvalue_get_type (const struct lvalue *lvalue) 
380 {
381   return (lvalue->variable != NULL
382           ? lvalue->variable->type
383           : lvalue->vector->var[0]->type);
384 }
385
386 /* Returns true if LVALUE has a vector as its target. */
387 static bool
388 lvalue_is_vector (const struct lvalue *lvalue) 
389 {
390   return lvalue->vector != NULL;
391 }
392
393 /* Finalizes making LVALUE the target of COMPUTE, by creating the
394    target variable if necessary and setting fields in COMPUTE. */
395 static void
396 lvalue_finalize (struct lvalue *lvalue, 
397                  struct compute_trns *compute, 
398                  struct dictionary *dict) 
399 {
400   if (lvalue->vector == NULL)
401     {
402       compute->variable = lvalue->variable;
403       compute->fv = compute->variable->fv;
404       compute->width = compute->variable->width;
405
406       /* Goofy behavior, but compatible: Turn off LEAVE. */
407       if (dict_class_from_id (compute->variable->name) != DC_SCRATCH)
408         compute->variable->leave = false;
409
410       /* Prevent lvalue_destroy from deleting variable. */
411       lvalue->is_new_variable = false;
412     }
413   else 
414     {
415       compute->vector = lvalue->vector;
416       compute->element = lvalue->element;
417       lvalue->element = NULL;
418     }
419
420   lvalue_destroy (lvalue, dict);
421 }
422
423 /* Destroys LVALUE. */
424 static void 
425 lvalue_destroy (struct lvalue *lvalue, struct dictionary *dict) 
426 {
427   if (lvalue == NULL) 
428      return;
429
430   if (lvalue->is_new_variable)
431     dict_delete_var (dict, lvalue->variable);
432   expr_free (lvalue->element);
433   free (lvalue);
434 }