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