d812c12fc70b352328c4f9db695b640659ccf8ac
[pspp-builds.git] / src / language / control / repeat.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 "repeat.h"
23
24 #include <ctype.h>
25 #include <math.h>
26 #include <stdlib.h>
27
28 #include <data/dictionary.h>
29 #include <data/procedure.h>
30 #include <data/settings.h>
31 #include <language/command.h>
32 #include <language/lexer/lexer.h>
33 #include <language/lexer/variable-parser.h>
34 #include <language/line-buffer.h>
35 #include <libpspp/alloc.h>
36 #include <libpspp/message.h>
37 #include <libpspp/message.h>
38 #include <libpspp/misc.h>
39 #include <libpspp/pool.h>
40 #include <libpspp/str.h>
41 #include <data/variable.h>
42
43 #include "intprops.h"
44
45 #include "gettext.h"
46 #define _(msgid) gettext (msgid)
47
48 /* Defines a list of lines used by DO REPEAT. */
49 struct line_list
50   {
51     struct line_list *next;     /* Next line. */
52     char *file_name;            /* File name. */
53     int line_number;            /* Line number. */
54     char *line;                 /* Contents. */
55   };
56
57 /* The type of substitution made for a DO REPEAT macro. */
58 enum repeat_entry_type 
59   {
60     VAR_NAMES,
61     OTHER
62   };
63
64 /* Describes one DO REPEAT macro. */
65 struct repeat_entry
66   {
67     struct repeat_entry *next;          /* Next entry. */
68     enum repeat_entry_type type;        /* Types of replacements. */
69     char id[LONG_NAME_LEN + 1];         /* Macro identifier. */
70     char **replacement;                 /* Macro replacement. */
71   };
72
73 /* A DO REPEAT...END REPEAT block. */
74 struct repeat_block 
75   {
76     struct pool *pool;                  /* Pool used for storage. */
77     struct dataset *ds;                 /* The dataset for this block */
78     struct line_list *first_line;       /* First line in line buffer. */
79     struct line_list *cur_line;         /* Current line in line buffer. */
80     int loop_cnt;                       /* Number of loops. */
81     int loop_idx;                       /* Number of loops so far. */
82     struct repeat_entry *macros;        /* Pointer to macro table. */
83     bool print;                         /* Print lines as executed? */
84   };
85
86 static bool parse_specification (struct repeat_block *);
87 static bool parse_lines (struct repeat_block *);
88 static void create_vars (struct repeat_block *);
89
90 static int parse_ids (const struct dictionary *dict, struct repeat_entry *, struct pool *);
91 static int parse_numbers (struct repeat_entry *, struct pool *);
92 static int parse_strings (struct repeat_entry *, struct pool *);
93
94 static void do_repeat_filter (struct string *line, void *block);
95 static bool do_repeat_read (struct string *line, char **file_name,
96                             int *line_number, void *block);
97 static void do_repeat_close (void *block);
98
99 int
100 cmd_do_repeat (struct dataset *ds)
101 {
102   struct repeat_block *block;
103
104   block = pool_create_container (struct repeat_block, pool);
105   block->ds = ds;
106
107   if (!parse_specification (block) || !parse_lines (block))
108     goto error;
109   
110   create_vars (block);
111   
112   block->cur_line = NULL;
113   block->loop_idx = -1;
114   getl_include_filter (do_repeat_filter, do_repeat_close, block);
115   getl_include_function (do_repeat_read, NULL, block);
116
117   return CMD_SUCCESS;
118
119  error:
120   pool_destroy (block->pool);
121   return CMD_CASCADING_FAILURE;
122 }
123
124 /* Parses the whole DO REPEAT command specification.
125    Returns success. */
126 static bool
127 parse_specification (struct repeat_block *block) 
128 {
129   char first_name[LONG_NAME_LEN + 1];
130
131   block->loop_cnt = 0;
132   block->macros = NULL;
133   do
134     {
135       struct repeat_entry *e;
136       struct repeat_entry *iter;
137       struct dictionary *dict = dataset_dict (block->ds);
138       int count;
139
140       /* Get a stand-in variable name and make sure it's unique. */
141       if (!lex_force_id ())
142         return false;
143       if (dict_lookup_var (dict, tokid))
144         msg (SW, _("Dummy variable name \"%s\" hides dictionary "
145                    "variable \"%s\"."),
146              tokid, tokid);
147       for (iter = block->macros; iter != NULL; iter = iter->next)
148         if (!strcasecmp (iter->id, tokid))
149           {
150             msg (SE, _("Dummy variable name \"%s\" is given twice."), tokid);
151             return false;
152           }
153
154       /* Make a new stand-in variable entry and link it into the
155          list. */
156       e = pool_alloc (block->pool, sizeof *e);
157       e->next = block->macros;
158       strcpy (e->id, tokid);
159       block->macros = e;
160
161       /* Skip equals sign. */
162       lex_get ();
163       if (!lex_force_match ('='))
164         return false;
165
166       /* Get the details of the variable's possible values. */
167       if (token == T_ID)
168         count = parse_ids (dict, e, block->pool);
169       else if (lex_is_number ())
170         count = parse_numbers (e, block->pool);
171       else if (token == T_STRING)
172         count = parse_strings (e, block->pool);
173       else
174         {
175           lex_error (NULL);
176           return false;
177         }
178       if (count == 0)
179         return false;
180       if (token != '/' && token != '.') 
181         {
182           lex_error (NULL);
183           return false;
184         }
185
186       /* If this is the first variable then it defines how many
187          replacements there must be; otherwise enforce this number of
188          replacements. */
189       if (block->loop_cnt == 0)
190         {
191           block->loop_cnt = count;
192           strcpy (first_name, e->id);
193         }
194       else if (block->loop_cnt != count)
195         {
196           msg (SE, _("Dummy variable \"%s\" had %d "
197                      "substitutions, so \"%s\" must also, but %d "
198                      "were specified."),
199                first_name, block->loop_cnt, e->id, count);
200           return false;
201         }
202
203       lex_match ('/');
204     }
205   while (token != '.');
206
207   return true;
208 }
209
210 /* If KEYWORD appears beginning at CP, possibly preceded by white
211    space, returns a pointer to the character just after the
212    keyword.  Otherwise, returns a null pointer. */
213 static const char *
214 recognize_keyword (const char *cp, const char *keyword)
215 {
216   const char *end;
217
218   while (isspace ((unsigned char) *cp))
219     cp++;
220
221   end = lex_skip_identifier (cp);
222   if (end != cp
223       && lex_id_match_len (keyword, strlen (keyword), cp, end - cp))
224     return end;
225   else
226     return NULL;
227 }
228
229 /* Returns CP, advanced past a '+' or '-' if present. */
230 static const char *
231 skip_indentor (const char *cp) 
232 {
233   if (*cp == '+' || *cp == '-')
234     cp++;
235   return cp;
236 }
237
238 /* Returns true if LINE contains a DO REPEAT command, false
239    otherwise. */
240 static bool
241 recognize_do_repeat (const char *line) 
242 {
243   const char *cp = recognize_keyword (skip_indentor (line), "do");
244   return cp != NULL && recognize_keyword (cp, "repeat") != NULL;
245 }
246
247 /* Returns true if LINE contains an END REPEAT command, false
248    otherwise.  Sets *PRINT to true for END REPEAT PRINT, false
249    otherwise. */
250 static bool
251 recognize_end_repeat (const char *line, bool *print)
252 {
253   const char *cp = recognize_keyword (skip_indentor (line), "end");
254   if (cp == NULL)
255     return false;
256
257   cp = recognize_keyword (cp, "repeat");
258   if (cp == NULL) 
259     return false; 
260
261   *print = recognize_keyword (cp, "print");
262   return true; 
263 }
264
265 /* Read all the lines we are going to substitute, inside the DO
266    REPEAT...END REPEAT block. */
267 static bool
268 parse_lines (struct repeat_block *block) 
269 {
270   char *previous_file_name;
271   struct line_list **last_line;
272   int nesting_level;
273
274   previous_file_name = NULL;
275   block->first_line = NULL;
276   last_line = &block->first_line;
277   nesting_level = 0;
278
279   for (;;)
280     {
281       const char *cur_file_name;
282       int cur_line_number;
283       struct line_list *line;
284       struct string cur_line_copy;
285       bool dot;
286
287       if (! lex_get_line_raw ())
288         return false;
289
290       /* If the current file has changed then record the fact. */
291       getl_location (&cur_file_name, &cur_line_number);
292       if (previous_file_name == NULL 
293           || !strcmp (cur_file_name, previous_file_name))
294         previous_file_name = pool_strdup (block->pool, cur_file_name);
295
296       ds_init_string (&cur_line_copy, lex_entire_line_ds () );
297       ds_rtrim (&cur_line_copy, ss_cstr (CC_SPACES));
298       dot = ds_chomp (&cur_line_copy, get_endcmd ());
299
300       if (recognize_do_repeat (ds_cstr (&cur_line_copy)))
301         nesting_level++; 
302       else if (recognize_end_repeat (ds_cstr (&cur_line_copy), &block->print)) 
303         {
304         if (nesting_level-- == 0)
305           {
306             lex_discard_line ();
307             ds_destroy (&cur_line_copy);
308             return true;
309           } 
310         }
311       if (dot)
312         ds_put_char (&cur_line_copy, get_endcmd ());
313       
314       line = *last_line = pool_alloc (block->pool, sizeof *line);
315       line->next = NULL;
316       line->file_name = previous_file_name;
317       line->line_number = cur_line_number;
318       line->line = pool_strdup (block->pool, ds_cstr (&cur_line_copy) );
319       last_line = &line->next;
320
321       ds_destroy (&cur_line_copy);
322     }
323
324   lex_discard_line ();
325   return true;
326 }
327
328 /* Creates variables for the given DO REPEAT. */
329 static void
330 create_vars (struct repeat_block *block)
331 {
332   struct repeat_entry *iter;
333  
334   for (iter = block->macros; iter; iter = iter->next)
335     if (iter->type == VAR_NAMES)
336       {
337         int i;
338
339         for (i = 0; i < block->loop_cnt; i++)
340           {
341             /* Ignore return value: if the variable already
342                exists there is no harm done. */
343             dict_create_var (dataset_dict (block->ds), iter->replacement[i], 0);
344           }
345       }
346 }
347
348 /* Parses a set of ids for DO REPEAT. */
349 static int
350 parse_ids (const struct dictionary *dict, struct repeat_entry *e, struct pool *pool)
351 {
352   size_t n = 0;
353   e->type = VAR_NAMES;
354   return parse_mixed_vars_pool (dict, pool, &e->replacement, &n, PV_NONE) ? n : 0;
355 }
356
357 /* Adds STRING to E's list of replacements, which has *USED
358    elements and has room for *ALLOCATED.  Allocates memory from
359    POOL. */
360 static void
361 add_replacement (char *string,
362                  struct repeat_entry *e, struct pool *pool,
363                  size_t *used, size_t *allocated) 
364 {
365   if (*used == *allocated)
366     e->replacement = pool_2nrealloc (pool, e->replacement, allocated,
367                                      sizeof *e->replacement);
368   e->replacement[(*used)++] = string;
369 }
370
371 /* Parses a list of numbers for DO REPEAT. */
372 static int
373 parse_numbers (struct repeat_entry *e, struct pool *pool)
374 {
375   size_t used = 0;
376   size_t allocated = 0;
377   
378   e->type = OTHER;
379   e->replacement = NULL;
380
381   do
382     {
383       long a, b, i;
384
385       /* Parse A TO B into a, b. */
386       if (!lex_force_int ())
387         return 0;
388       a = lex_integer ();
389
390       lex_get ();
391       if (token == T_TO)
392         {
393           lex_get ();
394           if (!lex_force_int ())
395             return 0;
396           b = lex_integer ();
397           if (b < a) 
398             {
399               msg (SE, _("%ld TO %ld is an invalid range."), a, b);
400               return 0;
401             }
402           lex_get ();
403         }
404       else
405         b = a;
406
407       for (i = a; i <= b; i++)
408         add_replacement (pool_asprintf (pool, "%ld", i),
409                          e, pool, &used, &allocated);
410
411
412       lex_match (',');
413     }
414   while (token != '/' && token != '.');
415
416   return used;
417 }
418
419 /* Parses a list of strings for DO REPEAT. */
420 int
421 parse_strings (struct repeat_entry *e, struct pool *pool)
422 {
423   size_t used = 0;
424   size_t allocated = 0;
425   
426   e->type = OTHER;
427   e->replacement = NULL;
428
429   do
430     {
431       char *string;
432       
433       if (token != T_STRING)
434         {
435           msg (SE, _("String expected."));
436           return 0;
437         }
438
439       string = lex_token_representation ();
440       pool_register (pool, free, string);
441       add_replacement (string, e, pool, &used, &allocated);
442
443       lex_get ();
444       lex_match (',');
445     }
446   while (token != '/' && token != '.');
447
448   return used;
449 }
450 \f
451 int
452 cmd_end_repeat (struct dataset *ds UNUSED)
453 {
454   msg (SE, _("No matching DO REPEAT."));
455   return CMD_CASCADING_FAILURE;
456 }
457 \f
458 /* Finds a DO REPEAT macro with name MACRO_NAME and returns the
459    appropriate subsitution if found, or NULL if not. */
460 static char *
461 find_substitution (struct repeat_block *block, const char *name, size_t length)
462 {
463   struct repeat_entry *e;
464
465   for (e = block->macros; e; e = e->next)
466     if (!memcasecmp (e->id, name, length) && strlen (e->id) == length)
467       return e->replacement[block->loop_idx];
468   
469   return NULL;
470 }
471
472 /* Makes appropriate DO REPEAT macro substitutions within the 
473    repeated lines. */
474 static void
475 do_repeat_filter (struct string *line, void *block_)
476 {
477   struct repeat_block *block = block_;
478   bool in_apos, in_quote;
479   char *cp;
480   struct string output;
481   bool dot;
482
483   ds_init_empty (&output);
484
485   /* Strip trailing whitespace, check for & remove terminal dot. */
486   while (isspace (ds_last (line)))
487     ds_truncate (line, ds_length (line) - 1);
488   dot = ds_chomp (line, get_endcmd ());
489
490   in_apos = in_quote = false;
491   for (cp = ds_cstr (line); cp < ds_end (line); )
492     {
493       if (*cp == '\'' && !in_quote)
494         in_apos = !in_apos;
495       else if (*cp == '"' && !in_apos)
496         in_quote = !in_quote;
497       
498       if (in_quote || in_apos || !lex_is_id1 (*cp))
499         ds_put_char (&output, *cp++);
500       else 
501         {
502           const char *start = cp;
503           char *end = lex_skip_identifier (start);
504           const char *substitution = find_substitution (block,
505                                                         start, end - start);
506           if (substitution != NULL) 
507             ds_put_cstr (&output, substitution);
508           else
509             ds_put_substring (&output, ss_buffer (start, end - start));
510           cp = end;
511         }
512     }
513   if (dot)
514     ds_put_char (&output, get_endcmd ());
515
516   ds_swap (line, &output);
517   ds_destroy (&output);
518 }
519
520 /* Function called by getl to read a line.
521    Puts the line in OUTPUT, sets the file name in *FILE_NAME and
522    line number in *LINE_NUMBER.  Returns true if a line was
523    obtained, false if the source is exhausted. */
524 static bool
525 do_repeat_read (struct string *output, char **file_name, int *line_number,
526                 void *block_) 
527 {
528   struct repeat_block *block = block_;
529   struct line_list *line;
530
531   if (block->cur_line == NULL) 
532     {
533       block->loop_idx++;
534       if (block->loop_idx >= block->loop_cnt)
535         return false;
536       block->cur_line = block->first_line;
537     }
538   line = block->cur_line;
539
540   ds_assign_cstr (output, line->line);
541   *file_name = line->file_name;
542   *line_number = -line->line_number;
543   block->cur_line = line->next;
544   return true;
545 }
546
547 /* Frees a DO REPEAT block.
548    Called by getl to close out the DO REPEAT block. */
549 static void
550 do_repeat_close (void *block_)
551 {
552   struct repeat_block *block = block_;
553   pool_destroy (block->pool);
554 }