1 /*******************************************************************************
2 Copyright (c) 2017, Michael Wallner <mike@php.net>.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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 *******************************************************************************/
29 # include "php_config.h"
35 void psi_cpp_tokiter_dump(struct psi_dump
*dump
, struct psi_cpp
*cpp
);
36 void psi_cpp_tokiter_dump(struct psi_dump
*dump
, struct psi_cpp
*cpp
)
38 size_t i
= cpp
->index
;
46 while (psi_plist_get(cpp
->tokens
.iter
, i
, &T
)) {
47 PSI_DUMP(dump
, "PSI: CPP tokens %5zu %c ", i
, cpp
->index
== i
? '*' : ' ');
49 psi_token_dump(dump
, T
);
51 PSI_DUMP(dump
, "TOKEN deleted\n");
53 if (i
>= cpp
->index
+ 40) {
54 PSI_DUMP(dump
, "PSI: CPP tokens .....\n");
61 void psi_cpp_tokiter_reset(struct psi_cpp
*cpp
)
64 PSI_DEBUG_PRINT(cpp
->parser
,
65 "PSI: CPP reset -> iter.count=%zu, next.count=%zu, exec.count=%zu\n",
66 psi_plist_count(cpp
->tokens
.iter
),
67 psi_plist_count(cpp
->tokens
.next
),
68 psi_plist_count(cpp
->tokens
.exec
));
69 # if PSI_CPP_DEBUG > 1
70 PSI_DEBUG_DUMP(cpp
->parser
, psi_cpp_tokiter_dump
, cpp
);
80 if (cpp
->tokens
.next
) {
81 psi_plist_free(cpp
->tokens
.iter
);
82 cpp
->tokens
.iter
= cpp
->tokens
.next
;
84 cpp
->tokens
.next
= psi_plist_init((psi_plist_dtor
) psi_token_free
);
85 if (cpp
->tokens
.exec
) {
86 assert(!psi_plist_count(cpp
->tokens
.exec
));
87 psi_plist_clean(cpp
->tokens
.exec
);
89 cpp
->tokens
.exec
= psi_plist_init((psi_plist_dtor
) psi_token_free
);
93 bool psi_cpp_tokiter_seek(struct psi_cpp
*cpp
, size_t index
)
95 if (index
< psi_plist_count(cpp
->tokens
.iter
)) {
102 struct psi_token
*psi_cpp_tokiter_current(struct psi_cpp
*cpp
)
104 struct psi_token
*current
= NULL
;
105 bool found
= psi_plist_get(cpp
->tokens
.iter
, cpp
->index
, ¤t
);
112 size_t psi_cpp_tokiter_index(struct psi_cpp
*cpp
)
117 bool psi_cpp_tokiter_add_cur(struct psi_cpp
*cpp
)
119 struct psi_token
*cur
= NULL
;
121 if (psi_plist_get(cpp
->tokens
.iter
, cpp
->index
, &cur
)) {
122 psi_plist_unset(cpp
->tokens
.iter
, cpp
->index
);
124 cpp
->tokens
.exec
= psi_plist_add(cpp
->tokens
.exec
, &cur
);
126 cpp
->tokens
.next
= psi_plist_add(cpp
->tokens
.next
, &cur
);
130 PSI_DEBUG_PRINT(cpp
->parser
,
131 "PSI: CPP add_cur -> index=%zu, iter.count=%zu, next.count=%zu, exec.count=%zu ",
132 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
),
133 psi_plist_count(cpp
->tokens
.exec
));
134 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, cur
);
143 bool psi_cpp_tokiter_add(struct psi_cpp
*cpp
, struct psi_token
*tok
)
146 cpp
->tokens
.exec
= psi_plist_add(cpp
->tokens
.exec
, &tok
);
148 cpp
->tokens
.next
= psi_plist_add(cpp
->tokens
.next
, &tok
);
152 PSI_DEBUG_PRINT(cpp
->parser
,
153 "PSI: CPP add -> index=%zu, iter.count=%zu, next.count=%zu, exec.count=%zu ",
154 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
),
155 psi_plist_count(cpp
->tokens
.exec
));
156 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, tok
);
163 bool psi_cpp_tokiter_add_range(struct psi_cpp
*cpp
, size_t num_eles
, void **eles
)
170 cpp
->tokens
.exec
= psi_plist_add_r(cpp
->tokens
.exec
, num_eles
, eles
);
172 cpp
->tokens
.next
= psi_plist_add_r(cpp
->tokens
.next
, num_eles
, eles
);
176 PSI_DEBUG_PRINT(cpp
->parser
,
177 "PSI: CPP add_range -> index=%zu, num_eles=%zu, iter.count=%zu, next.count=%zu, exec.count=%zu\n",
178 cpp
->index
, num_eles
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
),
179 psi_plist_count(cpp
->tokens
.exec
));
186 void psi_cpp_tokiter_next(struct psi_cpp
*cpp
)
188 #if 0 && PSI_CPP_DEBUG
189 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP next -> index=%zu -> index=%zu\n",
190 cpp
->index
, cpp
->index
+1);
195 bool psi_cpp_tokiter_valid(struct psi_cpp
*cpp
)
197 #if 0 && PSI_CPP_DEBUG
198 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP valid -> index=%zu -> %d\n",
199 cpp
->index
, cpp
->index
< psi_plist_count(cpp
->tokens
.iter
));
201 return cpp
->index
< psi_plist_count(cpp
->tokens
.iter
);
204 bool psi_cpp_tokiter_del_prev(struct psi_cpp
*cpp
, bool free_token
)
206 struct psi_token
*cur
= NULL
;
209 PSI_DEBUG_PRINT(cpp
->parser
,
210 "PSI: CPP del_prev -> index=%zu, iter.count=%zu, next.count=%zu, exec.count=%zu\n",
211 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
),
212 psi_plist_count(cpp
->tokens
.exec
));
216 if (!psi_plist_pop(cpp
->tokens
.exec
, &cur
)) {
219 } else if (!psi_plist_pop(cpp
->tokens
.next
, &cur
)) {
222 if (free_token
&& cur
) {
223 psi_token_free(&cur
);
228 bool psi_cpp_tokiter_del_cur(struct psi_cpp
*cpp
, bool free_token
)
230 struct psi_token
*cur
= NULL
;
233 PSI_DEBUG_PRINT(cpp
->parser
,
234 "PSI: CPP del_cur -> index=%zu, iter.count=%zu, next.count=%zu, exec.count=%zu ",
235 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
),
236 psi_plist_count(cpp
->tokens
.exec
));
239 if (psi_plist_get(cpp
->tokens
.iter
, cpp
->index
, &cur
)) {
241 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, cur
);
243 if (cpp
->index
== psi_plist_count(cpp
->tokens
.iter
) - 1) {
244 psi_plist_pop(cpp
->tokens
.iter
, NULL
);
246 psi_plist_unset(cpp
->tokens
.iter
, cpp
->index
);
248 if (free_token
&& cur
) {
249 psi_token_free(&cur
);
258 bool psi_cpp_tokiter_del_range(struct psi_cpp
*cpp
, size_t offset
, size_t num_eles
, bool free_tokens
)
260 struct psi_token
*ptr
;
264 PSI_DEBUG_PRINT(cpp
->parser
,
265 "PSI: CPP del_range -> index=%zu, offset=%zu, num_eles=%zu, iter.count=%zu, next.count=%zu, exec.count=%zu\n",
266 cpp
->index
, offset
, num_eles
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
),
267 psi_plist_count(cpp
->tokens
.exec
));
270 for (i
= offset
; i
< offset
+ num_eles
; ++i
) {
271 if (!psi_plist_get(cpp
->tokens
.iter
, i
, &ptr
)) {
275 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP del_range -> ");
276 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, ptr
);
278 psi_plist_unset(cpp
->tokens
.iter
, i
);
279 if (free_tokens
&& ptr
) {
280 psi_token_free(&ptr
);
288 bool psi_cpp_tokiter_defined(struct psi_cpp
*cpp
)
290 if (psi_cpp_tokiter_valid(cpp
)) {
291 struct psi_token
*current
= psi_cpp_tokiter_current(cpp
);
293 return psi_cpp_defined(cpp
, current
);
299 static inline size_t psi_cpp_tokiter_expand_tokens(struct psi_cpp
*cpp
,
300 struct psi_token
*target
, struct psi_plist
*tokens
)
303 bool stringify
= false, paste
= false;
304 struct psi_token
*tok
;
305 struct psi_plist
*exp
;
307 if (!psi_plist_count(tokens
)) {
311 exp
= psi_plist_init(NULL
);
312 while (psi_plist_get(tokens
, i
++, &tok
)) {
313 struct psi_token
*new_tok
;
315 if (tok
->type
== PSI_T_EOL
) {
318 if (tok
->type
== PSI_T_HASH
) {
322 if (tok
->type
== PSI_T_CPP_PASTE
) {
327 if (paste
&& psi_plist_count(exp
)) {
328 struct psi_token
*old_tok
;
329 struct psi_parser_input
*toscan
;
331 psi_plist_pop(exp
, &old_tok
);
332 new_tok
= psi_token_cat(NULL
, 2, old_tok
, tok
);
334 /* reclassify token(s) */
335 if ((toscan
= psi_parser_open_string(cpp
->parser
, new_tok
->text
->val
, new_tok
->text
->len
))) {
336 struct psi_plist
*scanned
;
338 scanned
= psi_parser_scan(cpp
->parser
, toscan
);
339 if (psi_plist_count(scanned
)) {
341 struct psi_token
*tmp_tok
;
343 exp
= psi_plist_add_r(exp
, psi_plist_count(scanned
), psi_plist_eles(scanned
));
346 while (psi_plist_get(scanned
, i
++, &tmp_tok
)) {
347 zend_string_release(tmp_tok
->file
);
348 tmp_tok
->file
= zend_string_copy(new_tok
->file
);
349 tmp_tok
->line
= new_tok
->line
;
350 tmp_tok
->col
= new_tok
->col
;
353 psi_token_free(&new_tok
);
354 psi_plist_top(scanned
, &new_tok
);
356 exp
= psi_plist_add(exp
, &new_tok
);
361 psi_parser_input_free(&toscan
);
363 exp
= psi_plist_add(exp
, &new_tok
);
366 psi_token_free(&old_tok
);
368 new_tok
= psi_token_init(stringify
? PSI_T_QUOTED_STRING
: tok
->type
,
369 tok
->text
->val
, tok
->text
->len
, target
->col
, target
->line
,
370 target
->file
?: zend_empty_string
);
372 exp
= psi_plist_add(exp
, &new_tok
);
376 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP expand > ");
377 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, new_tok
);
384 bool processed
= psi_cpp_process(cpp
, &exp
, target
);
387 n
= psi_plist_count(exp
);
388 psi_cpp_tokiter_add_range(cpp
, n
, psi_plist_eles(exp
));
394 static inline void psi_cpp_tokiter_free_call_tokens(
395 struct psi_plist
**arg_tokens_list
, size_t arg_count
, bool free_tokens
)
399 for (i
= 0; i
< arg_count
; ++i
) {
400 if (arg_tokens_list
[i
]) {
402 struct psi_token
*tok
;
404 while (psi_plist_pop(arg_tokens_list
[i
], &tok
)) {
405 psi_token_free(&tok
);
408 psi_plist_free(arg_tokens_list
[i
]);
411 free(arg_tokens_list
);
414 static inline struct psi_plist
**psi_cpp_tokiter_read_call_tokens(
415 struct psi_cpp
*cpp
, size_t arg_count
)
417 size_t arg_index
= 0, lparens
= 1, rparens
= 0, start
= psi_cpp_tokiter_index(cpp
);
418 struct psi_plist
**arg_tokens
= pecalloc(arg_count
, sizeof(*arg_tokens
), 1);
419 struct psi_plist
*free_tokens
= psi_plist_init((psi_plist_dtor
) psi_token_free
);
420 struct psi_token
*tok
;
422 arg_tokens
[0] = psi_plist_init(NULL
);
424 /* next token must be a LPAREN for a macro call */
425 psi_cpp_tokiter_next(cpp
);
426 tok
= psi_cpp_tokiter_current(cpp
);
427 if (!psi_cpp_tokiter_valid(cpp
) || tok
->type
!= PSI_T_LPAREN
) {
431 /* free LPAREN on success */
432 free_tokens
= psi_plist_add(free_tokens
, &tok
);
434 while (lparens
> rparens
) {
435 psi_cpp_tokiter_next(cpp
);
436 if (!psi_cpp_tokiter_valid(cpp
)) {
439 tok
= psi_cpp_tokiter_current(cpp
);
444 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
447 if (++rparens
== lparens
) {
449 if (arg_index
+ 1 < arg_count
) {
452 free_tokens
= psi_plist_add(free_tokens
, &tok
);
454 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
458 if (1 == (lparens
- rparens
)) {
459 /* too many commas? */
460 if (++arg_index
>= arg_count
) {
463 free_tokens
= psi_plist_add(free_tokens
, &tok
);
465 arg_tokens
[arg_index
] = psi_plist_init(NULL
);
467 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
471 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
475 /* ditch arg tokens */
476 psi_cpp_tokiter_del_range(cpp
, start
, psi_cpp_tokiter_index(cpp
) - start
+ 1, false);
477 psi_plist_free(free_tokens
);
482 psi_cpp_tokiter_free_call_tokens(arg_tokens
, arg_count
, false);
486 static inline void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp
*cpp
,
487 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
,
488 struct psi_plist
**arg_tokens_list
)
491 struct psi_token
*tok
;
492 struct psi_plist
*tokens
= psi_plist_init(NULL
);
495 for (i
= 0; psi_plist_get(macro
->tokens
, i
, &tok
); ++i
) {
496 struct psi_plist
*arg_tokens
= NULL
;
498 if (tok
->type
== PSI_T_HASH
|| tok
->type
== PSI_T_CPP_PASTE
) {
500 } else if (tok
->type
== PSI_T_NAME
) {
502 struct psi_token
*arg_name
;
504 for (s
= 0; psi_plist_get(macro
->sig
, s
, &arg_name
); ++s
) {
505 if (zend_string_equals(arg_name
->text
, tok
->text
)) {
507 bool processed
= psi_cpp_process(cpp
,
508 &arg_tokens_list
[s
], target
);
512 arg_tokens
= arg_tokens_list
[s
];
519 tokens
= psi_plist_add_r(tokens
, psi_plist_count(arg_tokens
), psi_plist_eles(arg_tokens
));
521 tokens
= psi_plist_add(tokens
, &tok
);
525 psi_cpp_tokiter_expand_tokens(cpp
, target
, tokens
);
526 psi_plist_free(tokens
);
529 static inline bool psi_cpp_tokiter_expand_call(struct psi_cpp
*cpp
,
530 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
)
532 /* function-like macro
533 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
534 * # if FOO(1,2) // expands to if 1 > 2
536 size_t start
= psi_cpp_tokiter_index(cpp
), argc
= psi_plist_count(macro
->sig
);
537 struct psi_plist
**arg_tokens_list
;
540 /* read in tokens, until we have balanced parens */
541 arg_tokens_list
= psi_cpp_tokiter_read_call_tokens(cpp
, argc
);
542 if (!arg_tokens_list
) {
543 psi_cpp_tokiter_seek(cpp
, start
);
547 /* insert and expand macro tokens */
548 psi_cpp_tokiter_expand_call_tokens(cpp
, target
, macro
, arg_tokens_list
);
549 psi_cpp_tokiter_free_call_tokens(arg_tokens_list
, argc
, true);
551 psi_token_free(&target
);
556 static inline void psi_cpp_tokiter_expand_builtin_tokens(struct psi_cpp
*cpp
,
557 struct psi_token
*target
, struct psi_builtin
*builtin
,
558 struct psi_plist
**arg_tokens_list
)
561 struct psi_plist
*res
= NULL
;
562 size_t argc
= psi_plist_count(builtin
->decl
->sig
);
565 for (s
= 0; s
< argc
; ++s
) {
566 bool processed
= psi_cpp_process(cpp
, &arg_tokens_list
[s
],
571 /* insert and expand macro tokens */
572 if (!builtin
->func(cpp
, target
, arg_tokens_list
, &res
)) {
573 struct psi_token
*zero
= psi_token_init(PSI_T_NUMBER
, "0", 1,
574 target
->col
, target
->line
, target
->file
);
575 psi_cpp_tokiter_add(cpp
, zero
);
577 struct psi_token
*one
= psi_token_init(PSI_T_NUMBER
, "1", 1,
578 target
->col
, target
->line
, target
->file
);
579 psi_cpp_tokiter_add(cpp
, one
);
581 psi_cpp_tokiter_expand_tokens(cpp
, target
, res
);
586 static inline bool psi_cpp_tokiter_expand_builtin(struct psi_cpp
*cpp
,
587 struct psi_token
*target
, struct psi_builtin
*builtin
)
589 size_t start
= psi_cpp_tokiter_index(cpp
), argc
= 0;
590 struct psi_plist
**arg_tokens_list
= NULL
;
592 if (builtin
->decl
->sig
) {
593 argc
= psi_plist_count(builtin
->decl
->sig
);
594 /* read in tokens, until we have balanced parens */
595 arg_tokens_list
= psi_cpp_tokiter_read_call_tokens(cpp
, argc
);
596 if (!arg_tokens_list
) {
597 psi_cpp_tokiter_seek(cpp
, start
);
601 psi_cpp_tokiter_next(cpp
);
604 psi_cpp_tokiter_expand_builtin_tokens(cpp
, target
, builtin
, arg_tokens_list
);
605 if (arg_tokens_list
) {
606 psi_cpp_tokiter_free_call_tokens(arg_tokens_list
, argc
, true);
609 psi_token_free(&target
);
615 static inline bool psi_cpp_tokiter_expand_def(struct psi_cpp
*cpp
,
616 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
)
618 /* delete current token from stream */
619 psi_cpp_tokiter_del_cur(cpp
, false);
622 struct psi_plist
*tokens
= psi_plist_copy(macro
->tokens
,
623 (void (*)(void *)) psi_token_copy_ctor
);
624 bool processed
= psi_cpp_process(cpp
, &tokens
, target
);
627 /* replace with tokens from macro */
628 psi_cpp_tokiter_expand_tokens(cpp
, target
, tokens
);
630 psi_plist_free(tokens
);
634 psi_token_free(&target
);
638 static inline int psi_cpp_tokiter_expand_cmp(struct psi_token
*t
,
639 struct psi_cpp_macro_decl
*m
)
641 if (psi_plist_count(m
->tokens
) == 1) {
644 if (psi_plist_get(m
->tokens
, 0, &r
) && r
->text
) {
645 return !zend_string_equals(r
->text
, t
->text
);
651 static inline bool psi_cpp_tokiter_expand_decl(struct psi_cpp
*cpp
,
652 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
)
658 /* don't expand itself */
659 // if (zend_string_equals(macro->token->text, target->text)) {
660 // PSI_DEBUG_PRINT(cpp->parser, "PSI: CPP expand ~ skipping self token\n");
665 return psi_cpp_tokiter_expand_call(cpp
, target
, macro
);
666 } else if (psi_cpp_tokiter_expand_cmp(target
, macro
)) {
667 return psi_cpp_tokiter_expand_def(cpp
, target
, macro
);
673 bool psi_cpp_tokiter_expand(struct psi_cpp
*cpp
)
675 struct psi_token
*current
;
677 if (!psi_cpp_tokiter_valid(cpp
)) {
681 current
= psi_cpp_tokiter_current(cpp
);
686 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP expand < ");
687 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, current
);
690 if (psi_builtin_exists(current
->text
)) {
691 return psi_cpp_tokiter_expand_builtin(cpp
, current
,
692 psi_builtin_get(current
->text
));
694 return psi_cpp_tokiter_expand_decl(cpp
, current
,
695 zend_hash_find_ptr(&cpp
->defs
, current
->text
));