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);
64 static void debug_print (void);
65 static void debug_print_lines (void);
71 if (internal_cmd_do_repeat ())
78 /* Garbage collects all the allocated memory that's no longer
83 struct repeat_entry *iter, *next;
91 if (iter->replacement)
93 for (i = 0; i < count; i++)
94 free (iter->replacement[i]);
95 free (iter->replacement);
103 /* Allocates & appends another record at the end of the line_buf_tail
108 struct getl_line_list *new = xmalloc (sizeof *new);
110 if (line_buf_head == NULL)
111 line_buf_head = line_buf_tail = new;
113 line_buf_tail = line_buf_tail->next = new;
116 /* Returns nonzero if KEYWORD appears beginning at CONTEXT. */
118 recognize_keyword (const char *context, const char *keyword)
120 const char *end = context;
121 while (isalpha ((unsigned char) *end))
123 return lex_id_match_len (keyword, strlen (keyword), context, end - context);
126 /* Does the real work of parsing the DO REPEAT command and its nested
129 internal_cmd_do_repeat (void)
131 /* Name of first DO REPEAT macro. */
134 /* Current filename. */
135 const char *current_filename = NULL;
137 /* 1=Print lines after preprocessing. */
140 /* The first step is parsing the DO REPEAT command itself. */
142 lex_match_id ("REPEAT");
145 line_buf_head = NULL;
148 struct repeat_entry *e;
149 struct repeat_entry *iter;
152 /* Get a stand-in variable name and make sure it's unique. */
153 if (!lex_force_id ())
155 for (iter = repeat_tab; iter; iter = iter->next)
156 if (!strcmp (iter->id, tokid))
158 msg (SE, _("Identifier %s is given twice."), tokid);
162 /* Make a new stand-in variable entry and link it into the
164 e = xmalloc (sizeof *e);
166 e->next = repeat_tab;
167 strcpy (e->id, tokid);
170 /* Skip equals sign. */
172 if (!lex_force_match ('='))
175 /* Get the details of the variable's possible values. */
178 result = parse_ids (e);
179 else if (token == T_NUM)
180 result = parse_numbers (e);
181 else if (token == T_STRING)
182 result = parse_strings (e);
191 /* If this is the first variable then it defines how many
192 replacements there must be; otherwise enforce this number of
197 strcpy (first_name, e->id);
199 else if (count != result)
201 msg (SE, _("There must be the same number of substitutions "
202 "for each dummy variable specified. Since there "
203 "were %d substitutions for %s, there must be %d "
204 "for %s as well, but %d were specified."),
205 count, first_name, count, e->id, result);
212 while (token != '.');
218 /* Read all the lines inside the DO REPEAT ... END REPEAT. */
224 if (!getl_read_line ())
225 msg (FE, _("Unexpected end of file."));
227 /* If the current file has changed then record the fact. */
232 getl_location (&curfn, &curln);
233 if (current_filename != curfn)
235 assert (curln > 0 && curfn != NULL);
238 line_buf_tail->len = -curln;
239 line_buf_tail->line = xstrdup (curfn);
240 current_filename = curfn;
244 /* FIXME? This code is not strictly correct, however if you
245 have begun a line with DO REPEAT or END REPEAT and it's
246 *not* a command name, then you are obviously *trying* to
247 break this mechanism. And you will. Also, the entire
248 command names must appear on a single line--they can't be
251 char *cp = ds_value (&getl_buf);
253 /* Skip leading indentors and any whitespace. */
254 if (*cp == '+' || *cp == '-' || *cp == '.')
256 while (isspace ((unsigned char) *cp))
259 /* Find END REPEAT. */
260 if (recognize_keyword (cp, "end"))
262 while (isalpha ((unsigned char) *cp))
264 while (isspace ((unsigned char) *cp))
266 if (recognize_keyword (cp, "repeat"))
272 while (isalpha ((unsigned char) *cp))
274 while (isspace ((unsigned char) *cp))
277 print = recognize_keyword (cp, "print");
282 else /* Find DO REPEAT. */
283 if (!strncasecmp (cp, "do", 2))
286 while (isspace ((unsigned char) *cp))
288 if (!strncasecmp (cp, "rep", 3))
294 line_buf_tail->len = ds_length (&getl_buf);
295 line_buf_tail->line = xmalloc (ds_length (&getl_buf) + 1);
296 memcpy (line_buf_tail->line,
297 ds_value (&getl_buf), ds_length (&getl_buf) + 1);
301 /* FIXME: For the moment we simply discard the contents of the END
302 REPEAT line. We should actually check for the PRINT specifier.
303 This can be done easier when we buffer entire commands instead of
304 doing it token by token; see TODO. */
307 /* Tie up the loose end of the chain. */
308 if (line_buf_head == NULL)
310 msg (SW, _("No commands in scope."));
313 line_buf_tail->next = NULL;
315 /* Show the line list. */
317 debug_print_lines ();
320 /* Make new variables. */
322 struct repeat_entry *iter;
323 for (iter = repeat_tab; iter; iter = iter->next)
327 for (i = 0; i < count; i++)
329 /* Note that if the variable already exists there is no
331 dict_create_var (default_dict, iter->replacement[i], 0);
336 /* Create the DO REPEAT virtual input file. */
338 struct getl_script *script = xmalloc (sizeof *script);
340 script->first_line = line_buf_head;
341 script->cur_line = NULL;
342 script->remaining_loops = count;
343 script->loop_index = -1;
344 script->macros = repeat_tab;
345 script->print = print;
347 getl_add_DO_REPEAT_file (script);
353 /* Parses a set of ids for DO REPEAT. */
355 parse_ids (struct repeat_entry * e)
361 e->replacement = NULL;
368 if (!parse_mixed_vars (&names, &nnames, PV_NONE))
371 e->replacement = xrealloc (e->replacement,
372 (nnames + n) * sizeof *e->replacement);
373 for (i = 0; i < nnames; i++)
375 e->replacement[n + i] = xstrdup (names[i]);
381 while (token != '/' && token != '.');
386 /* Stores VALUE into *REPL. */
388 store_numeric (char **repl, long value)
390 *repl = xmalloc (INT_DIGITS + 1);
391 sprintf (*repl, "%ld", value);
394 /* Parses a list of numbers for DO REPEAT. */
396 parse_numbers (struct repeat_entry *e)
398 /* First and last numbers for TO, plus the step factor. */
401 /* Alias to e->replacement. */
404 /* Number of entries in array; maximum number for this allocation
410 e->replacement = array = NULL;
414 /* Parse A TO B into a, b. */
415 if (!lex_force_int ())
423 if (!lex_force_int ())
431 if (n + (abs (b - a) + 1) > m)
433 m = n + (abs (b - a) + 1) + 16;
434 e->replacement = array = xrealloc (array,
435 m * sizeof *e->replacement);
439 store_numeric (&array[n++], a);
445 for (iter = a; iter <= b; iter++)
446 store_numeric (&array[n++], iter);
448 for (iter = a; iter >= b; iter--)
449 store_numeric (&array[n++], iter);
454 while (token != '/' && token != '.');
455 e->replacement = xrealloc (array, n * sizeof *e->replacement);
460 /* Parses a list of strings for DO REPEAT. */
462 parse_strings (struct repeat_entry * e)
468 string = e->replacement = NULL;
473 if (token != T_STRING)
476 msg (SE, _("String expected."));
477 for (i = 0; i < n; i++)
486 e->replacement = string = xrealloc (string,
487 m * sizeof *e->replacement);
489 string[n++] = lex_token_representation ();
494 while (token != '/' && token != '.');
495 e->replacement = xrealloc (string, n * sizeof *e->replacement);
501 cmd_end_repeat (void)
503 msg (SE, _("No matching DO REPEAT."));
507 /* Finds a DO REPEAT macro with name MACRO_NAME and returns the
508 appropriate subsitution if found, or NULL if not. */
510 find_DO_REPEAT_substitution (char *macro_name)
512 struct getl_script *s;
514 for (s = getl_head; s; s = s->included_from)
516 struct repeat_entry *e;
518 if (s->first_line == NULL)
521 for (e = s->macros; e; e = e->next)
522 if (!strcasecmp (e->id, macro_name))
523 return e->replacement[s->loop_index];
529 /* Makes appropriate DO REPEAT macro substitutions within getl_buf. */
531 perform_DO_REPEAT_substitutions (void)
533 /* Are we in an apostrophized string or a quoted string? */
534 int in_apos = 0, in_quote = 0;
536 /* Source pointer. */
539 /* Output buffer, size, pointer. */
540 struct string output;
545 ds_init (NULL, &output, ds_size (&getl_buf));
547 /* Strip trailing whitespace, check for & remove terminal dot. */
548 while (ds_length (&getl_buf) > 0
549 && isspace ((unsigned char) ds_end (&getl_buf)[-1]))
550 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
551 if (ds_length (&getl_buf) > 0 && ds_end (&getl_buf)[-1] == set_endcmd)
554 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
557 for (cp = ds_value (&getl_buf); cp < ds_end (&getl_buf); )
559 if (*cp == '\'' && !in_quote)
561 else if (*cp == '"' && !in_apos)
564 if (in_quote || in_apos || !CHAR_IS_ID1 (*cp))
566 ds_putchar (&output, *cp++);
570 /* Collect an identifier. */
577 while (CHAR_IS_IDN (*cp) && np < &name[8])
579 while (CHAR_IS_IDN (*cp))
583 substitution = find_DO_REPEAT_substitution (name);
586 ds_concat_buffer (&output, start, cp - start);
590 /* Force output buffer size, copy substitution. */
591 ds_concat (&output, substitution);
595 ds_putchar (&output, (unsigned char) set_endcmd);
597 ds_destroy (&getl_buf);
601 /* Debugging code. */
607 struct repeat_entry *iter;
610 printf ("DO REPEAT\n");
611 for (iter = repeat_tab; iter; iter = iter->next)
613 printf (" %s%s=", iter->id, iter->type ? "(ids)" : "");
614 for (j = 0; j < count; j++)
615 printf ("%s ", iter->replacement[j]);
616 putc (iter->next ? '/' : '.', stdout);
622 debug_print_lines (void)
624 struct getl_line_list *iter;
625 const char *fn = "(none)";
628 printf ("---begin DO REPEAT lines---\n");
629 for (iter = line_buf_head; iter; iter = iter->next)
636 printf ("%s:%d: %s", fn, ln++, iter->line);
639 printf ("---end DO REPEAT lines---\n");
641 #endif /* DEBUGGING */