Improve the way we handle the various parsing "states". Until now
[pspp-builds.git] / src / language / xforms / recode.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 <libpspp/message.h>
22 #include <ctype.h>
23 #include <math.h>
24 #include <stdlib.h>
25 #include <libpspp/alloc.h>
26 #include <data/case.h>
27 #include <language/command.h>
28 #include <libpspp/compiler.h>
29 #include <data/data-in.h>
30 #include <data/dictionary.h>
31 #include <libpspp/message.h>
32 #include <language/lexer/lexer.h>
33 #include <libpspp/magic.h>
34 #include <libpspp/pool.h>
35 #include <language/lexer/range-parser.h>
36 #include <libpspp/str.h>
37 #include <data/variable.h>
38
39 #include "gettext.h"
40 #define _(msgid) gettext (msgid)
41 \f
42 /* Definitions. */
43
44 /* Type of source value for RECODE. */
45 enum map_in_type
46   {
47     MAP_SINGLE,                 /* Specific value. */
48     MAP_RANGE,                  /* Range of values. */
49     MAP_SYSMIS,                 /* System missing value. */
50     MAP_MISSING,                /* Any missing value. */
51     MAP_ELSE,                   /* Any value. */
52     MAP_CONVERT                 /* "123" => 123. */
53   };
54
55 /* Describes input values to be mapped. */
56 struct map_in
57   {
58     enum map_in_type type;      /* One of MAP_*. */
59     union value x, y;           /* Source values. */
60   };
61
62 /* Describes the value used as output from a mapping. */
63 struct map_out 
64   {
65     bool copy_input;            /* If true, copy input to output. */
66     union value value;          /* If copy_input false, recoded value. */
67     int width;                  /* If copy_input false, output value width. */ 
68   };
69
70 /* Describes how to recode a single value or range of values into a
71    single value.  */
72 struct mapping 
73   {
74     struct map_in in;           /* Input values. */
75     struct map_out out;         /* Output value. */
76   };
77
78 /* RECODE transformation. */
79 struct recode_trns
80   {
81     struct pool *pool;
82
83     /* Variable types, for convenience. */
84     enum var_type src_type;     /* src_vars[*]->type. */
85     enum var_type dst_type;     /* dst_vars[*]->type. */
86
87     /* Variables. */
88     struct variable **src_vars; /* Source variables. */
89     struct variable **dst_vars; /* Destination variables. */
90     char **dst_names;           /* Name of dest variables, if they're new. */
91     size_t var_cnt;             /* Number of variables. */
92
93     /* Mappings. */
94     struct mapping *mappings;   /* Value mappings. */
95     size_t map_cnt;             /* Number of mappings. */
96   };
97
98 static bool parse_src_vars (struct recode_trns *);
99 static bool parse_mappings (struct recode_trns *);
100 static bool parse_dst_vars (struct recode_trns *);
101
102 static void add_mapping (struct recode_trns *,
103                          size_t *map_allocated, const struct map_in *);
104
105 static bool parse_map_in (struct map_in *, struct pool *,
106                           enum var_type src_type, size_t max_src_width);
107 static void set_map_in_generic (struct map_in *, enum map_in_type);
108 static void set_map_in_num (struct map_in *, enum map_in_type, double, double);
109 static void set_map_in_str (struct map_in *, struct pool *,
110                             const struct string *, size_t width);
111
112 static bool parse_map_out (struct pool *, struct map_out *);
113 static void set_map_out_num (struct map_out *, double);
114 static void set_map_out_str (struct map_out *, struct pool *,
115                              const struct string *);
116
117 static void enlarge_dst_widths (struct recode_trns *);
118 static void create_dst_vars (struct recode_trns *);
119
120 static trns_proc_func recode_trns_proc;
121 static trns_free_func recode_trns_free;
122 \f
123 /* Parser. */
124
125 /* Parses the RECODE transformation. */
126 int
127 cmd_recode (void)
128 {
129   do
130     {
131       struct recode_trns *trns
132         = pool_create_container (struct recode_trns, pool);
133
134       /* Parse source variable names,
135          then input to output mappings,
136          then destintation variable names. */
137       if (!parse_src_vars (trns)
138           || !parse_mappings (trns)
139           || !parse_dst_vars (trns))
140         {
141           recode_trns_free (trns);
142           return CMD_FAILURE;
143         }
144
145       /* Ensure that all the output strings are at least as wide
146          as the widest destination variable. */
147       if (trns->dst_type == ALPHA)
148         enlarge_dst_widths (trns);
149
150       /* Create destination variables, if needed.
151          This must be the final step; otherwise we'd have to
152          delete destination variables on failure. */
153       if (trns->src_vars != trns->dst_vars)
154         create_dst_vars (trns);
155
156       /* Done. */
157       add_transformation (recode_trns_proc, recode_trns_free, trns);
158     }
159   while (lex_match ('/'));
160   
161   return lex_end_of_command ();
162 }
163
164 /* Parses a set of variables to recode into TRNS->src_vars and
165    TRNS->var_cnt.  Sets TRNS->src_type.  Returns true if
166    successful, false on parse error. */
167 static bool
168 parse_src_vars (struct recode_trns *trns) 
169 {
170   if (!parse_variables (default_dict, &trns->src_vars, &trns->var_cnt,
171                         PV_SAME_TYPE))
172     return false;
173   pool_register (trns->pool, free, trns->src_vars);
174   trns->src_type = trns->src_vars[0]->type;
175   return true;
176 }
177
178 /* Parses a set of mappings, which take the form (input=output),
179    into TRNS->mappings and TRNS->map_cnt.  Sets TRNS->dst_type.
180    Returns true if successful, false on parse error. */
181 static bool
182 parse_mappings (struct recode_trns *trns) 
183 {
184   size_t max_src_width;
185   size_t map_allocated;
186   bool have_dst_type;
187   size_t i;
188   
189   /* Find length of longest source variable. */
190   max_src_width = trns->src_vars[0]->width;
191   for (i = 1; i < trns->var_cnt; i++) 
192     {
193       size_t var_width = trns->src_vars[i]->width;
194       if (var_width > max_src_width)
195         max_src_width = var_width;
196     }
197       
198   /* Parse the mappings in parentheses. */
199   trns->mappings = NULL;
200   trns->map_cnt = 0;
201   map_allocated = 0;
202   have_dst_type = false;
203   if (!lex_force_match ('('))
204     return false;
205   do
206     {
207       enum var_type dst_type;
208
209       if (!lex_match_id ("CONVERT")) 
210         {
211           struct map_out out;
212           size_t first_map_idx;
213           size_t i;
214
215           first_map_idx = trns->map_cnt;
216
217           /* Parse source specifications. */
218           do
219             {
220               struct map_in in;
221               if (!parse_map_in (&in, trns->pool,
222                                  trns->src_type, max_src_width))
223                 return false;
224               add_mapping (trns, &map_allocated, &in);
225               lex_match (',');
226             }
227           while (!lex_match ('='));
228
229           if (!parse_map_out (trns->pool, &out))
230             return false;
231           dst_type = out.width == 0 ? NUMERIC : ALPHA;
232           if (have_dst_type && dst_type != trns->dst_type)
233             {
234               msg (SE, _("Inconsistent target variable types.  "
235                          "Target variables "
236                          "must be all numeric or all string."));
237               return false;
238             }
239               
240           for (i = first_map_idx; i < trns->map_cnt; i++)
241             trns->mappings[i].out = out;
242         }
243       else 
244         {
245           /* Parse CONVERT as a special case. */
246           struct map_in in;
247           set_map_in_generic (&in, MAP_CONVERT);
248           add_mapping (trns, &map_allocated, &in);
249               
250           dst_type = NUMERIC;
251           if (trns->src_type != ALPHA
252               || (have_dst_type && trns->dst_type != NUMERIC)) 
253             {
254               msg (SE, _("CONVERT requires string input values and "
255                          "numeric output values."));
256               return false;
257             }
258         }
259       trns->dst_type = dst_type;
260       have_dst_type = true;
261
262       if (!lex_force_match (')'))
263         return false; 
264     }
265   while (lex_match ('('));
266
267   return true;
268 }
269
270 /* Parses a mapping input value into IN, allocating memory from
271    POOL.  The source value type must be provided as SRC_TYPE and,
272    if string, the maximum width of a string source variable must
273    be provided in MAX_SRC_WIDTH.  Returns true if successful,
274    false on parse error. */
275 static bool
276 parse_map_in (struct map_in *in, struct pool *pool,
277               enum var_type src_type, size_t max_src_width)
278 {
279   if (lex_match_id ("ELSE"))
280     set_map_in_generic (in, MAP_ELSE);
281   else if (src_type == NUMERIC)
282     {
283       if (lex_match_id ("MISSING"))
284         set_map_in_generic (in, MAP_MISSING);
285       else if (lex_match_id ("SYSMIS"))
286         set_map_in_generic (in, MAP_SYSMIS);
287       else 
288         {
289           double x, y;
290           if (!parse_num_range (&x, &y, NULL))
291             return false;
292           set_map_in_num (in, x == y ? MAP_SINGLE : MAP_RANGE, x, y);
293         }
294     }
295   else
296     {
297       if (!lex_force_string ())
298         return false;
299       set_map_in_str (in, pool, &tokstr, max_src_width);
300       lex_get ();
301     }
302
303   return true;
304 }
305
306 /* Adds IN to the list of mappings in TRNS.
307    MAP_ALLOCATED is the current number of allocated mappings,
308    which is updated as needed. */
309 static void
310 add_mapping (struct recode_trns *trns,
311              size_t *map_allocated, const struct map_in *in)
312 {
313   struct mapping *m;
314   if (trns->map_cnt >= *map_allocated)
315     trns->mappings = pool_2nrealloc (trns->pool, trns->mappings,
316                                      map_allocated,
317                                      sizeof *trns->mappings);
318   m = &trns->mappings[trns->map_cnt++];
319   m->in = *in;
320 }
321
322 /* Sets IN as a mapping of the given TYPE. */
323 static void
324 set_map_in_generic (struct map_in *in, enum map_in_type type) 
325 {
326   in->type = type;
327 }
328
329 /* Sets IN as a numeric mapping of the given TYPE,
330    with X and Y as the two numeric values. */
331 static void
332 set_map_in_num (struct map_in *in, enum map_in_type type, double x, double y) 
333 {
334   in->type = type;
335   in->x.f = x;
336   in->y.f = y;
337 }
338
339 /* Sets IN as a string mapping, with STRING as the string,
340    allocated from POOL.  The string is padded with spaces on the
341    right to WIDTH characters long. */
342 static void
343 set_map_in_str (struct map_in *in, struct pool *pool,
344                 const struct string *string, size_t width) 
345 {
346   in->type = MAP_SINGLE;
347   in->x.c = pool_alloc_unaligned (pool, width);
348   buf_copy_rpad (in->x.c, width, ds_data (string), ds_length (string));
349 }
350
351 /* Parses a mapping output value into OUT, allocating memory from
352    POOL.  Returns true if successful, false on parse error. */
353 static bool
354 parse_map_out (struct pool *pool, struct map_out *out)
355 {
356   if (lex_is_number ())
357     {
358       set_map_out_num (out, lex_number ());
359       lex_get ();
360     }
361   else if (lex_match_id ("SYSMIS"))
362     set_map_out_num (out, SYSMIS);
363   else if (token == T_STRING)
364     {
365       set_map_out_str (out, pool, &tokstr);
366       lex_get ();
367     }
368   else if (lex_match_id ("COPY"))
369     out->copy_input = true;
370   else 
371     {
372       lex_error (_("expecting output value"));
373       return false;
374     }
375   return true; 
376 }
377
378 /* Sets OUT as a numeric mapping output with the given VALUE. */
379 static void
380 set_map_out_num (struct map_out *out, double value) 
381 {
382   out->copy_input = false;
383   out->value.f = value;
384   out->width = 0;
385 }
386
387 /* Sets OUT as a string mapping output with the given VALUE. */
388 static void
389 set_map_out_str (struct map_out *out, struct pool *pool,
390                  const struct string *value)
391 {
392   const char *string = ds_data (value);
393   size_t length = ds_length (value);
394
395   out->copy_input = false;
396   out->value.c = pool_alloc_unaligned (pool, length);
397   memcpy (out->value.c, string, length);
398   out->width = length;
399 }
400
401 /* Parses a set of target variables into TRNS->dst_vars and
402    TRNS->dst_names. */
403 static bool
404 parse_dst_vars (struct recode_trns *trns) 
405 {
406   size_t i;
407   
408   if (lex_match_id ("INTO"))
409     {
410       size_t name_cnt;
411       size_t i;
412
413       if (!parse_mixed_vars_pool (trns->pool, &trns->dst_names, &name_cnt,
414                                   PV_NONE))
415         return false;
416
417       if (name_cnt != trns->var_cnt)
418         {
419           msg (SE, _("%u variable(s) cannot be recoded into "
420                      "%u variable(s).  Specify the same number "
421                      "of variables as source and target variables."),
422                (unsigned) trns->var_cnt, (unsigned) name_cnt);
423           return false;
424         }
425
426       trns->dst_vars = pool_nalloc (trns->pool,
427                                     trns->var_cnt, sizeof *trns->dst_vars);
428       for (i = 0; i < trns->var_cnt; i++)
429         {
430           struct variable *v;
431           v = trns->dst_vars[i] = dict_lookup_var (default_dict,
432                                                   trns->dst_names[i]);
433           if (v == NULL && trns->dst_type == ALPHA) 
434             {
435               msg (SE, _("There is no variable named "
436                          "%s.  (All string variables specified "
437                          "on INTO must already exist.  Use the "
438                          "STRING command to create a string "
439                          "variable.)"),
440                    trns->dst_names[i]);
441               return false;
442             }
443         }
444     }
445   else 
446     {
447       trns->dst_vars = trns->src_vars;
448       if (trns->src_type != trns->dst_type)
449         {
450           msg (SE, _("INTO is required with %s input values "
451                      "and %s output values."),
452                var_type_adj (trns->src_type),
453                var_type_adj (trns->dst_type));
454           return false;
455         }
456     }
457
458   for (i = 0; i < trns->var_cnt; i++)
459     {
460       struct variable *v = trns->dst_vars[i];
461       if (v != NULL && v->type != trns->dst_type)
462         {
463           msg (SE, _("Type mismatch.  Cannot store %s data in "
464                      "%s variable %s."),
465                trns->dst_type == ALPHA ? _("string") : _("numeric"),
466                v->type == ALPHA ? _("string") : _("numeric"),
467                v->name);
468           return false;
469         }
470     }
471
472   return true;
473 }
474
475 /* Ensures that all the output values in TRNS are as wide as the
476    widest destination variable. */
477 static void
478 enlarge_dst_widths (struct recode_trns *trns) 
479 {
480   size_t max_dst_width;
481   size_t i;
482
483   max_dst_width = 0;
484   for (i = 0; i < trns->var_cnt; i++)
485     {
486       struct variable *v = trns->dst_vars[i];
487       if (v->width > max_dst_width)
488         max_dst_width = v->width;
489     }
490
491   for (i = 0; i < trns->map_cnt; i++)
492     {
493       struct map_out *out = &trns->mappings[i].out;
494       if (!out->copy_input && out->width < max_dst_width) 
495         {
496           char *s = pool_alloc_unaligned (trns->pool, max_dst_width + 1);
497           str_copy_rpad (s, max_dst_width + 1, out->value.c);
498           out->value.c = s;
499         }
500     }
501 }
502
503 /* Creates destination variables that don't already exist. */
504 static void
505 create_dst_vars (struct recode_trns *trns)
506 {
507   size_t i;
508
509   for (i = 0; i < trns->var_cnt; i++) 
510     {
511       struct variable **var = &trns->dst_vars[i];
512       const char *name = trns->dst_names[i];
513           
514       *var = dict_lookup_var (default_dict, name);
515       if (*var == NULL)
516         *var = dict_create_var_assert (default_dict, name, 0);
517       assert ((*var)->type == trns->dst_type);
518     }
519 }
520 \f
521 /* Data transformation. */
522
523 /* Returns the output mapping in TRNS for an input of VALUE on
524    variable V, or a null pointer if there is no mapping. */
525 static const struct map_out *
526 find_src_numeric (struct recode_trns *trns, double value, struct variable *v)
527 {
528   struct mapping *m;
529
530   for (m = trns->mappings; m < trns->mappings + trns->map_cnt; m++)
531     {
532       const struct map_in *in = &m->in;
533       const struct map_out *out = &m->out;
534       bool match;
535       
536       switch (in->type)
537         {
538         case MAP_SINGLE:
539           match = value == in->x.f;
540           break;
541         case MAP_MISSING:
542           match = mv_is_num_user_missing (&v->miss, value);
543           break;
544         case MAP_RANGE:
545           match = value >= in->x.f && value <= in->y.f;
546           break;
547         case MAP_ELSE:
548           match = true;
549           break;
550         default:
551           abort ();
552         }
553
554       if (match)
555         return out;
556     }
557
558   return NULL;
559 }
560
561 /* Returns the output mapping in TRNS for an input of VALUE with
562    the given WIDTH, or a null pointer if there is no mapping. */
563 static const struct map_out *
564 find_src_string (struct recode_trns *trns, const char *value, int width)
565 {
566   struct mapping *m;
567
568   for (m = trns->mappings; m < trns->mappings + trns->map_cnt; m++)
569     {
570       const struct map_in *in = &m->in;
571       struct map_out *out = &m->out;
572       bool match;
573       
574       switch (in->type)
575         {
576         case MAP_SINGLE:
577           match = !memcmp (value, in->x.c, width);
578           break;
579         case MAP_ELSE:
580           match = true;
581           break;
582         case MAP_CONVERT:
583           {
584             struct data_in di;
585
586             di.s = value;
587             di.e = value + width;
588             di.v = &out->value;
589             di.flags = DI_IGNORE_ERROR;
590             di.f1 = di.f2 = 0;
591             di.format.type = FMT_F;
592             di.format.w = width;
593             di.format.d = 0;
594             match = data_in (&di);
595             break;
596           }
597         default:
598           abort ();
599         }
600
601       if (match)
602         return out;
603     }
604
605   return NULL;
606 }
607
608 /* Performs RECODE transformation. */
609 static int
610 recode_trns_proc (void *trns_, struct ccase *c, int case_idx UNUSED)
611 {
612   struct recode_trns *trns = trns_;
613   size_t i;
614
615   for (i = 0; i < trns->var_cnt; i++) 
616     {
617       struct variable *src_var = trns->src_vars[i];
618       struct variable *dst_var = trns->dst_vars[i];
619
620       const union value *src_data = case_data (c, src_var->fv);
621       union value *dst_data = case_data_rw (c, dst_var->fv);
622
623       const struct map_out *out;
624
625       if (trns->src_type == NUMERIC) 
626           out = find_src_numeric (trns, src_data->f, src_var);
627       else
628           out = find_src_string (trns, src_data->s, src_var->width);
629
630       if (trns->dst_type == NUMERIC) 
631         {
632           if (out != NULL)
633             dst_data->f = !out->copy_input ? out->value.f : src_data->f; 
634           else if (trns->src_vars != trns->dst_vars)
635             dst_data->f = SYSMIS;
636         }
637       else 
638         {
639           if (out != NULL)
640             {
641               if (!out->copy_input) 
642                 memcpy (dst_data->s, out->value.c, dst_var->width); 
643               else if (trns->src_vars != trns->dst_vars)
644                 buf_copy_rpad (dst_data->s, dst_var->width,
645                                src_data->s, src_var->width); 
646             }
647           else if (trns->src_vars != trns->dst_vars)
648             memset (dst_data->s, ' ', dst_var->width);
649         }
650     }
651
652   return TRNS_CONTINUE;
653 }
654
655 /* Frees a RECODE transformation. */
656 static bool
657 recode_trns_free (void *trns_)
658 {
659   struct recode_trns *trns = trns_;
660   pool_destroy (trns->pool);
661   return true;
662 }