cpp
[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 void psi_cpp_tokiter_expand_tokens(struct psi_cpp *cpp, struct psi_plist *tokens)
237 {
238 if (tokens && psi_plist_count(tokens)) {
239 size_t i = 0, n = 0;
240 bool stringify = false, paste = false;
241 struct psi_token *tok, **exp_tokens = calloc(psi_plist_count(tokens), sizeof(*exp_tokens));
242
243 while (psi_plist_get(tokens, i++, &tok)) {
244 struct psi_token *new_tok;
245
246 if (tok->type == PSI_T_HASH) {
247 if (stringify) {
248 stringify = false;
249 paste = true;
250 } else {
251 stringify = true;
252 }
253 continue;
254 }
255
256 if (paste && n > 0 && exp_tokens[n - 1] &&
257 (new_tok = psi_token_cat(NULL, 2, exp_tokens[n - 1], tok))) {
258 free(exp_tokens[n - 1]);
259 exp_tokens[n - 1] = new_tok;
260 } else {
261 struct psi_token *cpy = psi_token_copy(tok);
262
263 if (stringify) {
264 cpy = psi_token_append(NULL,
265 psi_token_prepend(NULL, cpy, 1, "\""), 1, "\"");
266 cpy->type = PSI_T_QUOTED_STRING;
267 }
268 exp_tokens[n++] = cpy;
269 }
270
271 #if PSI_CPP_DEBUG
272 fprintf(stderr, "PSI: CPP expand > ");
273 psi_token_dump(2, tok);
274 #endif
275 paste = false;
276 stringify = false;
277 }
278 psi_cpp_tokiter_ins_range(cpp, psi_cpp_tokiter_index(cpp), n, (void *) exp_tokens);
279 free(exp_tokens);
280 }
281 }
282
283 static void psi_cpp_tokiter_free_call_tokens(struct psi_plist **arg_tokens_list, size_t arg_count, bool free_tokens)
284 {
285 size_t i;
286
287 for (i = 0; i < arg_count; ++i) {
288 if (arg_tokens_list[i]) {
289 if (free_tokens) {
290 struct psi_token *tok;
291
292 while (psi_plist_pop(arg_tokens_list[i], &tok)) {
293 free(tok);
294 }
295 }
296 psi_plist_free(arg_tokens_list[i]);
297 }
298 }
299 free(arg_tokens_list);
300 }
301
302 static struct psi_plist **psi_cpp_tokiter_read_call_tokens(
303 struct psi_cpp *cpp, size_t arg_count)
304 {
305 size_t arg_index = 0, lparens = 1, rparens = 0;
306 struct psi_plist **arg_tokens = calloc(arg_count, sizeof(*arg_tokens));
307 struct psi_plist *free_tokens = psi_plist_init((psi_plist_dtor) psi_token_free);
308 struct psi_token *tok;
309
310 arg_tokens[0] = psi_plist_init(NULL);
311
312 /* free macro name token on success */
313 tok = psi_cpp_tokiter_current(cpp);
314 free_tokens = psi_plist_add(free_tokens, &tok);
315
316 /* next token must be a LPAREN for a macro call */
317 psi_cpp_tokiter_next(cpp);
318 tok = psi_cpp_tokiter_current(cpp);
319 if (!psi_cpp_tokiter_valid(cpp) || tok->type != PSI_T_LPAREN) {
320 goto fail;
321 }
322
323 /* free LPAREN on success */
324 free_tokens = psi_plist_add(free_tokens, &tok);
325
326 while (lparens > rparens) {
327 psi_cpp_tokiter_next(cpp);
328 if (!psi_cpp_tokiter_valid(cpp)) {
329 goto fail;
330 }
331 tok = psi_cpp_tokiter_current(cpp);
332
333 switch (tok->type) {
334 case PSI_T_LPAREN:
335 ++lparens;
336 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
337 break;
338 case PSI_T_RPAREN:
339 if (++rparens == lparens) {
340 /* closing RPAREN */
341 if (arg_index + 1 < arg_count) {
342 goto fail;
343 }
344 free_tokens = psi_plist_add(free_tokens, &tok);
345 } else {
346 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
347 }
348 break;
349 case PSI_T_COMMA:
350 if (1 == (lparens - rparens)) {
351 /* too many commas? */
352 if (++arg_index >= arg_count) {
353 goto fail;
354 }
355 free_tokens = psi_plist_add(free_tokens, &tok);
356 /* next arg */
357 arg_tokens[arg_index] = psi_plist_init(NULL);
358 } else {
359 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
360 }
361 break;
362 default:
363 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
364 }
365 }
366
367 psi_plist_free(free_tokens);
368 return arg_tokens;
369
370 fail:
371 psi_cpp_tokiter_free_call_tokens(arg_tokens, arg_count, false);
372 return NULL;
373 }
374
375 static void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp *cpp,
376 struct psi_cpp_macro_decl *macro, struct psi_plist **arg_tokens_list)
377 {
378 size_t i;
379 struct psi_token *tok;
380 struct psi_plist *tokens = psi_plist_init(NULL);
381
382 for (i = 0; psi_plist_get(macro->tokens, i, &tok); ++i) {
383 struct psi_plist *arg_tokens = NULL;
384
385 if (tok->type == PSI_T_NAME) {
386 size_t s;
387 struct psi_token *arg_name;
388
389 for (s = 0; psi_plist_get(macro->sig, s, &arg_name); ++s) {
390 if (arg_name->size == tok->size) {
391 if (!memcmp(arg_name->text, tok->text, tok->size)) {
392 arg_tokens = arg_tokens_list[s];
393 break;
394 }
395 }
396 }
397 }
398
399 if (arg_tokens) {
400 tokens = psi_plist_add_r(tokens, psi_plist_count(arg_tokens), psi_plist_eles(arg_tokens));
401 } else {
402 tokens = psi_plist_add(tokens, &tok);
403 }
404 }
405
406 psi_cpp_tokiter_expand_tokens(cpp, tokens);
407 psi_plist_free(tokens);
408 }
409
410 static bool psi_cpp_tokiter_expand_call(struct psi_cpp *cpp,
411 struct psi_cpp_macro_decl *macro)
412 {
413 /* function-like macro
414 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
415 * # if FOO(1,2) // expands to if 1 > 2
416 */
417 size_t start = psi_cpp_tokiter_index(cpp);
418 struct psi_plist **arg_tokens_list;
419
420 /* read in tokens, until we have balanced parens */
421 arg_tokens_list = psi_cpp_tokiter_read_call_tokens(cpp, psi_plist_count(macro->sig));
422 if (!arg_tokens_list) {
423 psi_cpp_tokiter_seek(cpp, start);
424 return false;
425 }
426
427 /* ditch arg tokens */
428 psi_cpp_tokiter_del_range(cpp, start, psi_cpp_tokiter_index(cpp) - start + 1, false);
429 psi_cpp_tokiter_seek(cpp, start);
430
431 /* insert and expand macro tokens */
432 psi_cpp_tokiter_expand_call_tokens(cpp, macro, arg_tokens_list);
433 psi_cpp_tokiter_free_call_tokens(arg_tokens_list, psi_plist_count(macro->sig), true);
434
435 /* back to where we took off */
436 psi_cpp_tokiter_seek(cpp, start);
437 ++cpp->expanded;
438 return true;
439 }
440
441 bool psi_cpp_tokiter_expand(struct psi_cpp *cpp)
442 {
443 if (psi_cpp_tokiter_valid(cpp)) {
444 struct psi_token *current = psi_cpp_tokiter_current(cpp);
445
446 if (current) {
447 struct psi_cpp_macro_decl *macro = zend_hash_str_find_ptr(
448 &cpp->defs, current->text, current->size);
449
450 /* don't expand itself */
451 if (macro && macro->token != current) {
452 #if PSI_CPP_DEBUG
453 fprintf(stderr, "PSI: CPP expand < ");
454 psi_token_dump(2, current);
455 #endif
456 if (macro->sig) {
457 return psi_cpp_tokiter_expand_call(cpp, macro);
458 } else {
459 size_t index = psi_cpp_tokiter_index(cpp);
460
461 /* delete current token from stream */
462 psi_cpp_tokiter_del_cur(cpp, true);
463
464 if (index != psi_cpp_tokiter_index(cpp)) {
465 /* might have been last token */
466 psi_cpp_tokiter_next(cpp);
467 }
468 /* replace with tokens from macro */
469 psi_cpp_tokiter_expand_tokens(cpp, macro->tokens);
470
471 ++cpp->expanded;
472 return true;
473 }
474 }
475 }
476 }
477 return false;
478 }