configure: _Float types
[m6w6/ext-psi] / src / cpp.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 <libgen.h>
29
30 #include "cpp.h"
31 #include "parser.h"
32
33 #define PSI_CPP_SEARCH
34 #define PSI_CPP_PREDEF
35 #include "php_psi_cpp.h"
36
37 #include "php_psi.h"
38
39 static void free_cpp_def(zval *p)
40 {
41 if (Z_TYPE_P(p) == IS_PTR) {
42 psi_cpp_macro_decl_free((void *) &Z_PTR_P(p));
43 }
44 }
45
46 struct psi_cpp *psi_cpp_init(struct psi_parser *P)
47 {
48 struct psi_cpp *cpp = calloc(1, sizeof(*cpp));
49
50 cpp->parser = P;
51 zend_hash_init(&cpp->defs, 0, NULL, free_cpp_def, 1);
52 zend_hash_init(&cpp->once, 0, NULL, NULL, 1);
53
54 return cpp;
55 }
56
57 bool psi_cpp_load_defaults(struct psi_cpp *cpp)
58 {
59 struct psi_parser_input *predef;
60
61 if ((predef = psi_parser_open_string(cpp->parser, psi_cpp_predef, sizeof(psi_cpp_predef) - 1))) {
62 bool parsed = psi_parser_parse(cpp->parser, predef);
63 free(predef);
64 return parsed;
65 }
66
67 return false;
68 }
69
70 #if PSI_CPP_DEBUG
71 static int dump_def(zval *p)
72 {
73 struct psi_cpp_macro_decl *decl = Z_PTR_P(p);
74
75 if (decl) {
76 dprintf(2, "#define ");
77 psi_cpp_macro_decl_dump(2, decl);
78 dprintf(2, "\n");
79 }
80 return ZEND_HASH_APPLY_KEEP;
81 }
82 #endif
83
84 void psi_cpp_free(struct psi_cpp **cpp_ptr)
85 {
86 if (*cpp_ptr) {
87 struct psi_cpp *cpp = *cpp_ptr;
88
89 #if PSI_CPP_DEBUG
90 fprintf(stderr, "PSI: CPP decls:\n");
91 zend_hash_apply(&cpp->defs, dump_def);
92 #endif
93 *cpp_ptr = NULL;
94 zend_hash_destroy(&cpp->defs);
95 zend_hash_destroy(&cpp->once);
96 free(cpp);
97 }
98 }
99
100 static bool psi_cpp_stage1(struct psi_cpp *cpp)
101 {
102 bool name = false, define = false, hash = false, eol = true, esc = false, ws = false;
103
104 PSI_DEBUG_PRINT(cpp->parser, "PSI: CPP %s\n", "stage1");
105
106 psi_cpp_tokiter_reset(cpp);
107 while (psi_cpp_tokiter_valid(cpp)) {
108 struct psi_token *token = psi_cpp_tokiter_current(cpp);
109
110 /* strip comments and attributes */
111 if (token->type == PSI_T_COMMENT
112 || token->type == PSI_T_CPP_ATTRIBUTE) {
113 psi_cpp_tokiter_del_cur(cpp, true);
114 continue;
115 }
116
117 /* line continuations */
118 if (token->type == PSI_T_EOL) {
119 if (esc) {
120 psi_cpp_tokiter_del_range(cpp, psi_cpp_tokiter_index(cpp) - 1, 2, true);
121 psi_cpp_tokiter_prev(cpp);
122 esc = false;
123 continue;
124 }
125 } else if (token->type == PSI_T_BSLASH) {
126 esc = !esc;
127 } else {
128 esc = false;
129 }
130
131 /* this whole turf is needed to distinct between:
132 * #define foo (1,2,3)
133 * #define foo(a,b,c)
134 */
135
136 if (token->type == PSI_T_WHITESPACE) {
137 if (name) {
138 name = false;
139 }
140 ws = true;
141 psi_cpp_tokiter_del_cur(cpp, true);
142 continue;
143 }
144
145 switch (token->type) {
146 case PSI_T_EOL:
147 eol = true;
148 break;
149 case PSI_T_HASH:
150 if (eol) {
151 hash = true;
152 eol = false;
153 }
154 break;
155 case PSI_T_DEFINE:
156 if (hash) {
157 define = true;
158 hash = false;
159 }
160 break;
161 case PSI_T_NAME:
162 if (define) {
163 name = true;
164 define = false;
165 }
166 break;
167 case PSI_T_LPAREN:
168 if (name) {
169 name = false;
170 if (!ws) {
171 /* mask special token for parser */
172 struct psi_token *no_ws = psi_token_copy(token);
173
174 no_ws->type = PSI_T_NO_WHITESPACE;
175 no_ws->text[0] = '\xA0';
176 psi_cpp_tokiter_ins_cur(cpp, no_ws);
177 continue;
178 }
179 }
180 /* no break */
181 default:
182 name = define = hash = eol = false;
183 break;
184 }
185
186 ws = false;
187 psi_cpp_tokiter_next(cpp);
188 }
189
190 return true;
191 }
192
193 static bool psi_cpp_stage2(struct psi_cpp *cpp)
194 {
195 struct psi_plist *parser_tokens = psi_plist_init((psi_plist_dtor) psi_token_free);
196 bool is_eol = true, do_cpp = false, do_expansion = true, skip_paren = false, skip_all = false;
197
198 PSI_DEBUG_PRINT(cpp->parser, "PSI: CPP %s\n", "stage2");
199
200 psi_cpp_tokiter_reset(cpp);
201 while (psi_cpp_tokiter_valid(cpp)) {
202 struct psi_token *current = psi_cpp_tokiter_current(cpp);
203
204 if (current->type == PSI_T_HASH) {
205 if (is_eol) {
206 do_cpp = true;
207 is_eol = false;
208 }
209 } else if (current->type == PSI_T_EOL) {
210 #if PSI_CPP_DEBUG
211 fprintf(stderr, "PSI: CPP do_expansion=true, PSI_T_EOL\n");
212 #endif
213 is_eol = true;
214 skip_all = false;
215 do_expansion = true;
216 if (!do_cpp) {
217 psi_cpp_tokiter_del_cur(cpp, true);
218 continue;
219 }
220 } else {
221 is_eol = false;
222
223 if (do_cpp) {
224 switch (current->type) {
225 case PSI_T_DEFINE:
226 #if PSI_CPP_DEBUG
227 fprintf(stderr, "PSI: CPP do_expansion=false, PSI_T_DEFINE, skip_all\n");
228 #endif
229 do_expansion = false;
230 skip_all = true;
231 break;
232 case PSI_T_DEFINED:
233 skip_paren = true;
234 /* no break */
235 case PSI_T_IFDEF:
236 case PSI_T_IFNDEF:
237 case PSI_T_UNDEF:
238 #if PSI_CPP_DEBUG
239 fprintf(stderr, "PSI: CPP do_expansion=false, PSI_T_{IF{,N},UN}DEF\n");
240 #endif
241 do_expansion = false;
242 break;
243 case PSI_T_LPAREN:
244
245 if (!skip_all) {
246 if (skip_paren) {
247 skip_paren = false;
248 } else {
249 do_expansion = true;
250 #if PSI_CPP_DEBUG
251 fprintf(stderr, "PSI: CPP do_expansion=true, PSI_T_LPAREN, !skip_all, !skip_paren\n");
252 #endif
253 }
254 }
255 break;
256 case PSI_T_NAME:
257 break;
258 default:
259 do_expansion = !skip_all;
260 #if PSI_CPP_DEBUG
261 fprintf(stderr, "PSI: CPP do_expansion=%s, <- !skip_all\n", do_expansion?"true":"false");
262 #endif
263 }
264 }
265 }
266
267 if (cpp->skip) {
268 /* FIXME: del_range */
269 if (!do_cpp) {
270 #if PSI_CPP_DEBUG
271 fprintf(stderr, "PSI: CPP skip ");
272 psi_token_dump(2, current);
273 #endif
274 psi_cpp_tokiter_del_cur(cpp, true);
275 continue;
276 }
277 }
278
279 if (do_expansion && current->type == PSI_T_NAME && psi_cpp_tokiter_defined(cpp)) {
280 bool expanded = false;
281
282 while (psi_cpp_tokiter_expand(cpp)) {
283 expanded = true;
284 }
285 if (expanded) {
286 continue;
287 }
288 }
289
290 if (do_cpp) {
291 parser_tokens = psi_plist_add(parser_tokens, &current);
292
293 if (is_eol) {
294 size_t processed = 0;
295 bool parsed = psi_parser_process(cpp->parser, parser_tokens, &processed);
296
297 /* EOL */
298 psi_plist_pop(parser_tokens, NULL);
299 psi_plist_clean(parser_tokens);
300 do_cpp = false;
301
302 if (!parsed) {
303 psi_plist_free(parser_tokens);
304 return false;
305 }
306 } else {
307 /* leave EOLs in the input stream, else we might end up
308 * with a hash not preceded with a new line after include */
309 psi_cpp_tokiter_del_cur(cpp, false);
310 }
311
312 #if PSI_CPP_DEBUG > 1
313 psi_cpp_tokiter_dump(2, cpp);
314 #endif
315
316 continue;
317 }
318
319 psi_cpp_tokiter_next(cpp);
320 }
321
322 psi_plist_free(parser_tokens);
323
324 return true;
325 }
326
327 bool psi_cpp_process(struct psi_cpp *cpp, struct psi_plist **tokens)
328 {
329 bool parsed = false;
330 struct psi_cpp temp = *cpp;
331
332 cpp->tokens = *tokens;
333 if (psi_cpp_stage1(cpp) && psi_cpp_stage2(cpp)) {
334 parsed = true;
335 }
336 *tokens = cpp->tokens;
337
338 if (temp.tokens) {
339 cpp->tokens = temp.tokens;
340 cpp->index = temp.index;
341 }
342
343 return parsed;
344 }
345
346 bool psi_cpp_defined(struct psi_cpp *cpp, struct psi_token *tok)
347 {
348 bool defined;
349
350 if (tok->type == PSI_T_NAME) {
351 defined = zend_hash_str_exists(&cpp->defs, tok->text, tok->size);
352 } else {
353 defined = false;
354 }
355
356 #if PSI_CPP_DEBUG
357 fprintf(stderr, "PSI: CPP defined -> %s ", defined ? "true" : "false");
358 if (defined) {
359 struct psi_cpp_macro_decl *macro = zend_hash_str_find_ptr(&cpp->defs, tok->text, tok->size);
360 fprintf(stderr, " @ %s:%u ", macro->token->file, macro->token->line);
361 }
362 psi_token_dump(2, tok);
363 #endif
364
365 return defined;
366 }
367
368 void psi_cpp_define(struct psi_cpp *cpp, struct psi_cpp_macro_decl *decl)
369 {
370 struct psi_cpp_macro_decl *old = zend_hash_str_find_ptr(&cpp->defs, decl->token->text, decl->token->size);
371
372 if (old && !psi_cpp_macro_decl_equal(old, decl)) {
373 cpp->parser->error(PSI_DATA(cpp->parser), decl->token, PSI_WARNING,
374 "'%s' redefined", decl->token->text);
375 cpp->parser->error(PSI_DATA(cpp->parser), old->token, PSI_WARNING,
376 "'%s' previously defined", old->token->text);
377 }
378 #if PSI_CPP_DEBUG
379 if (decl->exp) {
380 fprintf(stderr, "MACRO: num_exp: ", decl->token->text);
381 } else if (decl->tokens) {
382 fprintf(stderr, "MACRO: decl : ", decl->token->text);
383 }
384 psi_cpp_macro_decl_dump(2, decl);
385 fprintf(stderr, "\n");
386 #endif
387 zend_hash_str_update_ptr(&cpp->defs, decl->token->text, decl->token->size, decl);
388 }
389
390 bool psi_cpp_undef(struct psi_cpp *cpp, struct psi_token *tok)
391 {
392 return SUCCESS == zend_hash_str_del(&cpp->defs, tok->text, tok->size);
393 }
394
395 bool psi_cpp_if(struct psi_cpp *cpp, struct psi_cpp_exp *exp)
396 {
397 struct psi_validate_scope scope = {0};
398
399 scope.defs = &cpp->defs;
400 if (!psi_num_exp_validate(PSI_DATA(cpp->parser), exp->data.num, &scope)) {
401 return false;
402 }
403 if (!psi_num_exp_get_long(exp->data.num, NULL, &cpp->defs)) {
404 return false;
405 }
406 return true;
407 }
408
409 static inline bool try_include(struct psi_cpp *cpp, const char *path, bool *parsed)
410 {
411 struct psi_parser_input *include;
412
413 PSI_DEBUG_PRINT(cpp->parser, "PSI: CPP include trying %s\n", path);
414
415 include = psi_parser_open_file(cpp->parser, path, false);
416 if (include) {
417 struct psi_plist *tokens;
418
419 PSI_DEBUG_PRINT(cpp->parser, "PSI: CPP include scanning %s\n", path);
420
421 tokens = psi_parser_scan(cpp->parser, include);
422 if (tokens) {
423 *parsed = psi_cpp_process(cpp, &tokens);
424
425 if (*parsed) {
426 size_t num_tokens = psi_plist_count(tokens);
427
428 ++cpp->expanded;
429 psi_cpp_tokiter_ins_range(cpp, cpp->index,
430 num_tokens, psi_plist_eles(tokens));
431 /* skip already processed tokens */
432 cpp->index += num_tokens;
433 free(tokens);
434 } else {
435 psi_plist_free(tokens);
436 }
437 }
438 free(include);
439
440 zend_hash_str_add_empty_element(&cpp->once, path, strlen(path));
441 return true;
442 }
443 return false;
444 }
445
446 static inline void include_path(const struct psi_token *file, char **path)
447 {
448 if (*file->text == '/') {
449 *path = file->text;
450 } else {
451 char *dir;
452 size_t len;
453
454 strncpy(*path, file->file, PATH_MAX);
455
456 dir = dirname(*path);
457 len = strlen(dir);
458
459 assert(len + file->size + 1 < PATH_MAX);
460
461 memmove(*path, dir, len);
462 (*path)[len] = '/';
463 memcpy(&(*path)[len + 1], file->text, file->size + 1);
464 }
465 }
466
467 bool psi_cpp_include(struct psi_cpp *cpp, const struct psi_token *file, unsigned flags)
468 {
469 bool parsed = false;
470 int f_len = strlen(file->text);
471
472 if (file->type == PSI_T_QUOTED_STRING && (!(flags & PSI_CPP_INCLUDE_NEXT) || *file->text == '/')) {
473 /* first try as is, full or relative path */
474 char temp[PATH_MAX], *path = temp;
475
476 include_path(file, &path);
477
478 if ((flags & PSI_CPP_INCLUDE_ONCE) && zend_hash_str_exists(&cpp->once, path, f_len)) {
479 return true;
480 }
481 if (try_include(cpp, path, &parsed)) {
482 /* found */
483 return parsed;
484 }
485 }
486
487 /* look through search paths */
488 if (*file->text != '/') {
489 char path[PATH_MAX];
490 const char *sep;
491 int p_len;
492
493 if ((flags & PSI_CPP_INCLUDE_NEXT) && cpp->search) {
494 if ((sep = strchr(cpp->search, ':'))) {
495 cpp->search = sep + 1;
496 } else {
497 /* point to end of string */
498 cpp->search += strlen(cpp->search);
499 }
500 }
501
502 if (!(flags & PSI_CPP_INCLUDE_NEXT)) {
503 cpp->search = PSI_G(search_path);
504 }
505
506 do {
507 int d_len;
508
509 sep = strchr(cpp->search, ':');
510 d_len = sep ? sep - cpp->search : strlen(cpp->search);
511
512 if (PATH_MAX > (p_len = snprintf(path, PATH_MAX, "%.*s/%.*s", d_len, cpp->search, f_len, file->text))) {
513 if ((flags & PSI_CPP_INCLUDE_ONCE) && zend_hash_str_exists(&cpp->once, path, p_len)) {
514 return true;
515 }
516 if (try_include(cpp, path, &parsed)) {
517 break;
518 }
519 }
520
521 if (sep) {
522 cpp->search = sep + 1;
523 }
524 } while (sep);
525 }
526
527 return parsed;
528 }