#pragma lib
[m6w6/ext-psi] / src / types / cpp_exp.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 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #else
29 # include "php_config.h"
30 #endif
31
32 #include <assert.h>
33
34 #include "data.h"
35 #include "cpp.h"
36 #include "debug.h"
37
38 struct psi_cpp_exp *psi_cpp_exp_init(token_t type, void *data)
39 {
40 struct psi_cpp_exp *exp = pecalloc(1, sizeof(*exp), 1);
41
42 switch ((exp->type = type)) {
43 case PSI_T_WARNING:
44 case PSI_T_ERROR:
45 case PSI_T_UNDEF:
46 case PSI_T_IFDEF:
47 case PSI_T_IFNDEF:
48 case PSI_T_IMPORT:
49 case PSI_T_INCLUDE:
50 case PSI_T_INCLUDE_NEXT:
51 exp->data.tok = data;
52 break;
53 case PSI_T_DEFINE:
54 case PSI_T_PRAGMA:
55 exp->data.decl = data;
56 break;
57 case PSI_T_IF:
58 case PSI_T_ELIF:
59 exp->data.num = data;
60 break;
61 case PSI_T_ENDIF:
62 case PSI_T_ELSE:
63 break;
64 default:
65 assert(0);
66 break;
67 }
68
69 return exp;
70 }
71
72 void psi_cpp_exp_free(struct psi_cpp_exp **exp_ptr)
73 {
74 if (*exp_ptr) {
75 struct psi_cpp_exp *exp = *exp_ptr;
76
77 *exp_ptr = NULL;
78 switch (exp->type) {
79 case PSI_T_WARNING:
80 case PSI_T_ERROR:
81 case PSI_T_UNDEF:
82 case PSI_T_IFDEF:
83 case PSI_T_IFNDEF:
84 case PSI_T_IMPORT:
85 case PSI_T_INCLUDE:
86 case PSI_T_INCLUDE_NEXT:
87 psi_token_free(&exp->data.tok);
88 break;
89 case PSI_T_DEFINE:
90 case PSI_T_PRAGMA:
91 psi_cpp_macro_decl_free(&exp->data.decl);
92 break;
93 case PSI_T_IF:
94 case PSI_T_ELIF:
95 psi_num_exp_free(&exp->data.num);
96 break;
97 case PSI_T_ENDIF:
98 case PSI_T_ELSE:
99 case PSI_T_PRAGMA_ONCE:
100 break;
101 default:
102 assert(0);
103 break;
104 }
105 psi_token_free(&exp->token);
106 free(exp);
107 }
108 }
109
110 void psi_cpp_exp_dump(struct psi_dump *dump, struct psi_cpp_exp *exp)
111 {
112 PSI_DUMP(dump, "#%s ", exp->token->text->val);
113 switch (exp->type) {
114 case PSI_T_WARNING:
115 case PSI_T_ERROR:
116 if (!exp->data.tok) {
117 break;
118 }
119 /* no break */
120 case PSI_T_UNDEF:
121 case PSI_T_IFDEF:
122 case PSI_T_IFNDEF:
123 PSI_DUMP(dump, "%s", exp->data.tok->text->val);
124 break;
125 case PSI_T_IMPORT:
126 case PSI_T_INCLUDE:
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);
130 } else {
131 PSI_DUMP(dump, "\"%s\"", exp->data.tok->text->val);
132 }
133 break;
134 case PSI_T_DEFINE:
135 case PSI_T_PRAGMA:
136 psi_cpp_macro_decl_dump(dump, exp->data.decl);
137 break;
138 case PSI_T_IF:
139 case PSI_T_ELIF:
140 psi_num_exp_dump(dump, exp->data.num);
141 break;
142 case PSI_T_ENDIF:
143 case PSI_T_ELSE:
144 case PSI_T_PRAGMA_ONCE:
145 break;
146 default:
147 assert(0);
148 break;
149 }
150 PSI_DUMP(dump, "\n");
151 }
152
153
154 static inline bool psi_cpp_level_skipped(struct psi_cpp *cpp)
155 {
156 return cpp->skip == cpp->level;
157 }
158
159 static inline void psi_cpp_level_skip(struct psi_cpp *cpp)
160 {
161 assert(!cpp->skip);
162 cpp->skip = cpp->level;
163 }
164
165 static inline void psi_cpp_level_unskip(struct psi_cpp *cpp)
166 {
167 if (psi_cpp_level_skipped(cpp)) {
168 cpp->skip = 0;
169 }
170 }
171
172 static inline bool psi_cpp_level_masked(struct psi_cpp *cpp)
173 {
174 return cpp->seen & (1 << cpp->level);
175 }
176
177 static inline void psi_cpp_level_mask(struct psi_cpp *cpp)
178 {
179 assert(!psi_cpp_level_masked(cpp));
180 cpp->seen |= (1 << cpp->level);
181 }
182
183 static inline void psi_cpp_level_unmask(struct psi_cpp *cpp)
184 {
185 cpp->seen &= ~(1 << cpp->level);
186 }
187
188 void psi_cpp_exp_exec(struct psi_cpp_exp *exp, struct psi_cpp *cpp, struct psi_data *D)
189 {
190 PSI_DEBUG_PRINT(D, "PSI: CPP EVAL < %s (level=%u, skip=%u)\n",
191 exp->token->text->val, cpp->level, cpp->skip);
192
193 #if PSI_CPP_DEBUG
194 PSI_DEBUG_PRINT(cpp->parser, "PSI: CPP exec -> ");
195 PSI_DEBUG_DUMP(cpp->parser, psi_cpp_exp_dump, exp);
196 #endif
197
198 switch (exp->type) {
199 case PSI_T_ERROR:
200 if (!cpp->skip) {
201 D->error(D, exp->token, PSI_ERROR, "%s",
202 exp->data.tok ? exp->data.tok->text->val : "");
203 }
204 break;
205 case PSI_T_WARNING:
206 if (!cpp->skip) {
207 D->error(D, exp->token, PSI_WARNING, "%s",
208 exp->data.tok ? exp->data.tok->text->val : "");
209 }
210 break;
211 case PSI_T_UNDEF:
212 if (!cpp->skip) {
213 psi_cpp_undef(cpp, exp->data.tok);
214 }
215 break;
216 case PSI_T_DEFINE:
217 if (!cpp->skip) {
218 psi_cpp_define(cpp, exp->data.decl);
219 /* FIXME: copy */
220 exp->data.decl = NULL;
221 }
222 break;
223 case PSI_T_PRAGMA:
224 if (!cpp->skip) {
225 psi_cpp_pragma(cpp, exp->data.decl);
226 }
227 break;
228 case PSI_T_IFDEF:
229 ++cpp->level;
230 if (!cpp->skip) {
231 if (psi_cpp_defined(cpp, exp->data.tok)) {
232 psi_cpp_level_mask(cpp);
233 } else {
234 psi_cpp_level_skip(cpp);
235 }
236 }
237 break;
238 case PSI_T_IFNDEF:
239 ++cpp->level;
240 if (!cpp->skip) {
241 if (psi_cpp_defined(cpp, exp->data.tok)) {
242 psi_cpp_level_skip(cpp);
243 } else {
244 psi_cpp_level_mask(cpp);
245 }
246 }
247 break;
248 case PSI_T_IF:
249 ++cpp->level;
250 if (!cpp->skip) {
251 if (psi_cpp_if(cpp, exp)) {
252 psi_cpp_level_mask(cpp);
253 } else {
254 psi_cpp_level_skip(cpp);
255 }
256 }
257 break;
258 case PSI_T_ENDIF:
259 if (!cpp->level) {
260 D->error(D, exp->token, PSI_WARNING, "Ingoring lone #endif");
261 } else {
262 psi_cpp_level_unskip(cpp);
263 psi_cpp_level_unmask(cpp);
264 --cpp->level;
265 }
266 break;
267 case PSI_T_ELSE:
268 /* FIXME: catch "else" after "else" */
269 if (!cpp->level) {
270 D->error(D, exp->token, PSI_WARNING, "Ingoring lone #else");
271 } else if (psi_cpp_level_skipped(cpp) && !psi_cpp_level_masked(cpp)) {
272 /*
273 * if skip is set on this level and the level has
274 * not been masked yet, then unskip and mask this level
275 */
276 psi_cpp_level_unskip(cpp);
277 psi_cpp_level_mask(cpp);
278 } else if (!cpp->skip && psi_cpp_level_masked(cpp)) {
279 /*
280 * previous block masked this level
281 */
282 psi_cpp_level_skip(cpp);
283 } else {
284 assert(cpp->skip <= cpp->level);
285 }
286 break;
287 case PSI_T_ELIF:
288 if (!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)) {
291 /*
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
295 */
296 if (psi_cpp_if(cpp, exp)) {
297 psi_cpp_level_unskip(cpp);
298 psi_cpp_level_mask(cpp);
299 }
300 } else if (!cpp->skip && psi_cpp_level_masked(cpp)) {
301 /*
302 * previous block masked this level
303 */
304 psi_cpp_level_skip(cpp);
305 } else {
306 assert(cpp->skip <= cpp->level);
307 }
308 break;
309 case PSI_T_INCLUDE:
310 if (!cpp->skip) {
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));
314 }
315 }
316 break;
317 case PSI_T_INCLUDE_NEXT:
318 if (!cpp->skip) {
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));
322 }
323 }
324 break;
325 case PSI_T_IMPORT:
326 if (!cpp->skip) {
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));
330 }
331 }
332 break;
333 case PSI_T_PRAGMA_ONCE:
334 if (!cpp->skip) {
335 zend_hash_add_empty_element(&cpp->once, exp->token->file);
336 }
337 break;
338 default:
339 assert(0);
340 break;
341 }
342
343 PSI_DEBUG_PRINT(D, "PSI: CPP EVAL > %s (level=%u, skip=%u)\n",
344 exp->token->text->val, cpp->level, cpp->skip);
345 }