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
, "PSI: CPP reset -> iter.count=%zu, next.count=%zu\n",
65 psi_plist_count(cpp
->tokens
.iter
),
66 psi_plist_count(cpp
->tokens
.next
));
67 # if PSI_CPP_DEBUG > 1
68 PSI_DEBUG_DUMP(cpp
->parser
, psi_cpp_tokiter_dump
, cpp
);
77 if (cpp
->tokens
.next
) {
78 free(cpp
->tokens
.iter
);
79 cpp
->tokens
.iter
= cpp
->tokens
.next
;
81 cpp
->tokens
.next
= psi_plist_init((psi_plist_dtor
) psi_token_free
);
84 bool psi_cpp_tokiter_seek(struct psi_cpp
*cpp
, size_t index
)
86 if (index
< psi_plist_count(cpp
->tokens
.iter
)) {
93 struct psi_token
*psi_cpp_tokiter_current(struct psi_cpp
*cpp
)
95 struct psi_token
*current
= NULL
;
96 bool found
= psi_plist_get(cpp
->tokens
.iter
, cpp
->index
, ¤t
);
103 size_t psi_cpp_tokiter_index(struct psi_cpp
*cpp
)
108 bool psi_cpp_tokiter_add_cur(struct psi_cpp
*cpp
)
110 struct psi_token
*cur
= NULL
;
112 if (psi_plist_get(cpp
->tokens
.iter
, cpp
->index
, &cur
)) {
113 struct psi_plist
*tokens
= psi_plist_add(cpp
->tokens
.next
, &cur
);
116 cpp
->tokens
.next
= tokens
;
119 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP add_cur -> index=%zu, iter.count=%zu, next.count=%zu ",
120 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
121 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, cur
);
131 bool psi_cpp_tokiter_add(struct psi_cpp
*cpp
, struct psi_token
*tok
)
133 struct psi_plist
*tokens
= psi_plist_add(cpp
->tokens
.next
, &tok
);
138 cpp
->tokens
.next
= tokens
;
141 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP add -> index=%zu, iter.count=%zu, next.count=%zu ",
142 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
143 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, tok
);
150 bool psi_cpp_tokiter_add_range(struct psi_cpp
*cpp
, size_t num_eles
, void **eles
)
152 struct psi_plist
*tokens
;
158 tokens
= psi_plist_add_r(cpp
->tokens
.next
, num_eles
, eles
);
162 cpp
->tokens
.next
= tokens
;
165 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP add_range -> index=%zu, num_eles=%zu, iter.count=%zu, next.count=%zu\n",
166 cpp
->index
, num_eles
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
173 void psi_cpp_tokiter_next(struct psi_cpp
*cpp
)
175 #if 0 && PSI_CPP_DEBUG
176 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP next -> index=%zu -> index=%zu\n",
177 cpp
->index
, cpp
->index
+1);
182 bool psi_cpp_tokiter_valid(struct psi_cpp
*cpp
)
184 #if 0 && PSI_CPP_DEBUG
185 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP valid -> index=%zu -> %d\n",
186 cpp
->index
, cpp
->index
< psi_plist_count(cpp
->tokens
.iter
));
188 return cpp
->index
< psi_plist_count(cpp
->tokens
.iter
);
191 bool psi_cpp_tokiter_del_prev(struct psi_cpp
*cpp
, bool free_token
)
193 struct psi_token
*cur
= NULL
;
196 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP del_prev -> index=%zu, iter.count=%zu, next.count=%zu\n",
197 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
200 if (psi_plist_pop(cpp
->tokens
.next
, NULL
) && psi_plist_get(cpp
->tokens
.iter
, cpp
->index
- 1, &cur
)) {
201 psi_plist_unset(cpp
->tokens
.iter
, cpp
->index
- 1);
202 if (free_token
&& cur
) {
203 psi_token_free(&cur
);
210 bool psi_cpp_tokiter_del_cur(struct psi_cpp
*cpp
, bool free_token
)
212 struct psi_token
*cur
= NULL
;
215 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP del_cur -> index=%zu, iter.count=%zu, next.count=%zu ",
216 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
219 if (psi_plist_get(cpp
->tokens
.iter
, cpp
->index
, &cur
)) {
221 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, cur
);
223 psi_plist_unset(cpp
->tokens
.iter
, cpp
->index
);
224 if (free_token
&& cur
) {
225 psi_token_free(&cur
);
234 bool psi_cpp_tokiter_del_range(struct psi_cpp
*cpp
, size_t offset
, size_t num_eles
, bool free_tokens
)
236 struct psi_token
*ptr
;
240 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP del_range -> index=%zu, offset=%zu, num_eles=%zu, iter.count=%zu, next.count=%zu\n",
241 cpp
->index
, offset
, num_eles
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
244 for (i
= offset
; i
< offset
+ num_eles
; ++i
) {
245 if (!psi_plist_get(cpp
->tokens
.iter
, i
, &ptr
)) {
249 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP del_range -> ");
250 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, ptr
);
252 psi_plist_unset(cpp
->tokens
.iter
, i
);
253 if (free_tokens
&& ptr
) {
254 psi_token_free(&ptr
);
262 bool psi_cpp_tokiter_ins_range(struct psi_cpp
*cpp
, size_t num_eles
, void **eles
)
264 struct psi_plist
*tokens
;
270 tokens
= psi_plist_ins_r(cpp
->tokens
.iter
, cpp
->index
, num_eles
, eles
);
274 cpp
->tokens
.iter
= tokens
;
277 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP ins_range -> index=%zu, num_eles=%zu, iter.count=%zu, next.count=%zu\n",
278 cpp
->index
, num_eles
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
284 bool psi_cpp_tokiter_defined(struct psi_cpp
*cpp
)
286 if (psi_cpp_tokiter_valid(cpp
)) {
287 struct psi_token
*current
= psi_cpp_tokiter_current(cpp
);
289 return psi_cpp_defined(cpp
, current
);
295 static inline size_t psi_cpp_tokiter_expand_tokens(struct psi_cpp
*cpp
,
296 struct psi_token
*target
, struct psi_plist
*tokens
)
299 bool stringify
= false, paste
= false;
300 struct psi_token
*tok
;
301 struct psi_plist
*exp
;
303 if (!psi_plist_count(tokens
)) {
307 exp
= psi_plist_init(NULL
);
308 while (psi_plist_get(tokens
, i
++, &tok
)) {
309 struct psi_token
*new_tok
;
311 if (tok
->type
== PSI_T_EOL
) {
314 if (tok
->type
== PSI_T_HASH
) {
318 if (tok
->type
== PSI_T_CPP_PASTE
) {
323 if (paste
&& psi_plist_count(exp
)) {
324 struct psi_token
*old_tok
;
325 struct psi_parser_input
*toscan
;
327 psi_plist_pop(exp
, &old_tok
);
328 new_tok
= psi_token_cat(NULL
, 2, old_tok
, tok
);
330 /* reclassify token(s) */
331 if ((toscan
= psi_parser_open_string(cpp
->parser
, new_tok
->text
->val
, new_tok
->text
->len
))) {
332 struct psi_plist
*scanned
;
334 scanned
= psi_parser_scan(cpp
->parser
, toscan
);
335 if (psi_plist_count(scanned
)) {
337 struct psi_token
*tmp_tok
;
339 exp
= psi_plist_add_r(exp
, psi_plist_count(scanned
), psi_plist_eles(scanned
));
342 while (psi_plist_get(scanned
, i
++, &tmp_tok
)) {
343 zend_string_release(tmp_tok
->file
);
344 tmp_tok
->file
= zend_string_copy(new_tok
->file
);
345 tmp_tok
->line
= new_tok
->line
;
346 tmp_tok
->col
= new_tok
->col
;
349 psi_token_free(&new_tok
);
350 psi_plist_top(scanned
, &new_tok
);
352 exp
= psi_plist_add(exp
, &new_tok
);
357 psi_parser_input_free(&toscan
);
359 exp
= psi_plist_add(exp
, &new_tok
);
362 psi_token_free(&old_tok
);
364 new_tok
= psi_token_init(stringify
? PSI_T_QUOTED_STRING
: tok
->type
,
365 tok
->text
->val
, tok
->text
->len
, target
->col
, target
->line
,
366 target
->file
?: zend_empty_string
);
368 exp
= psi_plist_add(exp
, &new_tok
);
372 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP expand > ");
373 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, new_tok
);
380 n
= psi_plist_count(exp
);
381 psi_cpp_tokiter_ins_range(cpp
, n
, psi_plist_eles(exp
));
387 static inline void psi_cpp_tokiter_free_call_tokens(
388 struct psi_plist
**arg_tokens_list
, size_t arg_count
, bool free_tokens
)
392 for (i
= 0; i
< arg_count
; ++i
) {
393 if (arg_tokens_list
[i
]) {
395 struct psi_token
*tok
;
397 while (psi_plist_pop(arg_tokens_list
[i
], &tok
)) {
398 psi_token_free(&tok
);
401 psi_plist_free(arg_tokens_list
[i
]);
404 free(arg_tokens_list
);
407 static inline struct psi_plist
**psi_cpp_tokiter_read_call_tokens(
408 struct psi_cpp
*cpp
, size_t arg_count
)
410 size_t arg_index
= 0, lparens
= 1, rparens
= 0, start
= psi_cpp_tokiter_index(cpp
);
411 struct psi_plist
**arg_tokens
= pecalloc(arg_count
, sizeof(*arg_tokens
), 1);
412 struct psi_plist
*free_tokens
= psi_plist_init((psi_plist_dtor
) psi_token_free
);
413 struct psi_token
*tok
;
415 arg_tokens
[0] = psi_plist_init(NULL
);
417 /* next token must be a LPAREN for a macro call */
418 psi_cpp_tokiter_next(cpp
);
419 tok
= psi_cpp_tokiter_current(cpp
);
420 if (!psi_cpp_tokiter_valid(cpp
) || tok
->type
!= PSI_T_LPAREN
) {
424 /* free LPAREN on success */
425 free_tokens
= psi_plist_add(free_tokens
, &tok
);
427 while (lparens
> rparens
) {
428 psi_cpp_tokiter_next(cpp
);
429 if (!psi_cpp_tokiter_valid(cpp
)) {
432 tok
= psi_cpp_tokiter_current(cpp
);
437 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
440 if (++rparens
== lparens
) {
442 if (arg_index
+ 1 < arg_count
) {
445 free_tokens
= psi_plist_add(free_tokens
, &tok
);
447 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
451 if (1 == (lparens
- rparens
)) {
452 /* too many commas? */
453 if (++arg_index
>= arg_count
) {
456 free_tokens
= psi_plist_add(free_tokens
, &tok
);
458 arg_tokens
[arg_index
] = psi_plist_init(NULL
);
460 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
464 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
468 /* ditch arg tokens */
469 psi_cpp_tokiter_del_range(cpp
, start
, psi_cpp_tokiter_index(cpp
) - start
+ 1, false);
470 psi_plist_free(free_tokens
);
475 psi_cpp_tokiter_free_call_tokens(arg_tokens
, arg_count
, false);
479 static inline void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp
*cpp
,
480 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
,
481 struct psi_plist
**arg_tokens_list
)
484 struct psi_token
*tok
;
485 struct psi_plist
*tokens
= psi_plist_init(NULL
);
488 for (i
= 0; psi_plist_get(macro
->tokens
, i
, &tok
); ++i
) {
489 struct psi_plist
*arg_tokens
= NULL
;
491 if (tok
->type
== PSI_T_HASH
|| tok
->type
== PSI_T_CPP_PASTE
) {
493 } else if (tok
->type
== PSI_T_NAME
) {
495 struct psi_token
*arg_name
;
497 for (s
= 0; psi_plist_get(macro
->sig
, s
, &arg_name
); ++s
) {
498 if (zend_string_equals(arg_name
->text
, tok
->text
)) {
500 bool processed
= psi_cpp_process(cpp
, &arg_tokens_list
[s
]);
504 arg_tokens
= arg_tokens_list
[s
];
511 tokens
= psi_plist_add_r(tokens
, psi_plist_count(arg_tokens
), psi_plist_eles(arg_tokens
));
513 tokens
= psi_plist_add(tokens
, &tok
);
517 psi_cpp_tokiter_expand_tokens(cpp
, target
, tokens
);
518 psi_plist_free(tokens
);
521 static inline bool psi_cpp_tokiter_expand_call(struct psi_cpp
*cpp
,
522 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
)
524 /* function-like macro
525 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
526 * # if FOO(1,2) // expands to if 1 > 2
528 size_t start
= psi_cpp_tokiter_index(cpp
), argc
= psi_plist_count(macro
->sig
);
529 struct psi_plist
**arg_tokens_list
;
532 /* read in tokens, until we have balanced parens */
533 arg_tokens_list
= psi_cpp_tokiter_read_call_tokens(cpp
, argc
);
534 if (!arg_tokens_list
) {
535 psi_cpp_tokiter_seek(cpp
, start
);
539 /* insert and expand macro tokens */
540 psi_cpp_tokiter_expand_call_tokens(cpp
, target
, macro
, arg_tokens_list
);
541 psi_cpp_tokiter_free_call_tokens(arg_tokens_list
, argc
, true);
543 psi_token_free(&target
);
548 static inline void psi_cpp_tokiter_expand_builtin_tokens(struct psi_cpp
*cpp
,
549 struct psi_token
*target
, struct psi_builtin
*builtin
,
550 struct psi_plist
**arg_tokens_list
)
553 struct psi_plist
*res
= NULL
;
554 size_t argc
= psi_plist_count(builtin
->decl
->sig
);
557 for (s
= 0; s
< argc
; ++s
) {
558 bool processed
= psi_cpp_process(cpp
, &arg_tokens_list
[s
]);
562 /* insert and expand macro tokens */
563 if (!builtin
->func(cpp
, target
, arg_tokens_list
, &res
)) {
564 struct psi_token
*zero
= psi_token_init(PSI_T_NUMBER
, "0", 1,
565 target
->col
, target
->line
, target
->file
);
566 psi_cpp_tokiter_ins_range(cpp
, 1, (void *) &zero
);
568 struct psi_token
*one
= psi_token_init(PSI_T_NUMBER
, "1", 1,
569 target
->col
, target
->line
, target
->file
);
570 psi_cpp_tokiter_ins_range(cpp
, 1, (void *) &one
);
572 psi_cpp_tokiter_expand_tokens(cpp
, target
, res
);
577 static inline bool psi_cpp_tokiter_expand_builtin(struct psi_cpp
*cpp
,
578 struct psi_token
*target
, struct psi_builtin
*builtin
)
580 size_t start
= psi_cpp_tokiter_index(cpp
), argc
= 0;
581 struct psi_plist
**arg_tokens_list
= NULL
;
583 if (builtin
->decl
->sig
) {
584 argc
= psi_plist_count(builtin
->decl
->sig
);
585 /* read in tokens, until we have balanced parens */
586 arg_tokens_list
= psi_cpp_tokiter_read_call_tokens(cpp
, argc
);
587 if (!arg_tokens_list
) {
588 psi_cpp_tokiter_seek(cpp
, start
);
592 psi_cpp_tokiter_next(cpp
);
595 psi_cpp_tokiter_expand_builtin_tokens(cpp
, target
, builtin
, arg_tokens_list
);
596 if (arg_tokens_list
) {
597 psi_cpp_tokiter_free_call_tokens(arg_tokens_list
, argc
, true);
600 psi_token_free(&target
);
606 static inline bool psi_cpp_tokiter_expand_def(struct psi_cpp
*cpp
,
607 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
)
609 /* delete current token from stream */
610 psi_cpp_tokiter_del_cur(cpp
, false);
611 /* replace with tokens from macro */
612 psi_cpp_tokiter_expand_tokens(cpp
, target
, macro
->tokens
);
614 psi_token_free(&target
);
619 static inline int psi_cpp_tokiter_expand_cmp(struct psi_token
*t
,
620 struct psi_cpp_macro_decl
*m
)
622 if (psi_plist_count(m
->tokens
) == 1) {
625 if (psi_plist_get(m
->tokens
, 0, &r
) && r
->text
) {
626 return !zend_string_equals(r
->text
, t
->text
);
632 static inline bool psi_cpp_tokiter_expand_decl(struct psi_cpp
*cpp
,
633 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
)
639 /* don't expand itself */
640 if (macro
->token
== target
) {
645 return psi_cpp_tokiter_expand_call(cpp
, target
, macro
);
646 } else if (psi_cpp_tokiter_expand_cmp(target
, macro
)) {
647 return psi_cpp_tokiter_expand_def(cpp
, target
, macro
);
653 bool psi_cpp_tokiter_expand(struct psi_cpp
*cpp
)
655 struct psi_token
*current
;
657 if (!psi_cpp_tokiter_valid(cpp
)) {
661 current
= psi_cpp_tokiter_current(cpp
);
666 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP expand < ");
667 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, current
);
670 if (psi_builtin_exists(current
->text
)) {
671 return psi_cpp_tokiter_expand_builtin(cpp
, current
,
672 psi_builtin_get(current
->text
));
674 return psi_cpp_tokiter_expand_decl(cpp
, current
,
675 zend_hash_find_ptr(&cpp
->defs
, current
->text
));