7aa938e07da101dceb0d330bb62e2a16be4c4e35
[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
32 void psi_cpp_tokiter_dump(int fd, struct psi_cpp_data *cpp)
33 {
34 size_t i;
35 struct psi_token *T;
36
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);
40 }
41 }
42
43 void psi_cpp_tokiter_reset(struct psi_cpp_data *cpp)
44 {
45 #if PSI_CPP_DEBUG
46 fprintf(stderr, "PSI: CPP reset (%zu tokens)\n", psi_plist_count(cpp->tokens));
47 psi_cpp_tokiter_dump(2, cpp);
48 #endif
49 cpp->index = 0;
50 cpp->expanded = 0;
51 }
52
53 bool psi_cpp_tokiter_seek(struct psi_cpp_data *cpp, size_t index)
54 {
55 if (index < psi_plist_count(cpp->tokens)) {
56 cpp->index = index;
57 return true;
58 }
59 return false;
60 }
61
62 struct psi_token *psi_cpp_tokiter_current(struct psi_cpp_data *cpp)
63 {
64 struct psi_token *current = NULL;
65 bool found = psi_plist_get(cpp->tokens, cpp->index, &current);
66
67 assert(found);
68
69 return current;
70 }
71
72 size_t psi_cpp_tokiter_index(struct psi_cpp_data *cpp)
73 {
74 return cpp->index;
75 }
76
77 void psi_cpp_tokiter_next(struct psi_cpp_data *cpp)
78 {
79 #if 0 && PSI_CPP_DEBUG
80 fprintf(stderr, "PSI: CPP next -> index=%zu -> index=%zu\n", cpp->index, cpp->index+1);
81 #endif
82 ++cpp->index;
83 }
84
85 void psi_cpp_tokiter_prev(struct psi_cpp_data *cpp)
86 {
87 #if 0 && PSI_CPP_DEBUG
88 fprintf(stderr, "PSI: CPP prev -> index=%zu -> index=%zu\n", cpp->index, cpp->index-1);
89 #endif
90 if (cpp->index) {
91 --cpp->index;
92 }
93 }
94
95 bool psi_cpp_tokiter_valid(struct psi_cpp_data *cpp)
96 {
97 #if 0 && PSI_CPP_DEBUG
98 fprintf(stderr, "PSI: CPP valid -> index=%zu -> %d\n", cpp->index, cpp->index < psi_plist_count(cpp->tokens));
99 #endif
100 return cpp->index < psi_plist_count(cpp->tokens);
101 }
102
103 bool psi_cpp_tokiter_del_cur(struct psi_cpp_data *cpp, bool free_token)
104 {
105 struct psi_token *cur = NULL;
106 bool deleted = psi_plist_del(cpp->tokens, cpp->index, &cur);
107
108 #if PSI_CPP_DEBUG
109 fprintf(stderr, "PSI: CPP del_cur -> index=%zu, del=%d, free=%d ",
110 cpp->index, (int) deleted, (int) free_token);
111 if (cur) {
112 psi_token_dump(2, cur);
113 } else {
114 fprintf(stderr, "NULL\n");
115 }
116 #endif
117 if (cur && free_token) {
118 free(cur);
119 }
120 if (deleted && cpp->index >= psi_plist_count(cpp->tokens)) {
121 cpp->index = MAX(0, psi_plist_count(cpp->tokens)-1);
122 }
123 return deleted;
124 }
125
126 bool psi_cpp_tokiter_del_range(struct psi_cpp_data *cpp, size_t offset, size_t num_eles, bool free_tokens)
127 {
128 struct psi_token **ptr;
129 bool deleted;
130
131 if (free_tokens) {
132 ptr = calloc(num_eles, sizeof(*ptr));
133 } else {
134 ptr = NULL;
135 }
136
137 #if PSI_CPP_DEBUG
138 fprintf(stderr, "PSI: CPP del_range -> index=%zu, offset=%zu, num_eles=%zu\n", cpp->index, offset, num_eles);
139 #endif
140
141 deleted = psi_plist_del_r(cpp->tokens, offset, num_eles, (void *) ptr);
142
143 if (deleted) {
144 if (cpp->index >= psi_plist_count(cpp->tokens)) {
145 cpp->index = MAX(0, psi_plist_count(cpp->tokens)-1);
146 }
147 if (free_tokens) {
148 while (num_eles--) {
149 if (ptr[num_eles]) {
150 free(ptr[num_eles]);
151 }
152 }
153 free(ptr);
154 }
155 }
156 return deleted;
157 }
158
159 bool psi_cpp_tokiter_ins_cur(struct psi_cpp_data *cpp, struct psi_token *tok)
160 {
161 struct psi_plist *tokens = psi_plist_ins(cpp->tokens, cpp->index, &tok);
162
163 #if PSI_CPP_DEBUG
164 fprintf(stderr, "PSI: CPP ins_cur -> index=%zu ", cpp->index);
165 psi_token_dump(2, tok);
166 #endif
167
168 if (!tokens) {
169 return false;
170 }
171 cpp->tokens = tokens;
172 return true;
173 }
174
175 bool psi_cpp_tokiter_ins_range(struct psi_cpp_data *cpp, size_t offset,
176 size_t num_eles, void **eles)
177 {
178 struct psi_plist *tokens = psi_plist_ins_r(cpp->tokens, offset,
179 num_eles, eles);
180
181 #if PSI_CPP_DEBUG
182 fprintf(stderr, "PSI: CPP ins_range -> index=%zu, offset=%zu, num_eles=%zu\n", cpp->index, offset, num_eles);
183 #endif
184
185 if (!tokens) {
186 return false;
187 }
188 cpp->tokens = tokens;
189 return true;
190 }
191
192 bool psi_cpp_tokiter_defined(struct psi_cpp_data *cpp)
193 {
194 if (psi_cpp_tokiter_valid(cpp)) {
195 struct psi_token *current = psi_cpp_tokiter_current(cpp);
196
197 return psi_cpp_defined(cpp, current);
198 }
199
200 return false;
201 }
202
203 void psi_cpp_tokiter_expand_tokens(struct psi_cpp_data *cpp, struct psi_plist *tokens)
204 {
205 if (tokens && psi_plist_count(tokens)) {
206 size_t i = 0, n = 0;
207 bool stringify = false, paste = false;
208 struct psi_token *tok, **exp_tokens = calloc(psi_plist_count(tokens), sizeof(*exp_tokens));
209
210 while (psi_plist_get(tokens, i++, &tok)) {
211 struct psi_token *new_tok;
212
213 if (tok->type == PSI_T_HASH) {
214 if (stringify) {
215 stringify = false;
216 paste = true;
217 } else {
218 stringify = true;
219 }
220 continue;
221 }
222
223 if (paste && n > 0 && exp_tokens[n - 1] &&
224 (new_tok = psi_token_cat(NULL, 2, exp_tokens[n - 1], tok))) {
225 free(exp_tokens[n - 1]);
226 exp_tokens[n - 1] = new_tok;
227 } else {
228 struct psi_token *cpy = psi_token_copy(tok);
229
230 if (stringify) {
231 cpy = psi_token_append(NULL,
232 psi_token_prepend(NULL, cpy, 1, "\""), 1, "\"");
233 cpy->type = PSI_T_QUOTED_STRING;
234 }
235 exp_tokens[n++] = cpy;
236 }
237
238 #if PSI_CPP_DEBUG
239 fprintf(stderr, "PSI: CPP expand > ");
240 psi_token_dump(2, tok);
241 #endif
242 paste = false;
243 stringify = false;
244 }
245 psi_cpp_tokiter_ins_range(cpp, psi_cpp_tokiter_index(cpp), n, (void *) exp_tokens);
246 free(exp_tokens);
247 }
248 }
249
250 static void psi_cpp_tokiter_free_call_tokens(struct psi_plist **arg_tokens_list, size_t arg_count, bool free_tokens)
251 {
252 size_t i;
253
254 for (i = 0; i < arg_count; ++i) {
255 if (arg_tokens_list[i]) {
256 if (free_tokens) {
257 struct psi_token *tok;
258
259 while (psi_plist_pop(arg_tokens_list[i], &tok)) {
260 free(tok);
261 }
262 }
263 psi_plist_free(arg_tokens_list[i]);
264 }
265 }
266 free(arg_tokens_list);
267 }
268
269 static struct psi_plist **psi_cpp_tokiter_read_call_tokens(
270 struct psi_cpp_data *cpp, size_t arg_count)
271 {
272 size_t arg_index = 0, lparens = 1, rparens = 0;
273 struct psi_plist **arg_tokens = calloc(arg_count, sizeof(*arg_tokens));
274 struct psi_plist *free_tokens = psi_plist_init((void (*)(void *)) psi_token_free);
275 struct psi_token *tok;
276
277 arg_tokens[0] = psi_plist_init(NULL);
278
279 /* free macro name token on success */
280 tok = psi_cpp_tokiter_current(cpp);
281 free_tokens = psi_plist_add(free_tokens, &tok);
282
283 /* next token must be a LPAREN for a macro call */
284 psi_cpp_tokiter_next(cpp);
285 tok = psi_cpp_tokiter_current(cpp);
286 if (!psi_cpp_tokiter_valid(cpp) || tok->type != PSI_T_LPAREN) {
287 goto fail;
288 }
289
290 /* free LPAREN on success */
291 free_tokens = psi_plist_add(free_tokens, &tok);
292
293 while (lparens > rparens) {
294 psi_cpp_tokiter_next(cpp);
295 if (!psi_cpp_tokiter_valid(cpp)) {
296 goto fail;
297 }
298 tok = psi_cpp_tokiter_current(cpp);
299
300 switch (tok->type) {
301 case PSI_T_LPAREN:
302 ++lparens;
303 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
304 break;
305 case PSI_T_RPAREN:
306 if (++rparens == lparens) {
307 /* closing RPAREN */
308 if (arg_index + 1 < arg_count) {
309 goto fail;
310 }
311 free_tokens = psi_plist_add(free_tokens, &tok);
312 } else {
313 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
314 }
315 break;
316 case PSI_T_COMMA:
317 if (1 == (lparens - rparens)) {
318 /* too many commas? */
319 if (++arg_index >= arg_count) {
320 goto fail;
321 }
322 free_tokens = psi_plist_add(free_tokens, &tok);
323 /* next arg */
324 arg_tokens[arg_index] = psi_plist_init(NULL);
325 } else {
326 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
327 }
328 break;
329 default:
330 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
331 }
332 }
333
334 psi_plist_free(free_tokens);
335 return arg_tokens;
336
337 fail:
338 psi_cpp_tokiter_free_call_tokens(arg_tokens, arg_count, false);
339 return NULL;
340 }
341
342 static void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp_data *cpp,
343 struct psi_cpp_macro_decl *macro, struct psi_plist **arg_tokens_list)
344 {
345 size_t i;
346 struct psi_token *tok;
347 struct psi_plist *tokens = psi_plist_init(NULL);
348
349 for (i = 0; psi_plist_get(macro->tokens, i, &tok); ++i) {
350 struct psi_plist *arg_tokens = NULL;
351
352 if (tok->type == PSI_T_NAME) {
353 size_t s;
354 struct psi_token *arg_name;
355
356 for (s = 0; psi_plist_get(macro->sig, s, &arg_name); ++s) {
357 if (arg_name->size == tok->size) {
358 if (!memcmp(arg_name->text, tok->text, tok->size)) {
359 arg_tokens = arg_tokens_list[s];
360 break;
361 }
362 }
363 }
364 }
365
366 if (arg_tokens) {
367 tokens = psi_plist_add_r(tokens, psi_plist_count(arg_tokens), psi_plist_eles(arg_tokens));
368 } else {
369 tokens = psi_plist_add(tokens, &tok);
370 }
371 }
372
373 psi_cpp_tokiter_expand_tokens(cpp, tokens);
374 psi_plist_free(tokens);
375 }
376
377 static bool psi_cpp_tokiter_expand_call(struct psi_cpp_data *cpp,
378 struct psi_cpp_macro_decl *macro)
379 {
380 /* function-like macro
381 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
382 * # if FOO(1,2) // expands to if 1 > 2
383 */
384 size_t start = psi_cpp_tokiter_index(cpp);
385 struct psi_plist **arg_tokens_list;
386
387 /* read in tokens, until we have balanced parens */
388 arg_tokens_list = psi_cpp_tokiter_read_call_tokens(cpp, psi_plist_count(macro->sig));
389 if (!arg_tokens_list) {
390 psi_cpp_tokiter_seek(cpp, start);
391 return false;
392 }
393
394 /* ditch arg tokens */
395 psi_cpp_tokiter_del_range(cpp, start, psi_cpp_tokiter_index(cpp) - start + 1, false);
396 psi_cpp_tokiter_seek(cpp, start);
397
398 /* insert and expand macro tokens */
399 psi_cpp_tokiter_expand_call_tokens(cpp, macro, arg_tokens_list);
400 psi_cpp_tokiter_free_call_tokens(arg_tokens_list, psi_plist_count(macro->sig), true);
401
402 /* back to where we took off */
403 psi_cpp_tokiter_seek(cpp, start);
404 ++cpp->expanded;
405 return true;
406 }
407
408 bool psi_cpp_tokiter_expand(struct psi_cpp_data *cpp)
409 {
410 if (psi_cpp_tokiter_valid(cpp)) {
411 struct psi_token *current = psi_cpp_tokiter_current(cpp);
412
413 if (current) {
414 struct psi_cpp_macro_decl *macro = zend_hash_str_find_ptr(
415 &cpp->defs, current->text, current->size);
416
417 /* don't expand itself */
418 if (macro && macro->token != current) {
419 #if PSI_CPP_DEBUG
420 fprintf(stderr, "PSI: CPP expand < ");
421 psi_token_dump(2, current);
422 #endif
423 if (macro->sig) {
424 return psi_cpp_tokiter_expand_call(cpp, macro);
425 } else {
426 size_t index = psi_cpp_tokiter_index(cpp);
427
428 /* delete current token from stream */
429 psi_cpp_tokiter_del_cur(cpp, true);
430
431 if (index != psi_cpp_tokiter_index(cpp)) {
432 /* might have been last token */
433 psi_cpp_tokiter_next(cpp);
434 }
435 /* replace with tokens from macro */
436 psi_cpp_tokiter_expand_tokens(cpp, macro->tokens);
437
438 ++cpp->expanded;
439 return true;
440 }
441 }
442 }
443 }
444 return false;
445 }