first draft of a simple cpp scanner
[m6w6/ext-psi] / src / parser.re
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 <stdarg.h>
30
31 #include "parser.h"
32
33 void *psi_parser_proc_init(void);
34 void psi_parser_proc_free(void **parser_proc);
35 void psi_parser_proc_parse(void *parser_proc, token_t r, struct psi_token *token, struct psi_parser *parser);
36 void psi_parser_proc_trace(FILE *out, char *prefix);
37
38 struct psi_parser *psi_parser_init(struct psi_parser *P, psi_error_cb error, unsigned flags)
39 {
40 if (!P) {
41 P = malloc(sizeof(*P));
42 }
43 memset(P, 0, sizeof(*P));
44
45 psi_data_ctor_with_dtors(PSI_DATA(P), error, flags);
46
47 P->col = 1;
48 P->line = 1;
49 P->proc = psi_parser_proc_init();
50
51 ZEND_INIT_SYMTABLE(&P->cpp.defs);
52 zval tmp;
53 ZVAL_ARR(&tmp, &P->cpp.defs);
54 add_assoc_string(&tmp, "PHP_OS", PHP_OS);
55
56 if (flags & PSI_DEBUG) {
57 psi_parser_proc_trace(stderr, "PSI> ");
58 }
59
60 return P;
61 }
62
63 bool psi_parser_open_file(struct psi_parser *P, const char *filename)
64 {
65 FILE *fp = fopen(filename, "r");
66
67 if (!fp) {
68 P->error(PSI_DATA(P), NULL, PSI_WARNING,
69 "Could not open '%s' for reading: %s",
70 filename, strerror(errno));
71 return false;
72 }
73
74 P->input.type = PSI_PARSE_FILE;
75 P->input.data.file.handle = fp;
76
77 #if HAVE_MMAP
78 struct stat sb;
79 int fd = fileno(fp);
80
81 if (fstat(fd, &sb)) {
82 P->error(PSI_DATA(P), NULL, PSI_WARNING,
83 "Could not stat '%s': %s",
84 filename, strerror(errno));
85 return false;
86 }
87
88 P->input.data.file.buffer = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
89 if (MAP_FAILED == P->input.data.file.buffer) {
90 P->error(PSI_DATA(P), NULL, PSI_WARNING,
91 "Could not map '%s' for reading: %s",
92 filename, strerror(errno));
93 return false;
94 }
95 P->input.data.file.length = sb.st_size;
96 #else
97 P->input.data.file.buffer = malloc(BSIZE);
98 #endif
99
100 P->file.fn = strdup(filename);
101
102 return true;
103 }
104
105 bool psi_parser_open_string(struct psi_parser *P, const char *string, size_t length)
106 {
107 P->input.type = PSI_PARSE_STRING;
108 P->input.data.string.length = length;
109 if (!(P->input.data.string.buffer = strndup(string, length))) {
110 return false;
111 }
112
113 P->file.fn = strdup("<input>");
114
115 return true;
116 }
117
118 static ssize_t psi_parser_fill(struct psi_parser *P, size_t n)
119 {
120 PSI_DEBUG_PRINT(P, "PSI< Fill: n=%zu (input.type=%d)\n", n, P->input.type);
121
122 /* init if n==0 */
123 if (!n) {
124 switch (P->input.type) {
125 case PSI_PARSE_FILE:
126 P->cur = P->tok = P->mrk = P->input.data.file.buffer;
127 #if HAVE_MMAP
128 P->eof = P->input.data.file.buffer + P->input.data.file.length;
129 P->lim = P->eof;
130 #else
131 P->eof = NULL;
132 P->lim = P->input.data.file.buffer;
133 #endif
134 break;
135
136 case PSI_PARSE_STRING:
137 P->cur = P->tok = P->mrk = P->input.data.string.buffer;
138 P->eof = P->input.data.string.buffer + P->input.data.string.length;
139 P->lim = P->eof;
140 break;
141 }
142
143 PSI_DEBUG_PRINT(P, "PSI< Fill: cur=%p lim=%p eof=%p\n", P->cur, P->lim, P->eof);
144 }
145
146 switch (P->input.type) {
147 case PSI_PARSE_STRING:
148 break;
149
150 case PSI_PARSE_FILE:
151 #if !HAVE_MMAP
152 if (!P->eof) {
153 size_t consumed = P->tok - P->buf;
154 size_t reserved = P->lim - P->tok;
155 size_t available = BSIZE - reserved;
156 size_t didread;
157
158 if (consumed) {
159 memmove(P->buf, P->tok, reserved);
160 P->tok -= consumed;
161 P->cur -= consumed;
162 P->lim -= consumed;
163 P->mrk -= consumed;
164 }
165
166 didread = fread(P->lim, 1, available, P->fp);
167 P->lim += didread;
168 if (didread < available) {
169 P->eof = P->lim;
170 }
171 PSI_DEBUG_PRINT(P, "PSI< Fill: consumed=%zu reserved=%zu available=%zu didread=%zu\n",
172 consumed, reserved, available, didread);
173 }
174 #endif
175 break;
176 }
177
178 PSI_DEBUG_PRINT(P, "PSI< Fill: avail=%td\n", P->lim - P->cur);
179
180 return P->lim - P->cur;
181 }
182
183 void psi_parser_parse(struct psi_parser *P, struct psi_token *T)
184 {
185 if (T) {
186 psi_parser_proc_parse(P->proc, T->type, T, P);
187 } else {
188 psi_parser_proc_parse(P->proc, 0, NULL, P);
189 }
190 }
191
192 void psi_parser_dtor(struct psi_parser *P)
193 {
194 psi_parser_proc_free(&P->proc);
195
196 switch (P->input.type) {
197 case PSI_PARSE_FILE:
198 if (P->input.data.file.buffer) {
199 #if HAVE_MMAP
200 munmap(P->input.data.file.buffer, P->input.data.file.length);
201 #else
202 free(P->input.data.file.buffer);
203 #endif
204 }
205 if (P->input.data.file.handle) {
206 fclose(P->input.data.file.handle);
207 }
208 break;
209
210 case PSI_PARSE_STRING:
211 if (P->input.data.string.buffer) {
212 free(P->input.data.string.buffer);
213 }
214 break;
215 }
216
217 psi_data_dtor(PSI_DATA(P));
218
219 zend_hash_destroy(&P->cpp.defs);
220
221 memset(P, 0, sizeof(*P));
222 }
223
224 void psi_parser_free(struct psi_parser **P)
225 {
226 if (*P) {
227 psi_parser_dtor(*P);
228 free(*P);
229 *P = NULL;
230 }
231 }
232
233 static bool cpp_truth(struct psi_parser *P);
234 static bool cpp_defined(struct psi_parser *P);
235 static zval *cpp_define_var(struct psi_parser *P);
236 static void cpp_define_val(struct psi_parser *P, token_t typ, zval *val);
237 static void cpp_undefine(struct psi_parser *P);
238 static void cpp_error(struct psi_parser *P, const char *msg, ...);
239
240 /*!max:re2c*/
241 #if BSIZE < YYMAXFILL
242 # error BSIZE must be greater than YYMAXFILL
243 #endif
244
245 #define RETURN(t) do { \
246 P->num = t; \
247 PSI_DEBUG_PRINT(P, "PSI< TOKEN: %d %.*s (EOF=%d %s:%u:%u)\n", \
248 P->num, (int) (P->cur-P->tok), P->tok, P->num == PSI_T_EOF, \
249 P->file.fn, P->line, P->col); \
250 return t; \
251 } while(1)
252
253 #define NEWLINE(label) \
254 P->col = 1; \
255 ++P->line; \
256 goto label
257
258 token_t psi_parser_scan(struct psi_parser *P)
259 {
260 if (!P->cur) {
261 psi_parser_fill(P, 0);
262 }
263 for (;;) {
264 if (P->cpp.skip) {
265 /* we might come from EOL, so just go to cpp instead of cpp_skip */
266 goto cpp;
267 }
268
269 P->col += P->cur - P->tok;
270
271 nextline: ;
272 P->tok = P->cur;
273 /*!re2c
274 re2c:indent:top = 2;
275 re2c:define:YYCTYPE = "unsigned char";
276 re2c:define:YYCURSOR = P->cur;
277 re2c:define:YYLIMIT = P->lim;
278 re2c:define:YYMARKER = P->mrk;
279 re2c:define:YYFILL = "{ if (!psi_parser_fill(P,@@)) RETURN(PSI_T_EOF); }";
280 re2c:yyfill:parameter = 0;
281
282 B = [^a-zA-Z0-9_];
283 W = [a-zA-Z0-9_];
284 NAME = [a-zA-Z_]W*;
285 NSNAME = (NAME)? ("\\" NAME)+;
286 DOLLAR_NAME = '$' W+;
287 QUOTED_STRING = "\"" ([^\"])+ "\"";
288 NUMBER = [+-]? [0-9]* "."? [0-9]+ ([eE] [+-]? [0-9]+)?;
289
290 "#" { --P->cur; goto cpp; }
291 "/*" { goto comment; }
292 "//" .* "\n" { NEWLINE(nextline); }
293 "(" {RETURN(PSI_T_LPAREN);}
294 ")" {RETURN(PSI_T_RPAREN);}
295 ";" {RETURN(PSI_T_EOS);}
296 "," {RETURN(PSI_T_COMMA);}
297 ":" {RETURN(PSI_T_COLON);}
298 "{" {RETURN(PSI_T_LBRACE);}
299 "}" {RETURN(PSI_T_RBRACE);}
300 "[" {RETURN(PSI_T_LBRACKET);}
301 "]" {RETURN(PSI_T_RBRACKET);}
302 "!=" {RETURN(PSI_T_CMP_NE);}
303 "==" {RETURN(PSI_T_CMP_EQ);}
304 "&&" {RETURN(PSI_T_AND);}
305 "||" {RETURN(PSI_T_OR);}
306 "=" {RETURN(PSI_T_EQUALS);}
307 "*" {RETURN(PSI_T_ASTERISK);}
308 "~" {RETURN(PSI_T_TILDE);}
309 "!" {RETURN(PSI_T_NOT);}
310 "%" {RETURN(PSI_T_MODULO);}
311 "&" {RETURN(PSI_T_AMPERSAND);}
312 "+" {RETURN(PSI_T_PLUS);}
313 "-" {RETURN(PSI_T_MINUS);}
314 "/" {RETURN(PSI_T_SLASH);}
315 "|" {RETURN(PSI_T_PIPE);}
316 "^" {RETURN(PSI_T_CARET);}
317 "<<" {RETURN(PSI_T_LSHIFT);}
318 ">>" {RETURN(PSI_T_RSHIFT);}
319 "<=" {RETURN(PSI_T_CMP_LE);}
320 ">=" {RETURN(PSI_T_CMP_GE);}
321 "<" {RETURN(PSI_T_LCHEVR);}
322 ">" {RETURN(PSI_T_RCHEVR);}
323 "..." {RETURN(PSI_T_ELLIPSIS);}
324 [\r\n] { NEWLINE(nextline); }
325 [\t ]+ { continue; }
326 'TRUE' {RETURN(PSI_T_TRUE);}
327 'FALSE' {RETURN(PSI_T_FALSE);}
328 'NULL' {RETURN(PSI_T_NULL);}
329 'MIXED' {RETURN(PSI_T_MIXED);}
330 'CALLABLE' {RETURN(PSI_T_CALLABLE);}
331 'VOID' {RETURN(PSI_T_VOID);}
332 'BOOL' {RETURN(PSI_T_BOOL);}
333 'CHAR' {RETURN(PSI_T_CHAR);}
334 'SHORT' {RETURN(PSI_T_SHORT);}
335 'INT' {RETURN(PSI_T_INT);}
336 'LONG' {RETURN(PSI_T_LONG);}
337 'FLOAT' {RETURN(PSI_T_FLOAT);}
338 'DOUBLE' {RETURN(PSI_T_DOUBLE);}
339 'INT8_T' {RETURN(PSI_T_INT8);}
340 'UINT8_T' {RETURN(PSI_T_UINT8);}
341 'INT16_T' {RETURN(PSI_T_INT16);}
342 'UINT16_T' {RETURN(PSI_T_UINT16);}
343 'INT32_T' {RETURN(PSI_T_INT32);}
344 'UINT32_T' {RETURN(PSI_T_UINT32);}
345 'INT64_T' {RETURN(PSI_T_INT64);}
346 'UINT64_T' {RETURN(PSI_T_UINT64);}
347 'UNSIGNED' {RETURN(PSI_T_UNSIGNED);}
348 'SIGNED' {RETURN(PSI_T_SIGNED);}
349 'STRING' {RETURN(PSI_T_STRING);}
350 'ARRAY' {RETURN(PSI_T_ARRAY);}
351 'OBJECT' {RETURN(PSI_T_OBJECT);}
352 'CALLBACK' {RETURN(PSI_T_CALLBACK);}
353 'STATIC' {RETURN(PSI_T_STATIC);}
354 'FUNCTION' {RETURN(PSI_T_FUNCTION);}
355 'TYPEDEF' {RETURN(PSI_T_TYPEDEF);}
356 'STRUCT' {RETURN(PSI_T_STRUCT);}
357 'UNION' {RETURN(PSI_T_UNION);}
358 'ENUM' {RETURN(PSI_T_ENUM);}
359 'CONST' {RETURN(PSI_T_CONST);}
360 'LIB' {RETURN(PSI_T_LIB);}
361 'LET' {RETURN(PSI_T_LET);}
362 'SET' {RETURN(PSI_T_SET);}
363 'PRE_ASSERT' {RETURN(PSI_T_PRE_ASSERT);}
364 'POST_ASSERT' {RETURN(PSI_T_POST_ASSERT);}
365 'RETURN' {RETURN(PSI_T_RETURN);}
366 'FREE' {RETURN(PSI_T_FREE);}
367 'TEMP' {RETURN(PSI_T_TEMP);}
368 'STRLEN' {RETURN(PSI_T_STRLEN);}
369 'STRVAL' {RETURN(PSI_T_STRVAL);}
370 'PATHVAL' {RETURN(PSI_T_PATHVAL);}
371 'INTVAL' {RETURN(PSI_T_INTVAL);}
372 'FLOATVAL' {RETURN(PSI_T_FLOATVAL);}
373 'BOOLVAL' {RETURN(PSI_T_BOOLVAL);}
374 'ARRVAL' {RETURN(PSI_T_ARRVAL);}
375 'OBJVAL' {RETURN(PSI_T_OBJVAL);}
376 'ZVAL' {RETURN(PSI_T_ZVAL);}
377 'COUNT' {RETURN(PSI_T_COUNT);}
378 'CALLOC' {RETURN(PSI_T_CALLOC);}
379 'TO_OBJECT' {RETURN(PSI_T_TO_OBJECT);}
380 'TO_ARRAY' {RETURN(PSI_T_TO_ARRAY);}
381 'TO_STRING' {RETURN(PSI_T_TO_STRING);}
382 'TO_INT' {RETURN(PSI_T_TO_INT);}
383 'TO_FLOAT' {RETURN(PSI_T_TO_FLOAT);}
384 'TO_BOOL' {RETURN(PSI_T_TO_BOOL);}
385 NUMBER {RETURN(PSI_T_NUMBER);}
386 NAME {RETURN(PSI_T_NAME);}
387 NSNAME {RETURN(PSI_T_NSNAME);}
388 DOLLAR_NAME {RETURN(PSI_T_DOLLAR_NAME);}
389 QUOTED_STRING {RETURN(PSI_T_QUOTED_STRING);}
390 [^] {break;}
391 */
392
393 comment: ;
394 P->tok = P->cur;
395 /*!re2c
396 "\n" { NEWLINE(comment); }
397 "*" "/" { continue; }
398 [^] { goto comment; }
399 */
400
401 #define PSI_DEBUG_CPP(P, msg, ...) do { \
402 if (PSI_DATA(P)->flags & PSI_DEBUG) { \
403 fprintf(stderr, "PSI> CPP %.*s line=%u level=%u skip=%u ", \
404 (int) strcspn(P->tok, "\r\n"), P->tok, \
405 P->line, P->cpp.level, P->cpp.skip); \
406 fprintf(stderr, msg, __VA_ARGS__); \
407 } \
408 } while(0)
409
410 cpp: ;
411 P->tok = P->cur;
412 /*!re2c
413 [\t ] {goto cpp;}
414 "#" [\t ]* "if" { goto cpp_if; }
415 "#" [\t ]* "ifdef" { goto cpp_ifdef; }
416 "#" [\t ]* "ifndef" { goto cpp_ifndef; }
417 "#" [\t ]* "else" { goto cpp_else; }
418 "#" [\t ]* "endif" { goto cpp_endif; }
419 "#" [\t ]* "define" { goto cpp_define; }
420 "#" [\t ]* "undef" { goto cpp_undef; }
421 "#" [\t ]* "error" { goto cpp_error; }
422 [^] { goto cpp_default; }
423 */
424
425 cpp_default: ;
426 if (P->cpp.skip) {
427 goto cpp_skip;
428 } else {
429 assert(0);
430 break;
431 }
432
433 cpp_skip: ;
434 P->tok = P->cur;
435 /*!re2c
436 [\r\n] { goto cpp_skip_eol; }
437 [^] { goto cpp_skip; }
438 */
439
440 cpp_skip_eol:
441 PSI_DEBUG_PRINT(P, "PSI> CPP skip line %u\n", P->line);
442 NEWLINE(cpp);
443
444 cpp_if: ;
445 PSI_DEBUG_CPP(P, "%s\n", "");
446
447 ++P->cpp.level;
448
449 cpp_if_cont: ;
450 if (P->cpp.skip) {
451 goto cpp_skip;
452 }
453
454 P->tok = P->cur;
455
456 /*!re2c
457 [\t ]+ { goto cpp_if_cont; }
458 "!" [\t ]* "defined" [\t ]* "("? { goto cpp_ifndef_cont; }
459 "defined" [ \t]* "("? { goto cpp_ifdef_cont; }
460 NAME [\t ]* [\r\n] { goto cpp_if_name_eol; }
461 [^] { goto cpp_if_default; }
462 */
463
464 cpp_if_default: ;
465 cpp_error(P, "PSI syntax error: invalid #if");
466 continue;
467
468 cpp_if_name_eol: ;
469 if (cpp_truth(P)) {
470 NEWLINE(nextline);
471 } else {
472 P->cpp.skip = P->cpp.level;
473 NEWLINE(cpp);
474 }
475
476 cpp_ifdef: ;
477 PSI_DEBUG_CPP(P, "%s\n", "");
478
479 ++P->cpp.level;
480
481 cpp_ifdef_cont: ;
482 if (P->cpp.skip) {
483 goto cpp_skip;
484 }
485
486 P->tok = P->cur;
487
488 /*!re2c
489 [\t ]+ { goto cpp_ifdef_cont; }
490 NAME [\t ]* ")"? [\t ]* [\r\n] { goto cpp_ifdef_name_eol; }
491 [^] { goto cpp_ifdef_default; }
492 */
493
494 cpp_ifdef_default: ;
495 cpp_error(P, "PSI syntax error: invalid #ifdef");
496 continue;
497
498 cpp_ifdef_name_eol: ;
499 if (cpp_defined(P)) {
500 NEWLINE(nextline);
501 } else {
502 P->cpp.skip = P->cpp.level;
503 NEWLINE(cpp);
504 }
505
506 cpp_ifndef: ;
507 PSI_DEBUG_CPP(P, "%s\n", "");
508
509 ++P->cpp.level;
510
511 cpp_ifndef_cont:
512 if (P->cpp.skip) {
513 goto cpp_skip;
514 }
515
516 P->tok = P->cur;
517
518 /*!re2c
519 [\t ]+ { goto cpp_ifndef_cont; }
520 NAME [\t ]* ")"? [\t ]* [\r\n] { goto cpp_ifndef_name_eol; }
521 [^] { goto cpp_ifndef_default; }
522 */
523
524 cpp_ifndef_default: ;
525 cpp_error(P, "PSI syntax error: invalid #ifndef");
526 continue;
527
528 cpp_ifndef_name_eol: ;
529 if (!cpp_defined(P)) {
530 NEWLINE(nextline);
531 } else {
532 P->cpp.skip = P->cpp.level;
533 NEWLINE(cpp_skip);
534 }
535
536 cpp_else: ;
537 PSI_DEBUG_CPP(P, "%s\n", "");
538
539 P->tok = P->cur;
540
541 if (!P->cpp.level) {
542 cpp_error(P, "PSI syntax error: ignoring lone #else");
543 continue;
544 }
545 if (!P->cpp.skip) {
546 P->cpp.skip = P->cpp.level;
547 goto cpp_skip;
548 } else if (P->cpp.skip == P->cpp.level) {
549 P->cpp.skip = 0;
550 }
551 continue;
552
553 cpp_endif: ;
554 PSI_DEBUG_CPP(P, "%s\n", "");
555
556 P->tok = P->cur;
557 if (!P->cpp.level) {
558 cpp_error(P, "PSI syntax_error: ignoring lone #endif");
559 continue;
560 } else if (P->cpp.skip == P->cpp.level) {
561 P->cpp.skip = 0;
562 }
563 --P->cpp.level;
564 continue;
565
566 cpp_define: ;
567 PSI_DEBUG_CPP(P, "%s\n", "");
568
569 zval *val = NULL;
570
571 if (P->cpp.skip) {
572 goto cpp_skip;
573 }
574
575 cpp_define_cont: ;
576 P->tok = P->cur;
577
578 /*!re2c
579 [\t ]+ { goto cpp_define_cont; }
580 [\r\n] { goto cpp_define_eol; }
581 NAME { goto cpp_define_name; }
582 QUOTED_STRING { goto cpp_define_quoted_string; }
583 NUMBER { goto cpp_define_number; }
584 [^] { goto cpp_define_default; }
585 */
586
587 cpp_define_default: ;
588 cpp_error(P, "PSI syntax error: invalid #ifndef");
589 continue;
590
591 cpp_define_eol: ;
592 if (!val) {
593 cpp_error(P, "PSI syntax error: ignoring lone #define");
594 continue;
595 }
596 NEWLINE(nextline);
597
598 cpp_define_name: ;
599 if (val) {
600 if (Z_TYPE_P(val) != IS_TRUE) {
601 cpp_error(P, "PSI syntax error: invalid #define");
602 continue;
603 }
604 cpp_define_val(P, PSI_T_NAME, val);
605 } else {
606 val = cpp_define_var(P);
607 }
608 goto cpp_define_cont;
609
610 cpp_define_quoted_string: ;
611 if (!val) {
612 cpp_error(P, "PSI syntax error: invalid quoted string in #define");
613 continue;
614 } else {
615 cpp_define_val(P, PSI_T_QUOTED_STRING, val);
616 }
617 goto cpp_define_cont;
618
619 cpp_define_number: ;
620 if (!val) {
621 cpp_error(P, "PSI syntax error: invalid quoted string in #define");
622 continue;
623 } else {
624 cpp_define_val(P, PSI_T_NUMBER, val);
625 }
626 goto cpp_define_cont;
627
628 cpp_undef: ;
629 PSI_DEBUG_CPP(P, "%s\n", "");
630
631 if (P->cpp.skip) {
632 goto cpp_skip;
633 }
634
635 cpp_undef_cont: ;
636 P->tok = P->cur;
637
638 /*!re2c
639 [\t ]+ { goto cpp_undef_cont; }
640 NAME [\t ]* [\r\n] { goto cpp_undef_name_eol; }
641 [^] { goto cpp_undef_default; }
642 */
643
644 cpp_undef_default: ;
645 cpp_error(P, "PSI syntax error: invalid #undef");
646 continue;
647
648 cpp_undef_name_eol: ;
649 cpp_undefine(P);
650 NEWLINE(nextline);
651
652 cpp_error: ;
653 size_t len = strcspn(P->cur, "\r\n");
654
655 if (P->cpp.skip) {
656 P->tok = P->cur + len;
657 NEWLINE(cpp_skip);
658 } else {
659 cpp_error(P, "%.*s", (int) len, P->cur);
660 break;
661 }
662 }
663 return -1;
664 }
665
666 #include <ctype.h>
667
668 static bool cpp_truth(struct psi_parser *P)
669 {
670 size_t len = P->cur - P->tok;
671
672 while (len && isspace(P->tok[len - 1])) {
673 --len;
674 }
675
676 zval *val = zend_symtable_str_find(&P->cpp.defs, P->tok, len);
677 bool truth = val ? zend_is_true(val) : false;
678
679 PSI_DEBUG_PRINT(P, "PSI> CPP truth(%.*s)=%s\n",
680 (int) len, P->tok, truth ? "true" : "false");
681
682 return truth;
683 }
684
685 static bool cpp_defined(struct psi_parser *P)
686 {
687 size_t len = P->cur - P->tok;
688
689 while (len && isspace(P->tok[len - 1])) {
690 --len;
691 }
692
693
694 bool defined = zend_symtable_str_exists(&P->cpp.defs, P->tok, len);
695 PSI_DEBUG_PRINT(P, "PSI> CPP defined(%.*s)=%s\n",
696 (int) len, P->tok, defined ? "true" : "false");
697 return defined;
698 }
699
700 static zval *cpp_define_var(struct psi_parser *P)
701 {
702 if (cpp_defined(P)) {
703 psi_error(PSI_WARNING, P->file.fn, P->line, "PSI syntax error: Unexpected end of input");
704 }
705 size_t len = P->cur - P->tok;
706
707 while (len && isspace(P->tok[len - 1])) {
708 --len;
709 }
710
711 PSI_DEBUG_PRINT(P, "PSI> CPP define %.*s\n", (int) len, P->tok);
712
713 if (zend_symtable_str_exists(&P->cpp.defs, P->tok, len)) {
714 cpp_error(P, "Redefinition of %.*s", (int) len, P->tok);
715 }
716
717 zval val;
718 ZVAL_TRUE(&val);
719 return zend_symtable_str_update(&P->cpp.defs, P->tok, len, &val);
720 }
721
722 static void cpp_define_val(struct psi_parser *P, token_t typ, zval *val) {
723 size_t len = P->cur - P->tok;
724
725 while (len && isspace(P->tok[len - 1])) {
726 --len;
727 }
728
729 PSI_DEBUG_PRINT(P, "PSI> define = %.*s\n", (int) len, P->tok);
730
731 switch (typ) {
732 case PSI_T_QUOTED_STRING:
733 ZVAL_STRINGL(val, P->tok + 1, len - 2);
734 break;
735 case PSI_T_NUMBER:
736 ZVAL_STRINGL(val, P->tok, len);
737 convert_scalar_to_number(val);
738 break;
739 default:
740 assert(0);
741 }
742 }
743
744 static void cpp_undefine(struct psi_parser *P)
745 {
746 size_t len = P->cur - P->tok;
747
748 while (len && isspace(P->tok[len - 1])) {
749 --len;
750 }
751
752 zend_symtable_str_del(&P->cpp.defs, P->tok, len);
753 }
754
755 static void cpp_error(struct psi_parser *P, const char *msg, ...)
756 {
757 va_list argv;
758
759 va_start(argv, msg);
760 psi_verror(PSI_WARNING, P->file.fn, P->line, msg, argv);
761 va_end(argv);
762 }