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
37 #include "debug-print.h"
39 /* Describes one DO REPEAT macro. */
42 int type; /* 1=variable names, 0=any other. */
43 char id[9]; /* Macro identifier. */
44 char **replacement; /* Macro replacement. */
45 struct repeat_entry *next;
48 /* List of macro identifiers. */
49 static struct repeat_entry *repeat_tab;
51 /* Number of substitutions for each macro. */
54 /* List of lines before it's actually assigned to a file. */
55 static struct getl_line_list *line_buf_head;
56 static struct getl_line_list *line_buf_tail;
58 static int parse_ids (struct repeat_entry *);
59 static int parse_numbers (struct repeat_entry *);
60 static int parse_strings (struct repeat_entry *);
61 static void clean_up (void);
62 static int internal_cmd_do_repeat (void);
65 static void debug_print (void);
66 static void debug_print_lines (void);
72 if (internal_cmd_do_repeat ())
79 /* Garbage collects all the allocated memory that's no longer
84 struct repeat_entry *iter, *next;
92 if (iter->replacement)
94 for (i = 0; i < count; i++)
95 free (iter->replacement[i]);
96 free (iter->replacement);
104 /* Allocates & appends another record at the end of the line_buf_tail
109 struct getl_line_list *new = xmalloc (sizeof *new);
111 if (line_buf_head == NULL)
112 line_buf_head = line_buf_tail = new;
114 line_buf_tail = line_buf_tail->next = new;
117 /* Returns nonzero if KEYWORD appears beginning at CONTEXT. */
119 recognize_keyword (const char *context, const char *keyword)
121 const char *end = context;
122 while (isalpha ((unsigned char) *end))
124 return lex_id_match_len (keyword, strlen (keyword), context, end - context);
127 /* Does the real work of parsing the DO REPEAT command and its nested
130 internal_cmd_do_repeat (void)
132 /* Name of first DO REPEAT macro. */
135 /* Current filename. */
136 const char *current_filename = NULL;
138 /* 1=Print lines after preprocessing. */
141 /* The first step is parsing the DO REPEAT command itself. */
143 lex_match_id ("REPEAT");
146 line_buf_head = NULL;
149 struct repeat_entry *e;
150 struct repeat_entry *iter;
153 /* Get a stand-in variable name and make sure it's unique. */
154 if (!lex_force_id ())
156 for (iter = repeat_tab; iter; iter = iter->next)
157 if (!strcmp (iter->id, tokid))
159 msg (SE, _("Identifier %s is given twice."), tokid);
163 /* Make a new stand-in variable entry and link it into the
165 e = xmalloc (sizeof *e);
167 e->next = repeat_tab;
168 strcpy (e->id, tokid);
171 /* Skip equals sign. */
173 if (!lex_force_match ('='))
176 /* Get the details of the variable's possible values. */
179 result = parse_ids (e);
180 else if (token == T_NUM)
181 result = parse_numbers (e);
182 else if (token == T_STRING)
183 result = parse_strings (e);
192 /* If this is the first variable then it defines how many
193 replacements there must be; otherwise enforce this number of
198 strcpy (first_name, e->id);
200 else if (count != result)
202 msg (SE, _("There must be the same number of substitutions "
203 "for each dummy variable specified. Since there "
204 "were %d substitutions for %s, there must be %d "
205 "for %s as well, but %d were specified."),
206 count, first_name, count, e->id, result);
213 while (token != '.');
219 /* Read all the lines inside the DO REPEAT ... END REPEAT. */
225 if (!getl_read_line ())
226 msg (FE, _("Unexpected end of file."));
228 /* If the current file has changed then record the fact. */
233 getl_location (&curfn, &curln);
234 if (current_filename != curfn)
236 assert (curln > 0 && curfn != NULL);
239 line_buf_tail->len = -curln;
240 line_buf_tail->line = xstrdup (curfn);
241 current_filename = curfn;
245 /* FIXME? This code is not strictly correct, however if you
246 have begun a line with DO REPEAT or END REPEAT and it's
247 *not* a command name, then you are obviously *trying* to
248 break this mechanism. And you will. Also, the entire
249 command names must appear on a single line--they can't be
252 char *cp = ds_value (&getl_buf);
254 /* Skip leading indentors and any whitespace. */
255 if (*cp == '+' || *cp == '-' || *cp == '.')
257 while (isspace ((unsigned char) *cp))
260 /* Find END REPEAT. */
261 if (recognize_keyword (cp, "end"))
263 while (isalpha ((unsigned char) *cp))
265 while (isspace ((unsigned char) *cp))
267 if (recognize_keyword (cp, "repeat"))
273 while (isalpha ((unsigned char) *cp))
275 while (isspace ((unsigned char) *cp))
278 print = recognize_keyword (cp, "print");
283 else /* Find DO REPEAT. */
284 if (!strncasecmp (cp, "do", 2))
287 while (isspace ((unsigned char) *cp))
289 if (!strncasecmp (cp, "rep", 3))
295 line_buf_tail->len = ds_length (&getl_buf);
296 line_buf_tail->line = xmalloc (ds_length (&getl_buf) + 1);
297 memcpy (line_buf_tail->line,
298 ds_value (&getl_buf), ds_length (&getl_buf) + 1);
302 /* FIXME: For the moment we simply discard the contents of the END
303 REPEAT line. We should actually check for the PRINT specifier.
304 This can be done easier when we buffer entire commands instead of
305 doing it token by token; see TODO. */
308 /* Tie up the loose end of the chain. */
309 if (line_buf_head == NULL)
311 msg (SW, _("No commands in scope."));
314 line_buf_tail->next = NULL;
316 /* Show the line list. */
318 debug_print_lines ();
321 /* Make new variables. */
323 struct repeat_entry *iter;
324 for (iter = repeat_tab; iter; iter = iter->next)
328 for (i = 0; i < count; i++)
330 /* Note that if the variable already exists there is no
332 struct variable *v = dict_create_var (default_dict,
333 iter->replacement[i],
336 /* If we created the variable then we need to initialize
337 its observations to SYSMIS. */
344 /* Create the DO REPEAT virtual input file. */
346 struct getl_script *script = xmalloc (sizeof *script);
348 script->first_line = line_buf_head;
349 script->cur_line = NULL;
350 script->remaining_loops = count;
351 script->loop_index = -1;
352 script->macros = repeat_tab;
353 script->print = print;
355 getl_add_DO_REPEAT_file (script);
361 /* Parses a set of ids for DO REPEAT. */
363 parse_ids (struct repeat_entry * e)
369 e->replacement = NULL;
376 if (!parse_mixed_vars (&names, &nnames, PV_NONE))
379 e->replacement = xrealloc (e->replacement,
380 (nnames + n) * sizeof *e->replacement);
381 for (i = 0; i < nnames; i++)
383 e->replacement[n + i] = xstrdup (names[i]);
389 while (token != '/' && token != '.');
394 /* Stores VALUE into *REPL. */
396 store_numeric (char **repl, long value)
398 *repl = xmalloc (INT_DIGITS + 1);
399 sprintf (*repl, "%ld", value);
402 /* Parses a list of numbers for DO REPEAT. */
404 parse_numbers (struct repeat_entry *e)
406 /* First and last numbers for TO, plus the step factor. */
409 /* Alias to e->replacement. */
412 /* Number of entries in array; maximum number for this allocation
418 e->replacement = array = NULL;
422 /* Parse A TO B into a, b. */
423 if (!lex_force_int ())
431 if (!lex_force_int ())
439 if (n + (abs (b - a) + 1) > m)
441 m = n + (abs (b - a) + 1) + 16;
442 e->replacement = array = xrealloc (array,
443 m * sizeof *e->replacement);
447 store_numeric (&array[n++], a);
453 for (iter = a; iter <= b; iter++)
454 store_numeric (&array[n++], iter);
456 for (iter = a; iter >= b; iter--)
457 store_numeric (&array[n++], iter);
462 while (token != '/' && token != '.');
463 e->replacement = xrealloc (array, n * sizeof *e->replacement);
468 /* Parses a list of strings for DO REPEAT. */
470 parse_strings (struct repeat_entry * e)
476 string = e->replacement = NULL;
481 if (token != T_STRING)
484 msg (SE, _("String expected."));
485 for (i = 0; i < n; i++)
494 e->replacement = string = xrealloc (string,
495 m * sizeof *e->replacement);
497 string[n++] = lex_token_representation ();
502 while (token != '/' && token != '.');
503 e->replacement = xrealloc (string, n * sizeof *e->replacement);
509 cmd_end_repeat (void)
511 msg (SE, _("No matching DO REPEAT."));
515 /* Finds a DO REPEAT macro with name MACRO_NAME and returns the
516 appropriate subsitution if found, or NULL if not. */
518 find_DO_REPEAT_substitution (char *macro_name)
520 struct getl_script *s;
522 for (s = getl_head; s; s = s->included_from)
524 struct repeat_entry *e;
526 if (s->first_line == NULL)
529 for (e = s->macros; e; e = e->next)
530 if (!strcasecmp (e->id, macro_name))
531 return e->replacement[s->loop_index];
537 /* Makes appropriate DO REPEAT macro substitutions within getl_buf. */
539 perform_DO_REPEAT_substitutions (void)
541 /* Are we in an apostrophized string or a quoted string? */
542 int in_apos = 0, in_quote = 0;
544 /* Source pointer. */
547 /* Output buffer, size, pointer. */
548 struct string output;
553 ds_init (NULL, &output, ds_size (&getl_buf));
555 /* Strip trailing whitespace, check for & remove terminal dot. */
556 while (ds_length (&getl_buf) > 0
557 && isspace ((unsigned char) ds_end (&getl_buf)[-1]))
558 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
559 if (ds_length (&getl_buf) > 0 && ds_end (&getl_buf)[-1] == set_endcmd)
562 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
565 for (cp = ds_value (&getl_buf); cp < ds_end (&getl_buf); )
567 if (*cp == '\'' && !in_quote)
569 else if (*cp == '"' && !in_apos)
572 if (in_quote || in_apos || !CHAR_IS_ID1 (*cp))
574 ds_putchar (&output, *cp++);
578 /* Collect an identifier. */
585 while (CHAR_IS_IDN (*cp) && np < &name[8])
587 while (CHAR_IS_IDN (*cp))
591 substitution = find_DO_REPEAT_substitution (name);
594 ds_concat_buffer (&output, start, cp - start);
598 /* Force output buffer size, copy substitution. */
599 ds_concat (&output, substitution);
603 ds_putchar (&output, (unsigned char) set_endcmd);
605 ds_destroy (&getl_buf);
609 /* Debugging code. */
615 struct repeat_entry *iter;
618 printf ("DO REPEAT\n");
619 for (iter = repeat_tab; iter; iter = iter->next)
621 printf (" %s%s=", iter->id, iter->type ? "(ids)" : "");
622 for (j = 0; j < count; j++)
623 printf ("%s ", iter->replacement[j]);
624 putc (iter->next ? '/' : '.', stdout);
630 debug_print_lines (void)
632 struct getl_line_list *iter;
633 const char *fn = "(none)";
636 printf ("---begin DO REPEAT lines---\n");
637 for (iter = line_buf_head; iter; iter = iter->next)
644 printf ("%s:%d: %s", fn, ln++, iter->line);
647 printf ("---end DO REPEAT lines---\n");
649 #endif /* DEBUGGING */