Patch #5244.
[pspp-builds.git] / src / language / lexer / variable-parser.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 <language/lexer/variable-parser.h>
23
24 #include <ctype.h>
25 #include <stdbool.h>
26 #include <stdlib.h>
27
28 #include "lexer.h"
29 #include <data/dictionary.h>
30 #include <data/procedure.h>
31 #include <data/variable.h>
32 #include <libpspp/alloc.h>
33 #include <libpspp/bit-vector.h>
34 #include <libpspp/hash.h>
35 #include <libpspp/message.h>
36 #include <libpspp/misc.h>
37 #include <libpspp/pool.h>
38 #include <libpspp/str.h>
39
40 #include "gettext.h"
41 #define _(msgid) gettext (msgid)
42
43 /* Parses a name as a variable within VS.  Sets *IDX to the
44    variable's index and returns true if successful.  On failure
45    emits an error message and returns false. */
46 static bool
47 parse_vs_variable_idx (const struct var_set *vs, size_t *idx)
48 {
49   assert (idx != NULL);
50   
51   if (token != T_ID)
52     {
53       lex_error (_("expecting variable name"));
54       return false;
55     }
56   else if (var_set_lookup_var_idx (vs, tokid, idx)) 
57     {
58       lex_get ();
59       return true;
60     }
61   else 
62     {
63       msg (SE, _("%s is not a variable name."), tokid);
64       return false;
65     }
66 }
67
68 /* Parses a name as a variable within VS and returns the variable
69    if successful.  On failure emits an error message and returns
70    a null pointer. */
71 static struct variable *
72 parse_vs_variable (const struct var_set *vs)
73 {
74   size_t idx;
75   return parse_vs_variable_idx (vs, &idx) ? var_set_get_var (vs, idx) : NULL;
76 }
77
78 /* Parses a variable name in dictionary D and returns the
79    variable if successful.  On failure emits an error message and
80    returns a null pointer. */
81 struct variable *
82 parse_dict_variable (const struct dictionary *d) 
83 {
84   struct var_set *vs = var_set_create_from_dict (d);
85   struct variable *var = parse_vs_variable (vs);
86   var_set_destroy (vs);
87   return var;
88 }
89
90 /* Parses a variable name in default_dict and returns the
91    variable if successful.  On failure emits an error message and
92    returns a null pointer. */
93 struct variable *
94 parse_variable (void)
95 {
96   return parse_dict_variable (default_dict);
97 }
98
99
100 /* Parses a set of variables from dictionary D given options
101    OPTS.  Resulting list of variables stored in *VAR and the
102    number of variables into *CNT.  Returns nonzero only if
103    successful. */
104 int
105 parse_variables (const struct dictionary *d, struct variable ***var,
106                  size_t *cnt, int opts) 
107 {
108   struct var_set *vs;
109   int success;
110
111   assert (d != NULL);
112   assert (var != NULL);
113   assert (cnt != NULL);
114
115   vs = var_set_create_from_dict (d);
116   success = parse_var_set_vars (vs, var, cnt, opts);
117   if ( success == 0 )
118      free ( *var ) ;
119   var_set_destroy (vs);
120   return success;
121 }
122
123 /* Parses a set of variables from dictionary D given options
124    OPTS.  Resulting list of variables stored in *VARS and the
125    number of variables into *VAR_CNT.  Returns nonzero only if
126    successful.  Same behavior as parse_variables, except that all
127    allocations are taken from the given POOL. */
128 int
129 parse_variables_pool (struct pool *pool, const struct dictionary *dict,
130                       struct variable ***vars, size_t *var_cnt, int opts) 
131 {
132   int retval;
133
134   /* PV_APPEND is unsafe because parse_variables would free the
135      existing names on failure, but those names are presumably
136      already in the pool, which would attempt to re-free it
137      later. */
138   assert (!(opts & PV_APPEND));
139   
140   retval = parse_variables (dict, vars, var_cnt, opts);
141   if (retval)
142     pool_register (pool, free, *vars);
143   return retval;
144 }
145
146 /* Parses a variable name from VS.  If successful, sets *IDX to
147    the variable's index in VS, *CLASS to the variable's
148    dictionary class, and returns nonzero.  Returns zero on
149    failure. */
150 static int
151 parse_var_idx_class (const struct var_set *vs, size_t *idx,
152                      enum dict_class *class)
153 {
154   if (!parse_vs_variable_idx (vs, idx))
155     return 0;
156
157   *class = dict_class_from_id (var_set_get_var (vs, *idx)->name);
158   return 1;
159 }
160
161 /* Add the variable from VS with index IDX to the list of
162    variables V that has *NV elements and room for *MV.
163    Uses and updates INCLUDED to avoid duplicates if indicated by
164    PV_OPTS, which also affects what variables are allowed in
165    appropriate ways. */
166 static void
167 add_variable (struct variable ***v, size_t *nv, size_t *mv,
168               char *included, int pv_opts,
169               const struct var_set *vs, size_t idx)
170 {
171   struct variable *add = var_set_get_var (vs, idx);
172
173   if ((pv_opts & PV_NUMERIC) && add->type != NUMERIC) 
174     msg (SW, _("%s is not a numeric variable.  It will not be "
175                "included in the variable list."), add->name);
176   else if ((pv_opts & PV_STRING) && add->type != ALPHA) 
177     msg (SE, _("%s is not a string variable.  It will not be "
178                "included in the variable list."), add->name);
179   else if ((pv_opts & PV_NO_SCRATCH)
180            && dict_class_from_id (add->name) == DC_SCRATCH)
181     msg (SE, _("Scratch variables (such as %s) are not allowed "
182                "here."), add->name);
183   else if ((pv_opts & PV_SAME_TYPE) && *nv && add->type != (*v)[0]->type) 
184     msg (SE, _("%s and %s are not the same type.  All variables in "
185                "this variable list must be of the same type.  %s "
186                "will be omitted from list."),
187          (*v)[0]->name, add->name, add->name);
188   else if ((pv_opts & PV_NO_DUPLICATE) && included[idx]) 
189     msg (SE, _("Variable %s appears twice in variable list."), add->name);
190   else if ((pv_opts & PV_DUPLICATE) || !included[idx])
191     {
192       if (*nv >= *mv)
193         {
194           *mv = 2 * (*nv + 1);
195           *v = xnrealloc (*v, *mv, sizeof **v);
196         }
197       (*v)[(*nv)++] = add;
198       if (included != NULL)
199         included[idx] = 1;
200     }
201 }
202
203 /* Adds the variables in VS with indexes FIRST_IDX through
204    LAST_IDX, inclusive, to the list of variables V that has *NV
205    elements and room for *MV.  Uses and updates INCLUDED to avoid
206    duplicates if indicated by PV_OPTS, which also affects what
207    variables are allowed in appropriate ways. */
208 static void
209 add_variables (struct variable ***v, size_t *nv, size_t *mv, char *included,
210                int pv_opts,
211                const struct var_set *vs, int first_idx, int last_idx,
212                enum dict_class class) 
213 {
214   size_t i;
215   
216   for (i = first_idx; i <= last_idx; i++)
217     if (dict_class_from_id (var_set_get_var (vs, i)->name) == class)
218       add_variable (v, nv, mv, included, pv_opts, vs, i);
219 }
220
221 /* Note that if parse_variables() returns 0, *v is free()'d.
222    Conversely, if parse_variables() returns non-zero, then *nv is
223    nonzero and *v is non-NULL. */
224 int
225 parse_var_set_vars (const struct var_set *vs, 
226                     struct variable ***v, size_t *nv,
227                     int pv_opts)
228 {
229   size_t mv;
230   char *included;
231
232   assert (vs != NULL);
233   assert (v != NULL);
234   assert (nv != NULL);
235
236   /* At most one of PV_NUMERIC, PV_STRING, PV_SAME_TYPE may be
237      specified. */
238   assert ((((pv_opts & PV_NUMERIC) != 0)
239            + ((pv_opts & PV_STRING) != 0)
240            + ((pv_opts & PV_SAME_TYPE) != 0)) <= 1);
241
242   /* PV_DUPLICATE and PV_NO_DUPLICATE are incompatible. */
243   assert (!(pv_opts & PV_DUPLICATE) || !(pv_opts & PV_NO_DUPLICATE));
244
245   if (!(pv_opts & PV_APPEND))
246     {
247       *v = NULL;
248       *nv = 0;
249       mv = 0;
250     }
251   else
252     mv = *nv;
253
254   if (!(pv_opts & PV_DUPLICATE))
255     {
256       size_t i;
257       
258       included = xcalloc (var_set_get_cnt (vs), sizeof *included);
259       for (i = 0; i < *nv; i++)
260         included[(*v)[i]->index] = 1;
261     }
262   else
263     included = NULL;
264
265   do
266     {
267       if (lex_match (T_ALL))
268         add_variables (v, nv, &mv, included, pv_opts,
269                        vs, 0, var_set_get_cnt (vs) - 1, DC_ORDINARY);
270       else 
271         {
272           enum dict_class class;
273           size_t first_idx;
274
275           if (!parse_var_idx_class (vs, &first_idx, &class))
276             goto fail;
277
278           if (!lex_match (T_TO))
279             add_variable (v, nv, &mv, included, pv_opts, vs, first_idx);
280           else 
281             {
282               size_t last_idx;
283               enum dict_class last_class;
284               struct variable *first_var, *last_var;
285
286               if (!parse_var_idx_class (vs, &last_idx, &last_class))
287                 goto fail;
288
289               first_var = var_set_get_var (vs, first_idx);
290               last_var = var_set_get_var (vs, last_idx);
291
292               if (last_idx < first_idx)
293                 {
294                   msg (SE, _("%s TO %s is not valid syntax since %s "
295                              "precedes %s in the dictionary."),
296                        first_var->name, last_var->name,
297                        first_var->name, last_var->name);
298                   goto fail;
299                 }
300
301               if (class != last_class)
302                 {
303                   msg (SE, _("When using the TO keyword to specify several "
304                              "variables, both variables must be from "
305                              "the same variable dictionaries, of either "
306                              "ordinary, scratch, or system variables.  "
307                              "%s is a %s variable, whereas %s is %s."),
308                        first_var->name, dict_class_to_name (class),
309                        last_var->name, dict_class_to_name (last_class));
310                   goto fail;
311                 }
312
313               add_variables (v, nv, &mv, included, pv_opts,
314                              vs, first_idx, last_idx, class);
315             } 
316         }
317
318       if (pv_opts & PV_SINGLE)
319         break;
320       lex_match (',');
321     }
322   while (token == T_ALL
323          || (token == T_ID && var_set_lookup_var (vs, tokid) != NULL));
324   
325   if (*nv == 0)
326     goto fail;
327
328   free (included);
329   return 1;
330
331 fail:
332   free (included);
333   free (*v);
334   *v = NULL;
335   *nv = 0;
336   return 0;
337 }
338
339 /* Extracts a numeric suffix from variable name S, copying it
340    into string R.  Sets *D to the length of R and *N to its
341    value. */
342 static int
343 extract_num (char *s, char *r, int *n, int *d)
344 {
345   char *cp;
346
347   /* Find first digit. */
348   cp = s + strlen (s) - 1;
349   while (isdigit ((unsigned char) *cp) && cp > s)
350     cp--;
351   cp++;
352
353   /* Extract root. */
354   strncpy (r, s, cp - s);
355   r[cp - s] = 0;
356
357   /* Count initial zeros. */
358   *n = *d = 0;
359   while (*cp == '0')
360     {
361       (*d)++;
362       cp++;
363     }
364
365   /* Extract value. */
366   while (isdigit ((unsigned char) *cp))
367     {
368       (*d)++;
369       *n = (*n * 10) + (*cp - '0');
370       cp++;
371     }
372
373   /* Sanity check. */
374   if (*n == 0 && *d == 0)
375     {
376       msg (SE, _("incorrect use of TO convention"));
377       return 0;
378     }
379   return 1;
380 }
381
382 /* Parses a list of variable names according to the DATA LIST version
383    of the TO convention.  */
384 int
385 parse_DATA_LIST_vars (char ***names, size_t *nnames, int pv_opts)
386 {
387   int n1, n2;
388   int d1, d2;
389   int n;
390   size_t nvar, mvar;
391   char name1[LONG_NAME_LEN + 1], name2[LONG_NAME_LEN + 1];
392   char root1[LONG_NAME_LEN + 1], root2[LONG_NAME_LEN + 1];
393   int success = 0;
394
395   assert (names != NULL);
396   assert (nnames != NULL);
397   assert ((pv_opts & ~(PV_APPEND | PV_SINGLE
398                        | PV_NO_SCRATCH | PV_NO_DUPLICATE)) == 0);
399   /* FIXME: PV_NO_DUPLICATE is not implemented. */
400
401   if (pv_opts & PV_APPEND)
402     nvar = mvar = *nnames;
403   else
404     {
405       nvar = mvar = 0;
406       *names = NULL;
407     }
408
409   do
410     {
411       if (token != T_ID)
412         {
413           lex_error ("expecting variable name");
414           goto fail;
415         }
416       if (dict_class_from_id (tokid) == DC_SCRATCH
417           && (pv_opts & PV_NO_SCRATCH))
418         {
419           msg (SE, _("Scratch variables not allowed here."));
420           goto fail;
421         }
422       strcpy (name1, tokid);
423       lex_get ();
424       if (token == T_TO)
425         {
426           lex_get ();
427           if (token != T_ID)
428             {
429               lex_error ("expecting variable name");
430               goto fail;
431             }
432           strcpy (name2, tokid);
433           lex_get ();
434
435           if (!extract_num (name1, root1, &n1, &d1)
436               || !extract_num (name2, root2, &n2, &d2))
437             goto fail;
438
439           if (strcasecmp (root1, root2))
440             {
441               msg (SE, _("Prefixes don't match in use of TO convention."));
442               goto fail;
443             }
444           if (n1 > n2)
445             {
446               msg (SE, _("Bad bounds in use of TO convention."));
447               goto fail;
448             }
449           if (d2 > d1)
450             d2 = d1;
451
452           if (mvar < nvar + (n2 - n1 + 1))
453             {
454               mvar += ROUND_UP (n2 - n1 + 1, 16);
455               *names = xnrealloc (*names, mvar, sizeof **names);
456             }
457
458           for (n = n1; n <= n2; n++)
459             {
460               char name[LONG_NAME_LEN + 1];
461               sprintf (name, "%s%0*d", root1, d1, n);
462               (*names)[nvar] = xstrdup (name);
463               nvar++;
464             }
465         }
466       else
467         {
468           if (nvar >= mvar)
469             {
470               mvar += 16;
471               *names = xnrealloc (*names, mvar, sizeof **names);
472             }
473           (*names)[nvar++] = xstrdup (name1);
474         }
475
476       lex_match (',');
477
478       if (pv_opts & PV_SINGLE)
479         break;
480     }
481   while (token == T_ID);
482   success = 1;
483
484 fail:
485   *nnames = nvar;
486   if (!success)
487     {
488       int i;
489       for (i = 0; i < nvar; i++)
490         free ((*names)[i]);
491       free (*names);
492       *names = NULL;
493       *nnames = 0;
494     }
495   return success;
496 }
497
498 /* Registers each of the NAMES[0...NNAMES - 1] in POOL, as well
499    as NAMES itself. */
500 static void
501 register_vars_pool (struct pool *pool, char **names, size_t nnames)
502 {
503   size_t i;
504
505   for (i = 0; i < nnames; i++)
506     pool_register (pool, free, names[i]);
507   pool_register (pool, free, names);
508 }
509
510 /* Parses a list of variable names according to the DATA LIST
511    version of the TO convention.  Same args as
512    parse_DATA_LIST_vars(), except that all allocations are taken
513    from the given POOL. */
514 int
515 parse_DATA_LIST_vars_pool (struct pool *pool,
516                            char ***names, size_t *nnames, int pv_opts)
517 {
518   int retval;
519
520   /* PV_APPEND is unsafe because parse_DATA_LIST_vars would free
521      the existing names on failure, but those names are
522      presumably already in the pool, which would attempt to
523      re-free it later. */
524   assert (!(pv_opts & PV_APPEND));
525   
526   retval = parse_DATA_LIST_vars (names, nnames, pv_opts);
527   if (retval)
528     register_vars_pool (pool, *names, *nnames);
529   return retval;
530 }
531
532 /* Parses a list of variables where some of the variables may be
533    existing and the rest are to be created.  Same args as
534    parse_DATA_LIST_vars(). */
535 int
536 parse_mixed_vars (char ***names, size_t *nnames, int pv_opts)
537 {
538   size_t i;
539
540   assert (names != NULL);
541   assert (nnames != NULL);
542   assert ((pv_opts & ~PV_APPEND) == 0);
543
544   if (!(pv_opts & PV_APPEND))
545     {
546       *names = NULL;
547       *nnames = 0;
548     }
549   while (token == T_ID || token == T_ALL)
550     {
551       if (token == T_ALL || dict_lookup_var (default_dict, tokid) != NULL)
552         {
553           struct variable **v;
554           size_t nv;
555
556           if (!parse_variables (default_dict, &v, &nv, PV_NONE))
557             goto fail;
558           *names = xnrealloc (*names, *nnames + nv, sizeof **names);
559           for (i = 0; i < nv; i++)
560             (*names)[*nnames + i] = xstrdup (v[i]->name);
561           free (v);
562           *nnames += nv;
563         }
564       else if (!parse_DATA_LIST_vars (names, nnames, PV_APPEND))
565         goto fail;
566     }
567   return 1;
568
569 fail:
570   for (i = 0; i < *nnames; i++)
571     free ((*names)[i]);
572   free (*names);
573   *names = NULL;
574   *nnames = 0;
575   return 0;
576 }
577
578 /* Parses a list of variables where some of the variables may be
579    existing and the rest are to be created.  Same args as
580    parse_mixed_vars(), except that all allocations are taken
581    from the given POOL. */
582 int
583 parse_mixed_vars_pool (struct pool *pool,
584                        char ***names, size_t *nnames, int pv_opts)
585 {
586   int retval;
587
588   /* PV_APPEND is unsafe because parse_mixed_vars_pool would free
589      the existing names on failure, but those names are
590      presumably already in the pool, which would attempt to
591      re-free it later. */
592   assert (!(pv_opts & PV_APPEND));
593
594   retval = parse_mixed_vars (names, nnames, pv_opts);
595   if (retval)
596     register_vars_pool (pool, *names, *nnames);
597   return retval;
598 }
599 \f
600 /* A set of variables. */
601 struct var_set 
602   {
603     size_t (*get_cnt) (const struct var_set *);
604     struct variable *(*get_var) (const struct var_set *, size_t idx);
605     bool (*lookup_var_idx) (const struct var_set *, const char *, size_t *);
606     void (*destroy) (struct var_set *);
607     void *aux;
608   };
609
610 /* Returns the number of variables in VS. */
611 size_t
612 var_set_get_cnt (const struct var_set *vs) 
613 {
614   assert (vs != NULL);
615
616   return vs->get_cnt (vs);
617 }
618
619 /* Return variable with index IDX in VS.
620    IDX must be less than the number of variables in VS. */
621 struct variable *
622 var_set_get_var (const struct var_set *vs, size_t idx) 
623 {
624   assert (vs != NULL);
625   assert (idx < var_set_get_cnt (vs));
626
627   return vs->get_var (vs, idx);
628 }
629
630 /* Returns the variable in VS named NAME, or a null pointer if VS
631    contains no variable with that name. */
632 struct variable *
633 var_set_lookup_var (const struct var_set *vs, const char *name) 
634 {
635   size_t idx;
636   return (var_set_lookup_var_idx (vs, name, &idx)
637           ? var_set_get_var (vs, idx)
638           : NULL);
639 }
640
641 /* If VS contains a variable named NAME, sets *IDX to its index
642    and returns true.  Otherwise, returns false. */
643 bool
644 var_set_lookup_var_idx (const struct var_set *vs, const char *name,
645                         size_t *idx)
646 {
647   assert (vs != NULL);
648   assert (name != NULL);
649   assert (strlen (name) <= LONG_NAME_LEN);
650
651   return vs->lookup_var_idx (vs, name, idx);
652 }
653
654 /* Destroys VS. */
655 void
656 var_set_destroy (struct var_set *vs) 
657 {
658   if (vs != NULL)
659     vs->destroy (vs);
660 }
661 \f
662 /* Returns the number of variables in VS. */
663 static size_t
664 dict_var_set_get_cnt (const struct var_set *vs) 
665 {
666   struct dictionary *d = vs->aux;
667
668   return dict_get_var_cnt (d);
669 }
670
671 /* Return variable with index IDX in VS.
672    IDX must be less than the number of variables in VS. */
673 static struct variable *
674 dict_var_set_get_var (const struct var_set *vs, size_t idx) 
675 {
676   struct dictionary *d = vs->aux;
677
678   return dict_get_var (d, idx);
679 }
680
681 /* If VS contains a variable named NAME, sets *IDX to its index
682    and returns true.  Otherwise, returns false. */
683 static bool
684 dict_var_set_lookup_var_idx (const struct var_set *vs, const char *name,
685                              size_t *idx) 
686 {
687   struct dictionary *d = vs->aux;
688   struct variable *v = dict_lookup_var (d, name);
689   if (v != NULL) 
690     {
691       *idx = v->index;
692       return true;
693     }
694   else
695     return false;
696 }
697
698 /* Destroys VS. */
699 static void
700 dict_var_set_destroy (struct var_set *vs) 
701 {
702   free (vs);
703 }
704
705 /* Returns a variable set based on D. */
706 struct var_set *
707 var_set_create_from_dict (const struct dictionary *d) 
708 {
709   struct var_set *vs = xmalloc (sizeof *vs);
710   vs->get_cnt = dict_var_set_get_cnt;
711   vs->get_var = dict_var_set_get_var;
712   vs->lookup_var_idx = dict_var_set_lookup_var_idx;
713   vs->destroy = dict_var_set_destroy;
714   vs->aux = (void *) d;
715   return vs;
716 }
717 \f
718 /* A variable set based on an array. */
719 struct array_var_set 
720   {
721     struct variable *const *var;/* Array of variables. */
722     size_t var_cnt;             /* Number of elements in var. */
723     struct hsh_table *name_tab; /* Hash from variable names to variables. */
724   };
725
726 /* Returns the number of variables in VS. */
727 static size_t
728 array_var_set_get_cnt (const struct var_set *vs) 
729 {
730   struct array_var_set *avs = vs->aux;
731
732   return avs->var_cnt;
733 }
734
735 /* Return variable with index IDX in VS.
736    IDX must be less than the number of variables in VS. */
737 static struct variable *
738 array_var_set_get_var (const struct var_set *vs, size_t idx) 
739 {
740   struct array_var_set *avs = vs->aux;
741
742   return (struct variable *) avs->var[idx];
743 }
744
745 /* If VS contains a variable named NAME, sets *IDX to its index
746    and returns true.  Otherwise, returns false. */
747 static bool
748 array_var_set_lookup_var_idx (const struct var_set *vs, const char *name,
749                               size_t *idx) 
750 {
751   struct array_var_set *avs = vs->aux;
752   struct variable v, *vp, *const *vpp;
753
754   strcpy (v.name, name);
755   vp = &v;
756   vpp = hsh_find (avs->name_tab, &vp);
757   if (vpp != NULL) 
758     {
759       *idx = vpp - avs->var;
760       return true;
761     }
762   else
763     return false;
764 }
765
766 /* Destroys VS. */
767 static void
768 array_var_set_destroy (struct var_set *vs) 
769 {
770   struct array_var_set *avs = vs->aux;
771
772   hsh_destroy (avs->name_tab);
773   free (avs);
774   free (vs);
775 }
776
777 /* Returns a variable set based on the VAR_CNT variables in
778    VAR. */
779 struct var_set *
780 var_set_create_from_array (struct variable *const *var, size_t var_cnt) 
781 {
782   struct var_set *vs;
783   struct array_var_set *avs;
784   size_t i;
785
786   vs = xmalloc (sizeof *vs);
787   vs->get_cnt = array_var_set_get_cnt;
788   vs->get_var = array_var_set_get_var;
789   vs->lookup_var_idx = array_var_set_lookup_var_idx;
790   vs->destroy = array_var_set_destroy;
791   vs->aux = avs = xmalloc (sizeof *avs);
792   avs->var = var;
793   avs->var_cnt = var_cnt;
794   avs->name_tab = hsh_create (2 * var_cnt,
795                               compare_var_ptr_names, hash_var_ptr_name, NULL,
796                               NULL);
797   for (i = 0; i < var_cnt; i++)
798     if (hsh_insert (avs->name_tab, (void *) &var[i]) != NULL) 
799       {
800         var_set_destroy (vs);
801         return NULL;
802       }
803   
804   return vs;
805 }