work on macro calls
[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 "libpspp/assertion.h"
24 #include "libpspp/i18n.h"
25 #include "libpspp/message.h"
26
27 #include "gettext.h"
28 #define _(msgid) gettext (msgid)
29
30 void
31 macro_destroy (struct macro *m)
32 {
33   if (!m)
34     return;
35
36   free (m->name);
37   for (size_t i = 0; i < m->n_params; i++)
38     {
39       struct macro_param *p = &m->params[i];
40       free (p->name);
41
42       tokens_uninit (&p->def);
43
44       switch (p->arg_type)
45         {
46         case ARG_N_TOKENS:
47           break;
48
49         case ARG_CHAREND:
50           token_destroy (&p->charend);
51           break;
52
53         case ARG_ENCLOSE:
54           token_destroy (&p->enclose[0]);
55           token_destroy (&p->enclose[1]);
56           break;
57
58         case ARG_CMDEND:
59           break;
60         }
61     }
62   free (m->params);
63   for (size_t i = 0; i < m->n_body; i++)
64     free (m->body[i]);
65   free (m->body);
66   free (m);
67 }
68 \f
69 const struct macro *
70 macro_set_find (const struct macro_set *set, const char *name)
71 {
72   struct macro *macro;
73
74   HMAP_FOR_EACH_WITH_HASH (macro, struct macro, hmap_node,
75                            utf8_hash_case_string (name, 0), &set->macros)
76     {
77       if (!utf8_strcasecmp (macro->name, name))
78         return macro;
79     }
80
81   return NULL;
82 }
83 \f
84 enum me_state
85   {
86     ME_START,
87
88     /* Accumulating tokens in me->params toward the end of any type of
89        argument. */
90     ME_ARG,
91
92     /* Expecting the opening delimiter of an ARG_ENCLOSE argument. */
93     ME_ENCLOSE,
94
95     /* Expecting a keyword for a keyword argument. */
96     ME_KEYWORD,
97
98     /* Expecting an equal sign for a keyword argument. */
99     ME_EQUALS,
100   };
101
102
103 struct macro_expander
104   {
105     const struct macro_set *macros;
106
107     enum me_state state;
108     size_t n_tokens;
109
110     const struct macro *macro;
111     struct tokens **args;
112     size_t arg_index;
113   };
114
115 static int
116 me_finished (struct macro_expander *me)
117 {
118   for (size_t i = 0; i < me->macro->n_params; i++)
119     if (!me->args[i])
120       {
121         me->args[i] = xmalloc (sizeof *me->args[i]);
122         tokens_copy (me->args[i], &me->macro->params[i].def);
123       }
124   return me->n_tokens;
125 }
126
127 static int
128 me_next_arg (struct macro_expander *me)
129 {
130   if (me->arg_index >= me->macro->n_params)
131     {
132       assert (!me->macro->n_params);
133       return me_finished (me);
134     }
135   else if (!me->macro->params[me->arg_index].name)
136     {
137       me->arg_index++;
138       if (me->arg_index >= me->macro->n_params)
139         return me_finished (me);
140       else
141         {
142           if (!me->macro->params[me->arg_index].name)
143             me->state = ME_ARG;
144           else
145             me->state = ME_KEYWORD;
146           return 0;
147         }
148     }
149   else
150     {
151       for (size_t i = 0; i < me->macro->n_params; i++)
152         if (!me->args[i])
153           {
154             me->state = ME_KEYWORD;
155             return 0;
156           }
157       return me_finished (me);
158     }
159 }
160
161 static int
162 me_add_start (struct macro_expander *me, const struct token *token)
163 {
164   if (token->type != T_ID && token->type != T_MACRO_ID)
165     return -1;
166
167   me->macro = macro_set_find (me->macros, token->string.string);
168   if (!me->macro)
169     return -1;
170
171   me->n_tokens = 1;
172   me->args = xcalloc (me->macro->n_params, sizeof *me->args);
173   me->arg_index = 0;
174   return me_next_arg (me);
175 }
176
177 static int
178 me_add_arg (struct macro_expander *me, const struct token *token)
179 {
180   me->n_tokens++;
181
182   struct tokens **ap = &me->args[me->arg_index];
183   if (!*ap)
184     *ap = xzalloc (sizeof **ap);
185   struct tokens *a = *ap;
186   const struct macro_param *p = &me->macro->params[me->arg_index];
187   if (p->arg_type == ARG_N_TOKENS)
188     {
189       tokens_add (a, token);
190       if (a->n >= p->n_tokens)
191         return me_next_arg (me);
192       return 0;
193     }
194   else if (p->arg_type == ARG_CMDEND)
195     {
196       if (token->type == T_ENDCMD || token->type == T_STOP)
197         return me_next_arg (me);
198       tokens_add (a, token);
199       return 0;
200     }
201   else
202     {
203       const struct token *end
204         = p->arg_type == ARG_CMDEND ? &p->charend : &p->enclose[1];
205       if (token_equal (token, end))
206         return me_next_arg (me);
207       tokens_add (a, token);
208       return 0;
209     }
210 }
211
212 static int
213 me_error (struct macro_expander *me)
214 {
215   me->state = ME_START;
216   return -1;
217 }
218
219 static int
220 me_expected (struct macro_expander *me, const struct token *token,
221              const struct token *wanted)
222 {
223   const struct macro_param *p = &me->macro->params[me->arg_index];
224   char *param_name = (p->name
225                       ? xstrdup (p->name)
226                       : xasprintf ("%zu", me->arg_index));
227   char *actual = token_to_string (token);
228   if (!actual)
229     actual = xstrdup ("<eof>");
230   char *expected = token_to_string (wanted);
231   msg (SE, _("Found `%s' while expecting `%s' reading argument %s "
232              "in call to macro %s."),
233        actual, expected, param_name, me->macro->name);
234   free (expected);
235   free (actual);
236   free (param_name);
237
238   return me_error (me);
239 }
240
241 static int
242 me_enclose (struct macro_expander *me, const struct token *token)
243 {
244   me->n_tokens++;
245
246   const struct macro_param *p = &me->macro->params[me->arg_index];
247   if (token_equal (&p->enclose[0], token))
248     {
249       me->state = ME_ARG;
250       return 0;
251     }
252
253   return me_expected (me, token, &p->enclose[0]);
254 }
255
256 static int
257 me_keyword (struct macro_expander *me, const struct token *token)
258 {
259   if (token->type != T_ID)
260     return me_finished (me);
261
262   for (size_t i = 0; i < me->macro->n_params; i++)
263     {
264       const struct macro_param *p = &me->macro->params[i];
265       if (p->name && ss_equals_case (ss_cstr (p->name), token->string))
266         {
267           me->arg_index = i;
268           if (me->args[i])
269             {
270               msg (SE,
271                    _("Argument %s multiply specified in call to macro %s."),
272                    p->name, me->macro->name);
273               return me_error (me);
274             }
275
276           me->n_tokens++;
277           me->state = ME_EQUALS;
278           return 0;
279         }
280     }
281
282   return me_finished (me);
283 }
284
285 static int
286 me_equals (struct macro_expander *me, const struct token *token)
287 {
288   me->n_tokens++;
289
290   if (token->type == T_EQUALS)
291     {
292       me->state = ME_ARG;
293       return 0;
294     }
295
296   const struct token equals = { .type = T_EQUALS };
297   return me_expected (me, token, &equals);
298 }
299
300 int
301 macro_expander_add (struct macro_expander *me, const struct token *token)
302 {
303   switch (me->state)
304     {
305     case ME_START:
306       return me_add_start (me, token);
307
308     case ME_ARG:
309       return me_add_arg (me, token);
310
311     case ME_ENCLOSE:
312       return me_enclose (me, token);
313
314     case ME_KEYWORD:
315       return me_keyword (me, token);
316
317     case ME_EQUALS:
318       return me_equals (me, token);
319
320     default:
321       NOT_REACHED ();
322     }
323 }