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 *******************************************************************************/
26 #include "php_psi_stdinc.h"
31 void psi_cpp_tokiter_dump(int fd
, struct psi_cpp
*cpp
)
33 size_t i
= cpp
->index
;
41 while (psi_plist_get(cpp
->tokens
.iter
, i
, &T
)) {
42 dprintf(fd
, "PSI: CPP tokens %5zu %c ", i
, cpp
->index
== i
? '*' : ' ');
44 psi_token_dump(fd
, T
);
46 dprintf(fd
, "TOKEN deleted\n");
48 if (i
>= cpp
->index
+ 40) {
49 dprintf(fd
, "PSI: CPP tokens .....\n");
56 void psi_cpp_tokiter_reset(struct psi_cpp
*cpp
)
59 PSI_DEBUG_PRINT(cpp
->parser
, "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_DEBUG_DUMP(cpp
->parser
, psi_cpp_tokiter_dump
, cpp
);
72 if (cpp
->tokens
.next
) {
73 free(cpp
->tokens
.iter
);
74 cpp
->tokens
.iter
= cpp
->tokens
.next
;
76 cpp
->tokens
.next
= psi_plist_init((psi_plist_dtor
) psi_token_free
);
79 bool psi_cpp_tokiter_seek(struct psi_cpp
*cpp
, size_t index
)
81 if (index
< psi_plist_count(cpp
->tokens
.iter
)) {
88 struct psi_token
*psi_cpp_tokiter_current(struct psi_cpp
*cpp
)
90 struct psi_token
*current
= NULL
;
91 bool found
= psi_plist_get(cpp
->tokens
.iter
, cpp
->index
, ¤t
);
98 size_t psi_cpp_tokiter_index(struct psi_cpp
*cpp
)
103 bool psi_cpp_tokiter_add_cur(struct psi_cpp
*cpp
)
105 struct psi_token
*cur
= NULL
;
107 if (psi_plist_get(cpp
->tokens
.iter
, cpp
->index
, &cur
)) {
108 struct psi_plist
*tokens
= psi_plist_add(cpp
->tokens
.next
, &cur
);
111 cpp
->tokens
.next
= tokens
;
114 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP add_cur -> index=%zu, iter.count=%zu, next.count=%zu ",
115 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
116 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, cur
);
126 bool psi_cpp_tokiter_add(struct psi_cpp
*cpp
, struct psi_token
*tok
)
128 struct psi_plist
*tokens
= psi_plist_add(cpp
->tokens
.next
, &tok
);
133 cpp
->tokens
.next
= tokens
;
136 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP add -> index=%zu, iter.count=%zu, next.count=%zu ",
137 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
138 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, tok
);
145 bool psi_cpp_tokiter_add_range(struct psi_cpp
*cpp
, size_t num_eles
, void **eles
)
147 struct psi_plist
*tokens
;
153 tokens
= psi_plist_add_r(cpp
->tokens
.next
, num_eles
, eles
);
157 cpp
->tokens
.next
= tokens
;
160 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP add_range -> index=%zu, num_eles=%zu, iter.count=%zu, next.count=%zu\n",
161 cpp
->index
, num_eles
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
168 void psi_cpp_tokiter_next(struct psi_cpp
*cpp
)
170 #if 0 && PSI_CPP_DEBUG
171 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP next -> index=%zu -> index=%zu\n",
172 cpp
->index
, cpp
->index
+1);
177 bool psi_cpp_tokiter_valid(struct psi_cpp
*cpp
)
179 #if 0 && PSI_CPP_DEBUG
180 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP valid -> index=%zu -> %d\n",
181 cpp
->index
, cpp
->index
< psi_plist_count(cpp
->tokens
.iter
));
183 return cpp
->index
< psi_plist_count(cpp
->tokens
.iter
);
186 bool psi_cpp_tokiter_del_prev(struct psi_cpp
*cpp
, bool free_token
)
188 struct psi_token
*cur
= NULL
;
191 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP del_prev -> index=%zu, iter.count=%zu, next.count=%zu\n",
192 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
195 if (psi_plist_pop(cpp
->tokens
.next
, NULL
) && psi_plist_get(cpp
->tokens
.iter
, cpp
->index
- 1, &cur
)) {
196 psi_plist_unset(cpp
->tokens
.iter
, cpp
->index
- 1);
197 if (free_token
&& cur
) {
198 psi_token_free(&cur
);
205 bool psi_cpp_tokiter_del_cur(struct psi_cpp
*cpp
, bool free_token
)
207 struct psi_token
*cur
= NULL
;
210 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP del_cur -> index=%zu, iter.count=%zu, next.count=%zu ",
211 cpp
->index
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
214 if (psi_plist_get(cpp
->tokens
.iter
, cpp
->index
, &cur
)) {
216 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, cur
);
218 psi_plist_unset(cpp
->tokens
.iter
, cpp
->index
);
219 if (free_token
&& cur
) {
220 psi_token_free(&cur
);
229 bool psi_cpp_tokiter_del_range(struct psi_cpp
*cpp
, size_t offset
, size_t num_eles
, bool free_tokens
)
231 struct psi_token
*ptr
;
235 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP del_range -> index=%zu, offset=%zu, num_eles=%zu, iter.count=%zu, next.count=%zu\n",
236 cpp
->index
, offset
, num_eles
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
239 for (i
= offset
; i
< offset
+ num_eles
; ++i
) {
240 if (!psi_plist_get(cpp
->tokens
.iter
, i
, &ptr
)) {
244 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP del_range -> ");
245 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, ptr
);
247 psi_plist_unset(cpp
->tokens
.iter
, i
);
248 if (free_tokens
&& ptr
) {
249 psi_token_free(&ptr
);
257 bool psi_cpp_tokiter_ins_range(struct psi_cpp
*cpp
, size_t num_eles
, void **eles
)
259 struct psi_plist
*tokens
;
265 tokens
= psi_plist_ins_r(cpp
->tokens
.iter
, cpp
->index
, num_eles
, eles
);
269 cpp
->tokens
.iter
= tokens
;
272 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP ins_range -> index=%zu, num_eles=%zu, iter.count=%zu, next.count=%zu\n",
273 cpp
->index
, num_eles
, psi_plist_count(cpp
->tokens
.iter
), psi_plist_count(cpp
->tokens
.next
));
279 bool psi_cpp_tokiter_defined(struct psi_cpp
*cpp
)
281 if (psi_cpp_tokiter_valid(cpp
)) {
282 struct psi_token
*current
= psi_cpp_tokiter_current(cpp
);
284 return psi_cpp_defined(cpp
, current
);
290 static inline size_t psi_cpp_tokiter_expand_tokens(struct psi_cpp
*cpp
,
291 struct psi_token
*target
, struct psi_plist
*tokens
)
294 bool stringify
= false, paste
= false;
295 struct psi_token
*tok
;
296 struct psi_plist
*exp
;
298 if (!psi_plist_count(tokens
)) {
302 exp
= psi_plist_init(NULL
);
303 while (psi_plist_get(tokens
, i
++, &tok
)) {
304 struct psi_token
*new_tok
;
306 if (tok
->type
== PSI_T_EOL
) {
309 if (tok
->type
== PSI_T_HASH
) {
313 if (tok
->type
== PSI_T_CPP_PASTE
) {
318 if (paste
&& psi_plist_count(exp
)) {
319 struct psi_token
*old_tok
;
320 struct psi_parser_input
*toscan
;
322 psi_plist_pop(exp
, &old_tok
);
323 new_tok
= psi_token_cat(NULL
, 2, old_tok
, tok
);
325 /* reclassify token(s) */
326 if ((toscan
= psi_parser_open_string(cpp
->parser
, new_tok
->text
->val
, new_tok
->text
->len
))) {
327 struct psi_plist
*scanned
;
329 scanned
= psi_parser_scan(cpp
->parser
, toscan
);
330 if (psi_plist_count(scanned
)) {
332 struct psi_token
*tmp_tok
;
334 exp
= psi_plist_add_r(exp
, psi_plist_count(scanned
), psi_plist_eles(scanned
));
337 while (psi_plist_get(scanned
, i
++, &tmp_tok
)) {
338 zend_string_release(tmp_tok
->file
);
339 tmp_tok
->file
= zend_string_copy(new_tok
->file
);
340 tmp_tok
->line
= new_tok
->line
;
341 tmp_tok
->col
= new_tok
->col
;
344 psi_token_free(&new_tok
);
345 psi_plist_top(scanned
, &new_tok
);
347 exp
= psi_plist_add(exp
, &new_tok
);
352 psi_parser_input_free(&toscan
);
354 exp
= psi_plist_add(exp
, &new_tok
);
357 psi_token_free(&old_tok
);
359 new_tok
= psi_token_init(stringify
? PSI_T_QUOTED_STRING
: tok
->type
,
360 tok
->text
->val
, tok
->text
->len
, target
->col
, target
->line
,
361 target
->file
?: zend_empty_string
);
363 exp
= psi_plist_add(exp
, &new_tok
);
367 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP expand > ");
368 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, new_tok
);
375 n
= psi_plist_count(exp
);
376 psi_cpp_tokiter_ins_range(cpp
, n
, psi_plist_eles(exp
));
382 static inline void psi_cpp_tokiter_free_call_tokens(
383 struct psi_plist
**arg_tokens_list
, size_t arg_count
, bool free_tokens
)
387 for (i
= 0; i
< arg_count
; ++i
) {
388 if (arg_tokens_list
[i
]) {
390 struct psi_token
*tok
;
392 while (psi_plist_pop(arg_tokens_list
[i
], &tok
)) {
393 psi_token_free(&tok
);
396 psi_plist_free(arg_tokens_list
[i
]);
399 free(arg_tokens_list
);
402 static inline struct psi_plist
**psi_cpp_tokiter_read_call_tokens(
403 struct psi_cpp
*cpp
, size_t arg_count
)
405 size_t arg_index
= 0, lparens
= 1, rparens
= 0, start
= psi_cpp_tokiter_index(cpp
);
406 struct psi_plist
**arg_tokens
= pecalloc(arg_count
, sizeof(*arg_tokens
), 1);
407 struct psi_plist
*free_tokens
= psi_plist_init((psi_plist_dtor
) psi_token_free
);
408 struct psi_token
*tok
;
410 arg_tokens
[0] = psi_plist_init(NULL
);
412 /* next token must be a LPAREN for a macro call */
413 psi_cpp_tokiter_next(cpp
);
414 tok
= psi_cpp_tokiter_current(cpp
);
415 if (!psi_cpp_tokiter_valid(cpp
) || tok
->type
!= PSI_T_LPAREN
) {
419 /* free LPAREN on success */
420 free_tokens
= psi_plist_add(free_tokens
, &tok
);
422 while (lparens
> rparens
) {
423 psi_cpp_tokiter_next(cpp
);
424 if (!psi_cpp_tokiter_valid(cpp
)) {
427 tok
= psi_cpp_tokiter_current(cpp
);
432 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
435 if (++rparens
== lparens
) {
437 if (arg_index
+ 1 < arg_count
) {
440 free_tokens
= psi_plist_add(free_tokens
, &tok
);
442 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
446 if (1 == (lparens
- rparens
)) {
447 /* too many commas? */
448 if (++arg_index
>= arg_count
) {
451 free_tokens
= psi_plist_add(free_tokens
, &tok
);
453 arg_tokens
[arg_index
] = psi_plist_init(NULL
);
455 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
459 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
463 /* ditch arg tokens */
464 psi_cpp_tokiter_del_range(cpp
, start
, psi_cpp_tokiter_index(cpp
) - start
+ 1, false);
465 psi_plist_free(free_tokens
);
470 psi_cpp_tokiter_free_call_tokens(arg_tokens
, arg_count
, false);
474 static inline void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp
*cpp
,
475 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
,
476 struct psi_plist
**arg_tokens_list
)
479 struct psi_token
*tok
;
480 struct psi_plist
*tokens
= psi_plist_init(NULL
);
483 for (i
= 0; psi_plist_get(macro
->tokens
, i
, &tok
); ++i
) {
484 struct psi_plist
*arg_tokens
= NULL
;
486 if (tok
->type
== PSI_T_HASH
|| tok
->type
== PSI_T_CPP_PASTE
) {
488 } else if (tok
->type
== PSI_T_NAME
) {
490 struct psi_token
*arg_name
;
492 for (s
= 0; psi_plist_get(macro
->sig
, s
, &arg_name
); ++s
) {
493 if (zend_string_equals(arg_name
->text
, tok
->text
)) {
495 bool processed
= psi_cpp_process(cpp
, &arg_tokens_list
[s
]);
499 arg_tokens
= arg_tokens_list
[s
];
506 tokens
= psi_plist_add_r(tokens
, psi_plist_count(arg_tokens
), psi_plist_eles(arg_tokens
));
508 tokens
= psi_plist_add(tokens
, &tok
);
512 psi_cpp_tokiter_expand_tokens(cpp
, target
, tokens
);
513 psi_plist_free(tokens
);
516 static inline bool psi_cpp_tokiter_expand_call(struct psi_cpp
*cpp
,
517 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
)
519 /* function-like macro
520 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
521 * # if FOO(1,2) // expands to if 1 > 2
523 size_t start
= psi_cpp_tokiter_index(cpp
), argc
= psi_plist_count(macro
->sig
);
524 struct psi_plist
**arg_tokens_list
;
527 /* read in tokens, until we have balanced parens */
528 arg_tokens_list
= psi_cpp_tokiter_read_call_tokens(cpp
, argc
);
529 if (!arg_tokens_list
) {
530 psi_cpp_tokiter_seek(cpp
, start
);
534 /* insert and expand macro tokens */
535 psi_cpp_tokiter_expand_call_tokens(cpp
, target
, macro
, arg_tokens_list
);
536 psi_cpp_tokiter_free_call_tokens(arg_tokens_list
, argc
, true);
538 psi_token_free(&target
);
543 static inline void psi_cpp_tokiter_expand_builtin_tokens(struct psi_cpp
*cpp
,
544 struct psi_token
*target
, struct psi_builtin
*builtin
,
545 struct psi_plist
**arg_tokens_list
)
548 struct psi_plist
*res
= NULL
;
549 size_t argc
= psi_plist_count(builtin
->decl
->sig
);
552 for (s
= 0; s
< argc
; ++s
) {
553 bool processed
= psi_cpp_process(cpp
, &arg_tokens_list
[s
]);
557 /* insert and expand macro tokens */
558 if (!builtin
->func(cpp
, target
, arg_tokens_list
, &res
)) {
559 struct psi_token
*zero
= psi_token_init(PSI_T_NUMBER
, "0", 1,
560 target
->col
, target
->line
, target
->file
);
561 psi_cpp_tokiter_ins_range(cpp
, 1, (void *) &zero
);
563 struct psi_token
*one
= psi_token_init(PSI_T_NUMBER
, "1", 1,
564 target
->col
, target
->line
, target
->file
);
565 psi_cpp_tokiter_ins_range(cpp
, 1, (void *) &one
);
567 psi_cpp_tokiter_expand_tokens(cpp
, target
, res
);
572 static inline bool psi_cpp_tokiter_expand_builtin(struct psi_cpp
*cpp
,
573 struct psi_token
*target
, struct psi_builtin
*builtin
)
575 size_t start
= psi_cpp_tokiter_index(cpp
), argc
= 0;
576 struct psi_plist
**arg_tokens_list
= NULL
;
578 if (builtin
->decl
->sig
) {
579 argc
= psi_plist_count(builtin
->decl
->sig
);
580 /* read in tokens, until we have balanced parens */
581 arg_tokens_list
= psi_cpp_tokiter_read_call_tokens(cpp
, argc
);
582 if (!arg_tokens_list
) {
583 psi_cpp_tokiter_seek(cpp
, start
);
587 psi_cpp_tokiter_next(cpp
);
590 psi_cpp_tokiter_expand_builtin_tokens(cpp
, target
, builtin
, arg_tokens_list
);
591 if (arg_tokens_list
) {
592 psi_cpp_tokiter_free_call_tokens(arg_tokens_list
, argc
, true);
595 psi_token_free(&target
);
601 static inline bool psi_cpp_tokiter_expand_def(struct psi_cpp
*cpp
,
602 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
)
604 /* delete current token from stream */
605 psi_cpp_tokiter_del_cur(cpp
, false);
606 /* replace with tokens from macro */
607 psi_cpp_tokiter_expand_tokens(cpp
, target
, macro
->tokens
);
609 psi_token_free(&target
);
614 static inline int psi_cpp_tokiter_expand_cmp(struct psi_token
*t
,
615 struct psi_cpp_macro_decl
*m
)
617 if (psi_plist_count(m
->tokens
) == 1) {
620 if (psi_plist_get(m
->tokens
, 0, &r
) && r
->text
) {
621 return !zend_string_equals(r
->text
, t
->text
);
627 static inline bool psi_cpp_tokiter_expand_decl(struct psi_cpp
*cpp
,
628 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
)
634 /* don't expand itself */
635 if (macro
->token
== target
) {
640 return psi_cpp_tokiter_expand_call(cpp
, target
, macro
);
641 } else if (psi_cpp_tokiter_expand_cmp(target
, macro
)) {
642 return psi_cpp_tokiter_expand_def(cpp
, target
, macro
);
648 bool psi_cpp_tokiter_expand(struct psi_cpp
*cpp
)
650 struct psi_token
*current
;
652 if (!psi_cpp_tokiter_valid(cpp
)) {
656 current
= psi_cpp_tokiter_current(cpp
);
661 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP expand < ");
662 PSI_DEBUG_DUMP(cpp
->parser
, psi_token_dump
, current
);
665 if (psi_builtin_exists(current
->text
)) {
666 return psi_cpp_tokiter_expand_builtin(cpp
, current
,
667 psi_builtin_get(current
->text
));
669 return psi_cpp_tokiter_expand_decl(cpp
, current
,
670 zend_hash_find_ptr(&cpp
->defs
, current
->text
));