b94a04ad7f4be3455c796e7531e7b0f3f39a01fb
[pspp] / src / language / lexer / macro.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2021 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "language/lexer/macro.h"
20
21 #include <stdlib.h>
22
23 #include "language/lexer/segment.h"
24 #include "language/lexer/scan.h"
25 #include "libpspp/assertion.h"
26 #include "libpspp/i18n.h"
27 #include "libpspp/message.h"
28 #include "libpspp/str.h"
29
30 #include "gettext.h"
31 #define _(msgid) gettext (msgid)
32
33 void
34 macro_destroy (struct macro *m)
35 {
36   if (!m)
37     return;
38
39   free (m->name);
40   for (size_t i = 0; i < m->n_params; i++)
41     {
42       struct macro_param *p = &m->params[i];
43       free (p->name);
44
45       tokens_uninit (&p->def);
46
47       switch (p->arg_type)
48         {
49         case ARG_N_TOKENS:
50           break;
51
52         case ARG_CHAREND:
53           token_destroy (&p->charend);
54           break;
55
56         case ARG_ENCLOSE:
57           token_destroy (&p->enclose[0]);
58           token_destroy (&p->enclose[1]);
59           break;
60
61         case ARG_CMDEND:
62           break;
63         }
64     }
65   free (m->params);
66   ss_dealloc (&m->body);
67   free (m);
68 }
69 \f
70 struct macro_set *
71 macro_set_create (void)
72 {
73   struct macro_set *set = xmalloc (sizeof *set);
74   *set = (struct macro_set) {
75     .macros = HMAP_INITIALIZER (set->macros),
76   };
77   return set;
78 }
79
80 void
81 macro_set_destroy (struct macro_set *set)
82 {
83   if (!set)
84     return;
85
86   struct macro *macro, *next;
87   HMAP_FOR_EACH_SAFE (macro, next, struct macro, hmap_node, &set->macros)
88     {
89       hmap_delete (&set->macros, &macro->hmap_node);
90       macro_destroy (macro);
91     }
92   hmap_destroy (&set->macros);
93   free (set);
94 }
95
96 static unsigned int
97 hash_macro_name (const char *name)
98 {
99   return utf8_hash_case_string (name, 0);
100 }
101
102 static struct macro *
103 macro_set_find__ (struct macro_set *set, const char *name)
104 {
105   struct macro *macro;
106   HMAP_FOR_EACH_WITH_HASH (macro, struct macro, hmap_node,
107                            hash_macro_name (name), &set->macros)
108     if (!utf8_strcasecmp (macro->name, name))
109       return macro;
110
111   return NULL;
112 }
113
114 const struct macro *
115 macro_set_find (const struct macro_set *set, const char *name)
116 {
117   return macro_set_find__ (CONST_CAST (struct macro_set *, set), name);
118 }
119
120 /* Adds M to SET.  M replaces any existing macro with the same name.  Takes
121    ownership of M. */
122 void
123 macro_set_add (struct macro_set *set, struct macro *m)
124 {
125   struct macro *victim = macro_set_find__ (set, m->name);
126   if (victim)
127     {
128       hmap_delete (&set->macros, &victim->hmap_node);
129       macro_destroy (victim);
130     }
131
132   hmap_insert (&set->macros, &m->hmap_node, hash_macro_name (m->name));
133 }
134 \f
135 enum me_state
136   {
137     ME_START,
138
139     /* Accumulating tokens in me->params toward the end of any type of
140        argument. */
141     ME_ARG,
142
143     /* Expecting the opening delimiter of an ARG_ENCLOSE argument. */
144     ME_ENCLOSE,
145
146     /* Expecting a keyword for a keyword argument. */
147     ME_KEYWORD,
148
149     /* Expecting an equal sign for a keyword argument. */
150     ME_EQUALS,
151   };
152
153
154 struct macro_expander
155   {
156     const struct macro_set *macros;
157
158     enum me_state state;
159     size_t n_tokens;
160
161     const struct macro *macro;
162     struct tokens **args;
163     size_t arg_index;
164   };
165
166 static int
167 me_finished (struct macro_expander *me)
168 {
169   for (size_t i = 0; i < me->macro->n_params; i++)
170     if (!me->args[i])
171       {
172         me->args[i] = xmalloc (sizeof *me->args[i]);
173         tokens_copy (me->args[i], &me->macro->params[i].def);
174       }
175   return me->n_tokens;
176 }
177
178 static int
179 me_next_arg (struct macro_expander *me)
180 {
181   if (me->arg_index >= me->macro->n_params)
182     {
183       assert (!me->macro->n_params);
184       return me_finished (me);
185     }
186   else if (!me->macro->params[me->arg_index].name)
187     {
188       me->arg_index++;
189       if (me->arg_index >= me->macro->n_params)
190         return me_finished (me);
191       else
192         {
193           if (!me->macro->params[me->arg_index].name)
194             me->state = ME_ARG;
195           else
196             me->state = ME_KEYWORD;
197           return 0;
198         }
199     }
200   else
201     {
202       for (size_t i = 0; i < me->macro->n_params; i++)
203         if (!me->args[i])
204           {
205             me->state = ME_KEYWORD;
206             return 0;
207           }
208       return me_finished (me);
209     }
210 }
211
212 static int
213 me_add_start (struct macro_expander *me, const struct token *token)
214 {
215   if (token->type != T_ID && token->type != T_MACRO_ID)
216     return -1;
217
218   me->macro = macro_set_find (me->macros, token->string.string);
219   if (!me->macro)
220     return -1;
221
222   me->n_tokens = 1;
223   me->args = xcalloc (me->macro->n_params, sizeof *me->args);
224   me->arg_index = 0;
225   return me_next_arg (me);
226 }
227
228 static int
229 me_error (struct macro_expander *me)
230 {
231   me->state = ME_START;
232   return -1;
233 }
234
235 static int
236 me_add_arg (struct macro_expander *me, const struct token *token)
237 {
238   const struct macro_param *p = &me->macro->params[me->arg_index];
239   if (token->type == T_STOP)
240     {
241       char *param_name = (p->name
242                           ? xstrdup (p->name)
243                           : xasprintf ("%zu", me->arg_index));
244       msg (SE, _("Unexpected end of file reading argument %s "
245                  "to macro %s."), param_name, me->macro->name);
246       free (param_name);
247
248       return me_error (me);
249     }
250
251   me->n_tokens++;
252
253   struct tokens **argp = &me->args[me->arg_index];
254   if (!*argp)
255     *argp = xzalloc (sizeof **argp);
256   struct tokens *arg = *argp;
257   if (p->arg_type == ARG_N_TOKENS)
258     {
259       tokens_add (arg, token);
260       if (arg->n >= p->n_tokens)
261         return me_next_arg (me);
262       return 0;
263     }
264   else if (p->arg_type == ARG_CMDEND)
265     {
266       if (token->type == T_ENDCMD || token->type == T_STOP)
267         return me_next_arg (me);
268       tokens_add (arg, token);
269       return 0;
270     }
271   else
272     {
273       const struct token *end
274         = p->arg_type == ARG_CMDEND ? &p->charend : &p->enclose[1];
275       if (token_equal (token, end))
276         return me_next_arg (me);
277       tokens_add (arg, token);
278       return 0;
279     }
280 }
281
282 static int
283 me_expected (struct macro_expander *me, const struct token *token,
284              const struct token *wanted)
285 {
286   const struct macro_param *p = &me->macro->params[me->arg_index];
287   char *param_name = (p->name
288                       ? xstrdup (p->name)
289                       : xasprintf ("%zu", me->arg_index));
290   char *actual = token_to_string (token);
291   if (!actual)
292     actual = xstrdup ("<eof>");
293   char *expected = token_to_string (wanted);
294   msg (SE, _("Found `%s' while expecting `%s' reading argument %s "
295              "to macro %s."),
296        actual, expected, param_name, me->macro->name);
297   free (expected);
298   free (actual);
299   free (param_name);
300
301   return me_error (me);
302 }
303
304 static int
305 me_enclose (struct macro_expander *me, const struct token *token)
306 {
307   me->n_tokens++;
308
309   const struct macro_param *p = &me->macro->params[me->arg_index];
310   if (token_equal (&p->enclose[0], token))
311     {
312       me->state = ME_ARG;
313       return 0;
314     }
315
316   return me_expected (me, token, &p->enclose[0]);
317 }
318
319 static int
320 me_keyword (struct macro_expander *me, const struct token *token)
321 {
322   if (token->type != T_ID)
323     return me_finished (me);
324
325   for (size_t i = 0; i < me->macro->n_params; i++)
326     {
327       const struct macro_param *p = &me->macro->params[i];
328       if (p->name && ss_equals_case (ss_cstr (p->name), token->string))
329         {
330           me->arg_index = i;
331           if (me->args[i])
332             {
333               msg (SE,
334                    _("Argument %s multiply specified in call to macro %s."),
335                    p->name, me->macro->name);
336               return me_error (me);
337             }
338
339           me->n_tokens++;
340           me->state = ME_EQUALS;
341           return 0;
342         }
343     }
344
345   return me_finished (me);
346 }
347
348 static int
349 me_equals (struct macro_expander *me, const struct token *token)
350 {
351   me->n_tokens++;
352
353   if (token->type == T_EQUALS)
354     {
355       me->state = ME_ARG;
356       return 0;
357     }
358
359   const struct token equals = { .type = T_EQUALS };
360   return me_expected (me, token, &equals);
361 }
362
363 int
364 macro_expander_create (const struct macro_set *macros,
365                        const struct token *token,
366                        struct macro_expander **mep)
367 {
368   *mep = NULL;
369   if (macro_set_is_empty (macros))
370     return -1;
371   if (token->type != T_ID && token->type != T_MACRO_ID)
372     return -1;
373
374   const struct macro *macro = macro_set_find (macros, token->string.string);
375   if (!macro)
376     return -1;
377
378   struct macro_expander *me = xmalloc (sizeof *me);
379   *me = (struct macro_expander) {
380     .macros = macros,
381
382     .state = ME_START,
383     .n_tokens = 1,
384
385     .macro = macro,
386     .args = xcalloc (macro->n_params, sizeof *me->args),
387     .arg_index = 0,
388   };
389   *mep = me;
390   return me_next_arg (me);
391 }
392
393 void
394 macro_expander_destroy (struct macro_expander *me)
395 {
396   if (!me)
397     return;
398
399   for (size_t i = 0; i < me->macro->n_params; i++)
400     if (me->args[i])
401       {
402         tokens_uninit (me->args[i]);
403         free (me->args[i]);
404       }
405   free (me->args);
406   free (me);
407 }
408
409 /* Adds TOKEN to the collection of tokens in ME that potentially need to be
410    macro expanded.
411
412    Returns -1 if the tokens added do not actually invoke a macro.  The caller
413    should consume the first token without expanding it.
414
415    Returns 0 if the macro expander needs more tokens, for macro arguments or to
416    decide whether this is actually a macro invocation.  The caller should call
417    macro_expander_add() again with the next token.
418
419    Returns a positive number to indicate that the returned number of tokens
420    invoke a macro.  The number returned might be less than the number of tokens
421    added because it can take a few tokens of lookahead to determine whether the
422    macro invocation is finished.  The caller should call
423    macro_expander_get_expansion() to obtain the expansion. */
424 int
425 macro_expander_add (struct macro_expander *me, const struct token *token)
426 {
427   switch (me->state)
428     {
429     case ME_START:
430       return me_add_start (me, token);
431
432     case ME_ARG:
433       return me_add_arg (me, token);
434
435     case ME_ENCLOSE:
436       return me_enclose (me, token);
437
438     case ME_KEYWORD:
439       return me_keyword (me, token);
440
441     case ME_EQUALS:
442       return me_equals (me, token);
443
444     default:
445       NOT_REACHED ();
446     }
447 }
448
449 void
450 macro_expander_get_expansion (struct macro_expander *me, struct tokens *exp)
451 {
452   struct state
453     {
454       struct segmenter segmenter;
455       struct substring body;
456     };
457
458   struct state state;
459   segmenter_init (&state.segmenter, SEG_MODE_INTERACTIVE /*XXX*/);
460   state.body = me->macro->body;
461
462   struct state saved = state;
463
464   struct token token = { .type = T_STOP };
465
466   while (state.body.length > 0)
467     {
468       struct scanner scanner;
469       scanner_init (&scanner, &token);
470
471       for (;;)
472         {
473           enum segment_type type;
474           int seg_len = segmenter_push (&state.segmenter, state.body.string,
475                                         state.body.length, true, &type);
476           assert (seg_len >= 0);
477
478           struct substring segment = ss_head (state.body, seg_len);
479           ss_advance (&state.body, seg_len);
480           printf ("segment \"%.*s\" %s token.type=%d\n", (int) segment.length, segment.string, segment_type_to_string (type), token.type);
481
482           enum scan_result result = scanner_push (&scanner, type, segment, &token);
483           if (result == SCAN_SAVE)
484             saved = state;
485           else if (result == SCAN_BACK)
486             {
487               printf ("back\n");
488               state = saved;
489               break;
490             }
491           else if (result == SCAN_DONE)
492             {
493               printf ("done\n");
494               break;
495             }
496         }
497
498       /* We have a token in 'token'. */
499       printf ("add token %d %s\n", token.type, token_type_to_name (token.type));
500       if (is_scan_type (token.type))
501         {
502           /* XXX report error if it's not SCAN_SKIP */
503         }
504       else
505         tokens_add (exp, &token);
506       token_destroy (&token);
507     }
508 }