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 /*#define DEBUGGING 1*/
38 #include "debug-print.h"
40 /* Describes one DO REPEAT macro. */
43 int type; /* 1=variable names, 0=any other. */
44 char id[9]; /* Macro identifier. */
45 char **replacement; /* Macro replacement. */
46 struct repeat_entry *next;
49 /* List of macro identifiers. */
50 static struct repeat_entry *repeat_tab;
52 /* Number of substitutions for each macro. */
55 /* List of lines before it's actually assigned to a file. */
56 static struct getl_line_list *line_buf_head;
57 static struct getl_line_list *line_buf_tail;
59 static int parse_ids (struct repeat_entry *);
60 static int parse_numbers (struct repeat_entry *);
61 static int parse_strings (struct repeat_entry *);
62 static void clean_up (void);
63 static int internal_cmd_do_repeat (void);
66 static void debug_print (void);
67 static void debug_print_lines (void);
73 if (internal_cmd_do_repeat ())
80 /* Garbage collects all the allocated memory that's no longer
85 struct repeat_entry *iter, *next;
93 if (iter->replacement)
95 for (i = 0; i < count; i++)
96 free (iter->replacement[i]);
97 free (iter->replacement);
105 /* Allocates & appends another record at the end of the line_buf_tail
110 struct getl_line_list *new = xmalloc (sizeof *new);
112 if (line_buf_head == NULL)
113 line_buf_head = line_buf_tail = new;
115 line_buf_tail = line_buf_tail->next = new;
118 /* Returns nonzero if KEYWORD appears beginning at CONTEXT. */
120 recognize_keyword (const char *context, const char *keyword)
122 const char *end = context;
123 while (isalpha ((unsigned char) *end))
125 return lex_id_match_len (keyword, strlen (keyword), context, end - context);
128 /* Does the real work of parsing the DO REPEAT command and its nested
131 internal_cmd_do_repeat (void)
133 /* Name of first DO REPEAT macro. */
136 /* Current filename. */
137 const char *current_filename = NULL;
139 /* 1=Print lines after preprocessing. */
142 /* The first step is parsing the DO REPEAT command itself. */
144 lex_match_id ("REPEAT");
147 line_buf_head = NULL;
150 struct repeat_entry *e;
151 struct repeat_entry *iter;
154 /* Get a stand-in variable name and make sure it's unique. */
155 if (!lex_force_id ())
157 for (iter = repeat_tab; iter; iter = iter->next)
158 if (!strcmp (iter->id, tokid))
160 msg (SE, _("Identifier %s is given twice."), tokid);
164 /* Make a new stand-in variable entry and link it into the
166 e = xmalloc (sizeof *e);
168 e->next = repeat_tab;
169 strcpy (e->id, tokid);
172 /* Skip equals sign. */
174 if (!lex_force_match ('='))
177 /* Get the details of the variable's possible values. */
180 result = parse_ids (e);
181 else if (token == T_NUM)
182 result = parse_numbers (e);
183 else if (token == T_STRING)
184 result = parse_strings (e);
193 /* If this is the first variable then it defines how many
194 replacements there must be; otherwise enforce this number of
199 strcpy (first_name, e->id);
201 else if (count != result)
203 msg (SE, _("There must be the same number of substitutions "
204 "for each dummy variable specified. Since there "
205 "were %d substitutions for %s, there must be %d "
206 "for %s as well, but %d were specified."),
207 count, first_name, count, e->id, result);
214 while (token != '.');
220 /* Read all the lines inside the DO REPEAT ... END REPEAT. */
226 if (!getl_read_line ())
227 msg (FE, _("Unexpected end of file."));
229 /* If the current file has changed then record the fact. */
234 getl_location (&curfn, &curln);
235 if (current_filename != curfn)
237 assert (curln > 0 && curfn != NULL);
240 line_buf_tail->len = -curln;
241 line_buf_tail->line = xstrdup (curfn);
242 current_filename = curfn;
246 /* FIXME? This code is not strictly correct, however if you
247 have begun a line with DO REPEAT or END REPEAT and it's
248 *not* a command name, then you are obviously *trying* to
249 break this mechanism. And you will. Also, the entire
250 command names must appear on a single line--they can't be
253 char *cp = ds_value (&getl_buf);
255 /* Skip leading indentors and any whitespace. */
256 if (*cp == '+' || *cp == '-' || *cp == '.')
258 while (isspace ((unsigned char) *cp))
261 /* Find END REPEAT. */
262 if (recognize_keyword (cp, "end"))
264 while (isalpha ((unsigned char) *cp))
266 while (isspace ((unsigned char) *cp))
268 if (recognize_keyword (cp, "repeat"))
274 while (isalpha ((unsigned char) *cp))
276 while (isspace ((unsigned char) *cp))
279 print = recognize_keyword (cp, "print");
284 else /* Find DO REPEAT. */
285 if (!strncasecmp (cp, "do", 2))
288 while (isspace ((unsigned char) *cp))
290 if (!strncasecmp (cp, "rep", 3))
296 line_buf_tail->len = ds_length (&getl_buf);
297 line_buf_tail->line = xmalloc (ds_length (&getl_buf) + 1);
298 memcpy (line_buf_tail->line,
299 ds_value (&getl_buf), ds_length (&getl_buf) + 1);
303 /* FIXME: For the moment we simply discard the contents of the END
304 REPEAT line. We should actually check for the PRINT specifier.
305 This can be done easier when we buffer entire commands instead of
306 doing it token by token; see TODO. */
309 /* Tie up the loose end of the chain. */
310 if (line_buf_head == NULL)
312 msg (SW, _("No commands in scope."));
315 line_buf_tail->next = NULL;
317 /* Show the line list. */
319 debug_print_lines ();
322 /* Make new variables. */
324 struct repeat_entry *iter;
325 for (iter = repeat_tab; iter; iter = iter->next)
329 for (i = 0; i < count; i++)
331 /* Note that if the variable already exists there is no
333 struct variable *v = create_variable (&default_dict,
334 iter->replacement[i],
337 /* If we created the variable then we need to initialize
338 its observations to SYSMIS. */
345 /* Create the DO REPEAT virtual input file. */
347 struct getl_script *script = xmalloc (sizeof *script);
349 script->first_line = line_buf_head;
350 script->cur_line = NULL;
351 script->remaining_loops = count;
352 script->loop_index = -1;
353 script->macros = repeat_tab;
354 script->print = print;
356 getl_add_DO_REPEAT_file (script);
362 /* Parses a set of ids for DO REPEAT. */
364 parse_ids (struct repeat_entry * e)
370 e->replacement = NULL;
377 if (!parse_mixed_vars (&names, &nnames, PV_NONE))
380 e->replacement = xrealloc (e->replacement,
381 (nnames + n) * sizeof *e->replacement);
382 for (i = 0; i < nnames; i++)
384 e->replacement[n + i] = xstrdup (names[i]);
390 while (token != '/' && token != '.');
395 /* Stores VALUE into *REPL. */
397 store_numeric (char **repl, long value)
399 *repl = xmalloc (INT_DIGITS + 1);
400 sprintf (*repl, "%ld", value);
403 /* Parses a list of numbers for DO REPEAT. */
405 parse_numbers (struct repeat_entry *e)
407 /* First and last numbers for TO, plus the step factor. */
410 /* Alias to e->replacement. */
413 /* Number of entries in array; maximum number for this allocation
419 e->replacement = array = NULL;
423 /* Parse A TO B into a, b. */
424 if (!lex_force_int ())
432 if (!lex_force_int ())
440 if (n + (abs (b - a) + 1) > m)
442 m = n + (abs (b - a) + 1) + 16;
443 e->replacement = array = xrealloc (array,
444 m * sizeof *e->replacement);
448 store_numeric (&array[n++], a);
454 for (iter = a; iter <= b; iter++)
455 store_numeric (&array[n++], iter);
457 for (iter = a; iter >= b; iter--)
458 store_numeric (&array[n++], iter);
463 while (token != '/' && token != '.');
464 e->replacement = xrealloc (array, n * sizeof *e->replacement);
469 /* Parses a list of strings for DO REPEAT. */
471 parse_strings (struct repeat_entry * e)
477 string = e->replacement = NULL;
482 if (token != T_STRING)
485 msg (SE, _("String expected."));
486 for (i = 0; i < n; i++)
495 e->replacement = string = xrealloc (string,
496 m * sizeof *e->replacement);
498 string[n++] = lex_token_representation ();
503 while (token != '/' && token != '.');
504 e->replacement = xrealloc (string, n * sizeof *e->replacement);
510 cmd_end_repeat (void)
512 msg (SE, _("No matching DO REPEAT."));
516 /* Finds a DO REPEAT macro with name MACRO_NAME and returns the
517 appropriate subsitution if found, or NULL if not. */
519 find_DO_REPEAT_substitution (char *macro_name)
521 struct getl_script *s;
523 for (s = getl_head; s; s = s->included_from)
525 struct repeat_entry *e;
527 if (s->first_line == NULL)
530 for (e = s->macros; e; e = e->next)
531 if (!strcasecmp (e->id, macro_name))
532 return e->replacement[s->loop_index];
538 /* Makes appropriate DO REPEAT macro substitutions within getl_buf. */
540 perform_DO_REPEAT_substitutions (void)
542 /* Are we in an apostrophized string or a quoted string? */
543 int in_apos = 0, in_quote = 0;
545 /* Source pointer. */
548 /* Output buffer, size, pointer. */
549 struct string output;
554 ds_init (NULL, &output, ds_size (&getl_buf));
556 /* Strip trailing whitespace, check for & remove terminal dot. */
557 while (ds_length (&getl_buf) > 0
558 && isspace ((unsigned char) ds_end (&getl_buf)[-1]))
559 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
560 if (ds_length (&getl_buf) > 0 && ds_end (&getl_buf)[-1] == set_endcmd)
563 ds_truncate (&getl_buf, ds_length (&getl_buf) - 1);
566 for (cp = ds_value (&getl_buf); cp < ds_end (&getl_buf); )
568 if (*cp == '\'' && !in_quote)
570 else if (*cp == '"' && !in_apos)
573 if (in_quote || in_apos || !CHAR_IS_ID1 (*cp))
575 ds_putchar (&output, *cp++);
579 /* Collect an identifier. */
586 while (CHAR_IS_IDN (*cp) && np < &name[8])
588 while (CHAR_IS_IDN (*cp))
592 substitution = find_DO_REPEAT_substitution (name);
595 ds_concat_buffer (&output, start, cp - start);
599 /* Force output buffer size, copy substitution. */
600 ds_concat (&output, substitution);
604 ds_putchar (&output, (unsigned char) set_endcmd);
606 ds_destroy (&getl_buf);
610 /* Debugging code. */
616 struct repeat_entry *iter;
619 printf ("DO REPEAT\n");
620 for (iter = repeat_tab; iter; iter = iter->next)
622 printf (" %s%s=", iter->id, iter->type ? "(ids)" : "");
623 for (j = 0; j < count; j++)
624 printf ("%s ", iter->replacement[j]);
625 putc (iter->next ? '/' : '.', stdout);
631 debug_print_lines (void)
633 struct getl_line_list *iter;
634 const char *fn = "(none)";
637 printf ("---begin DO REPEAT lines---\n");
638 for (iter = line_buf_head; iter; iter = iter->next)
645 printf ("%s:%d: %s", fn, ln++, iter->line);
648 printf ("---end DO REPEAT lines---\n");
650 #endif /* DEBUGGING */