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 *******************************************************************************/
29 # include "php_config.h"
38 struct psi_cpp_exp
*psi_cpp_exp_init(token_t type
, void *data
)
40 struct psi_cpp_exp
*exp
= pecalloc(1, sizeof(*exp
), 1);
42 switch ((exp
->type
= type
)) {
50 case PSI_T_INCLUDE_NEXT
:
55 exp
->data
.decl
= data
;
72 void psi_cpp_exp_free(struct psi_cpp_exp
**exp_ptr
)
75 struct psi_cpp_exp
*exp
= *exp_ptr
;
86 case PSI_T_INCLUDE_NEXT
:
87 psi_token_free(&exp
->data
.tok
);
91 psi_cpp_macro_decl_free(&exp
->data
.decl
);
95 psi_num_exp_free(&exp
->data
.num
);
99 case PSI_T_PRAGMA_ONCE
:
105 psi_token_free(&exp
->token
);
110 void psi_cpp_exp_dump(struct psi_dump
*dump
, struct psi_cpp_exp
*exp
)
112 PSI_DUMP(dump
, "#%s ", exp
->token
->text
->val
);
116 if (!exp
->data
.tok
) {
123 PSI_DUMP(dump
, "%s", exp
->data
.tok
->text
->val
);
127 case PSI_T_INCLUDE_NEXT
:
128 if (exp
->data
.tok
->type
== PSI_T_CPP_HEADER
) {
129 PSI_DUMP(dump
, "<%s>", exp
->data
.tok
->text
->val
);
131 PSI_DUMP(dump
, "\"%s\"", exp
->data
.tok
->text
->val
);
136 psi_cpp_macro_decl_dump(dump
, exp
->data
.decl
);
140 psi_num_exp_dump(dump
, exp
->data
.num
);
144 case PSI_T_PRAGMA_ONCE
:
150 PSI_DUMP(dump
, "\n");
154 static inline bool psi_cpp_level_skipped(struct psi_cpp
*cpp
)
156 return cpp
->skip
== cpp
->level
;
159 static inline void psi_cpp_level_skip(struct psi_cpp
*cpp
)
162 cpp
->skip
= cpp
->level
;
165 static inline void psi_cpp_level_unskip(struct psi_cpp
*cpp
)
167 if (psi_cpp_level_skipped(cpp
)) {
172 static inline bool psi_cpp_level_masked(struct psi_cpp
*cpp
)
174 return cpp
->seen
& (1 << cpp
->level
);
177 static inline void psi_cpp_level_mask(struct psi_cpp
*cpp
)
179 assert(!psi_cpp_level_masked(cpp
));
180 cpp
->seen
|= (1 << cpp
->level
);
183 static inline void psi_cpp_level_unmask(struct psi_cpp
*cpp
)
185 cpp
->seen
&= ~(1 << cpp
->level
);
188 void psi_cpp_exp_exec(struct psi_cpp_exp
*exp
, struct psi_cpp
*cpp
, struct psi_data
*D
)
190 PSI_DEBUG_PRINT(D
, "PSI: CPP EVAL < %s (level=%u, skip=%u)\n",
191 exp
->token
->text
->val
, cpp
->level
, cpp
->skip
);
194 PSI_DEBUG_PRINT(cpp
->parser
, "PSI: CPP exec -> ");
195 PSI_DEBUG_DUMP(cpp
->parser
, psi_cpp_exp_dump
, exp
);
201 D
->error(D
, exp
->token
, PSI_ERROR
, "%s",
202 exp
->data
.tok
? exp
->data
.tok
->text
->val
: "");
207 D
->error(D
, exp
->token
, PSI_WARNING
, "%s",
208 exp
->data
.tok
? exp
->data
.tok
->text
->val
: "");
213 psi_cpp_undef(cpp
, exp
->data
.tok
);
218 psi_cpp_define(cpp
, exp
->data
.decl
);
220 exp
->data
.decl
= NULL
;
225 psi_cpp_pragma(cpp
, exp
->data
.decl
);
231 if (psi_cpp_defined(cpp
, exp
->data
.tok
)) {
232 psi_cpp_level_mask(cpp
);
234 psi_cpp_level_skip(cpp
);
241 if (psi_cpp_defined(cpp
, exp
->data
.tok
)) {
242 psi_cpp_level_skip(cpp
);
244 psi_cpp_level_mask(cpp
);
251 if (psi_cpp_if(cpp
, exp
)) {
252 psi_cpp_level_mask(cpp
);
254 psi_cpp_level_skip(cpp
);
260 D
->error(D
, exp
->token
, PSI_WARNING
, "Ingoring lone #endif");
262 psi_cpp_level_unskip(cpp
);
263 psi_cpp_level_unmask(cpp
);
268 /* FIXME: catch "else" after "else" */
270 D
->error(D
, exp
->token
, PSI_WARNING
, "Ingoring lone #else");
271 } else if (psi_cpp_level_skipped(cpp
) && !psi_cpp_level_masked(cpp
)) {
273 * if skip is set on this level and the level has
274 * not been masked yet, then unskip and mask this level
276 psi_cpp_level_unskip(cpp
);
277 psi_cpp_level_mask(cpp
);
278 } else if (!cpp
->skip
&& psi_cpp_level_masked(cpp
)) {
280 * previous block masked this level
282 psi_cpp_level_skip(cpp
);
284 assert(cpp
->skip
<= cpp
->level
);
289 D
->error(D
, exp
->token
, PSI_WARNING
, "Ingoring lone #elif");
290 } else if (psi_cpp_level_skipped(cpp
) && !psi_cpp_level_masked(cpp
)) {
292 * if skip is set on this level and the level has
293 * not been masked yet, then unskip and mask this
294 * level, if the condition evals truthy
296 if (psi_cpp_if(cpp
, exp
)) {
297 psi_cpp_level_unskip(cpp
);
298 psi_cpp_level_mask(cpp
);
300 } else if (!cpp
->skip
&& psi_cpp_level_masked(cpp
)) {
302 * previous block masked this level
304 psi_cpp_level_skip(cpp
);
306 assert(cpp
->skip
<= cpp
->level
);
311 if (!psi_cpp_include(cpp
, exp
->data
.tok
, PSI_CPP_INCLUDE
)) {
312 D
->error(D
, exp
->token
, PSI_WARNING
, "Failed to include %s: %s",
313 exp
->data
.tok
->text
->val
, strerror(errno
));
317 case PSI_T_INCLUDE_NEXT
:
319 if (!psi_cpp_include(cpp
, exp
->data
.tok
, PSI_CPP_INCLUDE_NEXT
)) {
320 D
->error(D
, exp
->token
, PSI_WARNING
, "Failed to include next %s: %s",
321 exp
->data
.tok
->text
->val
, strerror(errno
));
327 if (!psi_cpp_include(cpp
, exp
->data
.tok
, PSI_CPP_INCLUDE_ONCE
)) {
328 D
->error(D
, exp
->token
, PSI_WARNING
, "Failed to include once %s: %s",
329 exp
->data
.tok
->text
->val
, strerror(errno
));
333 case PSI_T_PRAGMA_ONCE
:
335 zend_hash_add_empty_element(&cpp
->once
, exp
->token
->file
);
343 PSI_DEBUG_PRINT(D
, "PSI: CPP EVAL > %s (level=%u, skip=%u)\n",
344 exp
->token
->text
->val
, cpp
->level
, cpp
->skip
);