8fcd5cf986fc3e0f9bf4d9c2e07c1d4fdd1fd4ab
1 /*******************************************************************************
2 Copyright (c) 2017, Michael Wallner <mike@php.net>.
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are met:
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.
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 *******************************************************************************/
26 #include "php_psi_stdinc.h"
31 static inline bool psi_cpp_level_skipped(struct psi_cpp_data
*cpp
)
33 return cpp
->skip
== cpp
->level
;
35 static inline void psi_cpp_level_skip(struct psi_cpp_data
*cpp
)
38 cpp
->skip
= cpp
->level
;
40 static inline void psi_cpp_level_unskip(struct psi_cpp_data
*cpp
)
42 if (psi_cpp_level_skipped(cpp
)) {
46 static inline bool psi_cpp_level_masked(struct psi_cpp_data
*cpp
)
48 return cpp
->seen
& (1 << cpp
->level
);
50 static inline void psi_cpp_level_mask(struct psi_cpp_data
*cpp
)
52 assert(!psi_cpp_level_masked(cpp
));
53 cpp
->seen
|= (1 << cpp
->level
);
55 static inline void psi_cpp_level_unmask(struct psi_cpp_data
*cpp
)
57 cpp
->seen
&= ~(1 << cpp
->level
);
60 static void psi_cpp_eval(struct psi_data
*D
, struct psi_cpp_data
*cpp
)
64 PSI_DEBUG_PRINT(D
, "PSI: CPP EVAL < %s (level=%u, skip=%u)\n",
65 cpp
->exp
->token
->text
, cpp
->level
, cpp
->skip
);
68 psi_cpp_exp_dump(2, cpp
->exp
);
71 switch (cpp
->exp
->type
) {
74 D
->error(D
, cpp
->exp
->token
, PSI_ERROR
, "%s",
75 cpp
->exp
->data
.tok
->text
);
80 D
->error(D
, cpp
->exp
->token
, PSI_WARNING
, "%s",
81 cpp
->exp
->data
.tok
->text
);
86 psi_cpp_undef(cpp
, cpp
->exp
->data
.tok
);
91 psi_cpp_define(cpp
, cpp
->exp
->data
.decl
);
93 cpp
->exp
->data
.decl
= NULL
;
99 if (psi_cpp_defined(cpp
, cpp
->exp
->data
.tok
)) {
100 psi_cpp_level_mask(cpp
);
102 psi_cpp_level_skip(cpp
);
109 if (psi_cpp_defined(cpp
, cpp
->exp
->data
.tok
)) {
110 psi_cpp_level_skip(cpp
);
112 psi_cpp_level_mask(cpp
);
119 if (psi_cpp_if(cpp
->exp
, &cpp
->defs
, D
)) {
120 psi_cpp_level_mask(cpp
);
122 psi_cpp_level_skip(cpp
);
128 D
->error(D
, cpp
->exp
->token
, PSI_WARNING
, "Ingoring lone #endif");
130 psi_cpp_level_unskip(cpp
);
131 psi_cpp_level_unmask(cpp
);
136 /* FIXME: catch "else" after "else" */
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
)) {
141 * if skip is set on this level and the level has
142 * not been masked yet, then unskip and mask this level
144 psi_cpp_level_unskip(cpp
);
145 psi_cpp_level_mask(cpp
);
146 } else if (!cpp
->skip
&& psi_cpp_level_masked(cpp
)) {
148 * previous block masked this level
150 psi_cpp_level_skip(cpp
);
152 assert(cpp
->skip
< 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
)) {
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
164 if (psi_cpp_if(cpp
->exp
, &cpp
->defs
, D
)) {
165 psi_cpp_level_unskip(cpp
);
166 psi_cpp_level_mask(cpp
);
168 } else if (!cpp
->skip
&& psi_cpp_level_masked(cpp
)) {
170 * previous block masked this level
172 psi_cpp_level_skip(cpp
);
174 assert(cpp
->skip
< cpp
->level
);
182 PSI_DEBUG_PRINT(D
, "PSI: CPP EVAL > %s (level=%u, skip=%u)\n",
183 cpp
->exp
->token
->text
, cpp
->level
, cpp
->skip
);
185 psi_cpp_exp_free(&cpp
->exp
);
188 static bool psi_cpp_stage1(struct psi_parser
*P
, struct psi_cpp_data
*cpp
)
190 bool name
= false, define
= false, hash
= false, eol
= true, esc
= false, ws
= false;
192 psi_cpp_tokiter_reset(cpp
);
193 while (psi_cpp_tokiter_valid(cpp
)) {
194 struct psi_token
*token
= psi_cpp_tokiter_current(cpp
);
197 if (token
->type
== PSI_T_COMMENT
) {
198 psi_cpp_tokiter_del_cur(cpp
, true);
202 /* line continuations */
203 if (token
->type
== PSI_T_EOL
) {
205 psi_cpp_tokiter_del_range(cpp
, psi_cpp_tokiter_index(cpp
) - 1, 2, true);
206 psi_cpp_tokiter_prev(cpp
);
210 } else if (token
->type
== PSI_T_BSLASH
) {
216 /* this whole turf is needed to distinct between:
217 * #define foo (1,2,3)
221 if (token
->type
== PSI_T_WHITESPACE
) {
223 psi_cpp_tokiter_del_cur(cpp
, true);
227 switch (token
->type
) {
253 /* mask special token for parser */
254 struct psi_token
*no_ws
= psi_token_copy(token
);
256 no_ws
->type
= PSI_T_NO_WHITESPACE
;
257 no_ws
->text
[0] = '\xA0';
258 psi_cpp_tokiter_ins_cur(cpp
, no_ws
);
264 name
= define
= hash
= eol
= false;
269 psi_cpp_tokiter_next(cpp
);
275 static bool psi_cpp_stage2(struct psi_parser
*P
, struct psi_cpp_data
*cpp
)
278 bool is_eol
= true, do_cpp
= false, do_expansion
= true, skip_paren
= false, skip_all
= false;
280 psi_cpp_tokiter_reset(cpp
);
282 while (psi_cpp_tokiter_valid(cpp
)) {
283 struct psi_token
*current
= psi_cpp_tokiter_current(cpp
);
285 if (current
->type
== PSI_T_HASH
) {
290 } else if (current
->type
== PSI_T_EOL
) {
292 fprintf(stderr
, "PSI: CPP do_expansion=true, PSI_T_EOL\n");
298 psi_cpp_tokiter_del_cur(cpp
, true);
305 switch (current
->type
) {
308 fprintf(stderr
, "PSI: CPP do_expansion=false, PSI_T_DEFINE, skip_all\n");
310 do_expansion
= false;
320 fprintf(stderr
, "PSI: CPP do_expansion=false, PSI_T_{IF{,N},UN}DEF\n");
322 do_expansion
= false;
331 fprintf(stderr
, "PSI: CPP do_expansion=true, PSI_T_LPAREN, !skip_all, !skip_paren\n");
339 do_expansion
= !skip_all
;
341 fprintf(stderr
, "PSI CPP do_expansion=%s, <- !skip_all\n", do_expansion
?"true":"false");
348 /* FIXME: del_range */
351 fprintf(stderr
, "PSI: CPP skip ");
352 psi_token_dump(2, current
);
354 psi_cpp_tokiter_del_cur(cpp
, true);
359 if (do_expansion
&& current
->type
== PSI_T_NAME
&& psi_cpp_tokiter_defined(cpp
)) {
360 bool expanded
= false;
362 while (psi_cpp_tokiter_expand(cpp
)) {
376 if (P
->flags
& PSI_DEBUG
) {
377 fprintf(stderr
, "PSI> Parse (%zu) ", psi_cpp_tokiter_index(cpp
));
378 psi_token_dump(2, current
);
381 psi_parser_proc_parse(P
->proc
, current
->type
, current
, P
);
382 psi_cpp_tokiter_del_cur(cpp
, false);
385 psi_parser_proc_parse(P
->proc
, 0, NULL
, P
);
386 psi_cpp_eval(PSI_DATA(P
), cpp
);
390 psi_cpp_tokiter_dump(2, cpp
);
396 psi_cpp_tokiter_next(cpp
);
398 } while (cpp
->expanded
);
403 bool psi_cpp_preprocess(struct psi_parser
*P
, struct psi_cpp_data
*cpp
)
405 if (!psi_cpp_stage1(P
, cpp
)) {
409 if (!psi_cpp_stage2(P
, cpp
)) {
416 bool psi_cpp_defined(struct psi_cpp_data
*cpp
, struct psi_token
*tok
)
420 if (tok
->type
== PSI_T_NAME
) {
421 defined
= zend_hash_str_exists(&cpp
->defs
, tok
->text
, tok
->size
);
427 fprintf(stderr
, "PSI: CPP defined -> %s ", defined
? "true" : "false");
428 psi_token_dump(2, tok
);
434 void psi_cpp_define(struct psi_cpp_data
*cpp
, struct psi_cpp_macro_decl
*decl
)
436 zend_hash_str_add_ptr(&cpp
->defs
, decl
->token
->text
, decl
->token
->size
, decl
);
439 bool psi_cpp_undef(struct psi_cpp_data
*cpp
, struct psi_token
*tok
)
441 return SUCCESS
== zend_hash_str_del(&cpp
->defs
, tok
->text
, tok
->size
);
444 bool psi_cpp_if(struct psi_cpp_exp
*exp
, HashTable
*defs
, struct psi_data
*D
)
446 if (!psi_num_exp_validate(D
, exp
->data
.num
, NULL
, NULL
, NULL
, NULL
, NULL
)) {
449 if (!psi_long_num_exp(exp
->data
.num
, NULL
, defs
)) {