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 line_buf_head = NULL;
145 struct repeat_entry *e;
146 struct repeat_entry *iter;
149 /* Get a stand-in variable name and make sure it's unique. */
150 if (!lex_force_id ())
152 for (iter = repeat_tab; iter; iter = iter->next)
153 if (!strcmp (iter->id, tokid))
155 msg (SE, _("Identifier %s is given twice."), tokid);
159 /* Make a new stand-in variable entry and link it into the
161 e = xmalloc (sizeof *e);
163 e->next = repeat_tab;
164 strcpy (e->id, tokid);
167 /* Skip equals sign. */
169 if (!lex_force_match ('='))
172 /* Get the details of the variable's possible values. */
175 result = parse_ids (e);
176 else if (token == T_NUM)
177 result = parse_numbers (e);
178 else if (token == T_STRING)
179 result = parse_strings (e);
188 /* If this is the first variable then it defines how many
189 replacements there must be; otherwise enforce this number of
194 strcpy (first_name, e->id);
196 else if (count != result)
198 msg (SE, _("There must be the same number of substitutions "
199 "for each dummy variable specified. Since there "
200 "were %d substitutions for %s, there must be %d "
201 "for %s as well, but %d were specified."),
202 count, first_name, count, e->id, result);
209 while (token != '.');
215 /* Read all the lines inside the DO REPEAT ... END REPEAT. */
221 if (!getl_read_line ())
222 msg (FE, _("Unexpected end of file."));
224 /* If the current file has changed then record the fact. */
229 getl_location (&curfn, &curln);
230 if (current_filename != curfn)
232 assert (curln > 0 && curfn != NULL);
235 line_buf_tail->len = -curln;
236 line_buf_tail->line = xstrdup (curfn);
237 current_filename = curfn;
241 /* FIXME? This code is not strictly correct, however if you
242 have begun a line with DO REPEAT or END REPEAT and it's
243 *not* a command name, then you are obviously *trying* to
244 break this mechanism. And you will. Also, the entire
245 command names must appear on a single line--they can't be
248 char *cp = ds_value (&getl_buf);
250 /* Skip leading indentors and any whitespace. */
251 if (*cp == '+' || *cp == '-' || *cp == '.')
253 while (isspace ((unsigned char) *cp))
256 /* Find END REPEAT. */
257 if (recognize_keyword (cp, "end"))
259 while (isalpha ((unsigned char) *cp))
261 while (isspace ((unsigned char) *cp))
263 if (recognize_keyword (cp, "repeat"))
269 while (isalpha ((unsigned char) *cp))
271 while (isspace ((unsigned char) *cp))
274 print = recognize_keyword (cp, "print");
279 else /* Find DO REPEAT. */
280 if (!strncasecmp (cp, "do", 2))
283 while (isspace ((unsigned char) *cp))
285 if (!strncasecmp (cp, "rep", 3))
291 line_buf_tail->len = ds_length (&getl_buf);
292 line_buf_tail->line = xmalloc (ds_length (&getl_buf) + 1);
293 memcpy (line_buf_tail->line,
294 ds_value (&getl_buf), ds_length (&getl_buf) + 1);
298 /* FIXME: For the moment we simply discard the contents of the END
299 REPEAT line. We should actually check for the PRINT specifier.
300 This can be done easier when we buffer entire commands instead of
301 doing it token by token; see TODO. */
304 /* Tie up the loose end of the chain. */
305 if (line_buf_head == NULL)
307 msg (SW, _("No commands in scope."));
310 line_buf_tail->next = NULL;
312 /* Show the line list. */
314 debug_print_lines ();
317 /* Make new variables. */
319 struct repeat_entry *iter;
320 for (iter = repeat_tab; iter; iter = iter->next)
324 for (i = 0; i < count; i++)
326 /* Note that if the variable already exists there is no
328 dict_create_var (default_dict, iter->replacement[i], 0);
333 /* Create the DO REPEAT virtual input file. */
335 struct getl_script *script = xmalloc (sizeof *script);
337 script->first_line = line_buf_head;
338 script->cur_line = NULL;
339 script->remaining_loops = count;
340 script->loop_index = -1;
341 script->macros = repeat_tab;
342 script->print = print;
344 getl_add_DO_REPEAT_file (script);
350 /* Parses a set of ids for DO REPEAT. */
352 parse_ids (struct repeat_entry * e)
358 e->replacement = NULL;
365 if (!parse_mixed_vars (&names, &nnames, PV_NONE))
368 e->replacement = xrealloc (e->replacement,
369 (nnames + n) * sizeof *e->replacement);
370 for (i = 0; i < nnames; i++)
372 e->replacement[n + i] = xstrdup (names[i]);
378 while (token != '/' && token != '.');
383 /* Stores VALUE into *REPL. */
385 store_numeric (char **repl, long value)
387 *repl = xmalloc (INT_DIGITS + 1);
388 sprintf (*repl, "%ld", value);
391 /* Parses a list of numbers for DO REPEAT. */
393 parse_numbers (struct repeat_entry *e)
395 /* First and last numbers for TO, plus the step factor. */
398 /* Alias to e->replacement. */
401 /* Number of entries in array; maximum number for this allocation
407 e->replacement = array = NULL;
411 /* Parse A TO B into a, b. */
412 if (!lex_force_int ())
420 if (!lex_force_int ())
428 if (n + (abs (b - a) + 1) > m)
430 m = n + (abs (b - a) + 1) + 16;
431 e->replacement = array = xrealloc (array,
432 m * sizeof *e->replacement);
436 store_numeric (&array[n++], a);
442 for (iter = a; iter <= b; iter++)
443 store_numeric (&array[n++], iter);
445 for (iter = a; iter >= b; iter--)
446 store_numeric (&array[n++], iter);
451 while (token != '/' && token != '.');
452 e->replacement = xrealloc (array, n * sizeof *e->replacement);
457 /* Parses a list of strings for DO REPEAT. */
459 parse_strings (struct repeat_entry * e)
465 string = e->replacement = NULL;
470 if (token != T_STRING)
473 msg (SE, _("String expected."));
474 for (i = 0; i < n; i++)
483 e->replacement = string = xrealloc (string,
484 m * sizeof *e->replacement);
486 string[n++] = lex_token_representation ();
491 while (token != '/' && token != '.');
492 e->replacement = xrealloc (string, n * sizeof *e->replacement);
498 cmd_end_repeat (void)
500 msg (SE, _("No matching DO REPEAT."));
504 /* Finds a DO REPEAT macro with name MACRO_NAME and returns the
505 appropriate subsitution if found, or NULL if not. */
507 find_DO_REPEAT_substitution (char *macro_name)
509 struct getl_script *s;
511 for (s = getl_head; s; s = s->included_from)
513 struct repeat_entry *e;
515 if (s->first_line == NULL)
518 for (e = s->macros; e; e = e->next)
519 if (!strcasecmp (e->id, macro_name))
520 return e->replacement[s->loop_index];
526 /* Makes appropriate DO REPEAT macro substitutions within getl_buf. */
528 perform_DO_REPEAT_substitutions (void)
530 /* Are we in an apostrophized string or a quoted string? */
531 int in_apos = 0, in_quote = 0;
533 /* Source pointer. */
536 /* Output buffer, size, pointer. */
537 struct string output;
542 ds_init (NULL, &output, ds_size (&getl_buf));
544 /* Strip trailing whitespace, check for & remove terminal dot. */
545 while (ds_length (&getl_buf) > 0
546 && isspace ((unsigned char) ds_end (&getl_buf)[-1]))
547 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
548 if (ds_length (&getl_buf) > 0 && ds_end (&getl_buf)[-1] == get_endcmd() )
551 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
554 for (cp = ds_value (&getl_buf); cp < ds_end (&getl_buf); )
556 if (*cp == '\'' && !in_quote)
558 else if (*cp == '"' && !in_apos)
561 if (in_quote || in_apos || !CHAR_IS_ID1 (*cp))
563 ds_putchar (&output, *cp++);
567 /* Collect an identifier. */
574 while (CHAR_IS_IDN (*cp) && np < &name[8])
576 while (CHAR_IS_IDN (*cp))
580 substitution = find_DO_REPEAT_substitution (name);
583 ds_concat_buffer (&output, start, cp - start);
587 /* Force output buffer size, copy substitution. */
588 ds_concat (&output, substitution);
592 ds_putchar (&output, get_endcmd() );
594 ds_destroy (&getl_buf);
598 /* Debugging code. */
604 struct repeat_entry *iter;
607 printf ("DO REPEAT\n");
608 for (iter = repeat_tab; iter; iter = iter->next)
610 printf (" %s%s=", iter->id, iter->type ? "(ids)" : "");
611 for (j = 0; j < count; j++)
612 printf ("%s ", iter->replacement[j]);
613 putc (iter->next ? '/' : '.', stdout);
619 debug_print_lines (void)
621 struct getl_line_list *iter;
622 const char *fn = "(none)";
625 printf ("---begin DO REPEAT lines---\n");
626 for (iter = line_buf_head; iter; iter = iter->next)
633 printf ("%s:%d: %s", fn, ln++, iter->line);
636 printf ("---end DO REPEAT lines---\n");
638 #endif /* DEBUGGING */