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"
32 void psi_cpp_tokiter_dump(int fd
, struct psi_cpp
*cpp
)
34 size_t i
= cpp
->index
;
42 while (psi_plist_get(cpp
->tokens
, i
, &T
)) {
43 dprintf(fd
, "PSI: CPP tokens %5zu %c ", i
, cpp
->index
== i
? '*' : ' ');
44 psi_token_dump(fd
, T
);
45 if (i
>= cpp
->index
+ 10) {
46 dprintf(fd
, "PSI: CPP tokens .....\n");
54 void psi_cpp_tokiter_reset(struct psi_cpp
*cpp
)
57 fprintf(stderr
, "PSI: CPP reset (%zu tokens)\n", psi_plist_count(cpp
->tokens
));
58 # if PSI_CPP_DEBUG > 1
59 psi_cpp_tokiter_dump(2, cpp
);
66 bool psi_cpp_tokiter_seek(struct psi_cpp
*cpp
, size_t index
)
68 if (index
< psi_plist_count(cpp
->tokens
)) {
75 struct psi_token
*psi_cpp_tokiter_current(struct psi_cpp
*cpp
)
77 struct psi_token
*current
= NULL
;
78 bool found
= psi_plist_get(cpp
->tokens
, cpp
->index
, ¤t
);
85 size_t psi_cpp_tokiter_index(struct psi_cpp
*cpp
)
90 void psi_cpp_tokiter_next(struct psi_cpp
*cpp
)
92 #if 0 && PSI_CPP_DEBUG
93 fprintf(stderr
, "PSI: CPP next -> index=%zu -> index=%zu\n", cpp
->index
, cpp
->index
+1);
98 void psi_cpp_tokiter_prev(struct psi_cpp
*cpp
)
100 #if 0 && PSI_CPP_DEBUG
101 fprintf(stderr
, "PSI: CPP prev -> index=%zu -> index=%zu\n", cpp
->index
, cpp
->index
-1);
108 bool psi_cpp_tokiter_valid(struct psi_cpp
*cpp
)
110 #if 0 && PSI_CPP_DEBUG
111 fprintf(stderr
, "PSI: CPP valid -> index=%zu -> %d\n", cpp
->index
, cpp
->index
< psi_plist_count(cpp
->tokens
));
113 return cpp
->index
< psi_plist_count(cpp
->tokens
);
116 bool psi_cpp_tokiter_del_cur(struct psi_cpp
*cpp
, bool free_token
)
118 struct psi_token
*cur
= NULL
;
119 bool deleted
= psi_plist_del(cpp
->tokens
, cpp
->index
, &cur
);
123 fprintf(stderr
, "PSI: CPP del_cur -> index=%zu, del=%d, free=%d, count=%zu ",
124 cpp
->index
, (int) deleted
, (int) free_token
, psi_plist_count(cpp
->tokens
));
126 psi_token_dump(2, cur
);
128 fprintf(stderr
, "NULL\n");
132 psi_token_free(&cur
);
134 count
= psi_plist_count(cpp
->tokens
);
135 if (deleted
&& cpp
->index
>= count
) {
137 cpp
->index
= count
- 1;
145 bool psi_cpp_tokiter_del_range(struct psi_cpp
*cpp
, size_t offset
, size_t num_eles
, bool free_tokens
)
147 struct psi_token
**ptr
;
151 ptr
= calloc(num_eles
, sizeof(*ptr
));
157 fprintf(stderr
, "PSI: CPP del_range -> index=%zu, offset=%zu, num_eles=%zu, count=%zu\n",
158 cpp
->index
, offset
, num_eles
, psi_plist_count(cpp
->tokens
));
161 deleted
= psi_plist_del_r(cpp
->tokens
, offset
, num_eles
, (void *) ptr
);
164 size_t count
= psi_plist_count(cpp
->tokens
);
166 if (cpp
->index
>= count
) {
168 cpp
->index
= count
- 1;
176 psi_token_free(&ptr
[num_eles
]);
184 bool psi_cpp_tokiter_ins_cur(struct psi_cpp
*cpp
, struct psi_token
*tok
)
186 struct psi_plist
*tokens
= psi_plist_ins(cpp
->tokens
, cpp
->index
, &tok
);
189 fprintf(stderr
, "PSI: CPP ins_cur -> index=%zu ", cpp
->index
);
190 psi_token_dump(2, tok
);
196 cpp
->tokens
= tokens
;
200 bool psi_cpp_tokiter_ins_range(struct psi_cpp
*cpp
, size_t offset
,
201 size_t num_eles
, void **eles
)
203 struct psi_plist
*tokens
;
209 tokens
= psi_plist_ins_r(cpp
->tokens
, offset
, num_eles
, eles
);
212 fprintf(stderr
, "PSI: CPP ins_range -> index=%zu, offset=%zu, num_eles=%zu, count=%zu\n",
213 cpp
->index
, offset
, num_eles
, psi_plist_count(tokens
));
219 cpp
->tokens
= tokens
;
223 bool psi_cpp_tokiter_defined(struct psi_cpp
*cpp
)
225 if (psi_cpp_tokiter_valid(cpp
)) {
226 struct psi_token
*current
= psi_cpp_tokiter_current(cpp
);
228 return psi_cpp_defined(cpp
, current
);
234 static size_t psi_cpp_tokiter_expand_tokens(struct psi_cpp
*cpp
,
235 struct psi_token
*target
, struct psi_plist
*tokens
)
237 if (tokens
&& psi_plist_count(tokens
)) {
239 bool stringify
= false, paste
= false;
240 struct psi_token
*tok
, **exp_tokens
= calloc(psi_plist_count(tokens
), sizeof(*exp_tokens
));
242 while (psi_plist_get(tokens
, i
++, &tok
)) {
243 struct psi_token
*new_tok
;
245 if (tok
->type
== PSI_T_EOL
) {
248 if (tok
->type
== PSI_T_HASH
) {
252 if (tok
->type
== PSI_T_CPP_PASTE
) {
257 if (paste
&& n
> 0 && exp_tokens
[n
- 1]) {
258 struct psi_token
*tmp_tok
, *old_tok
= exp_tokens
[n
- 1];
260 tmp_tok
= psi_token_init(old_tok
->type
, "", 0,
261 target
->col
, target
->line
,
262 target
->file
? target
->file
->val
: "");
264 new_tok
= psi_token_cat(NULL
, 3, tmp_tok
, old_tok
, tok
);
265 psi_token_free(&old_tok
);
266 psi_token_free(&tmp_tok
);
268 exp_tokens
[n
- 1] = new_tok
;
270 new_tok
= psi_token_init(stringify
? PSI_T_QUOTED_STRING
: tok
->type
,
271 tok
->text
->val
, tok
->text
->len
, target
->col
, target
->line
, target
->file
?:"");
273 exp_tokens
[n
++] = new_tok
;
277 fprintf(stderr
, "PSI: CPP expand > ");
278 psi_token_dump(2, tok
);
284 psi_cpp_tokiter_ins_range(cpp
, psi_cpp_tokiter_index(cpp
), n
, (void *) exp_tokens
);
293 static void psi_cpp_tokiter_free_call_tokens(struct psi_plist
**arg_tokens_list
, size_t arg_count
, bool free_tokens
)
297 for (i
= 0; i
< arg_count
; ++i
) {
298 if (arg_tokens_list
[i
]) {
300 struct psi_token
*tok
;
302 while (psi_plist_pop(arg_tokens_list
[i
], &tok
)) {
303 psi_token_free(&tok
);
306 psi_plist_free(arg_tokens_list
[i
]);
309 free(arg_tokens_list
);
312 static struct psi_plist
**psi_cpp_tokiter_read_call_tokens(
313 struct psi_cpp
*cpp
, size_t arg_count
)
315 size_t arg_index
= 0, lparens
= 1, rparens
= 0;
316 struct psi_plist
**arg_tokens
= calloc(arg_count
, sizeof(*arg_tokens
));
317 struct psi_plist
*free_tokens
= psi_plist_init((psi_plist_dtor
) psi_token_free
);
318 struct psi_token
*tok
;
320 arg_tokens
[0] = psi_plist_init(NULL
);
322 /* next token must be a LPAREN for a macro call */
323 psi_cpp_tokiter_next(cpp
);
324 tok
= psi_cpp_tokiter_current(cpp
);
325 if (!psi_cpp_tokiter_valid(cpp
) || tok
->type
!= PSI_T_LPAREN
) {
329 /* free LPAREN on success */
330 free_tokens
= psi_plist_add(free_tokens
, &tok
);
332 while (lparens
> rparens
) {
333 psi_cpp_tokiter_next(cpp
);
334 if (!psi_cpp_tokiter_valid(cpp
)) {
337 tok
= psi_cpp_tokiter_current(cpp
);
342 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
345 if (++rparens
== lparens
) {
347 if (arg_index
+ 1 < arg_count
) {
350 free_tokens
= psi_plist_add(free_tokens
, &tok
);
352 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
356 if (1 == (lparens
- rparens
)) {
357 /* too many commas? */
358 if (++arg_index
>= arg_count
) {
361 free_tokens
= psi_plist_add(free_tokens
, &tok
);
363 arg_tokens
[arg_index
] = psi_plist_init(NULL
);
365 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
369 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
373 psi_plist_free(free_tokens
);
377 psi_cpp_tokiter_free_call_tokens(arg_tokens
, arg_count
, false);
381 static void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp
*cpp
,
382 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
,
383 struct psi_plist
**arg_tokens_list
)
386 struct psi_token
*tok
;
387 struct psi_plist
*tokens
= psi_plist_init(NULL
);
389 for (i
= 0; psi_plist_get(macro
->tokens
, i
, &tok
); ++i
) {
390 struct psi_plist
*arg_tokens
= NULL
;
392 if (tok
->type
== PSI_T_NAME
) {
394 struct psi_token
*arg_name
;
396 for (s
= 0; psi_plist_get(macro
->sig
, s
, &arg_name
); ++s
) {
397 if (zend_string_equals(arg_name
->text
, tok
->text
)) {
398 arg_tokens
= arg_tokens_list
[s
];
405 tokens
= psi_plist_add_r(tokens
, psi_plist_count(arg_tokens
), psi_plist_eles(arg_tokens
));
407 tokens
= psi_plist_add(tokens
, &tok
);
411 psi_cpp_tokiter_expand_tokens(cpp
, target
, tokens
);
412 psi_plist_free(tokens
);
415 static bool psi_cpp_tokiter_expand_call(struct psi_cpp
*cpp
,
416 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
)
418 /* function-like macro
419 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
420 * # if FOO(1,2) // expands to if 1 > 2
422 size_t start
= psi_cpp_tokiter_index(cpp
);
423 struct psi_plist
**arg_tokens_list
;
425 /* read in tokens, until we have balanced parens */
426 arg_tokens_list
= psi_cpp_tokiter_read_call_tokens(cpp
, psi_plist_count(macro
->sig
));
427 if (!arg_tokens_list
) {
428 psi_cpp_tokiter_seek(cpp
, start
);
432 /* ditch arg tokens */
433 psi_cpp_tokiter_del_range(cpp
, start
, psi_cpp_tokiter_index(cpp
) - start
+ 1, false);
434 psi_cpp_tokiter_seek(cpp
, start
);
436 /* insert and expand macro tokens */
437 psi_cpp_tokiter_expand_call_tokens(cpp
, target
, macro
, arg_tokens_list
);
438 psi_cpp_tokiter_free_call_tokens(arg_tokens_list
, psi_plist_count(macro
->sig
), true);
440 /* back to where we took off */
441 psi_cpp_tokiter_seek(cpp
, start
);
443 psi_token_free(&target
);
448 static bool psi_cpp_tokiter_expand_def(struct psi_cpp
*cpp
,
449 struct psi_token
*target
, struct psi_cpp_macro_decl
*macro
)
451 size_t index
= psi_cpp_tokiter_index(cpp
);
453 /* delete current token from stream */
454 psi_cpp_tokiter_del_cur(cpp
, false);
456 if (index
!= psi_cpp_tokiter_index(cpp
)) {
457 /* might have been last token */
458 psi_cpp_tokiter_next(cpp
);
460 /* replace with tokens from macro */
461 psi_cpp_tokiter_expand_tokens(cpp
, target
, macro
->tokens
);
463 psi_token_free(&target
);
468 static inline int psi_cpp_tokiter_expand_cmp(struct psi_token
*t
,
469 struct psi_cpp_macro_decl
*m
)
471 if (psi_plist_count(m
->tokens
) == 1) {
474 psi_plist_get(m
->tokens
, 0, &r
);
476 return !zend_string_equals(r
->text
, t
->text
);
481 bool psi_cpp_tokiter_expand(struct psi_cpp
*cpp
)
483 if (psi_cpp_tokiter_valid(cpp
)) {
484 struct psi_token
*current
= psi_cpp_tokiter_current(cpp
);
487 struct psi_cpp_macro_decl
*macro
= zend_hash_find_ptr(
488 &cpp
->defs
, current
->text
);
490 /* don't expand itself */
491 if (macro
&& macro
->token
!= current
) {
493 fprintf(stderr
, "PSI: CPP expand < ");
494 psi_token_dump(2, current
);
497 return psi_cpp_tokiter_expand_call(cpp
, current
, macro
);
498 } else if (psi_cpp_tokiter_expand_cmp(current
, macro
)) {
499 return psi_cpp_tokiter_expand_def(cpp
, current
, macro
);