travis: update
[m6w6/ext-psi] / src / parser.c
1 /*******************************************************************************
2 Copyright (c) 2016, 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 #include <sys/mman.h>
32 #include <assert.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 #include <time.h>
36
37 #include <Zend/zend_smart_str.h>
38
39 #include "parser.h"
40
41 struct psi_parser *psi_parser_init(struct psi_parser *P, psi_error_cb error,
42 unsigned flags)
43 {
44 if (!P) {
45 P = pemalloc(sizeof(*P), 1);
46 }
47 memset(P, 0, sizeof(*P));
48
49 psi_data_ctor_with_dtors(PSI_DATA(P), error, flags);
50
51 P->preproc = psi_cpp_init(P);
52
53 return P;
54 }
55
56 struct psi_parser_input *psi_parser_open_file(struct psi_parser *P,
57 const char *filename, bool report_errors)
58 {
59 struct stat sb;
60 FILE *fp;
61 struct psi_parser_input *fb;
62
63 if (stat(filename, &sb)) {
64 if (report_errors) {
65 P->error(PSI_DATA(P), NULL, PSI_WARNING,
66 "Could not stat '%s': %s",
67 filename, strerror(errno));
68 }
69 return NULL;
70 }
71
72 if (!(fb = pemalloc(sizeof(*fb) + sb.st_size + psi_parser_maxfill(), 1))) {
73 if (report_errors) {
74 P->error(PSI_DATA(P), NULL, PSI_WARNING,
75 "Could not allocate %zu bytes for reading '%s': %s",
76 sb.st_size + psi_parser_maxfill(), filename, strerror(errno));
77 }
78 return NULL;
79 }
80
81 if (!(fp = fopen(filename, "r"))) {
82 free(fb);
83 if (report_errors) {
84 P->error(PSI_DATA(P), NULL, PSI_WARNING,
85 "Could not open '%s' for reading: %s",
86 filename, strerror(errno));
87 }
88 return NULL;
89 }
90
91 if (sb.st_size != fread(fb->buffer, 1, sb.st_size, fp)) {
92 free(fb);
93 fclose(fp);
94 if (report_errors) {
95 P->error(PSI_DATA(P), NULL, PSI_WARNING,
96 "Could not read %zu bytes from '%s': %s",
97 sb.st_size + psi_parser_maxfill(), filename, strerror(errno));
98 }
99 return NULL;
100 }
101 fclose(fp);
102
103 memset(fb->buffer + sb.st_size, 0, psi_parser_maxfill());
104
105 fb->lmod = sb.st_mtime;
106 fb->length = sb.st_size;
107 fb->file = psi_string_init_interned(filename, strlen(filename), 1);
108
109 return fb;
110 }
111
112 struct psi_parser_input *psi_parser_open_string(struct psi_parser *P,
113 const char *string, size_t length)
114 {
115 struct psi_parser_input *sb;
116
117 if (!(sb = pemalloc(sizeof(*sb) + length + psi_parser_maxfill(), 1))) {
118 P->error(PSI_DATA(P), NULL, PSI_WARNING,
119 "Could not allocate %zu bytes: %s",
120 length + psi_parser_maxfill(), strerror(errno));
121 return NULL;
122 }
123
124 memcpy(sb->buffer, string, length);
125 memset(sb->buffer + length, 0, psi_parser_maxfill());
126
127 sb->lmod = time(NULL);
128 sb->length = length;
129 sb->file = psi_string_init_interned("<stdin>", strlen("<stdin>"), 1);
130
131 return sb;
132 }
133
134 struct psi_plist *psi_parser_preprocess(struct psi_parser *P,
135 struct psi_plist **tokens)
136 {
137 if (psi_cpp_process(P->preproc, tokens, NULL)) {
138 return *tokens;
139 }
140 return NULL;
141 }
142
143 bool psi_parser_process(struct psi_parser *P, struct psi_plist *tokens,
144 size_t *processed)
145 {
146 if (psi_plist_count(tokens)) {
147 return 0 == psi_parser_proc_parse(P, tokens, processed);
148 }
149 return true;
150 }
151
152 static inline zend_string *macro_to_constant(struct psi_parser *parser,
153 zend_string *name, struct psi_validate_scope *scope)
154 {
155 smart_str str = {0};
156 size_t i = 0;
157 struct psi_token *tok;
158 #if PSI_THREADED_PARSER
159 int persistent = 1;
160 #else
161 int persistent = 0;
162 #endif
163
164 psi_smart_str_printf(&str, "const psi\\%s = ", name->val);
165 if (scope->macro->exp) {
166 struct psi_dump dump = {{.hn = &str},
167 .fun = (psi_dump_cb) psi_smart_str_printf};
168
169 psi_num_exp_dump(&dump, scope->macro->exp);
170
171 } else while (psi_plist_get(scope->macro->tokens, i++, &tok)) {
172 if (tok->type == PSI_T_QUOTED_STRING) {
173 smart_str_appendc_ex(&str, '"', persistent);
174 }
175 smart_str_append_ex(&str, tok->text, persistent);
176 if (tok->type == PSI_T_QUOTED_STRING) {
177 smart_str_appendc_ex(&str, '"', persistent);
178 }
179 smart_str_appendc_ex(&str, ' ', persistent);
180 }
181 smart_str_appendl_ex(&str, ";\n", 2, persistent);
182 return smart_str_extract(&str);
183 }
184
185 void psi_parser_postprocess(struct psi_parser *P)
186 {
187 unsigned flags;
188 zend_string *name;
189 struct psi_validate_scope scope = {0};
190
191 psi_validate_scope_ctor(&scope);
192 scope.cpp = P->preproc;
193
194 flags = P->flags;
195 P->flags |= PSI_SILENT;
196
197 ZEND_HASH_FOREACH_STR_KEY_PTR(&P->preproc->defs, name, scope.macro)
198 {
199 bool parsed;
200 size_t processed = 0;
201 struct psi_plist *scanned, *preproc;
202 struct psi_parser_input *I;
203 zend_string *cnst;
204
205 if (scope.macro->sig) {
206 continue;
207 } else if (!scope.macro->exp) {
208 struct psi_token *tok;
209 if (psi_plist_count(scope.macro->tokens) != 1) {
210 continue;
211 } else if (!psi_plist_get(scope.macro->tokens, 0, &tok)) {
212 continue;
213 } else if (tok->type != PSI_T_QUOTED_STRING) {
214 continue;
215 }
216 } else if (!psi_num_exp_validate(PSI_DATA(P), scope.macro->exp, &scope)) {
217 continue;
218 } else if (psi_plist_count(scope.macro->tokens) == 1) {
219 struct psi_token *tok;
220 if (!psi_plist_get(scope.macro->tokens, 0, &tok)) {
221 continue;
222 } else if (zend_string_equals(name, tok->text)) {
223 continue;
224 }
225 }
226
227 cnst = macro_to_constant(P, name, &scope);
228 if (!cnst) {
229 continue;
230 }
231
232 I = psi_parser_open_string(P, ZSTR_VAL(cnst), ZSTR_LEN(cnst));
233 zend_string_release(cnst);
234
235 if (!(scanned = psi_parser_scan(P, I))) {
236 psi_parser_input_free(&I);
237 continue;
238 }
239 psi_parser_input_free(&I);
240
241 if (!(preproc = psi_parser_preprocess(P, &scanned))) {
242 psi_plist_free(scanned);
243 continue;
244 }
245
246 parsed = psi_parser_process(P, preproc, &processed);
247 PSI_DEBUG_PRINT(PSI_DATA(P), "PSI: processed=%zu success=%d\n",
248 processed, (int) parsed);
249 psi_plist_free(preproc);
250 }
251 ZEND_HASH_FOREACH_END();
252
253 P->flags = flags;
254
255 psi_validate_scope_dtor(&scope);
256 }
257
258 bool psi_parser_parse(struct psi_parser *P, struct psi_parser_input *I)
259 {
260 struct psi_plist *scanned, *preproc;
261 size_t processed = 0;
262
263 P->input = I;
264
265 if (!(scanned = psi_parser_scan(P, I))) {
266 P->input = NULL;
267 return false;
268 }
269
270 if (!(preproc = psi_parser_preprocess(P, &scanned))) {
271 P->input = NULL;
272 psi_plist_free(scanned);
273 return false;
274 }
275
276 if (!psi_parser_process(P, preproc, &processed)) {
277 P->input = NULL;
278 psi_plist_free(preproc);
279 return false;
280 }
281
282 psi_parser_postprocess(P);
283
284 psi_plist_free(preproc);
285 P->input = NULL;
286 return true;
287 }
288
289 void psi_parser_dtor(struct psi_parser *P)
290 {
291 psi_cpp_free(&P->preproc);
292 psi_data_dtor(PSI_DATA(P));
293
294 memset(P, 0, sizeof(*P));
295 }
296
297 void psi_parser_free(struct psi_parser **P)
298 {
299 if (*P) {
300 psi_parser_dtor(*P);
301 free(*P);
302 *P = NULL;
303 }
304 }
305