Eliminated global variable current_dataset.
[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       bool dot;
285
286       if (!getl_read_line (NULL))
287         return false;
288
289       /* If the current file has changed then record the fact. */
290       getl_location (&cur_file_name, &cur_line_number);
291       if (previous_file_name == NULL 
292           || !strcmp (cur_file_name, previous_file_name))
293         previous_file_name = pool_strdup (block->pool, cur_file_name);
294
295       ds_rtrim (&getl_buf, ss_cstr (CC_SPACES));
296       dot = ds_chomp (&getl_buf, get_endcmd ());
297       if (recognize_do_repeat (ds_cstr (&getl_buf))) 
298         nesting_level++; 
299       else if (recognize_end_repeat (ds_cstr (&getl_buf), &block->print)) 
300         {
301         if (nesting_level-- == 0)
302           {
303             lex_discard_line ();
304             return true;
305           } 
306         }
307       if (dot)
308         ds_put_char (&getl_buf, get_endcmd ());
309       
310       line = *last_line = pool_alloc (block->pool, sizeof *line);
311       line->next = NULL;
312       line->file_name = previous_file_name;
313       line->line_number = cur_line_number;
314       line->line = pool_strdup (block->pool, ds_cstr (&getl_buf));
315       last_line = &line->next;
316     }
317
318   lex_discard_line ();
319   return true;
320 }
321
322 /* Creates variables for the given DO REPEAT. */
323 static void
324 create_vars (struct repeat_block *block)
325 {
326   struct repeat_entry *iter;
327  
328   for (iter = block->macros; iter; iter = iter->next)
329     if (iter->type == VAR_NAMES)
330       {
331         int i;
332
333         for (i = 0; i < block->loop_cnt; i++)
334           {
335             /* Ignore return value: if the variable already
336                exists there is no harm done. */
337             dict_create_var (dataset_dict (block->ds), iter->replacement[i], 0);
338           }
339       }
340 }
341
342 /* Parses a set of ids for DO REPEAT. */
343 static int
344 parse_ids (const struct dictionary *dict, struct repeat_entry *e, struct pool *pool)
345 {
346   size_t n = 0;
347   e->type = VAR_NAMES;
348   return parse_mixed_vars_pool (dict, pool, &e->replacement, &n, PV_NONE) ? n : 0;
349 }
350
351 /* Adds STRING to E's list of replacements, which has *USED
352    elements and has room for *ALLOCATED.  Allocates memory from
353    POOL. */
354 static void
355 add_replacement (char *string,
356                  struct repeat_entry *e, struct pool *pool,
357                  size_t *used, size_t *allocated) 
358 {
359   if (*used == *allocated)
360     e->replacement = pool_2nrealloc (pool, e->replacement, allocated,
361                                      sizeof *e->replacement);
362   e->replacement[(*used)++] = string;
363 }
364
365 /* Parses a list of numbers for DO REPEAT. */
366 static int
367 parse_numbers (struct repeat_entry *e, struct pool *pool)
368 {
369   size_t used = 0;
370   size_t allocated = 0;
371   
372   e->type = OTHER;
373   e->replacement = NULL;
374
375   do
376     {
377       long a, b, i;
378
379       /* Parse A TO B into a, b. */
380       if (!lex_force_int ())
381         return 0;
382       a = lex_integer ();
383
384       lex_get ();
385       if (token == T_TO)
386         {
387           lex_get ();
388           if (!lex_force_int ())
389             return 0;
390           b = lex_integer ();
391           if (b < a) 
392             {
393               msg (SE, _("%ld TO %ld is an invalid range."), a, b);
394               return 0;
395             }
396           lex_get ();
397         }
398       else
399         b = a;
400
401       for (i = a; i <= b; i++)
402         add_replacement (pool_asprintf (pool, "%ld", i),
403                          e, pool, &used, &allocated);
404
405
406       lex_match (',');
407     }
408   while (token != '/' && token != '.');
409
410   return used;
411 }
412
413 /* Parses a list of strings for DO REPEAT. */
414 int
415 parse_strings (struct repeat_entry *e, struct pool *pool)
416 {
417   size_t used = 0;
418   size_t allocated = 0;
419   
420   e->type = OTHER;
421   e->replacement = NULL;
422
423   do
424     {
425       char *string;
426       
427       if (token != T_STRING)
428         {
429           msg (SE, _("String expected."));
430           return 0;
431         }
432
433       string = lex_token_representation ();
434       pool_register (pool, free, string);
435       add_replacement (string, e, pool, &used, &allocated);
436
437       lex_get ();
438       lex_match (',');
439     }
440   while (token != '/' && token != '.');
441
442   return used;
443 }
444 \f
445 int
446 cmd_end_repeat (struct dataset *ds UNUSED)
447 {
448   msg (SE, _("No matching DO REPEAT."));
449   return CMD_CASCADING_FAILURE;
450 }
451 \f
452 /* Finds a DO REPEAT macro with name MACRO_NAME and returns the
453    appropriate subsitution if found, or NULL if not. */
454 static char *
455 find_substitution (struct repeat_block *block, const char *name, size_t length)
456 {
457   struct repeat_entry *e;
458
459   for (e = block->macros; e; e = e->next)
460     if (!memcasecmp (e->id, name, length) && strlen (e->id) == length)
461       return e->replacement[block->loop_idx];
462   
463   return NULL;
464 }
465
466 /* Makes appropriate DO REPEAT macro substitutions within getl_buf. */
467 static void
468 do_repeat_filter (struct string *line, void *block_)
469 {
470   struct repeat_block *block = block_;
471   bool in_apos, in_quote;
472   char *cp;
473   struct string output;
474   bool dot;
475
476   ds_init_empty (&output);
477
478   /* Strip trailing whitespace, check for & remove terminal dot. */
479   while (isspace (ds_last (line)))
480     ds_truncate (line, ds_length (line) - 1);
481   dot = ds_chomp (line, get_endcmd ());
482
483   in_apos = in_quote = false;
484   for (cp = ds_cstr (line); cp < ds_end (line); )
485     {
486       if (*cp == '\'' && !in_quote)
487         in_apos = !in_apos;
488       else if (*cp == '"' && !in_apos)
489         in_quote = !in_quote;
490       
491       if (in_quote || in_apos || !lex_is_id1 (*cp))
492         ds_put_char (&output, *cp++);
493       else 
494         {
495           const char *start = cp;
496           char *end = lex_skip_identifier (start);
497           const char *substitution = find_substitution (block,
498                                                         start, end - start);
499           if (substitution != NULL) 
500             ds_put_cstr (&output, substitution);
501           else
502             ds_put_substring (&output, ss_buffer (start, end - start));
503           cp = end;
504         }
505     }
506   if (dot)
507     ds_put_char (&output, get_endcmd ());
508
509   ds_swap (line, &output);
510   ds_destroy (&output);
511 }
512
513 /* Function called by getl to read a line.
514    Puts the line in OUTPUT, sets the file name in *FILE_NAME and
515    line number in *LINE_NUMBER.  Returns true if a line was
516    obtained, false if the source is exhausted. */
517 static bool
518 do_repeat_read (struct string *output, char **file_name, int *line_number,
519                 void *block_) 
520 {
521   struct repeat_block *block = block_;
522   struct line_list *line;
523
524   if (block->cur_line == NULL) 
525     {
526       block->loop_idx++;
527       if (block->loop_idx >= block->loop_cnt)
528         return false;
529       block->cur_line = block->first_line;
530     }
531   line = block->cur_line;
532
533   ds_assign_cstr (output, line->line);
534   *file_name = line->file_name;
535   *line_number = -line->line_number;
536   block->cur_line = line->next;
537   return true;
538 }
539
540 /* Frees a DO REPEAT block.
541    Called by getl to close out the DO REPEAT block. */
542 static void
543 do_repeat_close (void *block_)
544 {
545   struct repeat_block *block = block_;
546   pool_destroy (block->pool);
547 }