fix gdbinit; postprocessing macros
[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, "const psi\\%s = ", name->val);
149 if (scope->macro->exp) {
150 struct psi_dump dump = {{.hn = &str}, .fun = (psi_dump_cb) smart_str_append_printf};
151
152 psi_num_exp_dump(&dump, scope->macro->exp);
153 } else while (psi_plist_get(scope->macro->tokens, i++, &tok)) {
154 if (tok->type == PSI_T_QUOTED_STRING) {
155 smart_str_appendc(&str, '"');
156 }
157 smart_str_append(&str, tok->text);
158 if (tok->type == PSI_T_QUOTED_STRING) {
159 smart_str_appendc(&str, '"');
160 }
161 smart_str_appendc(&str, ' ');
162 }
163 smart_str_appendl(&str, ";\n", 2);
164 return smart_str_extract(&str);
165 }
166
167 void psi_parser_postprocess(struct psi_parser *P)
168 {
169 unsigned flags;
170 zend_string *name;
171 struct psi_validate_scope scope = {0};
172
173 psi_validate_scope_ctor(&scope);
174 scope.cpp = P->preproc;
175
176 flags = P->flags;
177 P->flags |= PSI_SILENT;
178
179 ZEND_HASH_FOREACH_STR_KEY_PTR(&P->preproc->defs, name, scope.macro)
180 {
181 bool parsed;
182 size_t processed = 0;
183 struct psi_plist *scanned, *preproc;
184 struct psi_parser_input *I;
185 zend_string *cnst;
186
187 if (scope.macro->sig) {
188 continue;
189 } else if (!scope.macro->exp) {
190 struct psi_token *tok;
191 if (psi_plist_count(scope.macro->tokens) != 1) {
192 continue;
193 } else if (!psi_plist_get(scope.macro->tokens, 0, &tok)) {
194 continue;
195 } else if (tok->type != PSI_T_QUOTED_STRING) {
196 continue;
197 }
198 } else if (!psi_num_exp_validate(PSI_DATA(P), scope.macro->exp, &scope)) {
199 continue;
200 }
201
202 cnst = macro_to_constant(P, name, &scope);
203 if (!cnst) {
204 continue;
205 }
206
207 I = psi_parser_open_string(P, ZSTR_VAL(cnst), ZSTR_LEN(cnst));
208 zend_string_release(cnst);
209
210 if (!(scanned = psi_parser_scan(P, I))) {
211 psi_parser_input_free(&I);
212 continue;
213 }
214 psi_parser_input_free(&I);
215
216 if (!(preproc = psi_parser_preprocess(P, &scanned))) {
217 psi_plist_free(scanned);
218 continue;
219 }
220
221 parsed = psi_parser_process(P, preproc, &processed);
222 PSI_DEBUG_PRINT(PSI_DATA(P), "PSI: processed=%zu success=%d\n",
223 processed, (int) parsed);
224 psi_plist_free(preproc);
225 }
226 ZEND_HASH_FOREACH_END();
227
228 P->flags = flags;
229
230 psi_validate_scope_dtor(&scope);
231 }
232
233 bool psi_parser_parse(struct psi_parser *P, struct psi_parser_input *I)
234 {
235 struct psi_plist *scanned, *preproc;
236 size_t processed = 0;
237
238 if (!(scanned = psi_parser_scan(P, I))) {
239 return false;
240 }
241
242 if (!(preproc = psi_parser_preprocess(P, &scanned))) {
243 psi_plist_free(scanned);
244 return false;
245 }
246
247 if (!psi_parser_process(P, preproc, &processed)) {
248 psi_plist_free(preproc);
249 return false;
250 }
251
252 psi_parser_postprocess(P);
253
254 psi_plist_free(preproc);
255 return true;
256 }
257
258 void psi_parser_dtor(struct psi_parser *P)
259 {
260 psi_cpp_free(&P->preproc);
261 psi_data_dtor(PSI_DATA(P));
262
263 memset(P, 0, sizeof(*P));
264 }
265
266 void psi_parser_free(struct psi_parser **P)
267 {
268 if (*P) {
269 psi_parser_dtor(*P);
270 free(*P);
271 *P = NULL;
272 }
273 }
274