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