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 struct variable *v = create_variable (&default_dict,
332 iter->replacement[i],
335 /* If we created the variable then we need to initialize
336 its observations to SYSMIS. */
343 /* Create the DO REPEAT virtual input file. */
345 struct getl_script *script = xmalloc (sizeof *script);
347 script->first_line = line_buf_head;
348 script->cur_line = NULL;
349 script->remaining_loops = count;
350 script->loop_index = -1;
351 script->macros = repeat_tab;
352 script->print = print;
354 getl_add_DO_REPEAT_file (script);
360 /* Parses a set of ids for DO REPEAT. */
362 parse_ids (struct repeat_entry * e)
368 e->replacement = NULL;
375 if (!parse_mixed_vars (&names, &nnames, PV_NONE))
378 e->replacement = xrealloc (e->replacement,
379 (nnames + n) * sizeof *e->replacement);
380 for (i = 0; i < nnames; i++)
382 e->replacement[n + i] = xstrdup (names[i]);
388 while (token != '/' && token != '.');
393 /* Stores VALUE into *REPL. */
395 store_numeric (char **repl, long value)
397 *repl = xmalloc (INT_DIGITS + 1);
398 sprintf (*repl, "%ld", value);
401 /* Parses a list of numbers for DO REPEAT. */
403 parse_numbers (struct repeat_entry *e)
405 /* First and last numbers for TO, plus the step factor. */
408 /* Alias to e->replacement. */
411 /* Number of entries in array; maximum number for this allocation
417 e->replacement = array = NULL;
421 /* Parse A TO B into a, b. */
422 if (!lex_force_int ())
430 if (!lex_force_int ())
438 if (n + (abs (b - a) + 1) > m)
440 m = n + (abs (b - a) + 1) + 16;
441 e->replacement = array = xrealloc (array,
442 m * sizeof *e->replacement);
446 store_numeric (&array[n++], a);
452 for (iter = a; iter <= b; iter++)
453 store_numeric (&array[n++], iter);
455 for (iter = a; iter >= b; iter--)
456 store_numeric (&array[n++], iter);
461 while (token != '/' && token != '.');
462 e->replacement = xrealloc (array, n * sizeof *e->replacement);
467 /* Parses a list of strings for DO REPEAT. */
469 parse_strings (struct repeat_entry * e)
475 string = e->replacement = NULL;
480 if (token != T_STRING)
483 msg (SE, _("String expected."));
484 for (i = 0; i < n; i++)
493 e->replacement = string = xrealloc (string,
494 m * sizeof *e->replacement);
496 string[n++] = lex_token_representation ();
501 while (token != '/' && token != '.');
502 e->replacement = xrealloc (string, n * sizeof *e->replacement);
508 cmd_end_repeat (void)
510 msg (SE, _("No matching DO REPEAT."));
514 /* Finds a DO REPEAT macro with name MACRO_NAME and returns the
515 appropriate subsitution if found, or NULL if not. */
517 find_DO_REPEAT_substitution (char *macro_name)
519 struct getl_script *s;
521 for (s = getl_head; s; s = s->included_from)
523 struct repeat_entry *e;
525 if (s->first_line == NULL)
528 for (e = s->macros; e; e = e->next)
529 if (!strcasecmp (e->id, macro_name))
530 return e->replacement[s->loop_index];
536 /* Makes appropriate DO REPEAT macro substitutions within getl_buf. */
538 perform_DO_REPEAT_substitutions (void)
540 /* Are we in an apostrophized string or a quoted string? */
541 int in_apos = 0, in_quote = 0;
543 /* Source pointer. */
546 /* Output buffer, size, pointer. */
547 struct string output;
552 ds_init (NULL, &output, ds_size (&getl_buf));
554 /* Strip trailing whitespace, check for & remove terminal dot. */
555 while (ds_length (&getl_buf) > 0
556 && isspace ((unsigned char) ds_end (&getl_buf)[-1]))
557 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
558 if (ds_length (&getl_buf) > 0 && ds_end (&getl_buf)[-1] == set_endcmd)
561 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
564 for (cp = ds_value (&getl_buf); cp < ds_end (&getl_buf); )
566 if (*cp == '\'' && !in_quote)
568 else if (*cp == '"' && !in_apos)
571 if (in_quote || in_apos || !CHAR_IS_ID1 (*cp))
573 ds_putchar (&output, *cp++);
577 /* Collect an identifier. */
584 while (CHAR_IS_IDN (*cp) && np < &name[8])
586 while (CHAR_IS_IDN (*cp))
590 substitution = find_DO_REPEAT_substitution (name);
593 ds_concat_buffer (&output, start, cp - start);
597 /* Force output buffer size, copy substitution. */
598 ds_concat (&output, substitution);
602 ds_putchar (&output, (unsigned char) set_endcmd);
604 ds_destroy (&getl_buf);
608 /* Debugging code. */
614 struct repeat_entry *iter;
617 printf ("DO REPEAT\n");
618 for (iter = repeat_tab; iter; iter = iter->next)
620 printf (" %s%s=", iter->id, iter->type ? "(ids)" : "");
621 for (j = 0; j < count; j++)
622 printf ("%s ", iter->replacement[j]);
623 putc (iter->next ? '/' : '.', stdout);
629 debug_print_lines (void)
631 struct getl_line_list *iter;
632 const char *fn = "(none)";
635 printf ("---begin DO REPEAT lines---\n");
636 for (iter = line_buf_head; iter; iter = iter->next)
643 printf ("%s:%d: %s", fn, ln++, iter->line);
646 printf ("---end DO REPEAT lines---\n");
648 #endif /* DEBUGGING */