validate: check anonymous decl
[m6w6/ext-psi] / src / cpp_tokiter.c
1 /*******************************************************************************
2 Copyright (c) 2017, Michael Wallner <mike@php.net>.
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
7
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.
13
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 *******************************************************************************/
25
26 #include "php_psi_stdinc.h"
27
28 #include "cpp.h"
29 #include "parser.h"
30
31 #if PSI_CPP_DEBUG > 1
32 void psi_cpp_tokiter_dump(int fd, struct psi_cpp *cpp)
33 {
34 size_t i = cpp->index;
35 struct psi_token *T;
36
37 if (i > 20) {
38 i -= 20;
39 } else {
40 i = 0;
41 }
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");
47 break;
48 }
49 ++i;
50 }
51 }
52 #endif
53
54 void psi_cpp_tokiter_reset(struct psi_cpp *cpp)
55 {
56 #if PSI_CPP_DEBUG
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);
60 # endif
61 #endif
62 cpp->index = 0;
63 cpp->expanded = 0;
64 }
65
66 bool psi_cpp_tokiter_seek(struct psi_cpp *cpp, size_t index)
67 {
68 if (index < psi_plist_count(cpp->tokens)) {
69 cpp->index = index;
70 return true;
71 }
72 return false;
73 }
74
75 struct psi_token *psi_cpp_tokiter_current(struct psi_cpp *cpp)
76 {
77 struct psi_token *current = NULL;
78 bool found = psi_plist_get(cpp->tokens, cpp->index, &current);
79
80 assert(found);
81
82 return current;
83 }
84
85 size_t psi_cpp_tokiter_index(struct psi_cpp *cpp)
86 {
87 return cpp->index;
88 }
89
90 void psi_cpp_tokiter_next(struct psi_cpp *cpp)
91 {
92 #if 0 && PSI_CPP_DEBUG
93 fprintf(stderr, "PSI: CPP next -> index=%zu -> index=%zu\n", cpp->index, cpp->index+1);
94 #endif
95 ++cpp->index;
96 }
97
98 void psi_cpp_tokiter_prev(struct psi_cpp *cpp)
99 {
100 #if 0 && PSI_CPP_DEBUG
101 fprintf(stderr, "PSI: CPP prev -> index=%zu -> index=%zu\n", cpp->index, cpp->index-1);
102 #endif
103 if (cpp->index) {
104 --cpp->index;
105 }
106 }
107
108 bool psi_cpp_tokiter_valid(struct psi_cpp *cpp)
109 {
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));
112 #endif
113 return cpp->index < psi_plist_count(cpp->tokens);
114 }
115
116 bool psi_cpp_tokiter_del_cur(struct psi_cpp *cpp, bool free_token)
117 {
118 struct psi_token *cur = NULL;
119 bool deleted = psi_plist_del(cpp->tokens, cpp->index, &cur);
120 size_t count;
121
122 #if PSI_CPP_DEBUG
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));
125 if (cur) {
126 psi_token_dump(2, cur);
127 } else {
128 fprintf(stderr, "NULL\n");
129 }
130 #endif
131 if (cur && free_token) {
132 free(cur);
133 }
134 count = psi_plist_count(cpp->tokens);
135 if (deleted && cpp->index >= count) {
136 if (count > 0) {
137 cpp->index = count - 1;
138 } else {
139 cpp->index = 0;
140 }
141 }
142 return deleted;
143 }
144
145 bool psi_cpp_tokiter_del_range(struct psi_cpp *cpp, size_t offset, size_t num_eles, bool free_tokens)
146 {
147 struct psi_token **ptr;
148 bool deleted;
149
150 if (free_tokens) {
151 ptr = calloc(num_eles, sizeof(*ptr));
152 } else {
153 ptr = NULL;
154 }
155
156 #if PSI_CPP_DEBUG
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));
159 #endif
160
161 deleted = psi_plist_del_r(cpp->tokens, offset, num_eles, (void *) ptr);
162
163 if (deleted) {
164 size_t count = psi_plist_count(cpp->tokens);
165
166 if (cpp->index >= count) {
167 if (count > 0) {
168 cpp->index = count - 1;
169 } else {
170 cpp->index = 0;
171 }
172 }
173
174 if (free_tokens) {
175 while (num_eles--) {
176 if (ptr[num_eles]) {
177 free(ptr[num_eles]);
178 }
179 }
180 free(ptr);
181 }
182 }
183 return deleted;
184 }
185
186 bool psi_cpp_tokiter_ins_cur(struct psi_cpp *cpp, struct psi_token *tok)
187 {
188 struct psi_plist *tokens = psi_plist_ins(cpp->tokens, cpp->index, &tok);
189
190 #if PSI_CPP_DEBUG
191 fprintf(stderr, "PSI: CPP ins_cur -> index=%zu ", cpp->index);
192 psi_token_dump(2, tok);
193 #endif
194
195 if (!tokens) {
196 return false;
197 }
198 cpp->tokens = tokens;
199 return true;
200 }
201
202 bool psi_cpp_tokiter_ins_range(struct psi_cpp *cpp, size_t offset,
203 size_t num_eles, void **eles)
204 {
205 struct psi_plist *tokens;
206
207 if (!num_eles) {
208 return true;
209 }
210
211 tokens = psi_plist_ins_r(cpp->tokens, offset, num_eles, eles);
212
213 #if PSI_CPP_DEBUG
214 fprintf(stderr, "PSI: CPP ins_range -> index=%zu, offset=%zu, num_eles=%zu, count=%zu\n",
215 cpp->index, offset, num_eles, psi_plist_count(tokens));
216 #endif
217
218 if (!tokens) {
219 return false;
220 }
221 cpp->tokens = tokens;
222 return true;
223 }
224
225 bool psi_cpp_tokiter_defined(struct psi_cpp *cpp)
226 {
227 if (psi_cpp_tokiter_valid(cpp)) {
228 struct psi_token *current = psi_cpp_tokiter_current(cpp);
229
230 return psi_cpp_defined(cpp, current);
231 }
232
233 return false;
234 }
235
236 static size_t psi_cpp_tokiter_expand_tokens(struct psi_cpp *cpp,
237 struct psi_token *target, struct psi_plist *tokens)
238 {
239 if (tokens && psi_plist_count(tokens)) {
240 size_t i = 0, n = 0;
241 bool stringify = false, paste = false;
242 struct psi_token *tok, **exp_tokens = calloc(psi_plist_count(tokens), sizeof(*exp_tokens));
243
244 while (psi_plist_get(tokens, i++, &tok)) {
245 struct psi_token *new_tok;
246
247 if (tok->type == PSI_T_EOL) {
248 continue;
249 }
250 if (tok->type == PSI_T_HASH) {
251 stringify = true;
252 continue;
253 }
254 if (tok->type == PSI_T_CPP_PASTE) {
255 paste = true;
256 continue;
257 }
258
259 if (paste && n > 0 && exp_tokens[n - 1]) {
260 struct psi_token *old_tok = exp_tokens[n - 1];
261
262 new_tok = psi_token_init(old_tok->type, "", 0,
263 target->col, target->line, target->file?:"");
264
265 new_tok = psi_token_cat(NULL, 2, new_tok, old_tok, tok);
266 free(old_tok);
267
268 exp_tokens[n - 1] = new_tok;
269 } else {
270 new_tok = psi_token_init(stringify ? PSI_T_QUOTED_STRING : tok->type,
271 tok->text, tok->size, target->col, target->line, target->file?:"");
272
273 exp_tokens[n++] = new_tok;
274 }
275
276 #if PSI_CPP_DEBUG
277 fprintf(stderr, "PSI: CPP expand > ");
278 psi_token_dump(2, tok);
279 #endif
280
281 paste = false;
282 stringify = false;
283 }
284 psi_cpp_tokiter_ins_range(cpp, psi_cpp_tokiter_index(cpp), n, (void *) exp_tokens);
285 free(exp_tokens);
286
287 return n;
288 } else {
289 return 0;
290 }
291 }
292
293 static void psi_cpp_tokiter_free_call_tokens(struct psi_plist **arg_tokens_list, size_t arg_count, bool free_tokens)
294 {
295 size_t i;
296
297 for (i = 0; i < arg_count; ++i) {
298 if (arg_tokens_list[i]) {
299 if (free_tokens) {
300 struct psi_token *tok;
301
302 while (psi_plist_pop(arg_tokens_list[i], &tok)) {
303 free(tok);
304 }
305 }
306 psi_plist_free(arg_tokens_list[i]);
307 }
308 }
309 free(arg_tokens_list);
310 }
311
312 static struct psi_plist **psi_cpp_tokiter_read_call_tokens(
313 struct psi_cpp *cpp, size_t arg_count)
314 {
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;
319
320 arg_tokens[0] = psi_plist_init(NULL);
321
322 /* free macro name token on success */
323 tok = psi_cpp_tokiter_current(cpp);
324 free_tokens = psi_plist_add(free_tokens, &tok);
325
326 /* next token must be a LPAREN for a macro call */
327 psi_cpp_tokiter_next(cpp);
328 tok = psi_cpp_tokiter_current(cpp);
329 if (!psi_cpp_tokiter_valid(cpp) || tok->type != PSI_T_LPAREN) {
330 goto fail;
331 }
332
333 /* free LPAREN on success */
334 free_tokens = psi_plist_add(free_tokens, &tok);
335
336 while (lparens > rparens) {
337 psi_cpp_tokiter_next(cpp);
338 if (!psi_cpp_tokiter_valid(cpp)) {
339 goto fail;
340 }
341 tok = psi_cpp_tokiter_current(cpp);
342
343 switch (tok->type) {
344 case PSI_T_LPAREN:
345 ++lparens;
346 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
347 break;
348 case PSI_T_RPAREN:
349 if (++rparens == lparens) {
350 /* closing RPAREN */
351 if (arg_index + 1 < arg_count) {
352 goto fail;
353 }
354 free_tokens = psi_plist_add(free_tokens, &tok);
355 } else {
356 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
357 }
358 break;
359 case PSI_T_COMMA:
360 if (1 == (lparens - rparens)) {
361 /* too many commas? */
362 if (++arg_index >= arg_count) {
363 goto fail;
364 }
365 free_tokens = psi_plist_add(free_tokens, &tok);
366 /* next arg */
367 arg_tokens[arg_index] = psi_plist_init(NULL);
368 } else {
369 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
370 }
371 break;
372 default:
373 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
374 }
375 }
376
377 psi_plist_free(free_tokens);
378 return arg_tokens;
379
380 fail:
381 psi_cpp_tokiter_free_call_tokens(arg_tokens, arg_count, false);
382 return NULL;
383 }
384
385 static void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp *cpp,
386 struct psi_token *target, struct psi_cpp_macro_decl *macro,
387 struct psi_plist **arg_tokens_list)
388 {
389 size_t i;
390 struct psi_token *tok;
391 struct psi_plist *tokens = psi_plist_init(NULL);
392
393 for (i = 0; psi_plist_get(macro->tokens, i, &tok); ++i) {
394 struct psi_plist *arg_tokens = NULL;
395
396 if (tok->type == PSI_T_NAME) {
397 size_t s;
398 struct psi_token *arg_name;
399
400 for (s = 0; psi_plist_get(macro->sig, s, &arg_name); ++s) {
401 if (arg_name->size == tok->size) {
402 if (!memcmp(arg_name->text, tok->text, tok->size)) {
403 arg_tokens = arg_tokens_list[s];
404 break;
405 }
406 }
407 }
408 }
409
410 if (arg_tokens) {
411 tokens = psi_plist_add_r(tokens, psi_plist_count(arg_tokens), psi_plist_eles(arg_tokens));
412 } else {
413 tokens = psi_plist_add(tokens, &tok);
414 }
415 }
416
417 psi_cpp_tokiter_expand_tokens(cpp, target, tokens);
418 psi_plist_free(tokens);
419 }
420
421 static bool psi_cpp_tokiter_expand_call(struct psi_cpp *cpp,
422 struct psi_token *target, struct psi_cpp_macro_decl *macro)
423 {
424 /* function-like macro
425 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
426 * # if FOO(1,2) // expands to if 1 > 2
427 */
428 size_t start = psi_cpp_tokiter_index(cpp);
429 struct psi_plist **arg_tokens_list;
430
431 /* read in tokens, until we have balanced parens */
432 arg_tokens_list = psi_cpp_tokiter_read_call_tokens(cpp, psi_plist_count(macro->sig));
433 if (!arg_tokens_list) {
434 psi_cpp_tokiter_seek(cpp, start);
435 return false;
436 }
437
438 /* ditch arg tokens */
439 psi_cpp_tokiter_del_range(cpp, start, psi_cpp_tokiter_index(cpp) - start + 1, false);
440 psi_cpp_tokiter_seek(cpp, start);
441
442 /* insert and expand macro tokens */
443 psi_cpp_tokiter_expand_call_tokens(cpp, target, macro, arg_tokens_list);
444 psi_cpp_tokiter_free_call_tokens(arg_tokens_list, psi_plist_count(macro->sig), true);
445
446 /* back to where we took off */
447 psi_cpp_tokiter_seek(cpp, start);
448 ++cpp->expanded;
449 return true;
450 }
451
452 static bool psi_cpp_tokiter_expand_def(struct psi_cpp *cpp,
453 struct psi_token *target, struct psi_cpp_macro_decl *macro)
454 {
455 size_t index = psi_cpp_tokiter_index(cpp);
456
457 /* delete current token from stream */
458 psi_cpp_tokiter_del_cur(cpp, false);
459
460 if (index != psi_cpp_tokiter_index(cpp)) {
461 /* might have been last token */
462 psi_cpp_tokiter_next(cpp);
463 }
464 /* replace with tokens from macro */
465 psi_cpp_tokiter_expand_tokens(cpp, target, macro->tokens);
466
467 free(target);
468 ++cpp->expanded;
469 return true;
470 }
471
472 static inline int psi_cpp_tokiter_expand_cmp(struct psi_token *t,
473 struct psi_cpp_macro_decl *m)
474 {
475 if (psi_plist_count(m->tokens) == 1) {
476 struct psi_token *r;
477
478 psi_plist_get(m->tokens, 0, &r);
479
480 return strcmp(r->text, t->text);
481 }
482 return -1;
483 }
484
485 bool psi_cpp_tokiter_expand(struct psi_cpp *cpp)
486 {
487 if (psi_cpp_tokiter_valid(cpp)) {
488 struct psi_token *current = psi_cpp_tokiter_current(cpp);
489
490 if (current) {
491 struct psi_cpp_macro_decl *macro = zend_hash_str_find_ptr(
492 &cpp->defs, current->text, current->size);
493
494 /* don't expand itself */
495 if (macro && macro->token != current) {
496 #if PSI_CPP_DEBUG
497 fprintf(stderr, "PSI: CPP expand < ");
498 psi_token_dump(2, current);
499 #endif
500 if (macro->sig) {
501 return psi_cpp_tokiter_expand_call(cpp, current, macro);
502 } else if (psi_cpp_tokiter_expand_cmp(current, macro)) {
503 return psi_cpp_tokiter_expand_def(cpp, current, macro);
504 }
505 }
506 }
507 }
508 return false;
509 }