pe*alloc
[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 void psi_cpp_tokiter_dump(int fd, struct psi_cpp *cpp)
32 {
33 size_t i = cpp->index;
34 struct psi_token *T;
35
36 if (i > 40) {
37 i -= 40;
38 } else {
39 i = 0;
40 }
41 while (psi_plist_get(cpp->tokens.iter, i, &T)) {
42 dprintf(fd, "PSI: CPP tokens %5zu %c ", i, cpp->index == i ? '*' : ' ');
43 if (T) {
44 psi_token_dump(fd, T);
45 } else {
46 dprintf(fd, "TOKEN deleted\n");
47 }
48 if (i >= cpp->index + 40) {
49 dprintf(fd, "PSI: CPP tokens .....\n");
50 break;
51 }
52 ++i;
53 }
54 }
55
56 void psi_cpp_tokiter_reset(struct psi_cpp *cpp)
57 {
58 #if PSI_CPP_DEBUG
59 fprintf(stderr, "PSI: CPP reset -> iter.count=%zu, next.count=%zu\n",
60 psi_plist_count(cpp->tokens.iter),
61 psi_plist_count(cpp->tokens.next));
62 # if PSI_CPP_DEBUG > 1
63 psi_cpp_tokiter_dump(2, cpp);
64 # endif
65 #endif
66 cpp->index = 0;
67 cpp->expanded = 0;
68
69 if (cpp->tokens.next) {
70 free(cpp->tokens.iter);
71 cpp->tokens.iter = cpp->tokens.next;
72 }
73 cpp->tokens.next = psi_plist_init((psi_plist_dtor) psi_token_free);
74 }
75
76 bool psi_cpp_tokiter_seek(struct psi_cpp *cpp, size_t index)
77 {
78 if (index < psi_plist_count(cpp->tokens.iter)) {
79 cpp->index = index;
80 return true;
81 }
82 return false;
83 }
84
85 struct psi_token *psi_cpp_tokiter_current(struct psi_cpp *cpp)
86 {
87 struct psi_token *current = NULL;
88 bool found = psi_plist_get(cpp->tokens.iter, cpp->index, &current);
89
90 assert(found);
91
92 return current;
93 }
94
95 size_t psi_cpp_tokiter_index(struct psi_cpp *cpp)
96 {
97 return cpp->index;
98 }
99
100 bool psi_cpp_tokiter_add_cur(struct psi_cpp *cpp)
101 {
102 struct psi_token *cur = NULL;
103
104 if (psi_plist_get(cpp->tokens.iter, cpp->index, &cur)) {
105 struct psi_plist *tokens = psi_plist_add(cpp->tokens.next, &cur);
106
107 if (tokens) {
108 cpp->tokens.next = tokens;
109
110 #if PSI_CPP_DEBUG
111 fprintf(stderr, "PSI: CPP add_cur -> index=%zu, iter.count=%zu, next.count=%zu ",
112 cpp->index, psi_plist_count(cpp->tokens.iter), psi_plist_count(cpp->tokens.next));
113 psi_token_dump(2, cur);
114 #endif
115
116 return true;
117 }
118 }
119
120 return false;
121 }
122
123 bool psi_cpp_tokiter_add(struct psi_cpp *cpp, struct psi_token *tok)
124 {
125 struct psi_plist *tokens = psi_plist_add(cpp->tokens.next, &tok);
126
127 if (!tokens) {
128 return false;
129 }
130 cpp->tokens.next = tokens;
131
132 #if PSI_CPP_DEBUG
133 fprintf(stderr, "PSI: CPP add -> index=%zu, iter.count=%zu, next.count=%zu ",
134 cpp->index, psi_plist_count(cpp->tokens.iter), psi_plist_count(cpp->tokens.next));
135 psi_token_dump(2, tok);
136 #endif
137
138 return true;
139 }
140
141
142 bool psi_cpp_tokiter_add_range(struct psi_cpp *cpp, size_t num_eles, void **eles)
143 {
144 struct psi_plist *tokens;
145
146 if (!num_eles) {
147 return true;
148 }
149
150 tokens = psi_plist_add_r(cpp->tokens.next, num_eles, eles);
151 if (!tokens) {
152 return false;
153 }
154 cpp->tokens.next = tokens;
155
156 #if PSI_CPP_DEBUG
157 fprintf(stderr, "PSI: CPP add_range -> index=%zu, num_eles=%zu, iter.count=%zu, next.count=%zu\n",
158 cpp->index, num_eles, psi_plist_count(cpp->tokens.iter), psi_plist_count(cpp->tokens.next));
159 #endif
160
161 return true;
162 }
163
164
165 void psi_cpp_tokiter_next(struct psi_cpp *cpp)
166 {
167 #if 0 && PSI_CPP_DEBUG
168 fprintf(stderr, "PSI: CPP next -> index=%zu -> index=%zu\n",
169 cpp->index, cpp->index+1);
170 #endif
171 ++cpp->index;
172 }
173
174 bool psi_cpp_tokiter_valid(struct psi_cpp *cpp)
175 {
176 #if 0 && PSI_CPP_DEBUG
177 fprintf(stderr, "PSI: CPP valid -> index=%zu -> %d\n",
178 cpp->index, cpp->index < psi_plist_count(cpp->tokens.iter));
179 #endif
180 return cpp->index < psi_plist_count(cpp->tokens.iter);
181 }
182
183 bool psi_cpp_tokiter_del_prev(struct psi_cpp *cpp, bool free_token)
184 {
185 struct psi_token *cur = NULL;
186
187 #if PSI_CPP_DEBUG
188 fprintf(stderr, "PSI: CPP del_prev -> index=%zu, iter.count=%zu, next.count\n",
189 cpp->index, psi_plist_count(cpp->tokens.iter));
190 #endif
191
192 if (psi_plist_pop(cpp->tokens.next, NULL) && psi_plist_get(cpp->tokens.iter, cpp->index - 1, &cur)) {
193 psi_plist_unset(cpp->tokens.iter, cpp->index - 1);
194 if (free_token && cur) {
195 psi_token_free(&cur);
196 }
197 return true;
198 }
199
200 return false;
201 }
202 bool psi_cpp_tokiter_del_cur(struct psi_cpp *cpp, bool free_token)
203 {
204 struct psi_token *cur = NULL;
205
206 #if PSI_CPP_DEBUG
207 fprintf(stderr, "PSI: CPP del_cur -> index=%zu, iter.count=%zu, next.count=%zu ",
208 cpp->index, psi_plist_count(cpp->tokens.iter), psi_plist_count(cpp->tokens.next));
209 #endif
210
211 if (psi_plist_get(cpp->tokens.iter, cpp->index, &cur)) {
212 #if PSI_CPP_DEBUG
213 psi_token_dump(2, cur);
214 #endif
215 psi_plist_unset(cpp->tokens.iter, cpp->index);
216 if (free_token && cur) {
217 psi_token_free(&cur);
218 }
219 ++cpp->index;
220 return true;
221 }
222
223 return false;
224 }
225
226 bool psi_cpp_tokiter_del_range(struct psi_cpp *cpp, size_t offset, size_t num_eles, bool free_tokens)
227 {
228 struct psi_token *ptr;
229 size_t i;
230
231 #if PSI_CPP_DEBUG
232 fprintf(stderr, "PSI: CPP del_range -> index=%zu, offset=%zu, num_eles=%zu, iter.count=%zu, next.count=%zu\n",
233 cpp->index, offset, num_eles, psi_plist_count(cpp->tokens.iter), psi_plist_count(cpp->tokens.next));
234 #endif
235
236 for (i = offset; i < offset + num_eles; ++i) {
237 if (!psi_plist_get(cpp->tokens.iter, i, &ptr)) {
238 return false;
239 }
240 #if PSI_CPP_DEBUG
241 fprintf(stderr, "PSI: CPP del_range -> ");
242 psi_token_dump(2, ptr);
243 #endif
244 psi_plist_unset(cpp->tokens.iter, i);
245 if (free_tokens && ptr) {
246 psi_token_free(&ptr);
247 }
248 }
249
250 cpp->index = i;
251 return true;
252 }
253
254 bool psi_cpp_tokiter_ins_range(struct psi_cpp *cpp, size_t num_eles, void **eles)
255 {
256 struct psi_plist *tokens;
257
258 if (!num_eles) {
259 return true;
260 }
261
262 tokens = psi_plist_ins_r(cpp->tokens.iter, cpp->index, num_eles, eles);
263 if (!tokens) {
264 return false;
265 }
266 cpp->tokens.iter = tokens;
267
268 #if PSI_CPP_DEBUG
269 fprintf(stderr, "PSI: CPP ins_range -> index=%zu, num_eles=%zu, iter.count=%zu, next.count=%zu\n",
270 cpp->index, num_eles, psi_plist_count(cpp->tokens.iter), psi_plist_count(cpp->tokens.next));
271 #endif
272
273 return true;
274 }
275
276 bool psi_cpp_tokiter_defined(struct psi_cpp *cpp)
277 {
278 if (psi_cpp_tokiter_valid(cpp)) {
279 struct psi_token *current = psi_cpp_tokiter_current(cpp);
280
281 return psi_cpp_defined(cpp, current);
282 }
283
284 return false;
285 }
286
287 static size_t psi_cpp_tokiter_expand_tokens(struct psi_cpp *cpp,
288 struct psi_token *target, struct psi_plist *tokens)
289 {
290 if (tokens && psi_plist_count(tokens)) {
291 size_t i = 0, n = 0;
292 bool stringify = false, paste = false;
293 struct psi_token *tok, **exp_tokens = pecalloc(psi_plist_count(tokens), sizeof(*exp_tokens), 1);
294
295 while (psi_plist_get(tokens, i++, &tok)) {
296 struct psi_token *new_tok;
297
298 if (tok->type == PSI_T_EOL) {
299 continue;
300 }
301 if (tok->type == PSI_T_HASH) {
302 stringify = true;
303 continue;
304 }
305 if (tok->type == PSI_T_CPP_PASTE) {
306 paste = true;
307 continue;
308 }
309
310 if (paste && n > 0 && exp_tokens[n - 1]) {
311 struct psi_token *tmp_tok, *old_tok = exp_tokens[n - 1];
312
313 tmp_tok = psi_token_init(old_tok->type, "", 0,
314 target->col, target->line,
315 target->file ? target->file : zend_empty_string);
316
317 new_tok = psi_token_cat(NULL, 3, tmp_tok, old_tok, tok);
318 psi_token_free(&old_tok);
319 psi_token_free(&tmp_tok);
320
321 exp_tokens[n - 1] = new_tok;
322 } else {
323 new_tok = psi_token_init(stringify ? PSI_T_QUOTED_STRING : tok->type,
324 tok->text->val, tok->text->len, target->col, target->line,
325 target->file ?: zend_empty_string);
326
327 exp_tokens[n++] = new_tok;
328 }
329
330 #if PSI_CPP_DEBUG
331 fprintf(stderr, "PSI: CPP expand > ");
332 psi_token_dump(2, tok);
333 #endif
334
335 paste = false;
336 stringify = false;
337 }
338 psi_cpp_tokiter_ins_range(cpp, n, (void *) exp_tokens);
339 free(exp_tokens);
340
341 return n;
342 } else {
343 return 0;
344 }
345 }
346
347 static void psi_cpp_tokiter_free_call_tokens(struct psi_plist **arg_tokens_list, size_t arg_count, bool free_tokens)
348 {
349 size_t i;
350
351 for (i = 0; i < arg_count; ++i) {
352 if (arg_tokens_list[i]) {
353 if (free_tokens) {
354 struct psi_token *tok;
355
356 while (psi_plist_pop(arg_tokens_list[i], &tok)) {
357 psi_token_free(&tok);
358 }
359 }
360 psi_plist_free(arg_tokens_list[i]);
361 }
362 }
363 free(arg_tokens_list);
364 }
365
366 static struct psi_plist **psi_cpp_tokiter_read_call_tokens(
367 struct psi_cpp *cpp, size_t arg_count)
368 {
369 size_t arg_index = 0, lparens = 1, rparens = 0;
370 struct psi_plist **arg_tokens = pecalloc(arg_count, sizeof(*arg_tokens), 1);
371 struct psi_plist *free_tokens = psi_plist_init((psi_plist_dtor) psi_token_free);
372 struct psi_token *tok;
373
374 arg_tokens[0] = psi_plist_init(NULL);
375
376 /* next token must be a LPAREN for a macro call */
377 psi_cpp_tokiter_next(cpp);
378 tok = psi_cpp_tokiter_current(cpp);
379 if (!psi_cpp_tokiter_valid(cpp) || tok->type != PSI_T_LPAREN) {
380 goto fail;
381 }
382
383 /* free LPAREN on success */
384 free_tokens = psi_plist_add(free_tokens, &tok);
385
386 while (lparens > rparens) {
387 psi_cpp_tokiter_next(cpp);
388 if (!psi_cpp_tokiter_valid(cpp)) {
389 goto fail;
390 }
391 tok = psi_cpp_tokiter_current(cpp);
392
393 switch (tok->type) {
394 case PSI_T_LPAREN:
395 ++lparens;
396 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
397 break;
398 case PSI_T_RPAREN:
399 if (++rparens == lparens) {
400 /* closing RPAREN */
401 if (arg_index + 1 < arg_count) {
402 goto fail;
403 }
404 free_tokens = psi_plist_add(free_tokens, &tok);
405 } else {
406 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
407 }
408 break;
409 case PSI_T_COMMA:
410 if (1 == (lparens - rparens)) {
411 /* too many commas? */
412 if (++arg_index >= arg_count) {
413 goto fail;
414 }
415 free_tokens = psi_plist_add(free_tokens, &tok);
416 /* next arg */
417 arg_tokens[arg_index] = psi_plist_init(NULL);
418 } else {
419 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
420 }
421 break;
422 default:
423 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
424 }
425 }
426
427 psi_plist_free(free_tokens);
428 return arg_tokens;
429
430 fail:
431 psi_cpp_tokiter_free_call_tokens(arg_tokens, arg_count, false);
432 return NULL;
433 }
434
435 static void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp *cpp,
436 struct psi_token *target, struct psi_cpp_macro_decl *macro,
437 struct psi_plist **arg_tokens_list)
438 {
439 size_t i;
440 struct psi_token *tok;
441 struct psi_plist *tokens = psi_plist_init(NULL);
442
443 for (i = 0; psi_plist_get(macro->tokens, i, &tok); ++i) {
444 struct psi_plist *arg_tokens = NULL;
445
446 if (tok->type == PSI_T_NAME) {
447 size_t s;
448 struct psi_token *arg_name;
449
450 for (s = 0; psi_plist_get(macro->sig, s, &arg_name); ++s) {
451 if (zend_string_equals(arg_name->text, tok->text)) {
452 arg_tokens = arg_tokens_list[s];
453 break;
454 }
455 }
456 }
457
458 if (arg_tokens) {
459 tokens = psi_plist_add_r(tokens, psi_plist_count(arg_tokens), psi_plist_eles(arg_tokens));
460 } else {
461 tokens = psi_plist_add(tokens, &tok);
462 }
463 }
464
465 psi_cpp_tokiter_expand_tokens(cpp, target, tokens);
466 psi_plist_free(tokens);
467 }
468
469 static bool psi_cpp_tokiter_expand_call(struct psi_cpp *cpp,
470 struct psi_token *target, struct psi_cpp_macro_decl *macro)
471 {
472 /* function-like macro
473 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
474 * # if FOO(1,2) // expands to if 1 > 2
475 */
476 size_t start = psi_cpp_tokiter_index(cpp);
477 struct psi_plist **arg_tokens_list;
478
479 /* read in tokens, until we have balanced parens */
480 arg_tokens_list = psi_cpp_tokiter_read_call_tokens(cpp, psi_plist_count(macro->sig));
481 if (!arg_tokens_list) {
482 psi_cpp_tokiter_seek(cpp, start);
483 return false;
484 }
485
486 /* ditch arg tokens */
487 psi_cpp_tokiter_del_range(cpp, start, psi_cpp_tokiter_index(cpp) - start + 1, false);
488
489 /* insert and expand macro tokens */
490 psi_cpp_tokiter_expand_call_tokens(cpp, target, macro, arg_tokens_list);
491 psi_cpp_tokiter_free_call_tokens(arg_tokens_list, psi_plist_count(macro->sig), true);
492
493 psi_token_free(&target);
494 ++cpp->expanded;
495 return true;
496 }
497
498 static bool psi_cpp_tokiter_expand_def(struct psi_cpp *cpp,
499 struct psi_token *target, struct psi_cpp_macro_decl *macro)
500 {
501 /* delete current token from stream */
502 psi_cpp_tokiter_del_cur(cpp, false);
503 /* replace with tokens from macro */
504 psi_cpp_tokiter_expand_tokens(cpp, target, macro->tokens);
505
506 psi_token_free(&target);
507 ++cpp->expanded;
508 return true;
509 }
510
511 static inline int psi_cpp_tokiter_expand_cmp(struct psi_token *t,
512 struct psi_cpp_macro_decl *m)
513 {
514 if (psi_plist_count(m->tokens) == 1) {
515 struct psi_token *r;
516
517 psi_plist_get(m->tokens, 0, &r);
518
519 return !zend_string_equals(r->text, t->text);
520 }
521 return -1;
522 }
523
524 bool psi_cpp_tokiter_expand(struct psi_cpp *cpp)
525 {
526 if (psi_cpp_tokiter_valid(cpp)) {
527 struct psi_token *current = psi_cpp_tokiter_current(cpp);
528
529 if (current) {
530 struct psi_cpp_macro_decl *macro = zend_hash_find_ptr(
531 &cpp->defs, current->text);
532
533 /* don't expand itself */
534 if (macro && macro->token != current) {
535 #if PSI_CPP_DEBUG
536 fprintf(stderr, "PSI: CPP expand < ");
537 psi_token_dump(2, current);
538 #endif
539 if (macro->sig) {
540 return psi_cpp_tokiter_expand_call(cpp, current, macro);
541 } else if (psi_cpp_tokiter_expand_cmp(current, macro)) {
542 return psi_cpp_tokiter_expand_def(cpp, current, macro);
543 }
544 }
545 }
546 }
547 return false;
548 }