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
)
37 for (i
= 0; psi_plist_get(cpp
->tokens
, i
, &T
); ++i
) {
38 dprintf(fd
, "PSI: CPP tokens %5zu %c ", i
, cpp
->index
== i
? '*' : ' ');
39 psi_token_dump(fd
, T
);
43 void psi_cpp_tokiter_reset(struct psi_cpp
*cpp
)
46 fprintf(stderr
, "PSI: CPP reset (%zu tokens)\n", psi_plist_count(cpp
->tokens
));
47 # if PSI_CPP_DEBUG > 1
48 psi_cpp_tokiter_dump(2, cpp
);
55 bool psi_cpp_tokiter_seek(struct psi_cpp
*cpp
, size_t index
)
57 if (index
< psi_plist_count(cpp
->tokens
)) {
64 struct psi_token
*psi_cpp_tokiter_current(struct psi_cpp
*cpp
)
66 struct psi_token
*current
= NULL
;
67 bool found
= psi_plist_get(cpp
->tokens
, cpp
->index
, ¤t
);
74 size_t psi_cpp_tokiter_index(struct psi_cpp
*cpp
)
79 void psi_cpp_tokiter_next(struct psi_cpp
*cpp
)
81 #if 0 && PSI_CPP_DEBUG
82 fprintf(stderr
, "PSI: CPP next -> index=%zu -> index=%zu\n", cpp
->index
, cpp
->index
+1);
87 void psi_cpp_tokiter_prev(struct psi_cpp
*cpp
)
89 #if 0 && PSI_CPP_DEBUG
90 fprintf(stderr
, "PSI: CPP prev -> index=%zu -> index=%zu\n", cpp
->index
, cpp
->index
-1);
97 bool psi_cpp_tokiter_valid(struct psi_cpp
*cpp
)
99 #if 0 && PSI_CPP_DEBUG
100 fprintf(stderr
, "PSI: CPP valid -> index=%zu -> %d\n", cpp
->index
, cpp
->index
< psi_plist_count(cpp
->tokens
));
102 return cpp
->index
< psi_plist_count(cpp
->tokens
);
105 bool psi_cpp_tokiter_del_cur(struct psi_cpp
*cpp
, bool free_token
)
107 struct psi_token
*cur
= NULL
;
108 bool deleted
= psi_plist_del(cpp
->tokens
, cpp
->index
, &cur
);
112 fprintf(stderr
, "PSI: CPP del_cur -> index=%zu, del=%d, free=%d, count=%zu ",
113 cpp
->index
, (int) deleted
, (int) free_token
, psi_plist_count(cpp
->tokens
));
115 psi_token_dump(2, cur
);
117 fprintf(stderr
, "NULL\n");
120 if (cur
&& free_token
) {
123 count
= psi_plist_count(cpp
->tokens
);
124 if (deleted
&& cpp
->index
>= count
) {
126 cpp
->index
= count
- 1;
134 bool psi_cpp_tokiter_del_range(struct psi_cpp
*cpp
, size_t offset
, size_t num_eles
, bool free_tokens
)
136 struct psi_token
**ptr
;
140 ptr
= calloc(num_eles
, sizeof(*ptr
));
146 fprintf(stderr
, "PSI: CPP del_range -> index=%zu, offset=%zu, num_eles=%zu, count=%zu\n",
147 cpp
->index
, offset
, num_eles
, psi_plist_count(cpp
->tokens
));
150 deleted
= psi_plist_del_r(cpp
->tokens
, offset
, num_eles
, (void *) ptr
);
153 if (cpp
->index
>= psi_plist_count(cpp
->tokens
)) {
154 cpp
->index
= MAX(0, psi_plist_count(cpp
->tokens
)-1);
168 bool psi_cpp_tokiter_ins_cur(struct psi_cpp
*cpp
, struct psi_token
*tok
)
170 struct psi_plist
*tokens
= psi_plist_ins(cpp
->tokens
, cpp
->index
, &tok
);
173 fprintf(stderr
, "PSI: CPP ins_cur -> index=%zu ", cpp
->index
);
174 psi_token_dump(2, tok
);
180 cpp
->tokens
= tokens
;
184 bool psi_cpp_tokiter_ins_range(struct psi_cpp
*cpp
, size_t offset
,
185 size_t num_eles
, void **eles
)
187 struct psi_plist
*tokens
;
193 tokens
= psi_plist_ins_r(cpp
->tokens
, offset
, num_eles
, eles
);
196 fprintf(stderr
, "PSI: CPP ins_range -> index=%zu, offset=%zu, num_eles=%zu, count=%zu\n",
197 cpp
->index
, offset
, num_eles
, psi_plist_count(tokens
));
203 cpp
->tokens
= tokens
;
207 bool psi_cpp_tokiter_defined(struct psi_cpp
*cpp
)
209 if (psi_cpp_tokiter_valid(cpp
)) {
210 struct psi_token
*current
= psi_cpp_tokiter_current(cpp
);
212 return psi_cpp_defined(cpp
, current
);
218 void psi_cpp_tokiter_expand_tokens(struct psi_cpp
*cpp
, struct psi_plist
*tokens
)
220 if (tokens
&& psi_plist_count(tokens
)) {
222 bool stringify
= false, paste
= false;
223 struct psi_token
*tok
, **exp_tokens
= calloc(psi_plist_count(tokens
), sizeof(*exp_tokens
));
225 while (psi_plist_get(tokens
, i
++, &tok
)) {
226 struct psi_token
*new_tok
;
228 if (tok
->type
== PSI_T_HASH
) {
238 if (paste
&& n
> 0 && exp_tokens
[n
- 1] &&
239 (new_tok
= psi_token_cat(NULL
, 2, exp_tokens
[n
- 1], tok
))) {
240 free(exp_tokens
[n
- 1]);
241 exp_tokens
[n
- 1] = new_tok
;
243 struct psi_token
*cpy
= psi_token_copy(tok
);
246 cpy
= psi_token_append(NULL
,
247 psi_token_prepend(NULL
, cpy
, 1, "\""), 1, "\"");
248 cpy
->type
= PSI_T_QUOTED_STRING
;
250 exp_tokens
[n
++] = cpy
;
254 fprintf(stderr
, "PSI: CPP expand > ");
255 psi_token_dump(2, tok
);
260 psi_cpp_tokiter_ins_range(cpp
, psi_cpp_tokiter_index(cpp
), n
, (void *) exp_tokens
);
265 static void psi_cpp_tokiter_free_call_tokens(struct psi_plist
**arg_tokens_list
, size_t arg_count
, bool free_tokens
)
269 for (i
= 0; i
< arg_count
; ++i
) {
270 if (arg_tokens_list
[i
]) {
272 struct psi_token
*tok
;
274 while (psi_plist_pop(arg_tokens_list
[i
], &tok
)) {
278 psi_plist_free(arg_tokens_list
[i
]);
281 free(arg_tokens_list
);
284 static struct psi_plist
**psi_cpp_tokiter_read_call_tokens(
285 struct psi_cpp
*cpp
, size_t arg_count
)
287 size_t arg_index
= 0, lparens
= 1, rparens
= 0;
288 struct psi_plist
**arg_tokens
= calloc(arg_count
, sizeof(*arg_tokens
));
289 struct psi_plist
*free_tokens
= psi_plist_init((psi_plist_dtor
) psi_token_free
);
290 struct psi_token
*tok
;
292 arg_tokens
[0] = psi_plist_init(NULL
);
294 /* free macro name token on success */
295 tok
= psi_cpp_tokiter_current(cpp
);
296 free_tokens
= psi_plist_add(free_tokens
, &tok
);
298 /* next token must be a LPAREN for a macro call */
299 psi_cpp_tokiter_next(cpp
);
300 tok
= psi_cpp_tokiter_current(cpp
);
301 if (!psi_cpp_tokiter_valid(cpp
) || tok
->type
!= PSI_T_LPAREN
) {
305 /* free LPAREN on success */
306 free_tokens
= psi_plist_add(free_tokens
, &tok
);
308 while (lparens
> rparens
) {
309 psi_cpp_tokiter_next(cpp
);
310 if (!psi_cpp_tokiter_valid(cpp
)) {
313 tok
= psi_cpp_tokiter_current(cpp
);
318 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
321 if (++rparens
== lparens
) {
323 if (arg_index
+ 1 < arg_count
) {
326 free_tokens
= psi_plist_add(free_tokens
, &tok
);
328 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
332 if (1 == (lparens
- rparens
)) {
333 /* too many commas? */
334 if (++arg_index
>= arg_count
) {
337 free_tokens
= psi_plist_add(free_tokens
, &tok
);
339 arg_tokens
[arg_index
] = psi_plist_init(NULL
);
341 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
345 arg_tokens
[arg_index
] = psi_plist_add(arg_tokens
[arg_index
], &tok
);
349 psi_plist_free(free_tokens
);
353 psi_cpp_tokiter_free_call_tokens(arg_tokens
, arg_count
, false);
357 static void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp
*cpp
,
358 struct psi_cpp_macro_decl
*macro
, struct psi_plist
**arg_tokens_list
)
361 struct psi_token
*tok
;
362 struct psi_plist
*tokens
= psi_plist_init(NULL
);
364 for (i
= 0; psi_plist_get(macro
->tokens
, i
, &tok
); ++i
) {
365 struct psi_plist
*arg_tokens
= NULL
;
367 if (tok
->type
== PSI_T_NAME
) {
369 struct psi_token
*arg_name
;
371 for (s
= 0; psi_plist_get(macro
->sig
, s
, &arg_name
); ++s
) {
372 if (arg_name
->size
== tok
->size
) {
373 if (!memcmp(arg_name
->text
, tok
->text
, tok
->size
)) {
374 arg_tokens
= arg_tokens_list
[s
];
382 tokens
= psi_plist_add_r(tokens
, psi_plist_count(arg_tokens
), psi_plist_eles(arg_tokens
));
384 tokens
= psi_plist_add(tokens
, &tok
);
388 psi_cpp_tokiter_expand_tokens(cpp
, tokens
);
389 psi_plist_free(tokens
);
392 static bool psi_cpp_tokiter_expand_call(struct psi_cpp
*cpp
,
393 struct psi_cpp_macro_decl
*macro
)
395 /* function-like macro
396 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
397 * # if FOO(1,2) // expands to if 1 > 2
399 size_t start
= psi_cpp_tokiter_index(cpp
);
400 struct psi_plist
**arg_tokens_list
;
402 /* read in tokens, until we have balanced parens */
403 arg_tokens_list
= psi_cpp_tokiter_read_call_tokens(cpp
, psi_plist_count(macro
->sig
));
404 if (!arg_tokens_list
) {
405 psi_cpp_tokiter_seek(cpp
, start
);
409 /* ditch arg tokens */
410 psi_cpp_tokiter_del_range(cpp
, start
, psi_cpp_tokiter_index(cpp
) - start
+ 1, false);
411 psi_cpp_tokiter_seek(cpp
, start
);
413 /* insert and expand macro tokens */
414 psi_cpp_tokiter_expand_call_tokens(cpp
, macro
, arg_tokens_list
);
415 psi_cpp_tokiter_free_call_tokens(arg_tokens_list
, psi_plist_count(macro
->sig
), true);
417 /* back to where we took off */
418 psi_cpp_tokiter_seek(cpp
, start
);
423 bool psi_cpp_tokiter_expand(struct psi_cpp
*cpp
)
425 if (psi_cpp_tokiter_valid(cpp
)) {
426 struct psi_token
*current
= psi_cpp_tokiter_current(cpp
);
429 struct psi_cpp_macro_decl
*macro
= zend_hash_str_find_ptr(
430 cpp
->defs
, current
->text
, current
->size
);
432 /* don't expand itself */
433 if (macro
&& macro
->token
!= current
) {
435 fprintf(stderr
, "PSI: CPP expand < ");
436 psi_token_dump(2, current
);
439 return psi_cpp_tokiter_expand_call(cpp
, macro
);
441 size_t index
= psi_cpp_tokiter_index(cpp
);
443 /* delete current token from stream */
444 psi_cpp_tokiter_del_cur(cpp
, true);
446 if (index
!= psi_cpp_tokiter_index(cpp
)) {
447 /* might have been last token */
448 psi_cpp_tokiter_next(cpp
);
450 /* replace with tokens from macro */
451 psi_cpp_tokiter_expand_tokens(cpp
, macro
->tokens
);