1 /* PSPP - computes sample statistics.
2 Copyright (C) 1997-9, 2000 Free Software Foundation, Inc.
3 Written by Ben Pfaff <blp@gnu.org>.
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.
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.
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., 59 Temple Place - Suite 330, Boston, MA
36 #include "debug-print.h"
38 /* Describes one DO REPEAT macro. */
41 int type; /* 1=variable names, 0=any other. */
42 char id[9]; /* Macro identifier. */
43 char **replacement; /* Macro replacement. */
44 struct repeat_entry *next;
47 /* List of macro identifiers. */
48 static struct repeat_entry *repeat_tab;
50 /* Number of substitutions for each macro. */
53 /* List of lines before it's actually assigned to a file. */
54 static struct getl_line_list *line_buf_head;
55 static struct getl_line_list *line_buf_tail;
57 static int parse_ids (struct repeat_entry *);
58 static int parse_numbers (struct repeat_entry *);
59 static int parse_strings (struct repeat_entry *);
60 static void clean_up (void);
61 static int internal_cmd_do_repeat (void);
66 if (internal_cmd_do_repeat ())
73 /* Garbage collects all the allocated memory that's no longer
78 struct repeat_entry *iter, *next;
86 if (iter->replacement)
88 for (i = 0; i < count; i++)
89 free (iter->replacement[i]);
90 free (iter->replacement);
98 /* Allocates & appends another record at the end of the line_buf_tail
103 struct getl_line_list *new = xmalloc (sizeof *new);
105 if (line_buf_head == NULL)
106 line_buf_head = line_buf_tail = new;
108 line_buf_tail = line_buf_tail->next = new;
111 /* Returns nonzero if KEYWORD appears beginning at CONTEXT. */
113 recognize_keyword (const char *context, const char *keyword)
115 const char *end = context;
116 while (isalpha ((unsigned char) *end))
118 return lex_id_match_len (keyword, strlen (keyword), context, end - context);
121 /* Does the real work of parsing the DO REPEAT command and its nested
124 internal_cmd_do_repeat (void)
126 /* Name of first DO REPEAT macro. */
129 /* Current filename. */
130 const char *current_filename = NULL;
132 /* 1=Print lines after preprocessing. */
135 /* The first step is parsing the DO REPEAT command itself. */
137 line_buf_head = NULL;
140 struct repeat_entry *e;
141 struct repeat_entry *iter;
144 /* Get a stand-in variable name and make sure it's unique. */
145 if (!lex_force_id ())
147 for (iter = repeat_tab; iter; iter = iter->next)
148 if (!strcmp (iter->id, tokid))
150 msg (SE, _("Identifier %s is given twice."), tokid);
154 /* Make a new stand-in variable entry and link it into the
156 e = xmalloc (sizeof *e);
158 e->next = repeat_tab;
159 strcpy (e->id, tokid);
162 /* Skip equals sign. */
164 if (!lex_force_match ('='))
167 /* Get the details of the variable's possible values. */
170 result = parse_ids (e);
171 else if (token == T_NUM)
172 result = parse_numbers (e);
173 else if (token == T_STRING)
174 result = parse_strings (e);
183 /* If this is the first variable then it defines how many
184 replacements there must be; otherwise enforce this number of
189 strcpy (first_name, e->id);
191 else if (count != result)
193 msg (SE, _("There must be the same number of substitutions "
194 "for each dummy variable specified. Since there "
195 "were %d substitutions for %s, there must be %d "
196 "for %s as well, but %d were specified."),
197 count, first_name, count, e->id, result);
204 while (token != '.');
206 /* Read all the lines inside the DO REPEAT ... END REPEAT. */
212 if (!getl_read_line ())
213 msg (FE, _("Unexpected end of file."));
215 /* If the current file has changed then record the fact. */
220 getl_location (&curfn, &curln);
221 if (current_filename != curfn)
223 assert (curln > 0 && curfn != NULL);
226 line_buf_tail->len = -curln;
227 line_buf_tail->line = xstrdup (curfn);
228 current_filename = curfn;
232 /* FIXME? This code is not strictly correct, however if you
233 have begun a line with DO REPEAT or END REPEAT and it's
234 *not* a command name, then you are obviously *trying* to
235 break this mechanism. And you will. Also, the entire
236 command names must appear on a single line--they can't be
239 char *cp = ds_c_str (&getl_buf);
241 /* Skip leading indentors and any whitespace. */
242 if (*cp == '+' || *cp == '-' || *cp == '.')
244 while (isspace ((unsigned char) *cp))
247 /* Find END REPEAT. */
248 if (recognize_keyword (cp, "end"))
250 while (isalpha ((unsigned char) *cp))
252 while (isspace ((unsigned char) *cp))
254 if (recognize_keyword (cp, "repeat"))
260 while (isalpha ((unsigned char) *cp))
262 while (isspace ((unsigned char) *cp))
265 print = recognize_keyword (cp, "print");
270 else /* Find DO REPEAT. */
271 if (!strncasecmp (cp, "do", 2))
274 while (isspace ((unsigned char) *cp))
276 if (!strncasecmp (cp, "rep", 3))
282 line_buf_tail->len = ds_length (&getl_buf);
283 line_buf_tail->line = xmalloc (ds_length (&getl_buf) + 1);
284 memcpy (line_buf_tail->line,
285 ds_c_str (&getl_buf), ds_length (&getl_buf) + 1);
289 /* FIXME: For the moment we simply discard the contents of the END
290 REPEAT line. We should actually check for the PRINT specifier.
291 This can be done easier when we buffer entire commands instead of
292 doing it token by token; see TODO. */
295 /* Tie up the loose end of the chain. */
296 if (line_buf_head == NULL)
298 msg (SW, _("No commands in scope."));
301 line_buf_tail->next = NULL;
303 /* Make new variables. */
305 struct repeat_entry *iter;
306 for (iter = repeat_tab; iter; iter = iter->next)
310 for (i = 0; i < count; i++)
312 /* Note that if the variable already exists there is no
314 dict_create_var (default_dict, iter->replacement[i], 0);
319 /* Create the DO REPEAT virtual input file. */
321 struct getl_script *script = xmalloc (sizeof *script);
323 script->first_line = line_buf_head;
324 script->cur_line = NULL;
325 script->remaining_loops = count;
326 script->loop_index = -1;
327 script->macros = repeat_tab;
328 script->print = print;
330 getl_add_DO_REPEAT_file (script);
336 /* Parses a set of ids for DO REPEAT. */
338 parse_ids (struct repeat_entry * e)
344 e->replacement = NULL;
351 if (!parse_mixed_vars (&names, &nnames, PV_NONE))
354 e->replacement = xrealloc (e->replacement,
355 (nnames + n) * sizeof *e->replacement);
356 for (i = 0; i < nnames; i++)
358 e->replacement[n + i] = xstrdup (names[i]);
364 while (token != '/' && token != '.');
369 /* Stores VALUE into *REPL. */
371 store_numeric (char **repl, long value)
373 *repl = xmalloc (INT_DIGITS + 1);
374 sprintf (*repl, "%ld", value);
377 /* Parses a list of numbers for DO REPEAT. */
379 parse_numbers (struct repeat_entry *e)
381 /* First and last numbers for TO, plus the step factor. */
384 /* Alias to e->replacement. */
387 /* Number of entries in array; maximum number for this allocation
393 e->replacement = array = NULL;
397 /* Parse A TO B into a, b. */
398 if (!lex_force_int ())
406 if (!lex_force_int ())
414 if (n + (abs (b - a) + 1) > m)
416 m = n + (abs (b - a) + 1) + 16;
417 e->replacement = array = xrealloc (array,
418 m * sizeof *e->replacement);
422 store_numeric (&array[n++], a);
428 for (iter = a; iter <= b; iter++)
429 store_numeric (&array[n++], iter);
431 for (iter = a; iter >= b; iter--)
432 store_numeric (&array[n++], iter);
437 while (token != '/' && token != '.');
438 e->replacement = xrealloc (array, n * sizeof *e->replacement);
443 /* Parses a list of strings for DO REPEAT. */
445 parse_strings (struct repeat_entry * e)
451 string = e->replacement = NULL;
456 if (token != T_STRING)
459 msg (SE, _("String expected."));
460 for (i = 0; i < n; i++)
469 e->replacement = string = xrealloc (string,
470 m * sizeof *e->replacement);
472 string[n++] = lex_token_representation ();
477 while (token != '/' && token != '.');
478 e->replacement = xrealloc (string, n * sizeof *e->replacement);
484 cmd_end_repeat (void)
486 msg (SE, _("No matching DO REPEAT."));
490 /* Finds a DO REPEAT macro with name MACRO_NAME and returns the
491 appropriate subsitution if found, or NULL if not. */
493 find_DO_REPEAT_substitution (char *macro_name)
495 struct getl_script *s;
497 for (s = getl_head; s; s = s->included_from)
499 struct repeat_entry *e;
501 if (s->first_line == NULL)
504 for (e = s->macros; e; e = e->next)
505 if (!strcasecmp (e->id, macro_name))
506 return e->replacement[s->loop_index];
512 /* Makes appropriate DO REPEAT macro substitutions within getl_buf. */
514 perform_DO_REPEAT_substitutions (void)
516 /* Are we in an apostrophized string or a quoted string? */
517 int in_apos = 0, in_quote = 0;
519 /* Source pointer. */
522 /* Output buffer, size, pointer. */
523 struct string output;
528 ds_init (&output, ds_capacity (&getl_buf));
530 /* Strip trailing whitespace, check for & remove terminal dot. */
531 while (ds_length (&getl_buf) > 0
532 && isspace ((unsigned char) ds_end (&getl_buf)[-1]))
533 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
534 if (ds_length (&getl_buf) > 0 && ds_end (&getl_buf)[-1] == get_endcmd() )
537 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
540 for (cp = ds_c_str (&getl_buf); cp < ds_end (&getl_buf); )
542 if (*cp == '\'' && !in_quote)
544 else if (*cp == '"' && !in_apos)
547 if (in_quote || in_apos || !CHAR_IS_ID1 (*cp))
549 ds_putc (&output, *cp++);
553 /* Collect an identifier. */
560 while (CHAR_IS_IDN (*cp) && np < &name[8])
562 while (CHAR_IS_IDN (*cp))
566 substitution = find_DO_REPEAT_substitution (name);
569 ds_concat (&output, start, cp - start);
573 /* Force output buffer size, copy substitution. */
574 ds_puts (&output, substitution);
578 ds_putc (&output, get_endcmd() );
580 ds_destroy (&getl_buf);