fix error message
[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 (free_token) {
132 psi_token_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 psi_token_free(&ptr[num_eles]);
177 }
178 free(ptr);
179 }
180 }
181 return deleted;
182 }
183
184 bool psi_cpp_tokiter_ins_cur(struct psi_cpp *cpp, struct psi_token *tok)
185 {
186 struct psi_plist *tokens = psi_plist_ins(cpp->tokens, cpp->index, &tok);
187
188 #if PSI_CPP_DEBUG
189 fprintf(stderr, "PSI: CPP ins_cur -> index=%zu ", cpp->index);
190 psi_token_dump(2, tok);
191 #endif
192
193 if (!tokens) {
194 return false;
195 }
196 cpp->tokens = tokens;
197 return true;
198 }
199
200 bool psi_cpp_tokiter_ins_range(struct psi_cpp *cpp, size_t offset,
201 size_t num_eles, void **eles)
202 {
203 struct psi_plist *tokens;
204
205 if (!num_eles) {
206 return true;
207 }
208
209 tokens = psi_plist_ins_r(cpp->tokens, offset, num_eles, eles);
210
211 #if PSI_CPP_DEBUG
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));
214 #endif
215
216 if (!tokens) {
217 return false;
218 }
219 cpp->tokens = tokens;
220 return true;
221 }
222
223 bool psi_cpp_tokiter_defined(struct psi_cpp *cpp)
224 {
225 if (psi_cpp_tokiter_valid(cpp)) {
226 struct psi_token *current = psi_cpp_tokiter_current(cpp);
227
228 return psi_cpp_defined(cpp, current);
229 }
230
231 return false;
232 }
233
234 static size_t psi_cpp_tokiter_expand_tokens(struct psi_cpp *cpp,
235 struct psi_token *target, struct psi_plist *tokens)
236 {
237 if (tokens && psi_plist_count(tokens)) {
238 size_t i = 0, n = 0;
239 bool stringify = false, paste = false;
240 struct psi_token *tok, **exp_tokens = calloc(psi_plist_count(tokens), sizeof(*exp_tokens));
241
242 while (psi_plist_get(tokens, i++, &tok)) {
243 struct psi_token *new_tok;
244
245 if (tok->type == PSI_T_EOL) {
246 continue;
247 }
248 if (tok->type == PSI_T_HASH) {
249 stringify = true;
250 continue;
251 }
252 if (tok->type == PSI_T_CPP_PASTE) {
253 paste = true;
254 continue;
255 }
256
257 if (paste && n > 0 && exp_tokens[n - 1]) {
258 struct psi_token *tmp_tok, *old_tok = exp_tokens[n - 1];
259
260 tmp_tok = psi_token_init(old_tok->type, "", 0,
261 target->col, target->line,
262 target->file ? target->file : zend_empty_string);
263
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);
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->val, tok->text->len, target->col, target->line,
272 target->file ?: zend_empty_string);
273
274 exp_tokens[n++] = new_tok;
275 }
276
277 #if PSI_CPP_DEBUG
278 fprintf(stderr, "PSI: CPP expand > ");
279 psi_token_dump(2, tok);
280 #endif
281
282 paste = false;
283 stringify = false;
284 }
285 psi_cpp_tokiter_ins_range(cpp, psi_cpp_tokiter_index(cpp), n, (void *) exp_tokens);
286 free(exp_tokens);
287
288 return n;
289 } else {
290 return 0;
291 }
292 }
293
294 static void psi_cpp_tokiter_free_call_tokens(struct psi_plist **arg_tokens_list, size_t arg_count, bool free_tokens)
295 {
296 size_t i;
297
298 for (i = 0; i < arg_count; ++i) {
299 if (arg_tokens_list[i]) {
300 if (free_tokens) {
301 struct psi_token *tok;
302
303 while (psi_plist_pop(arg_tokens_list[i], &tok)) {
304 psi_token_free(&tok);
305 }
306 }
307 psi_plist_free(arg_tokens_list[i]);
308 }
309 }
310 free(arg_tokens_list);
311 }
312
313 static struct psi_plist **psi_cpp_tokiter_read_call_tokens(
314 struct psi_cpp *cpp, size_t arg_count)
315 {
316 size_t arg_index = 0, lparens = 1, rparens = 0;
317 struct psi_plist **arg_tokens = calloc(arg_count, sizeof(*arg_tokens));
318 struct psi_plist *free_tokens = psi_plist_init((psi_plist_dtor) psi_token_free);
319 struct psi_token *tok;
320
321 arg_tokens[0] = psi_plist_init(NULL);
322
323 /* next token must be a LPAREN for a macro call */
324 psi_cpp_tokiter_next(cpp);
325 tok = psi_cpp_tokiter_current(cpp);
326 if (!psi_cpp_tokiter_valid(cpp) || tok->type != PSI_T_LPAREN) {
327 goto fail;
328 }
329
330 /* free LPAREN on success */
331 free_tokens = psi_plist_add(free_tokens, &tok);
332
333 while (lparens > rparens) {
334 psi_cpp_tokiter_next(cpp);
335 if (!psi_cpp_tokiter_valid(cpp)) {
336 goto fail;
337 }
338 tok = psi_cpp_tokiter_current(cpp);
339
340 switch (tok->type) {
341 case PSI_T_LPAREN:
342 ++lparens;
343 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
344 break;
345 case PSI_T_RPAREN:
346 if (++rparens == lparens) {
347 /* closing RPAREN */
348 if (arg_index + 1 < arg_count) {
349 goto fail;
350 }
351 free_tokens = psi_plist_add(free_tokens, &tok);
352 } else {
353 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
354 }
355 break;
356 case PSI_T_COMMA:
357 if (1 == (lparens - rparens)) {
358 /* too many commas? */
359 if (++arg_index >= arg_count) {
360 goto fail;
361 }
362 free_tokens = psi_plist_add(free_tokens, &tok);
363 /* next arg */
364 arg_tokens[arg_index] = psi_plist_init(NULL);
365 } else {
366 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
367 }
368 break;
369 default:
370 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
371 }
372 }
373
374 psi_plist_free(free_tokens);
375 return arg_tokens;
376
377 fail:
378 psi_cpp_tokiter_free_call_tokens(arg_tokens, arg_count, false);
379 return NULL;
380 }
381
382 static void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp *cpp,
383 struct psi_token *target, struct psi_cpp_macro_decl *macro,
384 struct psi_plist **arg_tokens_list)
385 {
386 size_t i;
387 struct psi_token *tok;
388 struct psi_plist *tokens = psi_plist_init(NULL);
389
390 for (i = 0; psi_plist_get(macro->tokens, i, &tok); ++i) {
391 struct psi_plist *arg_tokens = NULL;
392
393 if (tok->type == PSI_T_NAME) {
394 size_t s;
395 struct psi_token *arg_name;
396
397 for (s = 0; psi_plist_get(macro->sig, s, &arg_name); ++s) {
398 if (zend_string_equals(arg_name->text, tok->text)) {
399 arg_tokens = arg_tokens_list[s];
400 break;
401 }
402 }
403 }
404
405 if (arg_tokens) {
406 tokens = psi_plist_add_r(tokens, psi_plist_count(arg_tokens), psi_plist_eles(arg_tokens));
407 } else {
408 tokens = psi_plist_add(tokens, &tok);
409 }
410 }
411
412 psi_cpp_tokiter_expand_tokens(cpp, target, tokens);
413 psi_plist_free(tokens);
414 }
415
416 static bool psi_cpp_tokiter_expand_call(struct psi_cpp *cpp,
417 struct psi_token *target, struct psi_cpp_macro_decl *macro)
418 {
419 /* function-like macro
420 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
421 * # if FOO(1,2) // expands to if 1 > 2
422 */
423 size_t start = psi_cpp_tokiter_index(cpp);
424 struct psi_plist **arg_tokens_list;
425
426 /* read in tokens, until we have balanced parens */
427 arg_tokens_list = psi_cpp_tokiter_read_call_tokens(cpp, psi_plist_count(macro->sig));
428 if (!arg_tokens_list) {
429 psi_cpp_tokiter_seek(cpp, start);
430 return false;
431 }
432
433 /* ditch arg tokens */
434 psi_cpp_tokiter_del_range(cpp, start, psi_cpp_tokiter_index(cpp) - start + 1, false);
435 psi_cpp_tokiter_seek(cpp, start);
436
437 /* insert and expand macro tokens */
438 psi_cpp_tokiter_expand_call_tokens(cpp, target, macro, arg_tokens_list);
439 psi_cpp_tokiter_free_call_tokens(arg_tokens_list, psi_plist_count(macro->sig), true);
440
441 /* back to where we took off */
442 psi_cpp_tokiter_seek(cpp, start);
443
444 psi_token_free(&target);
445 ++cpp->expanded;
446 return true;
447 }
448
449 static bool psi_cpp_tokiter_expand_def(struct psi_cpp *cpp,
450 struct psi_token *target, struct psi_cpp_macro_decl *macro)
451 {
452 size_t index = psi_cpp_tokiter_index(cpp);
453
454 /* delete current token from stream */
455 psi_cpp_tokiter_del_cur(cpp, false);
456
457 if (index != psi_cpp_tokiter_index(cpp)) {
458 /* might have been last token */
459 psi_cpp_tokiter_next(cpp);
460 }
461 /* replace with tokens from macro */
462 psi_cpp_tokiter_expand_tokens(cpp, target, macro->tokens);
463
464 psi_token_free(&target);
465 ++cpp->expanded;
466 return true;
467 }
468
469 static inline int psi_cpp_tokiter_expand_cmp(struct psi_token *t,
470 struct psi_cpp_macro_decl *m)
471 {
472 if (psi_plist_count(m->tokens) == 1) {
473 struct psi_token *r;
474
475 psi_plist_get(m->tokens, 0, &r);
476
477 return !zend_string_equals(r->text, t->text);
478 }
479 return -1;
480 }
481
482 bool psi_cpp_tokiter_expand(struct psi_cpp *cpp)
483 {
484 if (psi_cpp_tokiter_valid(cpp)) {
485 struct psi_token *current = psi_cpp_tokiter_current(cpp);
486
487 if (current) {
488 struct psi_cpp_macro_decl *macro = zend_hash_find_ptr(
489 &cpp->defs, current->text);
490
491 /* don't expand itself */
492 if (macro && macro->token != current) {
493 #if PSI_CPP_DEBUG
494 fprintf(stderr, "PSI: CPP expand < ");
495 psi_token_dump(2, current);
496 #endif
497 if (macro->sig) {
498 return psi_cpp_tokiter_expand_call(cpp, current, macro);
499 } else if (psi_cpp_tokiter_expand_cmp(current, macro)) {
500 return psi_cpp_tokiter_expand_def(cpp, current, macro);
501 }
502 }
503 }
504 }
505 return false;
506 }