e1d45f3bef82ecb6e1031e7fcf8b2b175b13a351
[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_range(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_range(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;
207 struct psi_token **exp_tokens = calloc(psi_plist_count(tokens), sizeof(*exp_tokens));
208
209 for (i = 0; psi_plist_get(tokens, i, &exp_tokens[i]); ++i) {
210 exp_tokens[i] = psi_token_copy(exp_tokens[i]);
211
212 #if PSI_CPP_DEBUG
213 fprintf(stderr, "PSI: CPP expand > ");
214 psi_token_dump(2, exp_tokens[i]);
215 #endif
216 }
217 psi_cpp_tokiter_ins_range(cpp, psi_cpp_tokiter_index(cpp), i, (void *) exp_tokens);
218 free(exp_tokens);
219 }
220 }
221
222 static void psi_cpp_tokiter_free_call_tokens(struct psi_plist **arg_tokens_list, size_t arg_count, bool free_tokens)
223 {
224 size_t i;
225
226 for (i = 0; i < arg_count; ++i) {
227 if (arg_tokens_list[i]) {
228 if (free_tokens) {
229 struct psi_token *tok;
230
231 while (psi_plist_pop(arg_tokens_list[i], &tok)) {
232 free(tok);
233 }
234 }
235 psi_plist_free(arg_tokens_list[i]);
236 }
237 }
238 free(arg_tokens_list);
239 }
240
241 static struct psi_plist **psi_cpp_tokiter_read_call_tokens(
242 struct psi_cpp_data *cpp, size_t arg_count)
243 {
244 size_t arg_index = 0, lparens = 1, rparens = 0;
245 struct psi_plist **arg_tokens = calloc(arg_count, sizeof(*arg_tokens));
246 struct psi_plist *free_tokens = psi_plist_init((void (*)(void *)) psi_token_free);
247 struct psi_token *tok;
248
249 arg_tokens[0] = psi_plist_init(NULL);
250
251 /* free macro name token on success */
252 tok = psi_cpp_tokiter_current(cpp);
253 free_tokens = psi_plist_add(free_tokens, &tok);
254
255 /* next token must be a LPAREN for a macro call */
256 psi_cpp_tokiter_next(cpp);
257 tok = psi_cpp_tokiter_current(cpp);
258 if (!psi_cpp_tokiter_valid(cpp) || tok->type != PSI_T_LPAREN) {
259 goto fail;
260 }
261
262 /* free LPAREN on success */
263 free_tokens = psi_plist_add(free_tokens, &tok);
264
265 while (lparens > rparens) {
266 psi_cpp_tokiter_next(cpp);
267 if (!psi_cpp_tokiter_valid(cpp)) {
268 goto fail;
269 }
270 tok = psi_cpp_tokiter_current(cpp);
271
272 switch (tok->type) {
273 case PSI_T_LPAREN:
274 ++lparens;
275 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
276 break;
277 case PSI_T_RPAREN:
278 if (++rparens == lparens) {
279 /* closing RPAREN */
280 if (arg_index + 1 < arg_count) {
281 goto fail;
282 }
283 free_tokens = psi_plist_add(free_tokens, &tok);
284 } else {
285 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
286 }
287 break;
288 case PSI_T_COMMA:
289 if (1 == (lparens - rparens)) {
290 /* too many commas? */
291 if (++arg_index >= arg_count) {
292 goto fail;
293 }
294 free_tokens = psi_plist_add(free_tokens, &tok);
295 /* next arg */
296 arg_tokens[arg_index] = psi_plist_init(NULL);
297 } else {
298 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
299 }
300 break;
301 default:
302 arg_tokens[arg_index] = psi_plist_add(arg_tokens[arg_index], &tok);
303 }
304 }
305
306 psi_plist_free(free_tokens);
307 return arg_tokens;
308
309 fail:
310 psi_cpp_tokiter_free_call_tokens(arg_tokens, arg_count, false);
311 return NULL;
312 }
313
314 static void psi_cpp_tokiter_expand_call_tokens(struct psi_cpp_data *cpp,
315 struct psi_cpp_macro_decl *macro, struct psi_plist **arg_tokens_list)
316 {
317 size_t i;
318 struct psi_token *tok;
319
320 for (i = 0; psi_plist_get(macro->tokens, i, &tok); ++i) {
321 struct psi_plist *arg_tokens = NULL;
322
323 if (tok->type == PSI_T_NAME) {
324 size_t s;
325 struct psi_token *arg_name;
326
327 for (s = 0; psi_plist_get(macro->sig, s, &arg_name); ++s) {
328 if (arg_name->size == tok->size) {
329 if (!memcmp(arg_name->text, tok->text, tok->size)) {
330 arg_tokens = arg_tokens_list[s];
331 break;
332 }
333 }
334 }
335 }
336
337 if (arg_tokens) {
338 size_t tok_count = psi_plist_count(arg_tokens);
339
340 if (tok_count) {
341 psi_cpp_tokiter_expand_tokens(cpp, arg_tokens);
342 psi_cpp_tokiter_seek(cpp, psi_cpp_tokiter_index(cpp) + tok_count);
343 }
344 } else {
345 psi_cpp_tokiter_ins_cur(cpp, psi_token_copy(tok));
346 psi_cpp_tokiter_next(cpp);
347 }
348 }
349 }
350
351 static bool psi_cpp_tokiter_expand_call(struct psi_cpp_data *cpp,
352 struct psi_cpp_macro_decl *macro)
353 {
354 /* function-like macro
355 * #define FOO(a,b) a>b // macro->sig == {a, b}, macro->tokens = {a, >, b}
356 * # if FOO(1,2) // expands to if 1 > 2
357 */
358 size_t start = psi_cpp_tokiter_index(cpp);
359 struct psi_plist **arg_tokens_list;
360
361 /* read in tokens, until we have balanced parens */
362 arg_tokens_list = psi_cpp_tokiter_read_call_tokens(cpp, psi_plist_count(macro->sig));
363 if (!arg_tokens_list) {
364 psi_cpp_tokiter_seek(cpp, start);
365 return false;
366 }
367
368 /* ditch arg tokens */
369 psi_cpp_tokiter_del_range(cpp, start, psi_cpp_tokiter_index(cpp) - start + 1, false);
370 psi_cpp_tokiter_seek(cpp, start);
371
372 /* insert and expand macro tokens */
373 psi_cpp_tokiter_expand_call_tokens(cpp, macro, arg_tokens_list);
374 psi_cpp_tokiter_free_call_tokens(arg_tokens_list, psi_plist_count(macro->sig), true);
375
376 /* back to where we took off */
377 psi_cpp_tokiter_seek(cpp, start);
378 ++cpp->expanded;
379 return true;
380 }
381
382 bool psi_cpp_tokiter_expand(struct psi_cpp_data *cpp)
383 {
384 if (psi_cpp_tokiter_valid(cpp)) {
385 struct psi_token *current = psi_cpp_tokiter_current(cpp);
386
387 if (current) {
388 struct psi_cpp_macro_decl *macro = zend_hash_str_find_ptr(
389 &cpp->defs, current->text, current->size);
390
391 /* don't expand itself */
392 if (macro && macro->token != current) {
393 #if PSI_CPP_DEBUG
394 fprintf(stderr, "PSI: CPP expand < ");
395 psi_token_dump(2, current);
396 #endif
397 if (macro->sig) {
398 return psi_cpp_tokiter_expand_call(cpp, macro);
399 } else {
400 size_t index = psi_cpp_tokiter_index(cpp);
401
402 /* delete current token from stream */
403 psi_cpp_tokiter_del_cur(cpp, true);
404
405 if (index != psi_cpp_tokiter_index(cpp)) {
406 /* might have been last token */
407 psi_cpp_tokiter_next(cpp);
408 }
409 /* replace with tokens from macro */
410 psi_cpp_tokiter_expand_tokens(cpp, macro->tokens);
411
412 ++cpp->expanded;
413 return true;
414 }
415 }
416 }
417 }
418 return false;
419 }