cpp
[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 "cpp.h"
29 #include "parser.h"
30
31 static inline bool psi_cpp_level_skipped(struct psi_cpp_data *cpp)
32 {
33 return cpp->skip == cpp->level;
34 }
35 static inline void psi_cpp_level_skip(struct psi_cpp_data *cpp)
36 {
37 assert(!cpp->skip);
38 cpp->skip = cpp->level;
39 }
40 static inline void psi_cpp_level_unskip(struct psi_cpp_data *cpp)
41 {
42 if (psi_cpp_level_skipped(cpp)) {
43 cpp->skip = 0;
44 }
45 }
46 static inline bool psi_cpp_level_masked(struct psi_cpp_data *cpp)
47 {
48 return cpp->seen & (1 << cpp->level);
49 }
50 static inline void psi_cpp_level_mask(struct psi_cpp_data *cpp)
51 {
52 assert(!psi_cpp_level_masked(cpp));
53 cpp->seen |= (1 << cpp->level);
54 }
55 static inline void psi_cpp_level_unmask(struct psi_cpp_data *cpp)
56 {
57 cpp->seen &= ~(1 << cpp->level);
58 }
59
60 static void psi_cpp_eval(struct psi_data *D, struct psi_cpp_data *cpp)
61 {
62 assert(cpp->exp);
63
64 PSI_DEBUG_PRINT(D, "PSI: CPP EVAL < %s (level=%u, skip=%u)\n",
65 cpp->exp->token->text, cpp->level, cpp->skip);
66
67 #if PSI_CPP_DEBUG
68 psi_cpp_exp_dump(2, cpp->exp);
69 #endif
70
71 switch (cpp->exp->type) {
72 case PSI_T_ERROR:
73 if (!cpp->skip) {
74 D->error(D, cpp->exp->token, PSI_ERROR, "%s",
75 cpp->exp->data.tok->text);
76 }
77 break;
78 case PSI_T_WARNING:
79 if (!cpp->skip) {
80 D->error(D, cpp->exp->token, PSI_WARNING, "%s",
81 cpp->exp->data.tok->text);
82 }
83 break;
84 case PSI_T_UNDEF:
85 if (!cpp->skip) {
86 psi_cpp_undef(cpp, cpp->exp->data.tok);
87 }
88 break;
89 case PSI_T_DEFINE:
90 if (!cpp->skip) {
91 psi_cpp_define(cpp, cpp->exp->data.decl);
92 /* FIXME: copy */
93 cpp->exp->data.decl = NULL;
94 }
95 break;
96 case PSI_T_IFDEF:
97 ++cpp->level;
98 if (!cpp->skip) {
99 if (psi_cpp_defined(cpp, cpp->exp->data.tok)) {
100 psi_cpp_level_mask(cpp);
101 } else {
102 psi_cpp_level_skip(cpp);
103 }
104 }
105 break;
106 case PSI_T_IFNDEF:
107 ++cpp->level;
108 if (!cpp->skip) {
109 if (psi_cpp_defined(cpp, cpp->exp->data.tok)) {
110 psi_cpp_level_skip(cpp);
111 } else {
112 psi_cpp_level_mask(cpp);
113 }
114 }
115 break;
116 case PSI_T_IF:
117 ++cpp->level;
118 if (!cpp->skip) {
119 if (psi_cpp_if(cpp->exp, &cpp->defs, D)) {
120 psi_cpp_level_mask(cpp);
121 } else {
122 psi_cpp_level_skip(cpp);
123 }
124 }
125 break;
126 case PSI_T_ENDIF:
127 if (!cpp->level) {
128 D->error(D, cpp->exp->token, PSI_WARNING, "Ingoring lone #endif");
129 } else {
130 psi_cpp_level_unskip(cpp);
131 psi_cpp_level_unmask(cpp);
132 --cpp->level;
133 }
134 break;
135 case PSI_T_ELSE:
136 /* FIXME: catch "else" after "else" */
137 if (!cpp->level) {
138 D->error(D, cpp->exp->token, PSI_WARNING, "Ingoring lone #else");
139 } else if (psi_cpp_level_skipped(cpp) && !psi_cpp_level_masked(cpp)) {
140 /*
141 * if skip is set on this level and the level has
142 * not been masked yet, then unskip and mask this level
143 */
144 psi_cpp_level_unskip(cpp);
145 psi_cpp_level_mask(cpp);
146 } else if (!cpp->skip && psi_cpp_level_masked(cpp)) {
147 /*
148 * previous block masked this level
149 */
150 psi_cpp_level_skip(cpp);
151 } else {
152 assert(cpp->skip < cpp->level);
153 }
154 break;
155 case PSI_T_ELIF:
156 if (!cpp->level) {
157 D->error(D, cpp->exp->token, PSI_WARNING, "Ingoring lone #elif");
158 } else if (psi_cpp_level_skipped(cpp) && !psi_cpp_level_masked(cpp)) {
159 /*
160 * if skip is set on this level and the level has
161 * not been masked yet, then unskip and mask this
162 * level, if the condition evals truthy
163 */
164 if (psi_cpp_if(cpp->exp, &cpp->defs, D)) {
165 psi_cpp_level_unskip(cpp);
166 psi_cpp_level_mask(cpp);
167 }
168 } else if (!cpp->skip && psi_cpp_level_masked(cpp)) {
169 /*
170 * previous block masked this level
171 */
172 psi_cpp_level_skip(cpp);
173 } else {
174 assert(cpp->skip < cpp->level);
175 }
176 break;
177 default:
178 assert(0);
179 break;
180 }
181
182 PSI_DEBUG_PRINT(D, "PSI: CPP EVAL > %s (level=%u, skip=%u)\n",
183 cpp->exp->token->text, cpp->level, cpp->skip);
184
185 psi_cpp_exp_free(&cpp->exp);
186 }
187
188 static bool psi_cpp_stage1(struct psi_parser *P, struct psi_cpp_data *cpp)
189 {
190 bool name = false, define = false, hash = false, eol = true, esc = false, ws = false;
191
192 psi_cpp_tokiter_reset(cpp);
193 while (psi_cpp_tokiter_valid(cpp)) {
194 struct psi_token *token = psi_cpp_tokiter_current(cpp);
195
196 /* strip comments */
197 if (token->type == PSI_T_COMMENT) {
198 psi_cpp_tokiter_del_cur(cpp, true);
199 continue;
200 }
201
202 /* line continuations */
203 if (token->type == PSI_T_EOL) {
204 if (esc) {
205 psi_cpp_tokiter_del_range(cpp, psi_cpp_tokiter_index(cpp) - 1, 2, true);
206 psi_cpp_tokiter_prev(cpp);
207 esc = false;
208 continue;
209 }
210 } else if (token->type == PSI_T_BSLASH) {
211 esc = !esc;
212 } else {
213 esc = false;
214 }
215
216 /* this whole turf is needed to distinct between:
217 * #define foo (1,2,3)
218 * #define foo(a,b,c)
219 */
220
221 if (token->type == PSI_T_WHITESPACE) {
222 ws = true;
223 psi_cpp_tokiter_del_cur(cpp, true);
224 continue;
225 }
226
227 switch (token->type) {
228 case PSI_T_EOL:
229 eol = true;
230 break;
231 case PSI_T_HASH:
232 if (eol) {
233 hash = true;
234 eol = false;
235 }
236 break;
237 case PSI_T_DEFINE:
238 if (hash) {
239 define = true;
240 hash = false;
241 }
242 break;
243 case PSI_T_NAME:
244 if (define) {
245 name = true;
246 define = false;
247 }
248 break;
249 case PSI_T_LPAREN:
250 if (name) {
251 name = false;
252 if (!ws) {
253 /* mask special token for parser */
254 struct psi_token *no_ws = psi_token_copy(token);
255
256 no_ws->type = PSI_T_NO_WHITESPACE;
257 no_ws->text[0] = '\xA0';
258 psi_cpp_tokiter_ins_cur(cpp, no_ws);
259 continue;
260 }
261 }
262 /* no break */
263 default:
264 name = define = hash = eol = false;
265 break;
266 }
267
268 ws = false;
269 psi_cpp_tokiter_next(cpp);
270 }
271
272 return true;
273 }
274
275 static bool psi_cpp_stage2(struct psi_parser *P, struct psi_cpp_data *cpp)
276 {
277 do {
278 bool is_eol = true, do_cpp = false, do_expansion = true, skip_paren = false, skip_all = false;
279
280 psi_cpp_tokiter_reset(cpp);
281
282 while (psi_cpp_tokiter_valid(cpp)) {
283 struct psi_token *current = psi_cpp_tokiter_current(cpp);
284
285 if (current->type == PSI_T_HASH) {
286 if (is_eol) {
287 do_cpp = true;
288 is_eol = false;
289 }
290 } else if (current->type == PSI_T_EOL) {
291 #if PSI_CPP_DEBUG
292 fprintf(stderr, "PSI: CPP do_expansion=true, PSI_T_EOL\n");
293 #endif
294 is_eol = true;
295 skip_all = false;
296 do_expansion = true;
297 if (!do_cpp) {
298 psi_cpp_tokiter_del_cur(cpp, true);
299 continue;
300 }
301 } else {
302 is_eol = false;
303
304 if (do_cpp) {
305 switch (current->type) {
306 case PSI_T_DEFINE:
307 #if PSI_CPP_DEBUG
308 fprintf(stderr, "PSI: CPP do_expansion=false, PSI_T_DEFINE, skip_all\n");
309 #endif
310 do_expansion = false;
311 skip_all = true;
312 break;
313 case PSI_T_DEFINED:
314 skip_paren = true;
315 /* no break */
316 case PSI_T_IFDEF:
317 case PSI_T_IFNDEF:
318 case PSI_T_UNDEF:
319 #if PSI_CPP_DEBUG
320 fprintf(stderr, "PSI: CPP do_expansion=false, PSI_T_{IF{,N},UN}DEF\n");
321 #endif
322 do_expansion = false;
323 break;
324 case PSI_T_LPAREN:
325 if (!skip_all) {
326 if (skip_paren) {
327 skip_paren = false;
328 } else {
329 do_expansion = true;
330 #if PSI_CPP_DEBUG
331 fprintf(stderr, "PSI: CPP do_expansion=true, PSI_T_LPAREN, !skip_all, !skip_paren\n");
332 #endif
333 }
334 }
335 break;
336 case PSI_T_NAME:
337 break;
338 default:
339 do_expansion = !skip_all;
340 #if PSI_CPP_DEBUG
341 fprintf(stderr, "PSI CPP do_expansion=%s, <- !skip_all\n", do_expansion?"true":"false");
342 #endif
343 }
344 }
345 }
346
347 if (cpp->skip) {
348 /* FIXME: del_range */
349 if (!do_cpp) {
350 #if PSI_CPP_DEBUG
351 fprintf(stderr, "PSI: CPP skip ");
352 psi_token_dump(2, current);
353 #endif
354 psi_cpp_tokiter_del_cur(cpp, true);
355 continue;
356 }
357 }
358
359 if (do_expansion && current->type == PSI_T_NAME && psi_cpp_tokiter_defined(cpp)) {
360 bool expanded = false;
361
362 while (psi_cpp_tokiter_expand(cpp)) {
363 expanded = true;
364 }
365 if (expanded) {
366 continue;
367 }
368 }
369
370 if (do_cpp) {
371 if (is_eol) {
372 do_cpp = false;
373 skip_all = false;
374 }
375
376 if (P->flags & PSI_DEBUG) {
377 fprintf(stderr, "PSI> Parse (%zu) ", psi_cpp_tokiter_index(cpp));
378 psi_token_dump(2, current);
379 }
380
381 psi_parser_proc_parse(P->proc, current->type, current, P);
382 psi_cpp_tokiter_del_cur(cpp, false);
383
384 if (is_eol) {
385 psi_parser_proc_parse(P->proc, 0, NULL, P);
386 psi_cpp_eval(PSI_DATA(P), cpp);
387 }
388
389 #if PSI_CPP_DEBUG
390 psi_cpp_tokiter_dump(2, cpp);
391 #endif
392
393 continue;
394 }
395
396 psi_cpp_tokiter_next(cpp);
397 }
398 } while (cpp->expanded);
399
400 return true;
401 }
402
403 bool psi_cpp_preprocess(struct psi_parser *P, struct psi_cpp_data *cpp)
404 {
405 if (!psi_cpp_stage1(P, cpp)) {
406 return false;
407 }
408
409 if (!psi_cpp_stage2(P, cpp)) {
410 return false;
411 }
412
413 return true;
414 }
415
416 bool psi_cpp_defined(struct psi_cpp_data *cpp, struct psi_token *tok)
417 {
418 bool defined;
419
420 if (tok->type == PSI_T_NAME) {
421 defined = zend_hash_str_exists(&cpp->defs, tok->text, tok->size);
422 } else {
423 defined = false;
424 }
425
426 #if PSI_CPP_DEBUG
427 fprintf(stderr, "PSI: CPP defined -> %s ", defined ? "true" : "false");
428 psi_token_dump(2, tok);
429 #endif
430
431 return defined;
432 }
433
434 void psi_cpp_define(struct psi_cpp_data *cpp, struct psi_cpp_macro_decl *decl)
435 {
436 zend_hash_str_add_ptr(&cpp->defs, decl->token->text, decl->token->size, decl);
437 }
438
439 bool psi_cpp_undef(struct psi_cpp_data *cpp, struct psi_token *tok)
440 {
441 return SUCCESS == zend_hash_str_del(&cpp->defs, tok->text, tok->size);
442 }
443
444 bool psi_cpp_if(struct psi_cpp_exp *exp, HashTable *defs, struct psi_data *D)
445 {
446 if (!psi_num_exp_validate(D, exp->data.num, NULL, NULL, NULL, NULL, NULL)) {
447 return false;
448 }
449 if (!psi_long_num_exp(exp->data.num, NULL, defs)) {
450 return false;
451 }
452 return true;
453 }