fbcc170d84b34b33f790a852a64853744f27e12c
[pspp-builds.git] / src / language / lexer / variable-parser.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 1997-9, 2000, 2009, 2010, 2011 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "language/lexer/variable-parser.h"
20
21 #include <ctype.h>
22 #include <limits.h>
23 #include <stdbool.h>
24 #include <stdlib.h>
25
26 #include "data/dictionary.h"
27 #include "data/procedure.h"
28 #include "data/variable.h"
29 #include "language/lexer/lexer.h"
30 #include "libpspp/assertion.h"
31 #include "libpspp/cast.h"
32 #include "libpspp/hash-functions.h"
33 #include "libpspp/hmapx.h"
34 #include "libpspp/message.h"
35 #include "libpspp/misc.h"
36 #include "libpspp/pool.h"
37 #include "libpspp/str.h"
38 #include "libpspp/stringi-set.h"
39
40 #include "gl/c-ctype.h"
41 #include "gl/xalloc.h"
42
43 #include "gettext.h"
44 #define _(msgid) gettext (msgid)
45
46 static struct variable * var_set_get_var (const struct var_set *, size_t );
47
48 static struct variable *var_set_lookup_var (const struct var_set *,
49                                             const char *);
50
51 static bool var_set_lookup_var_idx (const struct var_set *, const char *,
52                                     size_t *);
53
54
55
56 /* Parses a name as a variable within VS.  Sets *IDX to the
57    variable's index and returns true if successful.  On failure
58    emits an error message and returns false. */
59 static bool
60 parse_vs_variable_idx (struct lexer *lexer, const struct var_set *vs,
61                 size_t *idx)
62 {
63   assert (idx != NULL);
64
65   if (lex_token (lexer) != T_ID)
66     {
67       lex_error (lexer, _("expecting variable name"));
68       return false;
69     }
70   else if (var_set_lookup_var_idx (vs, lex_tokcstr (lexer), idx))
71     {
72       lex_get (lexer);
73       return true;
74     }
75   else
76     {
77       msg (SE, _("%s is not a variable name."), lex_tokcstr (lexer));
78       return false;
79     }
80 }
81
82 /* Parses a name as a variable within VS and returns the variable
83    if successful.  On failure emits an error message and returns
84    a null pointer. */
85 static struct variable *
86 parse_vs_variable (struct lexer *lexer, const struct var_set *vs)
87 {
88   size_t idx;
89   return parse_vs_variable_idx (lexer, vs, &idx) ? var_set_get_var (vs, idx) : NULL;
90 }
91
92 /* Parses a variable name in dictionary D and returns the
93    variable if successful.  On failure emits an error message and
94    returns a null pointer. */
95 struct variable *
96 parse_variable (struct lexer *lexer, const struct dictionary *d)
97 {
98   struct var_set *vs = var_set_create_from_dict (d);
99   struct variable *var = parse_vs_variable (lexer, vs);
100   var_set_destroy (vs);
101   return var;
102 }
103
104 /* Parses a set of variables from dictionary D given options
105    OPTS.  Resulting list of variables stored in *VAR and the
106    number of variables into *CNT.  Returns true only if
107    successful. */
108 bool
109 parse_variables (struct lexer *lexer, const struct dictionary *d,
110                         struct variable ***var,
111                         size_t *cnt, int opts)
112 {
113   struct var_set *vs;
114   int success;
115
116   assert (d != NULL);
117   assert (var != NULL);
118   assert (cnt != NULL);
119
120   vs = var_set_create_from_dict (d);
121   success = parse_var_set_vars (lexer, vs, var, cnt, opts);
122   var_set_destroy (vs);
123   return success;
124 }
125
126 /* Parses a set of variables from dictionary D given options
127    OPTS.  Resulting list of variables stored in *VARS and the
128    number of variables into *VAR_CNT.  Returns true only if
129    successful.  Same behavior as parse_variables, except that all
130    allocations are taken from the given POOL. */
131 bool
132 parse_variables_pool (struct lexer *lexer, struct pool *pool,
133                 const struct dictionary *dict,
134                 struct variable ***vars, size_t *var_cnt, int opts)
135 {
136   int retval;
137
138   /* PV_APPEND is unsafe because parse_variables would free the
139      existing names on failure, but those names are presumably
140      already in the pool, which would attempt to re-free it
141      later. */
142   assert (!(opts & PV_APPEND));
143
144   retval = parse_variables (lexer, dict, vars, var_cnt, opts);
145   if (retval)
146     pool_register (pool, free, *vars);
147   return retval;
148 }
149
150 /* Parses a variable name from VS.  If successful, sets *IDX to
151    the variable's index in VS, *CLASS to the variable's
152    dictionary class, and returns true.  Returns false on
153    failure. */
154 static bool
155 parse_var_idx_class (struct lexer *lexer, const struct var_set *vs,
156                         size_t *idx,
157                         enum dict_class *class)
158 {
159   if (!parse_vs_variable_idx (lexer, vs, idx))
160     return false;
161
162   *class = dict_class_from_id (var_get_name (var_set_get_var (vs, *idx)));
163   return true;
164 }
165
166 /* Add the variable from VS with index IDX to the list of
167    variables V that has *NV elements and room for *MV.
168    Uses and updates INCLUDED to avoid duplicates if indicated by
169    PV_OPTS, which also affects what variables are allowed in
170    appropriate ways. */
171 static void
172 add_variable (struct variable ***v, size_t *nv, size_t *mv,
173               char *included, int pv_opts,
174               const struct var_set *vs, size_t idx)
175 {
176   struct variable *add = var_set_get_var (vs, idx);
177   const char *add_name = var_get_name (add);
178
179   if ((pv_opts & PV_NUMERIC) && !var_is_numeric (add))
180     msg (SW, _("%s is not a numeric variable.  It will not be "
181                "included in the variable list."), add_name);
182   else if ((pv_opts & PV_STRING) && !var_is_alpha (add))
183     msg (SE, _("%s is not a string variable.  It will not be "
184                "included in the variable list."), add_name);
185   else if ((pv_opts & PV_NO_SCRATCH)
186            && dict_class_from_id (add_name) == DC_SCRATCH)
187     msg (SE, _("Scratch variables (such as %s) are not allowed "
188                "here."), add_name);
189   else if ((pv_opts & (PV_SAME_TYPE | PV_SAME_WIDTH)) && *nv
190            && var_get_type (add) != var_get_type ((*v)[0]))
191     msg (SE, _("%s and %s are not the same type.  All variables in "
192                "this variable list must be of the same type.  %s "
193                "will be omitted from the list."),
194          var_get_name ((*v)[0]), add_name, add_name);
195   else if ((pv_opts & PV_SAME_WIDTH) && *nv
196            && var_get_width (add) != var_get_width ((*v)[0]))
197     msg (SE, _("%s and %s are string variables with different widths.  "
198                "All variables in this variable list must have the "
199                "same width.  %s will be omitted from the list."),
200          var_get_name ((*v)[0]), add_name, add_name);
201   else if ((pv_opts & PV_NO_DUPLICATE) && included[idx])
202     msg (SE, _("Variable %s appears twice in variable list."), add_name);
203   else if ((pv_opts & PV_DUPLICATE) || !included[idx])
204     {
205       if (*nv >= *mv)
206         {
207           *mv = 2 * (*nv + 1);
208           *v = xnrealloc (*v, *mv, sizeof **v);
209         }
210       (*v)[(*nv)++] = add;
211       if (included != NULL)
212         included[idx] = 1;
213     }
214 }
215
216 /* Adds the variables in VS with indexes FIRST_IDX through
217    LAST_IDX, inclusive, to the list of variables V that has *NV
218    elements and room for *MV.  Uses and updates INCLUDED to avoid
219    duplicates if indicated by PV_OPTS, which also affects what
220    variables are allowed in appropriate ways. */
221 static void
222 add_variables (struct variable ***v, size_t *nv, size_t *mv, char *included,
223                int pv_opts,
224                const struct var_set *vs, int first_idx, int last_idx,
225                enum dict_class class)
226 {
227   size_t i;
228
229   for (i = first_idx; i <= last_idx; i++)
230     if (dict_class_from_id (var_get_name (var_set_get_var (vs, i))) == class)
231       add_variable (v, nv, mv, included, pv_opts, vs, i);
232 }
233
234 /* Note that if parse_variables() returns false, *v is free()'d.
235    Conversely, if parse_variables() returns true, then *nv is
236    nonzero and *v is non-NULL. */
237 bool
238 parse_var_set_vars (struct lexer *lexer, const struct var_set *vs,
239                     struct variable ***v, size_t *nv,
240                     int pv_opts)
241 {
242   size_t mv;
243   char *included;
244
245   assert (vs != NULL);
246   assert (v != NULL);
247   assert (nv != NULL);
248
249   /* At most one of PV_NUMERIC, PV_STRING, PV_SAME_TYPE,
250      PV_SAME_WIDTH may be specified. */
251   assert (((pv_opts & PV_NUMERIC) != 0)
252           + ((pv_opts & PV_STRING) != 0)
253           + ((pv_opts & PV_SAME_TYPE) != 0)
254           + ((pv_opts & PV_SAME_WIDTH) != 0) <= 1);
255
256   /* PV_DUPLICATE and PV_NO_DUPLICATE are incompatible. */
257   assert (!(pv_opts & PV_DUPLICATE) || !(pv_opts & PV_NO_DUPLICATE));
258
259   if (!(pv_opts & PV_APPEND))
260     {
261       *v = NULL;
262       *nv = 0;
263       mv = 0;
264     }
265   else
266     mv = *nv;
267
268   if (!(pv_opts & PV_DUPLICATE))
269     {
270       size_t i;
271
272       included = xcalloc (var_set_get_cnt (vs), sizeof *included);
273       for (i = 0; i < *nv; i++)
274         {
275           size_t index;
276           if (!var_set_lookup_var_idx (vs, var_get_name ((*v)[i]), &index))
277             NOT_REACHED ();
278           included[index] = 1;
279         }
280     }
281   else
282     included = NULL;
283
284   do
285     {
286       if (lex_match (lexer, T_ALL))
287         add_variables (v, nv, &mv, included, pv_opts,
288                        vs, 0, var_set_get_cnt (vs) - 1, DC_ORDINARY);
289       else
290         {
291           enum dict_class class;
292           size_t first_idx;
293
294           if (!parse_var_idx_class (lexer, vs, &first_idx, &class))
295             goto fail;
296
297           if (!lex_match (lexer, T_TO))
298             add_variable (v, nv, &mv, included, pv_opts, vs, first_idx);
299           else
300             {
301               size_t last_idx;
302               enum dict_class last_class;
303               struct variable *first_var, *last_var;
304
305               if (!parse_var_idx_class (lexer, vs, &last_idx, &last_class))
306                 goto fail;
307
308               first_var = var_set_get_var (vs, first_idx);
309               last_var = var_set_get_var (vs, last_idx);
310
311               if (last_idx < first_idx)
312                 {
313                   const char *first_name = var_get_name (first_var);
314                   const char *last_name = var_get_name (last_var);
315                   msg (SE, _("%s TO %s is not valid syntax since %s "
316                              "precedes %s in the dictionary."),
317                        first_name, last_name, first_name, last_name);
318                   goto fail;
319                 }
320
321               if (class != last_class)
322                 {
323                   msg (SE, _("When using the TO keyword to specify several "
324                              "variables, both variables must be from "
325                              "the same variable dictionaries, of either "
326                              "ordinary, scratch, or system variables.  "
327                              "%s is a %s variable, whereas %s is %s."),
328                        var_get_name (first_var), dict_class_to_name (class),
329                        var_get_name (last_var),
330                        dict_class_to_name (last_class));
331                   goto fail;
332                 }
333
334               add_variables (v, nv, &mv, included, pv_opts,
335                              vs, first_idx, last_idx, class);
336             }
337         }
338
339       if (pv_opts & PV_SINGLE)
340         break;
341       lex_match (lexer, T_COMMA);
342     }
343   while (lex_token (lexer) == T_ALL
344          || (lex_token (lexer) == T_ID && var_set_lookup_var (vs, lex_tokcstr (lexer)) != NULL));
345
346   if (*nv == 0)
347     goto fail;
348
349   free (included);
350   return 1;
351
352 fail:
353   free (included);
354   free (*v);
355   *v = NULL;
356   *nv = 0;
357   return 0;
358 }
359
360 /* Attempts to break UTF-8 encoded NAME into a root (whose contents are
361    arbitrary except that it does not end in a digit) followed by an integer
362    numeric suffix.  On success, stores the value of the suffix into *NUMBERP,
363    the number of digits in the suffix into *N_DIGITSP, and returns the number
364    of bytes in the root.  On failure, returns 0. */
365 static int
366 extract_numeric_suffix (const char *name,
367                         unsigned long int *numberp, int *n_digitsp)
368 {
369   size_t root_len, n_digits;
370   size_t i;
371
372   /* Count length of root. */
373   root_len = 1;                 /* Valid identifier never starts with digit. */
374   for (i = 1; name[i] != '\0'; i++)
375     if (!c_isdigit (name[i]))
376       root_len = i + 1;
377   n_digits = i - root_len;
378
379   if (n_digits == 0)
380     {
381       msg (SE, _("`%s' cannot be used with TO because it does not end in "
382                  "a digit."), name);
383       return 0;
384     }
385
386   *numberp = strtoull (name + root_len, NULL, 10);
387   if (*numberp == ULONG_MAX)
388     {
389       msg (SE, _("Numeric suffix on `%s' is larger than supported with TO."),
390            name);
391       return 0;
392     }
393   *n_digitsp = n_digits;
394   return root_len;
395 }
396
397 static bool
398 add_var_name (char *name,
399               char ***names, size_t *n_vars, size_t *allocated_vars,
400               struct stringi_set *set, int pv_opts)
401 {
402   if (pv_opts & PV_NO_DUPLICATE && !stringi_set_insert (set, name))
403     {
404       msg (SE, _("Variable %s appears twice in variable list."),
405            name);
406       return false;
407     }
408
409   if (*n_vars >= *allocated_vars)
410     *names = x2nrealloc (*names, allocated_vars, sizeof **names);
411   (*names)[(*n_vars)++] = name;
412   return true;
413 }
414
415 /* Parses a list of variable names according to the DATA LIST version
416    of the TO convention.  */
417 bool
418 parse_DATA_LIST_vars (struct lexer *lexer, const struct dictionary *dict,
419                       char ***namesp, size_t *n_varsp, int pv_opts)
420 {
421   char **names;
422   size_t n_vars;
423   size_t allocated_vars;
424
425   struct stringi_set set;
426
427   char *name1 = NULL;
428   char *name2 = NULL;
429   bool ok = false;
430
431   assert ((pv_opts & ~(PV_APPEND | PV_SINGLE
432                        | PV_NO_SCRATCH | PV_NO_DUPLICATE)) == 0);
433   stringi_set_init (&set);
434
435   if (pv_opts & PV_APPEND)
436     {
437       n_vars = allocated_vars = *n_varsp;
438       names = *namesp;
439
440       if (pv_opts & PV_NO_DUPLICATE)
441         {
442           size_t i;
443
444           for (i = 0; i < n_vars; i++)
445             stringi_set_insert (&set, names[i]);
446         }
447     }
448   else
449     {
450       n_vars = allocated_vars = 0;
451       names = NULL;
452     }
453
454   do
455     {
456       if (lex_token (lexer) != T_ID
457           || !dict_id_is_valid (dict, lex_tokcstr (lexer), true))
458         {
459           lex_error (lexer, "expecting variable name");
460           goto exit;
461         }
462       if (dict_class_from_id (lex_tokcstr (lexer)) == DC_SCRATCH
463           && (pv_opts & PV_NO_SCRATCH))
464         {
465           msg (SE, _("Scratch variables not allowed here."));
466           goto exit;
467         }
468       name1 = xstrdup (lex_tokcstr (lexer));
469       lex_get (lexer);
470       if (lex_token (lexer) == T_TO)
471         {
472           unsigned long int num1, num2;
473           int n_digits1, n_digits2;
474           int root_len1, root_len2;
475           unsigned long int number;
476
477           lex_get (lexer);
478           if (lex_token (lexer) != T_ID
479               || !dict_id_is_valid (dict, lex_tokcstr (lexer), true))
480             {
481               lex_error (lexer, "expecting variable name");
482               goto exit;
483             }
484           name2 = xstrdup (lex_tokcstr (lexer));
485           lex_get (lexer);
486
487           root_len1 = extract_numeric_suffix (name1, &num1, &n_digits1);
488           if (root_len1 == 0)
489             goto exit;
490
491           root_len2 = extract_numeric_suffix (name2, &num2, &n_digits2);
492           if (root_len2 == 0)
493             goto exit;
494
495           if (root_len1 != root_len2 || memcasecmp (name1, name2, root_len1))
496             {
497               msg (SE, _("Prefixes don't match in use of TO convention."));
498               goto exit;
499             }
500           if (num1 > num2)
501             {
502               msg (SE, _("Bad bounds in use of TO convention."));
503               goto exit;
504             }
505
506           for (number = num1; number <= num2; number++)
507             {
508               char *name = xasprintf ("%.*s%0*lu",
509                                       root_len1, name1,
510                                       n_digits1, number);
511               if (!add_var_name (name, &names, &n_vars, &allocated_vars,
512                                  &set, pv_opts))
513                 {
514                   free (name);
515                   goto exit;
516                 }
517             }
518
519           free (name1);
520           name1 = NULL;
521           free (name2);
522           name2 = NULL;
523         }
524       else
525         {
526           if (!add_var_name (name1, &names, &n_vars, &allocated_vars,
527                              &set, pv_opts))
528             goto exit;
529           name1 = NULL;
530         }
531
532       lex_match (lexer, T_COMMA);
533
534       if (pv_opts & PV_SINGLE)
535         break;
536     }
537   while (lex_token (lexer) == T_ID);
538   ok = true;
539
540 exit:
541   stringi_set_destroy (&set);
542   if (ok)
543     {
544       *namesp = names;
545       *n_varsp = n_vars;
546     }
547   else
548     {
549       int i;
550       for (i = 0; i < n_vars; i++)
551         free (names[i]);
552       free (names);
553       *namesp = NULL;
554       *n_varsp = 0;
555
556       free (name1);
557       free (name2);
558     }
559   return ok;
560 }
561
562 /* Registers each of the NAMES[0...NNAMES - 1] in POOL, as well
563    as NAMES itself. */
564 static void
565 register_vars_pool (struct pool *pool, char **names, size_t nnames)
566 {
567   size_t i;
568
569   for (i = 0; i < nnames; i++)
570     pool_register (pool, free, names[i]);
571   pool_register (pool, free, names);
572 }
573
574 /* Parses a list of variable names according to the DATA LIST
575    version of the TO convention.  Same args as
576    parse_DATA_LIST_vars(), except that all allocations are taken
577    from the given POOL. */
578 bool
579 parse_DATA_LIST_vars_pool (struct lexer *lexer, const struct dictionary *dict,
580                            struct pool *pool,
581                            char ***names, size_t *nnames, int pv_opts)
582 {
583   int retval;
584
585   /* PV_APPEND is unsafe because parse_DATA_LIST_vars would free
586      the existing names on failure, but those names are
587      presumably already in the pool, which would attempt to
588      re-free it later. */
589   assert (!(pv_opts & PV_APPEND));
590
591   retval = parse_DATA_LIST_vars (lexer, dict, names, nnames, pv_opts);
592   if (retval)
593     register_vars_pool (pool, *names, *nnames);
594   return retval;
595 }
596
597 /* Parses a list of variables where some of the variables may be
598    existing and the rest are to be created.  Same args as
599    parse_DATA_LIST_vars(). */
600 bool
601 parse_mixed_vars (struct lexer *lexer, const struct dictionary *dict,
602                   char ***names, size_t *nnames, int pv_opts)
603 {
604   size_t i;
605
606   assert (names != NULL);
607   assert (nnames != NULL);
608   assert ((pv_opts & ~PV_APPEND) == 0);
609
610   if (!(pv_opts & PV_APPEND))
611     {
612       *names = NULL;
613       *nnames = 0;
614     }
615   while (lex_token (lexer) == T_ID || lex_token (lexer) == T_ALL)
616     {
617       if (lex_token (lexer) == T_ALL || dict_lookup_var (dict, lex_tokcstr (lexer)) != NULL)
618         {
619           struct variable **v;
620           size_t nv;
621
622           if (!parse_variables (lexer, dict, &v, &nv, PV_NONE))
623             goto fail;
624           *names = xnrealloc (*names, *nnames + nv, sizeof **names);
625           for (i = 0; i < nv; i++)
626             (*names)[*nnames + i] = xstrdup (var_get_name (v[i]));
627           free (v);
628           *nnames += nv;
629         }
630       else if (!parse_DATA_LIST_vars (lexer, dict, names, nnames, PV_APPEND))
631         goto fail;
632     }
633   return 1;
634
635 fail:
636   for (i = 0; i < *nnames; i++)
637     free ((*names)[i]);
638   free (*names);
639   *names = NULL;
640   *nnames = 0;
641   return 0;
642 }
643
644 /* Parses a list of variables where some of the variables may be
645    existing and the rest are to be created.  Same args as
646    parse_mixed_vars(), except that all allocations are taken
647    from the given POOL. */
648 bool
649 parse_mixed_vars_pool (struct lexer *lexer, const struct dictionary *dict, struct pool *pool,
650                        char ***names, size_t *nnames, int pv_opts)
651 {
652   int retval;
653
654   /* PV_APPEND is unsafe because parse_mixed_vars_pool would free
655      the existing names on failure, but those names are
656      presumably already in the pool, which would attempt to
657      re-free it later. */
658   assert (!(pv_opts & PV_APPEND));
659
660   retval = parse_mixed_vars (lexer, dict, names, nnames, pv_opts);
661   if (retval)
662     register_vars_pool (pool, *names, *nnames);
663   return retval;
664 }
665 \f
666 /* A set of variables. */
667 struct var_set
668   {
669     size_t (*get_cnt) (const struct var_set *);
670     struct variable *(*get_var) (const struct var_set *, size_t idx);
671     bool (*lookup_var_idx) (const struct var_set *, const char *, size_t *);
672     void (*destroy) (struct var_set *);
673     void *aux;
674   };
675
676 /* Returns the number of variables in VS. */
677 size_t
678 var_set_get_cnt (const struct var_set *vs)
679 {
680   assert (vs != NULL);
681
682   return vs->get_cnt (vs);
683 }
684
685 /* Return variable with index IDX in VS.
686    IDX must be less than the number of variables in VS. */
687 static struct variable *
688 var_set_get_var (const struct var_set *vs, size_t idx)
689 {
690   assert (vs != NULL);
691   assert (idx < var_set_get_cnt (vs));
692
693   return vs->get_var (vs, idx);
694 }
695
696 /* Returns the variable in VS named NAME, or a null pointer if VS
697    contains no variable with that name. */
698 struct variable *
699 var_set_lookup_var (const struct var_set *vs, const char *name)
700 {
701   size_t idx;
702   return (var_set_lookup_var_idx (vs, name, &idx)
703           ? var_set_get_var (vs, idx)
704           : NULL);
705 }
706
707 /* If VS contains a variable named NAME, sets *IDX to its index
708    and returns true.  Otherwise, returns false. */
709 bool
710 var_set_lookup_var_idx (const struct var_set *vs, const char *name,
711                         size_t *idx)
712 {
713   assert (vs != NULL);
714   assert (name != NULL);
715
716   return vs->lookup_var_idx (vs, name, idx);
717 }
718
719 /* Destroys VS. */
720 void
721 var_set_destroy (struct var_set *vs)
722 {
723   if (vs != NULL)
724     vs->destroy (vs);
725 }
726 \f
727 /* Returns the number of variables in VS. */
728 static size_t
729 dict_var_set_get_cnt (const struct var_set *vs)
730 {
731   struct dictionary *d = vs->aux;
732
733   return dict_get_var_cnt (d);
734 }
735
736 /* Return variable with index IDX in VS.
737    IDX must be less than the number of variables in VS. */
738 static struct variable *
739 dict_var_set_get_var (const struct var_set *vs, size_t idx)
740 {
741   struct dictionary *d = vs->aux;
742
743   return dict_get_var (d, idx);
744 }
745
746 /* If VS contains a variable named NAME, sets *IDX to its index
747    and returns true.  Otherwise, returns false. */
748 static bool
749 dict_var_set_lookup_var_idx (const struct var_set *vs, const char *name,
750                              size_t *idx)
751 {
752   struct dictionary *d = vs->aux;
753   struct variable *v = dict_lookup_var (d, name);
754   if (v != NULL)
755     {
756       *idx = var_get_dict_index (v);
757       return true;
758     }
759   else
760     return false;
761 }
762
763 /* Destroys VS. */
764 static void
765 dict_var_set_destroy (struct var_set *vs)
766 {
767   free (vs);
768 }
769
770 /* Returns a variable set based on D. */
771 struct var_set *
772 var_set_create_from_dict (const struct dictionary *d)
773 {
774   struct var_set *vs = xmalloc (sizeof *vs);
775   vs->get_cnt = dict_var_set_get_cnt;
776   vs->get_var = dict_var_set_get_var;
777   vs->lookup_var_idx = dict_var_set_lookup_var_idx;
778   vs->destroy = dict_var_set_destroy;
779   vs->aux = (void *) d;
780   return vs;
781 }
782 \f
783 /* A variable set based on an array. */
784 struct array_var_set
785   {
786     struct variable *const *var;/* Array of variables. */
787     size_t var_cnt;             /* Number of elements in var. */
788     struct hmapx vars_by_name;  /* Variables hashed by name. */
789   };
790
791 /* Returns the number of variables in VS. */
792 static size_t
793 array_var_set_get_cnt (const struct var_set *vs)
794 {
795   struct array_var_set *avs = vs->aux;
796
797   return avs->var_cnt;
798 }
799
800 /* Return variable with index IDX in VS.
801    IDX must be less than the number of variables in VS. */
802 static struct variable *
803 array_var_set_get_var (const struct var_set *vs, size_t idx)
804 {
805   struct array_var_set *avs = vs->aux;
806
807   return CONST_CAST (struct variable *, avs->var[idx]);
808 }
809
810 /* If VS contains a variable named NAME, sets *IDX to its index
811    and returns true.  Otherwise, returns false. */
812 static bool
813 array_var_set_lookup_var_idx (const struct var_set *vs, const char *name,
814                               size_t *idx)
815 {
816   struct array_var_set *avs = vs->aux;
817   struct hmapx_node *node;
818   struct variable **varp;
819
820   HMAPX_FOR_EACH_WITH_HASH (varp, node, hash_case_string (name, 0),
821                             &avs->vars_by_name)
822     if (!strcasecmp (name, var_get_name (*varp)))
823       {
824         *idx = varp - avs->var;
825         return true;
826       }
827
828   return false;
829 }
830
831 /* Destroys VS. */
832 static void
833 array_var_set_destroy (struct var_set *vs)
834 {
835   struct array_var_set *avs = vs->aux;
836
837   hmapx_destroy (&avs->vars_by_name);
838   free (avs);
839   free (vs);
840 }
841
842 /* Returns a variable set based on the VAR_CNT variables in VAR. */
843 struct var_set *
844 var_set_create_from_array (struct variable *const *var, size_t var_cnt)
845 {
846   struct var_set *vs;
847   struct array_var_set *avs;
848   size_t i;
849
850   vs = xmalloc (sizeof *vs);
851   vs->get_cnt = array_var_set_get_cnt;
852   vs->get_var = array_var_set_get_var;
853   vs->lookup_var_idx = array_var_set_lookup_var_idx;
854   vs->destroy = array_var_set_destroy;
855   vs->aux = avs = xmalloc (sizeof *avs);
856   avs->var = var;
857   avs->var_cnt = var_cnt;
858   hmapx_init (&avs->vars_by_name);
859   for (i = 0; i < var_cnt; i++)
860     {
861       const char *name = var_get_name (var[i]);
862       size_t idx;
863
864       if (array_var_set_lookup_var_idx (vs, name, &idx))
865         {
866           var_set_destroy (vs);
867           return NULL;
868         }
869       hmapx_insert (&avs->vars_by_name, CONST_CAST (void *, &avs->var[i]),
870                     hash_case_string (name, 0));
871     }
872
873   return vs;
874 }
875