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