Graphviz 13.1.3~dev.20250813.1130
Loading...
Searching...
No Matches
extoken.c
Go to the documentation of this file.
1/*************************************************************************
2 * Copyright (c) 2011 AT&T Intellectual Property
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11/*
12 * Glenn Fowler
13 * AT&T Research
14 *
15 * expression library default lexical analyzer
16 */
17
18#include "config.h"
19#include <expr/exlib.h>
20#include <stdalign.h>
21#include <stdbool.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
25#include <util/agxbuf.h>
26#include <util/arena.h>
27#include <util/gv_ctype.h>
28#include <util/streq.h>
29#include <util/unreachable.h>
30
31#if defined(TRACE_lex) && TRACE_lex
32
33/*
34 * trace c for op
35 */
36
37static void
38trace(Expr_t* ex, int lev, char* op, int c)
39{
40 char* s = 0;
41 char* t;
42 bool free_t = false;
43 char buf[16];
44 void* x = 0;
45
46 t = "";
47 switch (c)
48 {
49 case 0:
50 s = " EOF";
51 break;
52 case '=':
53 s = t = buf;
54 *t++ = ' ';
55 if (!lev && ex_lval.op != c)
56 *t++ = ex_lval.op;
57 *t++ = c;
58 *t = 0;
59 break;
60 case AND:
61 s = " AND ";
62 t = "&&";
63 break;
64 case DEC:
65 s = " DEC ";
66 t = "--";
67 break;
68 case DECLARE:
69 s = " DECLARE ";
70 t = ex_lval.id->name;
71 break;
72 case DYNAMIC:
73 s = " DYNAMIC ";
74 t = ex_lval.id->name;
75 x = (void*)ex_lval.id;
76 break;
77 case EQ:
78 s = " EQ ";
79 t = "==";
80 break;
81 case FLOATING:
82 s = " FLOATING ";
83 snprintf(t = buf, sizeof(buf), "%f", ex_lval.floating);
84 break;
85 case GE:
86 s = " GE ";
87 t = ">=";
88 break;
89 case CONSTANT:
90 s = " CONSTANT ";
91 t = ex_lval.id->name;
92 break;
93 case ID:
94 s = " ID ";
95 t = ex_lval.id->name;
96 break;
97 case INC:
98 s = "INC ";
99 t = "++";
100 break;
101 case INTEGER:
102 s = " INTEGER ";
103 snprintf(t = buf, sizeof(buf), "%lld", ex_lval.integer);
104 break;
105 case LABEL:
106 s = " LABEL ";
107 t = ex_lval.id->name;
108 break;
109 case LE:
110 s = " LE ";
111 t = "<=";
112 break;
113 case LSH:
114 s = " LSH ";
115 t = "<<";
116 break;
117 case NAME:
118 s = " NAME ";
119 t = ex_lval.id->name;
120 x = (void*)ex_lval.id;
121 break;
122 case NE:
123 s = " NE ";
124 t = "!=";
125 break;
126 case OR:
127 s = " OR ";
128 t = "||";
129 break;
130 case RSH:
131 s = " RSH ";
132 t = ">>";
133 break;
134 case STRING:
135 s = " STRING ";
136 t = fmtesc(ex_lval.string);
137 free_t = true;
138 break;
139 case UNSIGNED:
140 s = " UNSIGNED ";
141 snprintf(t = buf, sizeof(buf), "%llu", (unsigned long long)ex_lval.integer);
142 break;
143 case BREAK:
144 s = " break";
145 break;
146 case CASE:
147 s = " case";
148 break;
149 case CONTINUE:
150 s = " continue";
151 break;
152 case DEFAULT:
153 s = " default";
154 break;
155 case ELSE:
156 s = " else";
157 break;
158 case EXIT:
159 s = " exit";
160 break;
161 case FOR:
162 s = " for";
163 break;
164 case ITERATOR:
165 s = " forf";
166 break;
167 case GSUB:
168 s = " gsub";
169 break;
170 case IF:
171 s = " if";
172 break;
173 case IN_OP:
174 s = " in";
175 break;
176 case PRAGMA:
177 s = " pragma";
178 break;
179 case PRINT:
180 s = " print";
181 break;
182 case PRINTF:
183 s = " printf";
184 break;
185 case QUERY:
186 s = " query";
187 break;
188 case RAND:
189 s = " rand";
190 break;
191 case RETURN:
192 s = " return";
193 break;
194 case SPLIT:
195 s = " split";
196 break;
197 case SPRINTF:
198 s = " sprintf";
199 break;
200 case SRAND:
201 s = " srand";
202 break;
203 case SUB:
204 s = " sub";
205 break;
206 case SUBSTR:
207 s = " substr";
208 break;
209 case SWITCH:
210 s = " switch";
211 break;
212 case TOKENS:
213 s = " tokens";
214 break;
215 case UNSET:
216 s = " unset";
217 break;
218 case WHILE:
219 s = " while";
220 break;
221 default:
222 if (c < 0177)
223 {
224 s = buf;
225 *s++ = c;
226 *s = 0;
227 t = fmtesc(buf);
228 free_t = true;
229 s = " ";
230 }
231 break;
232 }
233 if (x)
234 error(TRACE_lex + lev, "%s: [%d] %04d%s%s (%x)", op, ex->input->nesting, c, s, t, x);
235
236 else
237 error(TRACE_lex + lev, "%s: [%d] %04d%s%s", op, ex->input->nesting, c, s, t);
238 if (free_t) {
239 free(t);
240 }
241}
242
243/*
244 * trace wrapper for extoken()
245 */
246
247extern int _extoken_fn_(Expr_t*);
248
249int
251{
252 int c;
253
254#define extoken_fn _extoken_fn_
255
256 c = extoken_fn(ex);
257 trace(ex, 0, "ex_lex", c);
258 return c;
259}
260
261#else
262
263#define trace(p,a,b,c) do { } while (0)
264
265#endif
266
267/*
268 * get the next expression char
269 */
270
271static int
273{
274 int c;
275
276 for (;;)
277 {
278 if ((c = ex->input->peek))
279 ex->input->peek = 0;
280 else if (ex->input->pp)
281 {
282 if (!(c = *ex->input->pp++))
283 {
284 ex->input->pp = 0;
285 continue;
286 }
287 }
288 else if (ex->input->fp)
289 {
290 if ((c = getc(ex->input->fp)) == EOF)
291 {
292 if (!expop(ex))
293 continue;
294 else trace(ex, -1, "expop fp FAIL", 0);
295 c = 0;
296 }
297 }
298 else c = 0;
299 if (c == '\n')
300 setcontext(ex);
301 else if (c)
302 putcontext(ex, c);
303 trace(ex, -3, "ex--lex", c);
304 return c;
305 }
306}
307
308/*
309 * get the next expression token
310 */
311
312int
314{
315 int c;
316 char* s;
317 int q;
318 char* e;
319
320 if (ex->eof || ex->errors)
321 return 0;
322 again:
323 for (;;)
324 switch (c = lex(ex))
325 {
326 case 0:
327 goto eof;
328 case '/':
329 switch (q = lex(ex))
330 {
331 case '*':
332 for (;;) switch (lex(ex))
333 {
334 case '\n':
335 if (error_info.line)
337 else error_info.line = 2;
338 continue;
339 case '*':
340 switch (lex(ex))
341 {
342 case 0:
343 goto eof;
344 case '\n':
345 if (error_info.line)
347 else error_info.line = 2;
348 break;
349 case '*':
350 exunlex(ex, '*');
351 break;
352 case '/':
353 goto again;
354 }
355 break;
356 }
357 break;
358 case '/':
359 while ((c = lex(ex)) != '\n')
360 if (!c)
361 goto eof;
362 break;
363 default:
364 goto opeq;
365 }
366 /*FALLTHROUGH*/
367 case '\n':
368 if (error_info.line)
370 else error_info.line = 2;
371 /*FALLTHROUGH*/
372 case ' ':
373 case '\t':
374 case '\r':
375 break;
376 case '(':
377 case '{':
378 case '[':
379 ex->input->nesting++;
380 return ex_lval.op = c;
381 case ')':
382 case '}':
383 case ']':
384 ex->input->nesting--;
385 return ex_lval.op = c;
386 case '+':
387 case '-':
388 if ((q = lex(ex)) == c)
389 return ex_lval.op = c == '+' ? INC : DEC;
390 goto opeq;
391 case '*':
392 case '%':
393 case '^':
394 q = lex(ex);
395 opeq:
396 ex_lval.op = c;
397 if (q == '=')
398 c = '=';
399 else if (q == '%' && c == '%')
400 {
401 goto eof;
402 }
403 else exunlex(ex, q);
404 return c;
405 case '&':
406 case '|':
407 if ((q = lex(ex)) == '=')
408 {
409 ex_lval.op = c;
410 return '=';
411 }
412 if (q == c)
413 c = c == '&' ? AND : OR;
414 else exunlex(ex, q);
415 return ex_lval.op = c;
416 case '<':
417 case '>':
418 if ((q = lex(ex)) == c)
419 {
420 ex_lval.op = c = c == '<' ? LSH : RSH;
421 if ((q = lex(ex)) == '=')
422 c = '=';
423 else exunlex(ex, q);
424 return c;
425 }
426 goto relational;
427 case '=':
428 case '!':
429 q = lex(ex);
430 relational:
431 if (q == '=') switch (c)
432 {
433 case '<':
434 c = LE;
435 break;
436 case '>':
437 c = GE;
438 break;
439 case '=':
440 c = EQ;
441 break;
442 case '!':
443 c = NE;
444 break;
445 default:
446 UNREACHABLE();
447 }
448 else exunlex(ex, q);
449 return ex_lval.op = c;
450 case '#':
451 if (!ex->linewrap) {
452 s = ex->linep - 1;
453 while (s > ex->line && gv_isspace(*(s - 1)))
454 s--;
455 if (s == ex->line)
456 {
457 switch (extoken_fn(ex))
458 {
459 case DYNAMIC:
460 case ID:
461 case NAME:
462 s = ex_lval.id->name;
463 break;
464 default:
465 s = "";
466 break;
467 }
468 if (streq(s, "include"))
469 {
470 if (extoken_fn(ex) != STRING)
471 exerror("#%s: string argument expected", s);
472 else if (!expush(ex, ex_lval.string, 1, NULL))
473 {
474 setcontext(ex);
475 goto again;
476 }
477 }
478 else exerror("unknown directive");
479 }
480 }
481 return ex_lval.op = c;
482 case '\'':
483 case '"':
484 q = c;
485 agxbclear(&ex->tmp);
486 ex->input->nesting++;
487 while ((c = lex(ex)) != q)
488 {
489 if (c == '\\')
490 {
491 agxbputc(&ex->tmp, '\\');
492 c = lex(ex);
493 }
494 if (!c)
495 {
496 exerror("unterminated %c string", q);
497 goto eof;
498 }
499 if (c == '\n')
500 {
501 if (error_info.line)
503 else error_info.line = 2;
504 }
505 agxbputc(&ex->tmp, (char)c);
506 }
507 ex->input->nesting--;
508 s = agxbuse(&ex->tmp);
509 if (q == '"' || (ex->disc->flags & EX_CHARSTRING))
510 {
513 return STRING;
514 }
516 return INTEGER;
517 case '.':
518 if (gv_isdigit(c = lex(ex)))
519 {
520 agxbclear(&ex->tmp);
521 agxbput(&ex->tmp, "0.");
522 goto floating;
523 }
524 exunlex(ex, c);
525 return ex_lval.op = '.';
526 case '0': case '1': case '2': case '3': case '4':
527 case '5': case '6': case '7': case '8': case '9': {
528 agxbclear(&ex->tmp);
529 agxbputc(&ex->tmp, (char)c);
530 q = INTEGER;
531 int b = 0;
532 if ((c = lex(ex)) == 'x' || c == 'X')
533 {
534 b = 16;
535 agxbputc(&ex->tmp, (char)c);
536 for (c = lex(ex); gv_isxdigit(c); c = lex(ex))
537 {
538 agxbputc(&ex->tmp, (char)c);
539 }
540 }
541 else
542 {
543 while (gv_isdigit(c))
544 {
545 agxbputc(&ex->tmp, (char)c);
546 c = lex(ex);
547 }
548 if (c == '#')
549 {
550 agxbputc(&ex->tmp, (char)c);
551 do
552 {
553 agxbputc(&ex->tmp, (char)c);
554 } while (gv_isalnum(c = lex(ex)));
555 }
556 else
557 {
558 if (c == '.')
559 {
560 floating:
561 q = FLOATING;
562 agxbputc(&ex->tmp, (char)c);
563 while (gv_isdigit(c = lex(ex)))
564 agxbputc(&ex->tmp, (char)c);
565 }
566 if (c == 'e' || c == 'E')
567 {
568 q = FLOATING;
569 agxbputc(&ex->tmp, (char)c);
570 if ((c = lex(ex)) == '-' || c == '+')
571 {
572 agxbputc(&ex->tmp, (char)c);
573 c = lex(ex);
574 }
575 while (gv_isdigit(c))
576 {
577 agxbputc(&ex->tmp, (char)c);
578 c = lex(ex);
579 }
580 }
581 }
582 }
583 s = agxbuse(&ex->tmp);
584 if (q == FLOATING)
585 ex_lval.floating = strtod(s, &e);
586 else
587 {
588 if (c == 'u' || c == 'U')
589 {
590 q = UNSIGNED;
591 c = lex(ex);
592 ex_lval.integer = strtoull(s, &e, b);
593 }
594 else
595 ex_lval.integer = strtoll(s, &e, b);
596 }
597 exunlex(ex, c);
598 if (*e || gv_isalpha(c) || c == '_' || c == '$')
599 {
600 exerror("%s: invalid numeric constant", s);
601 goto eof;
602 }
603 return q;
604 }
605 default:
606 if (gv_isalpha(c) || c == '_' || c == '$')
607 {
608 agxbclear(&ex->tmp);
609 agxbputc(&ex->tmp, (char)c);
610 while (gv_isalnum(c = lex(ex)) || c == '_' || c == '$')
611 agxbputc(&ex->tmp, (char)c);
612 exunlex(ex, c);
613 s = agxbuse(&ex->tmp);
614 ex_lval.id = dtmatch(ex->symbols, s);
615 if (!ex_lval.id)
616 {
617 const size_t size = sizeof(Exid_t) + strlen(s) - EX_NAMELEN + 1;
618 ex_lval.id = gv_arena_alloc(&ex->vm, alignof(Exid_t), size);
619 strcpy(ex_lval.id->name, s);
620 ex_lval.id->lex = NAME;
621
622 /*
623 * LABELs are in the parent scope!
624 */
625
626 if (c == ':' && !expr.nolabel && ex->frame && ex->frame->view)
627 dtinsert(ex->frame->view, ex_lval.id);
628 else
630 }
631
632 /*
633 * lexical analyzer state controlled by the grammar
634 */
635
636 switch (ex_lval.id->lex)
637 {
638 case DECLARE:
639 if (ex_lval.id->index == CHARACTER)
640 {
641 /*
642 * `char*' === `string'
643 * the * must immediately follow char
644 */
645
646 if (c == '*')
647 {
648 lex(ex);
650 }
651 }
652 break;
653 case NAME:
654 /*
655 * action labels are disambiguated from ?:
656 * through the expr.nolabel grammar hook
657 * the : must immediately follow labels
658 */
659
660 if (c == ':' && !expr.nolabel)
661 return LABEL;
662 break;
663 case PRAGMA:
664 /*
665 * user specific statement stripped and
666 * passed as string
667 */
668
669 {
670 int b;
671 int n;
672 int pc = 0;
673 int po;
674 int t;
675
676 /*UNDENT...*/
677 agxbclear(&ex->tmp);
678 b = 1;
679 n = 0;
680 po = 0;
681 for (c = t = lex(ex);; c = lex(ex))
682 {
683 switch (c)
684 {
685 case 0:
686 goto eof;
687 case '/':
688 switch (q = lex(ex))
689 {
690 case '*':
691 for (;;)
692 {
693 switch (lex(ex))
694 {
695 case '\n':
696 if (error_info.line)
698 else error_info.line = 2;
699 continue;
700 case '*':
701 switch (lex(ex))
702 {
703 case 0:
704 goto eof;
705 case '\n':
706 if (error_info.line)
708 else error_info.line = 2;
709 continue;
710 case '*':
711 exunlex(ex, '*');
712 continue;
713 case '/':
714 break;
715 default:
716 continue;
717 }
718 break;
719 default: // ignore; keep consuming characters
720 break;
721 }
722 if (!b++)
723 goto eof;
724 agxbputc(&ex->tmp, ' ');
725 break;
726 }
727 break;
728 case '/':
729 while ((c = lex(ex)) != '\n')
730 if (!c)
731 goto eof;
732 if (error_info.line)
734 else error_info.line = 2;
735 b = 1;
736 agxbputc(&ex->tmp, '\n');
737 break;
738 default:
739 b = 0;
740 agxbputc(&ex->tmp, (char)c);
741 agxbputc(&ex->tmp, (char)q);
742 break;
743 }
744 continue;
745 case '\n':
746 if (error_info.line)
748 else error_info.line = 2;
749 b = 1;
750 agxbputc(&ex->tmp, '\n');
751 continue;
752 case ' ':
753 case '\t':
754 if (!b++)
755 goto eof;
756 agxbputc(&ex->tmp, ' ');
757 continue;
758 case '(':
759 case '{':
760 case '[':
761 b = 0;
762 if (!po)
763 {
764 switch (po = c)
765 {
766 case '(':
767 pc = ')';
768 break;
769 case '{':
770 pc = '}';
771 break;
772 case '[':
773 pc = ']';
774 break;
775 default:
776 UNREACHABLE();
777 }
778 n++;
779 }
780 else if (c == po)
781 n++;
782 agxbputc(&ex->tmp, (char)c);
783 continue;
784 case ')':
785 case '}':
786 case ']':
787 b = 0;
788 if (!po)
789 {
790 exunlex(ex, c);
791 break;
792 }
793 agxbputc(&ex->tmp, (char)c);
794 if (c == pc && --n <= 0)
795 {
796 if (t == po)
797 break;
798 po = 0;
799 }
800 continue;
801 case ';':
802 b = 0;
803 if (!n)
804 break;
805 agxbputc(&ex->tmp, (char)c);
806 continue;
807 case '\'':
808 case '"':
809 b = 0;
810 agxbputc(&ex->tmp, (char)c);
811 ex->input->nesting++;
812 q = c;
813 while ((c = lex(ex)) != q)
814 {
815 if (c == '\\')
816 {
817 agxbputc(&ex->tmp, '\\');
818 c = lex(ex);
819 }
820 if (!c)
821 {
822 exerror("unterminated %c string", q);
823 goto eof;
824 }
825 if (c == '\n')
826 {
827 if (error_info.line)
829 else error_info.line = 2;
830 }
831 agxbputc(&ex->tmp, (char)c);
832 }
833 ex->input->nesting--;
834 continue;
835 default:
836 b = 0;
837 agxbputc(&ex->tmp, (char)c);
838 continue;
839 }
840 break;
841 }
842 ex->disc->reff(ex, NULL, ex_lval.id, NULL);
843
844 /*..INDENT*/
845 }
846 goto again;
847 }
848 return ex_lval.id->lex;
849 }
850 return ex_lval.op = c;
851 }
852 eof:
853 ex->eof = 1;
854 return ex_lval.op = ';';
855}
static void agxbclear(agxbuf *xb)
resets pointer to data
Definition agxbuf.h:293
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:306
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:276
void * gv_arena_alloc(arena_t *arena, size_t alignment, size_t size)
Definition arena.c:116
char * gv_arena_strdup(arena_t *arena, const char *s)
Definition arena.c:134
Region-based memory allocator.
int chrtoi(const char *)
Definition chrtoi.c:22
char * fmtesc(const char *as)
Definition fmtesc.c:132
void stresc(char *)
Definition stresc.c:20
#define dtmatch(d, o)
Definition cdt.h:184
#define dtinsert(d, o)
Definition cdt.h:185
Error_info_t error_info
Definition error.c:22
void exerror(const char *format,...)
Definition exerror.c:62
Exstate_t expr
#define setcontext(p)
Definition exlib.h:128
#define id_string
Definition exlib.h:124
#define putcontext(p, c)
Definition exlib.h:127
#define exunlex(p, c)
Definition exlib.h:126
#define UNSIGNED
Definition exparse.h:153
#define ELSE
Definition exparse.h:168
#define CHARACTER
Definition exparse.h:154
#define DYNAMIC
Definition exparse.h:167
#define RSH
Definition exparse.h:222
#define OR
Definition exparse.h:215
#define GE
Definition exparse.h:220
#define DEFAULT
Definition exparse.h:166
#define WHILE
Definition exparse.h:199
#define NE
Definition exparse.h:218
#define SRAND
Definition exparse.h:192
#define SUB
Definition exparse.h:194
#define LSH
Definition exparse.h:221
#define FLOATING
Definition exparse.h:155
#define FOR
Definition exparse.h:170
#define IN_OP
Definition exparse.h:223
#define SUBSTR
Definition exparse.h:195
#define TOKENS
Definition exparse.h:197
#define ITERATOR
Definition exparse.h:174
#define RAND
Definition exparse.h:187
#define PRINT
Definition exparse.h:183
#define SPRINTF
Definition exparse.h:191
EX_STYPE ex_lval
#define CONSTANT
Definition exparse.h:163
#define LE
Definition exparse.h:219
#define GSUB
Definition exparse.h:172
#define PRAGMA
Definition exparse.h:181
#define UNSET
Definition exparse.h:198
#define CONTINUE
Definition exparse.h:164
#define SPLIT
Definition exparse.h:190
#define EQ
Definition exparse.h:217
#define BREAK
Definition exparse.h:160
#define IF
Definition exparse.h:176
#define SWITCH
Definition exparse.h:196
#define AND
Definition exparse.h:216
#define DECLARE
Definition exparse.h:165
#define EXIT
Definition exparse.h:169
#define PRINTF
Definition exparse.h:184
#define QUERY
Definition exparse.h:186
#define CASE
Definition exparse.h:162
#define INC
Definition exparse.h:225
#define DEC
Definition exparse.h:226
$1 lex
Definition exparse.y:206
#define EX_NAMELEN
Definition expr.h:51
struct Exid_s Exid_t
#define EX_CHARSTRING
Definition expr.h:44
int expop(Expr_t *)
int expush(Expr_t *, const char *, int, FILE *)
int extoken_fn(Expr_t *ex)
Definition extoken.c:313
#define trace(p, a, b, c)
Definition extoken.c:263
void free(void *)
#define LABEL
Definition gmlparse.h:115
#define NAME
Definition gmlparse.h:135
#define ID
Definition gmlparse.h:134
#define STRING
Definition gmlparse.h:133
#define INTEGER
Definition gmlparse.h:131
node NULL
Definition grammar.y:181
replacements for ctype.h functions
static bool gv_isxdigit(int c)
Definition gv_ctype.h:71
static bool gv_isalnum(int c)
Definition gv_ctype.h:43
static bool gv_isdigit(int c)
Definition gv_ctype.h:41
static bool gv_isalpha(int c)
Definition gv_ctype.h:29
static bool gv_isspace(int c)
Definition gv_ctype.h:55
agxbput(xb, staging)
table Syntax error
Definition htmlparse.y:294
static bool streq(const char *a, const char *b)
are a and b equal?
Definition streq.h:11
#define RETURN(v)
Definition strmatch.c:144
int line
Definition error.h:27
Definition expr.h:91
long lex
Definition expr.h:93
char name[EX_NAMELEN]
Definition expr.h:99
long index
Definition expr.h:94
Definition expr.h:218
arena_t vm
Definition expr.h:222
Dt_t * symbols
Definition expr.h:220
int nolabel
Definition exlib.h:155
struct Exid_s * id
Definition exparse.h:239
long long integer
Definition exparse.h:240
double floating
Definition exparse.h:237
char * string
Definition exparse.h:242
int op
Definition exparse.h:241
Definition grammar.c:90
#define UNREACHABLE()
Definition unreachable.h:30