bison
[m6w6/ext-psi] / src / cpp_tokiter.c
1 /*******************************************************************************
2 Copyright (c) 2017, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10 * Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
15 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
18 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
20 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
21 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22 OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *******************************************************************************/
25
26 #include "php_psi_stdinc.h"
27
28 #include "cpp.h"
29 #include "parser.h"
30
31
32 void psi_cpp_tokiter_dump(int fd, struct psi_cpp *cpp)
33 {
34 size_t i;
35 struct psi_token *T;
36
37 for (i = 0; psi_plist_get(cpp->tokens, i, &T); ++i) {
38 dprintf(fd, "PSI: CPP tokens %5zu %c ", i, cpp->index == i ? '*' : ' ');
39 psi_token_dump(fd, T);
40 }
41 }
42
43 void psi_cpp_tokiter_reset(struct psi_cpp *cpp)
44 {
45 #if PSI_CPP_DEBUG
46 fprintf(stderr, "PSI: CPP reset (%zu tokens)\n", psi_plist_count(cpp->tokens));
47 # if PSI_CPP_DEBUG > 1
48 psi_cpp_tokiter_dump(2, cpp);
49 # endif
50 #endif
51 cpp->index = 0;
52 cpp->expanded = 0;
53 }
54
55 bool psi_cpp_tokiter_seek(struct psi_cpp *cpp, size_t index)
56 {
57 if (index < psi_plist_count(cpp->tokens)) {
58 cpp->index = index;
59 return true;
60 }
61 return false;
62 }
63
64 struct psi_token *psi_cpp_tokiter_current(struct psi_cpp *cpp)
65 {
66 struct psi_token *current = NULL;
67 bool found = psi_plist_get(cpp->tokens, cpp->index, &current);
68
69 assert(found);
70
71 return current;
72 }
73
74 size_t psi_cpp_tokiter_index(struct psi_cpp *cpp)
75 {
76 return cpp->index;
77 }
78
79 void psi_cpp_tokiter_next(struct psi_cpp *cpp)
80 {
81 #if 0 && PSI_CPP_DEBUG
82 fprintf(stderr, "PSI: CPP next -> index=%zu -> index=%zu\n", cpp->index, cpp->index+1);
83 #endif
84 ++cpp->index;
85 }
86
87 void psi_cpp_tokiter_prev(struct psi_cpp *cpp)
88 {
89 #if 0 && PSI_CPP_DEBUG
90 fprintf(stderr, "PSI: CPP prev -> index=%zu -> index=%zu\n", cpp->index, cpp->index-1);
91 #endif
92 if (cpp->index) {
93 --cpp->index;
94 }
95 }
96
97 bool psi_cpp_tokiter_valid(struct psi_cpp *cpp)
98 {
99 #if 0 && PSI_CPP_DEBUG
100 fprintf(stderr, "PSI: CPP valid -> index=%zu -> %d\n", cpp->index, cpp->index < psi_plist_count(cpp->tokens));
101 #endif
102 return cpp->index < psi_plist_count(cpp->tokens);
103 }
104
105 bool psi_cpp_tokiter_del_cur(struct psi_cpp *cpp, bool free_token)
106 {
107 struct psi_token *cur = NULL;
108 bool deleted = psi_plist_del(cpp->tokens, cpp->index, &cur);
109 size_t count;
110
111 #if PSI_CPP_DEBUG
112 fprintf(stderr, "PSI: CPP del_cur -> index=%zu, del=%d, free=%d, count=%zu ",
113 cpp->index, (int) deleted, (int) free_token, psi_plist_count(cpp->tokens));
114 if (cur) {
115 psi_token_dump(2, cur);
116 } else {
117 fprintf(stderr, "NULL\n");
118 }
119 #endif
120 if (cur && free_token) {
121 free(cur);
122 }
123 count = psi_plist_count(cpp->tokens);
124 if (deleted && cpp->index >= count) {
125 if (count > 0) {
126 cpp->index = count - 1;
127 } else {
128 cpp->index = 0;
129 }
130 }
131 return deleted;
132 }
133
134 bool psi_cpp_tokiter_del_range(struct psi_cpp *cpp, size_t offset, size_t num_eles, bool free_tokens)
135 {
136 struct psi_token **ptr;
137 bool deleted;
138
139 if (free_tokens) {
140 ptr = calloc(num_eles, sizeof(*ptr));
141 } else {
142 ptr = NULL;
143 }
144
145 #if PSI_CPP_DEBUG
146 fprintf(stderr, "PSI: CPP del_range -> index=%zu, offset=%zu, num_eles=%zu, count=%zu\n",
147 cpp->index, offset, num_eles, psi_plist_count(cpp->tokens));
148 #endif
149
150 deleted = psi_plist_del_r(cpp->tokens, offset, num_eles, (void *) ptr);
151
152 if (deleted) {
153 if (cpp->index >= psi_plist_count(cpp->tokens)) {
154 cpp->index = MAX(0, psi_plist_count(cpp->tokens)-1);
155 }
156 if (free_tokens) {
157 while (num_eles--) {
158 if (ptr[num_eles]) {
159 free(ptr[num_eles]);
160 }
161 }
162 free(ptr);
163 }
164 }
165 return deleted;
166 }
167
168 bool psi_cpp_tokiter_ins_cur(struct psi_cpp *cpp, struct psi_token *tok)
169 {
170 struct psi_plist *tokens = psi_plist_ins(cpp->tokens, cpp->index, &tok);
171
172 #if PSI_CPP_DEBUG
173 fprintf(stderr, "PSI: CPP ins_cur -> index=%zu ", cpp->index);
174 psi_token_dump(2, tok);
175 #endif
176
177 if (!tokens) {
178 return false;
179 }
180 cpp->tokens = tokens;
181 return true;
182 }
183
184 bool psi_cpp_tokiter_ins_range(struct psi_cpp *cpp, size_t offset,
185 size_t num_eles, void **eles)
186 {
187 struct psi_plist *tokens;
188
189 if (!num_eles) {
190 return true;
191 }
192
193 tokens = psi_plist_ins_r(cpp->tokens, offset, num_eles, eles);
194
195 #if PSI_CPP_DEBUG
196 fprintf(stderr, "PSI: CPP ins_range -> index=%zu, offset=%zu, num_eles=%zu, count=%zu\n",
197 cpp->index, offset, num_eles, psi_plist_count(tokens));
198 #endif
199
200 if (!tokens) {
201 return false;
202 }
203 cpp->tokens = tokens;
204 return true;
205 }
206
207 bool psi_cpp_tokiter_defined(struct psi_cpp *cpp)
208 {
209 if (psi_cpp_tokiter_valid(cpp)) {
210 struct psi_token *current = psi_cpp_tokiter_current(cpp);
211
212 return psi_cpp_defined(cpp, current);
213 }
214
215 return false;
216 }
217
218 void psi_cpp_tokiter_expand_tokens(struct psi_cpp *cpp, struct psi_plist *tokens)
219 {
220 if (tokens && psi_plist_count(tokens)) {
221 size_t i = 0, n = 0;
222 bool stringify = false, paste = false;
223 struct psi_token *tok, **exp_tokens = calloc(psi_plist_count(tokens), sizeof(*exp_tokens));
224
225 while (psi_plist_get(tokens, i++, &tok)) {
226 struct psi_token *new_tok;
227
228 if (tok->type == PSI_T_HASH) {
229 if (stringify) {
230 stringify = false;
231 paste = true;
232 } else {
233 stringify = true;
234 }
235 continue;
236 }
237
238 if (paste && n > 0 && exp_tokens[n - 1] &&
239 (new_tok = psi_token_cat(NULL, 2, exp_tokens[n - 1], tok))) {
240 free(exp_tokens[n - 1]);
241 exp_tokens[n - 1] = new_tok;
242 } else {
243 struct psi_token *cpy = psi_token_copy(tok);
244
245 if (stringify) {
246 cpy = psi_token_append(NULL,
247 psi_token_prepend(NULL, cpy, 1, "\""), 1, "\"");
248 cpy->type = PSI_T_QUOTED_STRING;
249 }
250 exp_tokens[n++] = cpy;
251 }
252
253 #if PSI_CPP_DEBUG
254 fprintf(stderr, "PSI: CPP expand > ");
255 psi_token_dump(2, tok);
256 #endif
257 paste = false;
258 stringify = false;
259 }
260 psi_cpp_tokiter_ins_range(cpp, psi_cpp_tokiter_index(cpp), n, (void *) exp_tokens);
261 free(exp_tokens);
262 }
263 }
264
265 static void psi_cpp_tokiter_free_call_tokens(struct psi_plist **arg_tokens_list, size_t arg_count, bool free_tokens)
266 {
267 size_t i;
268
269 for (i = 0; i < arg_count; ++i) {
270 if (arg_tokens_list[i]) {
271 if (free_tokens) {
272 struct psi_token *tok;
273
274 while (psi_plist_pop(arg_tokens_list[i], &tok)) {
275 free(tok);
276 }
277 }
278 psi_plist_free(arg_tokens_list[i]);
279 }
280 }
281 free(arg_tokens_list);
282 }
283
284 static struct psi_plist **psi_cpp_tokiter_read_call_tokens(
285 struct psi_cpp *cpp, size_t arg_count)
286 {
287 size_t arg_index = 0, lparens = 1, rparens = 0;
288 struct psi_plist **arg_tokens = calloc(arg_count, sizeof(*arg_tokens));
289 struct psi_plist *free_tokens = psi_plist_init((psi_plist_dtor) psi_token_free);
290 struct psi_token *tok;
291
292 arg_tokens[0] = psi_plist_init(NULL);
293
294 /* free macro name token on success */
295 tok = psi_cpp_tokiter_current(cpp);
296 free_tokens = psi_plist_add(free_tokens, &tok);
297
298 /* next token must be a LPAREN for a macro call */
299 psi_cpp_tokiter_next(cpp);
300 tok = psi_cpp_tokiter_current(cpp);
301 if (!psi_cpp_tokiter_valid(cpp) || tok->type != PSI_T_LPAREN) {
302 goto fail;
303 }
304
305 /* free LPAREN on success */
306 free_tokens = psi_plist_add(free_tokens, &tok);
307
308 while (lparens > rparens) {
309 psi_cpp_tokiter_next(cpp);
310 if (!psi_cpp_tokiter_valid(cpp)) {
311 goto fail;
312 }
313 tok = psi_cpp_tokiter_current(cpp);
314
315 switch (tok->type) {
316 case PSI_T_LPAREN:
317 ++lparens;
318 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
319 break;
320 case PSI_T_RPAREN:
321 if (++rparens == lparens) {
322 /* closing RPAREN */
323 if (arg_index + 1 < arg_count) {
324 goto fail;
325 }
326 free_tokens = psi_plist_add(free_tokens, &tok);
327 } else {
328 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
329 }
330 break;
331 case PSI_T_COMMA:
332 if (1 == (lparens - rparens)) {
333 /* too many commas? */
334 if (++arg_index >= arg_count) {
335 goto fail;
336 }
337 free_tokens = psi_plist_add(free_tokens, &tok);
338 /* next arg */
339 arg_tokens[arg_index] = psi_plist_init(NULL);
340 } else {
341 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
342 }
343 break;
344 default:
345 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
346 }
347 }
348
349 psi_plist_free(free_tokens);
350 return arg_tokens;
351
352 fail:
353 psi_cpp_tokiter_free_call_tokens(arg_tokens, arg_count, false);
354 return NULL;
355 }
356
357 static void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp *cpp,
358 struct psi_cpp_macro_decl *macro, struct psi_plist **arg_tokens_list)
359 {
360 size_t i;
361 struct psi_token *tok;
362 struct psi_plist *tokens = psi_plist_init(NULL);
363
364 for (i = 0; psi_plist_get(macro->tokens, i, &tok); ++i) {
365 struct psi_plist *arg_tokens = NULL;
366
367 if (tok->type == PSI_T_NAME) {
368 size_t s;
369 struct psi_token *arg_name;
370
371 for (s = 0; psi_plist_get(macro->sig, s, &arg_name); ++s) {
372 if (arg_name->size == tok->size) {
373 if (!memcmp(arg_name->text, tok->text, tok->size)) {
374 arg_tokens = arg_tokens_list[s];
375 break;
376 }
377 }
378 }
379 }
380
381 if (arg_tokens) {
382 tokens = psi_plist_add_r(tokens, psi_plist_count(arg_tokens), psi_plist_eles(arg_tokens));
383 } else {
384 tokens = psi_plist_add(tokens, &tok);
385 }
386 }
387
388 psi_cpp_tokiter_expand_tokens(cpp, tokens);
389 psi_plist_free(tokens);
390 }
391
392 static bool psi_cpp_tokiter_expand_call(struct psi_cpp *cpp,
393 struct psi_cpp_macro_decl *macro)
394 {
395 /* function-like macro
396 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
397 * # if FOO(1,2) // expands to if 1 > 2
398 */
399 size_t start = psi_cpp_tokiter_index(cpp);
400 struct psi_plist **arg_tokens_list;
401
402 /* read in tokens, until we have balanced parens */
403 arg_tokens_list = psi_cpp_tokiter_read_call_tokens(cpp, psi_plist_count(macro->sig));
404 if (!arg_tokens_list) {
405 psi_cpp_tokiter_seek(cpp, start);
406 return false;
407 }
408
409 /* ditch arg tokens */
410 psi_cpp_tokiter_del_range(cpp, start, psi_cpp_tokiter_index(cpp) - start + 1, false);
411 psi_cpp_tokiter_seek(cpp, start);
412
413 /* insert and expand macro tokens */
414 psi_cpp_tokiter_expand_call_tokens(cpp, macro, arg_tokens_list);
415 psi_cpp_tokiter_free_call_tokens(arg_tokens_list, psi_plist_count(macro->sig), true);
416
417 /* back to where we took off */
418 psi_cpp_tokiter_seek(cpp, start);
419 ++cpp->expanded;
420 return true;
421 }
422
423 bool psi_cpp_tokiter_expand(struct psi_cpp *cpp)
424 {
425 if (psi_cpp_tokiter_valid(cpp)) {
426 struct psi_token *current = psi_cpp_tokiter_current(cpp);
427
428 if (current) {
429 struct psi_cpp_macro_decl *macro = zend_hash_str_find_ptr(
430 cpp->defs, current->text, current->size);
431
432 /* don't expand itself */
433 if (macro && macro->token != current) {
434 #if PSI_CPP_DEBUG
435 fprintf(stderr, "PSI: CPP expand < ");
436 psi_token_dump(2, current);
437 #endif
438 if (macro->sig) {
439 return psi_cpp_tokiter_expand_call(cpp, macro);
440 } else {
441 size_t index = psi_cpp_tokiter_index(cpp);
442
443 /* delete current token from stream */
444 psi_cpp_tokiter_del_cur(cpp, true);
445
446 if (index != psi_cpp_tokiter_index(cpp)) {
447 /* might have been last token */
448 psi_cpp_tokiter_next(cpp);
449 }
450 /* replace with tokens from macro */
451 psi_cpp_tokiter_expand_tokens(cpp, macro->tokens);
452
453 ++cpp->expanded;
454 return true;
455 }
456 }
457 }
458 }
459 return false;
460 }