Graphviz 16.1.1~dev.20261104.1411
Loading...
Searching...
No Matches
compile.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 v2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/org/documents/epl-2.0/EPL-2.0.html
7 *
8 * Contributors: Details at https://graphviz.org
9 *************************************************************************/
10
11/*
12 * Compile-time and run-time interface between gvpr and libexpr
13 */
14
15#include "config.h"
16#include <assert.h>
17#include <ast/error.h>
18#include <cgraph/cgraph.h>
19#include <gvpr/actions.h>
20#include <gvpr/compile.h>
21#include <inttypes.h>
22#include <limits.h>
23#include <math.h>
24#include <stdbool.h>
25#include <stddef.h>
26#include <stdint.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <unistd.h>
31#include <util/agxbuf.h>
32#include <util/alloc.h>
33#include <util/list.h>
34#include <util/prisize_t.h>
35#include <util/startswith.h>
36#include <util/strview.h>
37#include <util/unreachable.h>
38
39static int isedge(Agobj_t *obj) {
40 return AGTYPE(obj) == AGOUTEDGE || AGTYPE(obj) == AGINEDGE;
41}
42
43#define MIN(a, b) ((a) < (b) ? (a) : (b))
44#define MAX(a, b) ((a) > (b) ? (a) : (b))
45
46#include <gvpr/gdefs.h>
47
48#include <ctype.h>
49#include <gvpr/trie.c>
50
51static void *int2ptr(long long i) { return (void *)(intptr_t)i; }
52
53static long long ptr2int(const void *p) { return (long long)(intptr_t)p; }
54
55/* Return name of object.
56 * Assumes obj != NULL
57 */
58static char *nameOf(Expr_t *ex, Agobj_t *obj, agxbuf *tmps) {
59 char *s;
60 char *key;
61 Agedge_t *e;
62
63 switch (AGTYPE(obj)) {
64 case AGNODE:
65 case AGRAPH:
66 s = agnameof(obj);
67 break;
68 default: /* edge */
69 e = (Agedge_t *)obj;
70 key = agnameof(AGMKOUT(e));
71 agxbput(tmps, agnameof(AGTAIL(e)));
72 if (agisdirected(agraphof(e)))
73 agxbput(tmps, "->");
74 else
75 agxbput(tmps, "--");
76 agxbput(tmps, agnameof(AGHEAD(e)));
77 if (key && *key) {
78 agxbputc(tmps, '[');
79 agxbput(tmps, key);
80 agxbputc(tmps, ']');
81 }
82 s = exstring(ex, agxbuse(tmps));
83 break;
84 }
85 return s;
86}
87
88/* If string as form "x,y,u,v" where is all are numeric,
89 * return "x,y" or "u,v", depending on getll, else return ""
90 */
91static char *bbOf(Expr_t *pgm, char *pt, bool getll) {
92 double x, y, u, v;
93
94 if (sscanf(pt, "%lf,%lf,%lf,%lf", &x, &y, &u, &v) == 4) {
95 char *p = strchr(pt, ',');
96 p = strchr(p + 1, ',');
97 if (getll) {
98 size_t len = (size_t)(p - pt);
99 char *const s = exstralloc(pgm, len + 1);
100 memcpy(s, pt, len);
101 s[len] = '\0';
102 return s;
103 }
104 return exstring(pgm, p + 1);
105 }
106 return "";
107}
108
109/* If string as form "x,y" where is x and y are numeric,
110 * return "x" or "y", depending on getx, else return ""
111 */
112static char *xyOf(Expr_t *pgm, char *pt, bool getx) {
113 double x, y;
114
115 if (sscanf(pt, "%lf,%lf", &x, &y) == 2) {
116 char *const p = strchr(pt, ',');
117 if (getx) {
118 size_t len = (size_t)(p - pt);
119 char *const v = exstralloc(pgm, len + 1);
120 memcpy(v, pt, len);
121 v[len] = '\0';
122 return v;
123 }
124 return exstring(pgm, p + 1);
125 }
126 return "";
127}
128
129/* Get pos data from node; store x or y into v if successful and return 0;
130 * else return -1
131 */
132static int posOf(Agnode_t *np, int idx, double *v) {
133 static Agraph_t *root;
134 static Agsym_t *pos;
135 Agraph_t *nroot = agroot(np);
136 char *ps;
137 double p[2];
138
139 if (root != nroot) {
140 root = nroot;
141 pos = agattr_text(root, AGNODE, "pos", 0);
142 }
143 if (!pos)
144 return -1;
145 ps = agxget(np, pos);
146 if (sscanf(ps, "%lf,%lf", &p[0], &p[1]) == 2) {
147 *v = p[idx];
148 return 0;
149 } else
150 return -1;
151}
152
153/* Convert string argument to graph to type of graph desired.
154 * u => undirected
155 * d => directed
156 * s => strict
157 * n => non-strict
158 * Case-insensitive
159 * By default, the graph is directed, non-strict.
160 */
161static Agdesc_t xargs(char *args) {
162 Agdesc_t desc = Agdirected;
163 char c;
164
165 while ((c = *args++)) {
166 switch (c) {
167 case 'u':
168 case 'U':
169 desc.directed = false;
170 break;
171 case 'd':
172 case 'D':
173 desc.directed = true;
174 break;
175 case 's':
176 case 'S':
177 desc.strict = true;
178 break;
179 case 'n':
180 case 'N':
181 desc.directed = false;
182 break;
183 default:
184 error(ERROR_WARNING, "unknown graph descriptor '%c' : ignored", c);
185 break;
186 }
187 }
188 return desc;
189}
190
191/* Recreate string representation of expression involving
192 * a reference and a symbol.
193 */
194static char *deparse(Expr_t *ex, Exnode_t *n, agxbuf *xb) {
195 exdump(ex, n, xb);
196 return agxbuse(xb);
197}
198
199/* Evaluate reference to derive desired graph object.
200 * A reference is either DI* or II*
201 * The parameter objp is the current object.
202 * Assume ref is type-correct.
203 */
204static Agobj_t *deref(Expr_t *pgm, Exnode_t *x, Exref_t *ref, Agobj_t *objp,
205 Gpr_t *state) {
206 void *ptr;
207
208 if (ref == 0)
209 return objp;
210 else if (ref->symbol->lex == DYNAMIC) {
211 ptr = int2ptr(
213 if (!ptr) {
214 agxbuf xb = {0};
215 exerror("null reference %s in expression %s.%s", ref->symbol->name,
216 ref->symbol->name, deparse(pgm, x, &xb));
217 agxbfree(&xb);
218 return ptr;
219 } else
220 return deref(pgm, x, ref->next, ptr, state);
221 } else
222 switch (ref->symbol->index) { /* sym->lex == ID */
223 case V_outgraph:
224 return deref(pgm, x, ref->next, (Agobj_t *)state->outgraph, state);
225 case V_this:
226 return deref(pgm, x, ref->next, state->curobj, state);
227 case V_thisg:
228 return deref(pgm, x, ref->next, (Agobj_t *)state->curgraph, state);
229 case V_nextg:
230 return deref(pgm, x, ref->next, (Agobj_t *)state->nextgraph, state);
231 case V_targt:
232 return deref(pgm, x, ref->next, (Agobj_t *)state->target, state);
233 case V_travedge:
234 return deref(pgm, x, ref->next, (Agobj_t *)state->tvedge, state);
235 case V_travroot:
236 return deref(pgm, x, ref->next, (Agobj_t *)state->tvroot, state);
237 case V_travnext:
238 return deref(pgm, x, ref->next, (Agobj_t *)state->tvnext, state);
239 case M_head:
240 if (!objp && !(objp = state->curobj)) {
241 exerror("Current object $ not defined");
242 return 0;
243 }
244 if (isedge(objp))
245 return deref(pgm, x, ref->next, (Agobj_t *)AGHEAD((Agedge_t *)objp),
246 state);
247 else
248 exerror("head of non-edge");
249 break;
250 case M_tail:
251 if (!objp && !(objp = state->curobj)) {
252 exerror("Current object $ not defined");
253 return 0;
254 }
255 if (isedge(objp))
256 return deref(pgm, x, ref->next, (Agobj_t *)AGTAIL((Agedge_t *)objp),
257 state);
258 else
259 exerror("tail of non-edge %p", objp);
260 break;
261 default:
262 exerror("%s : illegal reference", ref->symbol->name);
263 break;
264 }
265 return 0;
266}
267
268/* Check that attribute is not a read-only, pseudo-attribute.
269 * fatal if not OK.
270 */
271static void assignable(Agobj_t *objp, unsigned char *name) {
272 unsigned int ch;
273 int rv;
274 unsigned char *p = name;
275
276 TFA_Init();
277 while (TFA_State >= 0 && (ch = *p)) {
278 TFA_Advance(ch > 127 ? 127 : (char)ch);
279 p++;
280 }
281 rv = TFA_Definition();
282 if (rv < 0)
283 return;
284
285 switch (AGTYPE(objp)) {
286 case AGRAPH:
287 if (rv & Y(G))
288 exerror("Cannot assign to pseudo-graph attribute %s", name);
289 break;
290 case AGNODE:
291 if (rv & Y(V))
292 exerror("Cannot assign to pseudo-node attribute %s", name);
293 break;
294 default: /* edge */
295 if (rv & Y(E))
296 exerror("Cannot assign to pseudo-edge attribute %s", name);
297 break;
298 }
299}
300
301/* Set object's attribute name to val.
302 * Initialize attribute if necessary.
303 */
304static int setattr(Agobj_t *objp, char *name, char *val) {
305 Agsym_t *gsym = agattrsym(objp, name);
306 if (!gsym) {
307 gsym = agattr_text(agroot(agraphof(objp)), AGTYPE(objp), name, "");
308 }
309 return agxset(objp, gsym, val);
310}
311
312static char *kindToStr(int kind) {
313 switch (kind) {
314 case AGRAPH:
315 return "graph";
316 case AGNODE:
317 return "node";
318 default:
319 break;
320 }
321 return "edge";
322}
323
325static char *kindOf(Agobj_t *objp) { return kindToStr(agobjkind(objp)); }
326
327/* Apply symbol to get field value of objp
328 * Assume objp != NULL
329 */
330static int lookup(Expr_t *pgm, Agobj_t *objp, Exid_t *sym, Extype_t *v) {
331 if (sym->lex == ID) {
332 switch (sym->index) {
333 case M_head:
334 if (isedge(objp))
335 v->integer = ptr2int(AGHEAD((Agedge_t *)objp));
336 else {
337 error(ERROR_WARNING, "head of non-edge");
338 return -1;
339 }
340 break;
341 case M_tail:
342 if (isedge(objp))
343 v->integer = ptr2int(AGTAIL((Agedge_t *)objp));
344 else {
345 error(ERROR_WARNING, "tail of non-edge");
346 return -1;
347 }
348 break;
349 case M_name: {
350 agxbuf tmp = {0};
351 v->string = nameOf(pgm, objp, &tmp);
352 agxbfree(&tmp);
353 break;
354 }
355 case M_indegree:
356 if (AGTYPE(objp) == AGNODE)
357 v->integer = agdegree(agroot(objp), (Agnode_t *)objp, 1, 0);
358 else {
359 exerror("indegree of non-node");
360 return -1;
361 }
362 break;
363 case M_outdegree:
364 if (AGTYPE(objp) == AGNODE)
365 v->integer = agdegree(agroot(objp), (Agnode_t *)objp, 0, 1);
366 else {
367 exerror("outdegree of non-node");
368 return -1;
369 }
370 break;
371 case M_degree:
372 if (AGTYPE(objp) == AGNODE)
373 v->integer = agdegree(agroot(objp), (Agnode_t *)objp, 1, 1);
374 else {
375 exerror("degree of non-node");
376 return -1;
377 }
378 break;
379 case M_X:
380 if (AGTYPE(objp) == AGNODE) {
381 if (posOf((Agnode_t *)objp, 0, &v->floating))
382 exerror("no x coordinate for node \"%s\"", agnameof(objp));
383 } else {
384 exerror("x coordinate of non-node");
385 return -1;
386 }
387 break;
388 case M_Y:
389 if (AGTYPE(objp) == AGNODE) {
390 if (posOf((Agnode_t *)objp, 1, &v->floating))
391 exerror("no y coordinate for node \"%s\"", agnameof(objp));
392 } else {
393 exerror("x coordinate of non-node");
394 return -1;
395 }
396 break;
397 case M_parent:
398 if (AGTYPE(objp) == AGRAPH)
399 v->integer = ptr2int(agparent((Agraph_t *)objp));
400 else {
401 exerror("parent of non-graph");
402 return -1;
403 }
404 break;
405 case M_root:
406 v->integer = ptr2int(agroot(agraphof(objp)));
407 break;
408 case M_n_edges:
409 if (AGTYPE(objp) == AGRAPH)
410 v->integer = agnedges((Agraph_t *)objp);
411 else {
412 exerror("n_edges of non-graph");
413 return -1;
414 }
415 break;
416 case M_n_nodes:
417 if (AGTYPE(objp) == AGRAPH)
418 v->integer = agnnodes((Agraph_t *)objp);
419 else {
420 exerror("n_nodes of non-graph");
421 return -1;
422 }
423 break;
424 case M_directed:
425 if (AGTYPE(objp) == AGRAPH)
426 v->integer = agisdirected((Agraph_t *)objp);
427 else {
428 exerror("directed of non-graph");
429 return -1;
430 }
431 break;
432 case M_strict:
433 if (AGTYPE(objp) == AGRAPH)
434 v->integer = agisstrict((Agraph_t *)objp);
435 else {
436 exerror("strict of non-graph");
437 return -1;
438 }
439 break;
440 default:
441 error(ERROR_WARNING, "%s : illegal reference", sym->name);
442 return -1;
443 }
444 } else {
445 Agsym_t *gsym = agattrsym(objp, sym->name);
446 if (!gsym) {
447 gsym = agattr_text(agroot(agraphof(objp)), AGTYPE(objp), sym->name, "");
448 agxbuf tmp = {0};
450 "Using value of uninitialized %s attribute \"%s\" of \"%s\"",
451 kindOf(objp), sym->name, nameOf(pgm, objp, &tmp));
452 agxbfree(&tmp);
453 }
454 v->string = agxget(objp, gsym);
455 }
456
457 return 0;
458}
459
464static int getArg(long long n, Gpr_t *state, strview_t *out) {
465 assert(out != NULL);
466 if (n < 0 || (unsigned long long)n >= LIST_SIZE(&state->args)) {
467 exerror("program references ARGV[%lld] - undefined", n);
468 return -1;
469 }
470 *out = LIST_GET(&state->args, (size_t)n);
471 return 0;
472}
473
474static int setDfltAttr(Agraph_t *gp, char *k, char *name, char *value) {
475 int kind;
476
477 switch (*k) {
478 case 'G':
479 kind = AGRAPH;
480 break;
481 case 'E':
482 kind = AGEDGE;
483 break;
484 case 'N':
485 kind = AGNODE;
486 break;
487 default:
488 error(ERROR_WARNING, "Unknown kind \"%s\" passed to setDflt()", k);
489 return 1;
490 }
491
492 // make the implicit default on the root graph explicit in order to avoid the
493 // next `agattr_text` thinking its assignment should be hoisted to the root
494 {
495 Agraph_t *const root = agroot(gp);
496 if (agattr_text(root, kind, name, NULL) == NULL) {
497 agattr_text(root, kind, name, "");
498 }
499 }
500
501 agattr_text(gp, kind, name, value);
502 return 0;
503}
504
505// map string to object kind
506static int toKind(char *k, char *fn) {
507 switch (*k) {
508 case 'G':
509 return AGRAPH;
510 case 'E':
511 return AGEDGE;
512 case 'N':
513 return AGNODE;
514 default:
515 exerror("Unknown kind \"%s\" passed to %s()", k, fn);
516 break;
517 }
518 return 0;
519}
520
521static char *nxtAttr(Agraph_t *gp, char *k, char *name) {
522 char *fn = name ? "nxtAttr" : "fstAttr";
523 int kind = toKind(k, fn);
524 Agsym_t *sym;
525
526 if (name) {
527 sym = agattr_text(gp, kind, name, 0);
528 if (!sym) {
529 exerror("Third argument \"%s\" in nxtAttr() must be the name of an "
530 "existing attribute",
531 name);
532 return "";
533 }
534
535 } else
536 sym = NULL;
537
538 sym = agnxtattr(gp, kind, sym);
539 if (sym)
540 return sym->name;
541 else
542 return "";
543}
544
545static char *getDfltAttr(Agraph_t *gp, char *k, char *name) {
546 int kind = toKind(k, "getDflt");
547 Agsym_t *sym = agattr_text(gp, kind, name, 0);
548 if (!sym) {
549 sym = agattr_text(gp, kind, name, "");
550 error(ERROR_WARNING, "Uninitialized %s attribute \"%s\" in %s",
551 kindToStr(kind), name, "getDflt");
552 }
553 return sym->defval;
554}
555
556// return value associated with gpr identifier
557static Extype_t getval(Expr_t *pgm, Exnode_t *node, Exid_t *sym, Exref_t *ref,
558 void *env, int elt, Exdisc_t *disc) {
559 Extype_t v;
560 Gpr_t *state;
561 Extype_t *args;
562 Agobj_t *objp;
563 Agobj_t *objp1;
564 char *key;
565 Agraph_t *gp;
566 Agnode_t *np;
567 Agnode_t *hp;
568 Agedge_t *ep;
569 gvprbinding *bp;
570
571 assert(sym->lex != CONSTANT);
572 if (elt == EX_CALL) {
573 args = env;
574 state = disc->user;
575 switch (sym->index) {
576 case F_graph:
577 gp = openG(args[0].string, xargs(args[1].string));
578 if (gp != NULL) {
579 LIST_APPEND(&state->open_graphs, gp);
580 }
581 v.integer = ptr2int(gp);
582 break;
583 case F_subg:
584 gp = int2ptr(args[0].integer);
585 if (gp) {
586 gp = openSubg(gp, args[1].string);
587 v.integer = ptr2int(gp);
588 } else {
589 error(ERROR_WARNING, "NULL graph passed to subg()");
590 v.integer = 0;
591 }
592 break;
593 case F_issubg:
594 gp = int2ptr(args[0].integer);
595 if (gp) {
596 v.integer = ptr2int(agsubg(gp, args[1].string, 0));
597 } else {
598 error(ERROR_WARNING, "NULL graph passed to isSubg()");
599 v.integer = 0;
600 }
601 break;
602 case F_fstsubg:
603 gp = int2ptr(args[0].integer);
604 if (gp) {
605 gp = agfstsubg(gp);
606 v.integer = ptr2int(gp);
607 } else {
608 error(ERROR_WARNING, "NULL graph passed to fstsubg()");
609 v.integer = 0;
610 }
611 break;
612 case F_nxtsubg:
613 gp = int2ptr(args[0].integer);
614 if (gp) {
615 gp = agnxtsubg(gp);
616 v.integer = ptr2int(gp);
617 } else {
618 error(ERROR_WARNING, "NULL graph passed to nxtsubg()");
619 v.integer = 0;
620 }
621 break;
622 case F_node:
623 gp = int2ptr(args[0].integer);
624 if (gp) {
625 np = openNode(gp, args[1].string);
626 v.integer = ptr2int(np);
627 } else {
628 error(ERROR_WARNING, "NULL graph passed to node()");
629 v.integer = 0;
630 }
631 break;
632 case F_addnode:
633 gp = int2ptr(args[0].integer);
634 np = int2ptr(args[1].integer);
635 if (!gp) {
636 error(ERROR_WARNING, "NULL graph passed to addNode()");
637 v.integer = 0;
638 } else if (!np) {
639 error(ERROR_WARNING, "NULL node passed to addNode()");
640 v.integer = 0;
641 } else
642 v.integer = ptr2int(addNode(gp, np, 1));
643 break;
644 case F_fstnode:
645 gp = int2ptr(args[0].integer);
646 if (gp) {
647 np = agfstnode(gp);
648 v.integer = ptr2int(np);
649 } else {
650 error(ERROR_WARNING, "NULL graph passed to fstnode()");
651 v.integer = 0;
652 }
653 break;
654 case F_nxtnode:
655 np = int2ptr(args[0].integer);
656 if (np) {
657 np = agnxtnode(agroot(np), np);
658 v.integer = ptr2int(np);
659 } else {
660 error(ERROR_WARNING, "NULL node passed to nxtnode()");
661 v.integer = 0;
662 }
663 break;
664 case F_nxtnodesg:
665 gp = int2ptr(args[0].integer);
666 np = int2ptr(args[1].integer);
667 if (!gp)
668 gp = agroot(np);
669 if (np) {
670 np = agnxtnode(gp, np);
671 v.integer = ptr2int(np);
672 } else {
673 error(ERROR_WARNING, "NULL node passed to nxtnode_sg()");
674 v.integer = 0;
675 }
676 break;
677 case F_isnode:
678 gp = int2ptr(args[0].integer);
679 if (gp) {
680 v.integer = ptr2int(agnode(gp, args[1].string, 0));
681 } else {
682 error(ERROR_WARNING, "NULL graph passed to isNode()");
683 v.integer = 0;
684 }
685 break;
686 case F_issubnode:
687 gp = int2ptr(args[0].integer);
688 np = int2ptr(args[1].integer);
689 if (!gp)
690 gp = agroot(np);
691 if (np) {
692 v.integer = ptr2int(addNode(gp, np, 0));
693 } else {
694 error(ERROR_WARNING, "NULL node passed to isSubnode()");
695 v.integer = 0;
696 }
697 break;
698 case F_indegree:
699 gp = int2ptr(args[0].integer);
700 np = int2ptr(args[1].integer);
701 if (!gp)
702 gp = agroot(np);
703 if (np) {
704 v.integer = agdegree(gp, np, 1, 0);
705 } else {
706 error(ERROR_WARNING, "NULL node passed to indegreeOf()");
707 v.integer = 0;
708 }
709 break;
710 case F_outdegree:
711 gp = int2ptr(args[0].integer);
712 np = int2ptr(args[1].integer);
713 if (!gp)
714 gp = agroot(np);
715 if (np) {
716 v.integer = agdegree(gp, np, 0, 1);
717 } else {
718 error(ERROR_WARNING, "NULL node passed to outdegreeOf()");
719 v.integer = 0;
720 }
721 break;
722 case F_degree:
723 gp = int2ptr(args[0].integer);
724 np = int2ptr(args[1].integer);
725 if (!gp)
726 gp = agroot(np);
727 if (np) {
728 v.integer = agdegree(gp, np, 1, 1);
729 } else {
730 error(ERROR_WARNING, "NULL node passed to degreeOf()");
731 v.integer = 0;
732 }
733 break;
734 case F_isin:
735 gp = int2ptr(args[0].integer);
736 objp = int2ptr(args[1].integer);
737 if (!gp) {
738 error(ERROR_WARNING, "NULL graph passed to isIn()");
739 v.integer = 0;
740 } else if (!objp) {
741 error(ERROR_WARNING, "NULL object passed to isIn()");
742 v.integer = 0;
743 } else
744 v.integer = agcontains(gp, objp);
745 break;
746 case F_compof:
747 gp = int2ptr(args[0].integer);
748 np = int2ptr(args[1].integer);
749 if (!gp) {
750 error(ERROR_WARNING, "NULL graph passed to compOf()");
751 v.integer = 0;
752 } else if (!np) {
753 error(ERROR_WARNING, "NULL node passed to compOf()");
754 v.integer = 0;
755 } else
756 v.integer = ptr2int(compOf(gp, np));
757 break;
758 case F_kindof:
759 objp = int2ptr(args[0].integer);
760 if (!objp) {
761 exerror("NULL object passed to kindOf()");
762 v.string = 0;
763 } else
764 switch (AGTYPE(objp)) {
765 case AGRAPH:
766 v.string = "G";
767 break;
768 case AGNODE:
769 v.string = "N";
770 break;
771 case AGINEDGE:
772 case AGOUTEDGE:
773 v.string = "E";
774 break;
775 default:
776 UNREACHABLE();
777 }
778 break;
779 case F_edge:
780 key = args[2].string;
781 if (*key == '\0')
782 key = 0;
783 np = int2ptr(args[0].integer);
784 hp = int2ptr(args[1].integer);
785 if (!np) {
786 error(ERROR_WARNING, "NULL tail node passed to edge()");
787 v.integer = 0;
788 } else if (!hp) {
789 error(ERROR_WARNING, "NULL head node passed to edge()");
790 v.integer = 0;
791 } else {
792 ep = openEdge(0, np, hp, key);
793 v.integer = ptr2int(ep);
794 }
795 break;
796 case F_edgesg:
797 key = args[3].string;
798 if (*key == '\0')
799 key = 0;
800 gp = int2ptr(args[0].integer);
801 np = int2ptr(args[1].integer);
802 hp = int2ptr(args[2].integer);
803 if (!np) {
804 error(ERROR_WARNING, "NULL tail node passed to edge_sg()");
805 v.integer = 0;
806 } else if (!hp) {
807 error(ERROR_WARNING, "NULL head node passed to edge_sg()");
808 v.integer = 0;
809 } else {
810 ep = openEdge(gp, np, hp, key);
811 v.integer = ptr2int(ep);
812 }
813 break;
814 case F_addedge:
815 gp = int2ptr(args[0].integer);
816 ep = int2ptr(args[1].integer);
817 if (!gp) {
818 error(ERROR_WARNING, "NULL graph passed to addEdge()");
819 v.integer = 0;
820 } else if (!ep) {
821 error(ERROR_WARNING, "NULL edge passed to addEdge()");
822 v.integer = 0;
823 } else
824 v.integer = ptr2int(addEdge(gp, ep, 1));
825 break;
826 case F_opp:
827 ep = int2ptr(args[0].integer);
828 np = int2ptr(args[1].integer);
829 if (!ep) {
830 error(ERROR_WARNING, "NULL edge passed to opp()");
831 v.integer = 0;
832 } else if (!np) {
833 error(ERROR_WARNING, "NULL node passed to opp()");
834 v.integer = 0;
835 } else {
836 if (aghead(ep) == np)
837 np = agtail(ep);
838 else
839 np = aghead(ep);
840 v.integer = ptr2int(np);
841 }
842 break;
843 case F_isedge:
844 key = args[2].string;
845 if (*key == '\0')
846 key = 0;
847 np = int2ptr(args[0].integer);
848 hp = int2ptr(args[1].integer);
849 if (!np) {
850 error(ERROR_WARNING, "NULL tail node passed to isEdge()");
851 v.integer = 0;
852 } else if (!hp) {
853 error(ERROR_WARNING, "NULL head node passed to isEdge()");
854 v.integer = 0;
855 } else
856 v.integer = ptr2int(isEdge(agroot(np), np, hp, key));
857 break;
858 case F_isedgesg:
859 key = args[3].string;
860 if (*key == '\0')
861 key = 0;
862 gp = int2ptr(args[0].integer);
863 np = int2ptr(args[1].integer);
864 hp = int2ptr(args[2].integer);
865 if (!gp)
866 gp = agroot(np);
867 if (!np) {
868 error(ERROR_WARNING, "NULL tail node passed to isEdge_sg()");
869 v.integer = 0;
870 } else if (!hp) {
871 error(ERROR_WARNING, "NULL head node passed to isEdge_sg()");
872 v.integer = 0;
873 } else
874 v.integer = ptr2int(isEdge(gp, np, hp, key));
875 break;
876 case F_issubedge:
877 gp = int2ptr(args[0].integer);
878 ep = int2ptr(args[1].integer);
879 if (!gp)
880 gp = agroot(ep);
881 if (ep) {
882 v.integer = ptr2int(addEdge(gp, ep, 0));
883 } else {
884 error(ERROR_WARNING, "NULL edge passed to isSubedge()");
885 v.integer = 0;
886 }
887 break;
888 case F_fstout:
889 np = int2ptr(args[0].integer);
890 if (np) {
891 ep = agfstout(agroot(np), np);
892 v.integer = ptr2int(ep);
893 } else {
894 error(ERROR_WARNING, "NULL node passed to fstout()");
895 v.integer = 0;
896 }
897 break;
898 case F_fstoutsg:
899 gp = int2ptr(args[0].integer);
900 np = int2ptr(args[1].integer);
901 if (!gp)
902 gp = agroot(np);
903 if (np) {
904 ep = agfstout(gp, np);
905 v.integer = ptr2int(ep);
906 } else {
907 error(ERROR_WARNING, "NULL node passed to fstout_sg()");
908 v.integer = 0;
909 }
910 break;
911 case F_nxtout:
912 ep = int2ptr(args[0].integer);
913 if (ep) {
914 ep = agnxtout(agroot(ep), ep);
915 v.integer = ptr2int(ep);
916 } else {
917 error(ERROR_WARNING, "NULL edge passed to nxtout()");
918 v.integer = 0;
919 }
920 break;
921 case F_nxtoutsg:
922 gp = int2ptr(args[0].integer);
923 ep = int2ptr(args[1].integer);
924 if (!gp)
925 gp = agroot(ep);
926 if (ep) {
927 ep = agnxtout(gp, ep);
928 v.integer = ptr2int(ep);
929 } else {
930 error(ERROR_WARNING, "NULL edge passed to nxtout_sg()");
931 v.integer = 0;
932 }
933 break;
934 case F_fstin:
935 np = int2ptr(args[0].integer);
936 if (np) {
937 ep = agfstin(agroot(np), np);
938 v.integer = ptr2int(ep);
939 } else {
940 error(ERROR_WARNING, "NULL node passed to fstin()");
941 v.integer = 0;
942 }
943 break;
944 case F_fstinsg:
945 gp = int2ptr(args[0].integer);
946 np = int2ptr(args[1].integer);
947 if (!gp)
948 gp = agroot(np);
949 if (np) {
950 ep = agfstin(gp, np);
951 v.integer = ptr2int(ep);
952 } else {
953 error(ERROR_WARNING, "NULL node passed to fstin_sg()");
954 v.integer = 0;
955 }
956 break;
957 case F_nxtin:
958 ep = int2ptr(args[0].integer);
959 if (ep) {
960 ep = agnxtin(agroot(ep), ep);
961 v.integer = ptr2int(ep);
962 } else {
963 error(ERROR_WARNING, "NULL edge passed to nxtin()");
964 v.integer = 0;
965 }
966 break;
967 case F_nxtinsg:
968 gp = int2ptr(args[0].integer);
969 ep = int2ptr(args[1].integer);
970 if (!gp)
971 gp = agroot(ep);
972 if (ep) {
973 ep = agnxtin(gp, ep);
974 v.integer = ptr2int(ep);
975 } else {
976 error(ERROR_WARNING, "NULL edge passed to nxtin_sg()");
977 v.integer = 0;
978 }
979 break;
980 case F_fstedge:
981 np = int2ptr(args[0].integer);
982 if (np) {
983 ep = agfstedge(agroot(np), np);
984 v.integer = ptr2int(ep);
985 } else {
986 error(ERROR_WARNING, "NULL node passed to fstedge()");
987 v.integer = 0;
988 }
989 break;
990 case F_fstedgesg:
991 gp = int2ptr(args[0].integer);
992 np = int2ptr(args[1].integer);
993 if (!gp)
994 gp = agroot(np);
995 if (np) {
996 ep = agfstedge(gp, np);
997 v.integer = ptr2int(ep);
998 } else {
999 error(ERROR_WARNING, "NULL node passed to fstedge_sg()");
1000 v.integer = 0;
1001 }
1002 break;
1003 case F_nxtedge:
1004 ep = int2ptr(args[0].integer);
1005 np = int2ptr(args[1].integer);
1006 if (!ep) {
1007 error(ERROR_WARNING, "NULL edge passed to nxtedge()");
1008 v.integer = 0;
1009 } else if (!np) {
1010 error(ERROR_WARNING, "NULL node passed to nxtedge()");
1011 v.integer = 0;
1012 } else {
1013 ep = agnxtedge(agroot(np), ep, np);
1014 v.integer = ptr2int(ep);
1015 }
1016 break;
1017 case F_nxtedgesg:
1018 gp = int2ptr(args[0].integer);
1019 ep = int2ptr(args[1].integer);
1020 np = int2ptr(args[2].integer);
1021 if (!gp)
1022 gp = agroot(np);
1023 if (!ep) {
1024 error(ERROR_WARNING, "NULL edge passed to nxtedge_sg()");
1025 v.integer = 0;
1026 } else if (!np) {
1027 error(ERROR_WARNING, "NULL node passed to nxtedge_sg()");
1028 v.integer = 0;
1029 } else {
1030 ep = agnxtedge(gp, ep, np);
1031 v.integer = ptr2int(ep);
1032 }
1033 break;
1034 case F_copy:
1035 gp = int2ptr(args[0].integer);
1036 objp = int2ptr(args[1].integer);
1037 if (!objp) {
1038 error(ERROR_WARNING, "NULL object passed to clone()");
1039 v.integer = 0;
1040 } else
1041 v.integer = ptr2int(copy(gp, objp));
1042 break;
1043 case F_clone:
1044 gp = int2ptr(args[0].integer);
1045 objp = int2ptr(args[1].integer);
1046 if (!objp) {
1047 error(ERROR_WARNING, "NULL object passed to clone()");
1048 v.integer = 0;
1049 } else
1050 v.integer = ptr2int(cloneO(gp, objp));
1051 break;
1052 case F_cloneG:
1053 gp = int2ptr(args[0].integer);
1054 if (gp) {
1055 gp = cloneG(gp, args[1].string);
1056 if (gp != NULL) {
1057 LIST_APPEND(&state->open_graphs, gp);
1058 }
1059 v.integer = ptr2int(gp);
1060 } else {
1061 error(ERROR_WARNING, "NULL graph passed to cloneG()");
1062 v.integer = 0;
1063 }
1064 break;
1065 case F_copya:
1066 objp = int2ptr(args[0].integer);
1067 objp1 = int2ptr(args[1].integer);
1068 if (!(objp && objp1)) {
1069 error(ERROR_WARNING, "NULL object passed to copyA()");
1070 v.integer = 0;
1071 } else
1072 v.integer = copyAttr(objp, objp1);
1073 break;
1074 case F_rename:
1075 objp = int2ptr(args[0].integer);
1076 if (!objp) {
1077 error(ERROR_WARNING, "NULL object passed to rename()");
1078 v.integer = -1;
1079 } else
1080 v.integer = agrelabel_node((Agnode_t *)objp, args[1].string);
1081 break;
1082 case F_induce:
1083 gp = int2ptr(args[0].integer);
1084 if (!gp) {
1085 error(ERROR_WARNING, "NULL graph passed to induce()");
1086 v.integer = 1;
1087 } else {
1088 (void)graphviz_node_induce(gp, NULL);
1089 v.integer = 0;
1090 }
1091 break;
1092 case F_write:
1093 gp = int2ptr(args[0].integer);
1094 if (!gp) {
1095 error(ERROR_WARNING, "NULL graph passed to write()");
1096 v.integer = 1;
1097 } else
1098 v.integer = sfioWrite(gp, state->outFile);
1099 break;
1100 case F_writeg:
1101 gp = int2ptr(args[0].integer);
1102 if (!gp) {
1103 error(ERROR_WARNING, "NULL graph passed to writeG()");
1104 v.integer = 1;
1105 } else
1106 v.integer = writeFile(gp, args[1].string);
1107 break;
1108 case F_readg:
1109 gp = readFile(args[0].string);
1110 v.integer = ptr2int(gp);
1111 break;
1112 case F_fwriteg:
1113 gp = int2ptr(args[0].integer);
1114 if (!gp) {
1115 error(ERROR_WARNING, "NULL graph passed to fwriteG()");
1116 v.integer = 1;
1117 } else
1118 v.integer = fwriteFile(pgm, gp, args[1].integer);
1119 break;
1120 case F_freadg:
1121 gp = freadFile(pgm, args[0].integer);
1122 v.integer = ptr2int(gp);
1123 break;
1124 case F_openf:
1125 v.integer = openFile(pgm, args[0].string, args[1].string);
1126 break;
1127 case F_closef:
1128 v.integer = closeFile(pgm, args[0].integer);
1129 break;
1130 case F_readl:
1131 v.string = readLine(pgm, args[0].integer);
1132 break;
1133 case F_isdirect:
1134 gp = int2ptr(args[0].integer);
1135 if (!gp) {
1136 error(ERROR_WARNING, "NULL graph passed to isDirect()");
1137 v.integer = 0;
1138 } else {
1139 v.integer = agisdirected(gp);
1140 }
1141 break;
1142 case F_isstrict:
1143 gp = int2ptr(args[0].integer);
1144 if (!gp) {
1145 error(ERROR_WARNING, "NULL graph passed to isStrict()");
1146 v.integer = 0;
1147 } else {
1148 v.integer = agisstrict(gp);
1149 }
1150 break;
1151 case F_delete:
1152 gp = int2ptr(args[0].integer);
1153 objp = int2ptr(args[1].integer);
1154 if (!objp) {
1155 error(ERROR_WARNING, "NULL object passed to delete()");
1156 v.integer = 1;
1157 } else if (objp == (Agobj_t *)state->curgraph) {
1158 error(ERROR_WARNING, "cannot delete current graph $G");
1159 v.integer = 1;
1160 } else if (objp == (Agobj_t *)state->target) {
1161 error(ERROR_WARNING, "cannot delete target graph $T");
1162 v.integer = 1;
1163 } else if (objp == state->curobj) {
1164 if (!(v.integer = deleteObj(gp, objp)))
1165 state->curobj = NULL;
1166 } else {
1167 // Drop this from the list of managed open graphs. If it was not open,
1168 // let this silently fail.
1169 if (AGTYPE(objp) == AGRAPH) {
1170 LIST_REMOVE(&state->open_graphs, (Agraph_t *)objp);
1171 }
1172 v.integer = deleteObj(gp, objp);
1173 }
1174 break;
1175 case F_lock:
1176 gp = int2ptr(args[0].integer);
1177 if (!gp) {
1178 error(ERROR_WARNING, "NULL graph passed to lock()");
1179 v.integer = -1;
1180 } else {
1181 const int op = args[1].integer > 0 ? 1 : args[1].integer < 0 ? -1 : 0;
1182 v.integer = lockGraph(gp, op);
1183 }
1184 break;
1185 case F_nnodes:
1186 gp = int2ptr(args[0].integer);
1187 if (!gp) {
1188 error(ERROR_WARNING, "NULL graph passed to nNodes()");
1189 v.integer = 0;
1190 } else {
1191 v.integer = agnnodes(gp);
1192 }
1193 break;
1194 case F_nedges:
1195 gp = int2ptr(args[0].integer);
1196 if (!gp) {
1197 error(ERROR_WARNING, "NULL graph passed to nEdges()");
1198 v.integer = 0;
1199 } else {
1200 v.integer = agnedges(gp);
1201 }
1202 break;
1203 case F_atoi:
1204 v.integer = atoi(args[0].string);
1205 break;
1206 case F_atof:
1207 v.floating = atof(args[0].string);
1208 break;
1209 case F_sqrt:
1210 v.floating = sqrt(args[0].floating);
1211 break;
1212 case F_cos:
1213 v.floating = cos(args[0].floating);
1214 break;
1215 case F_sin:
1216 v.floating = sin(args[0].floating);
1217 break;
1218 case F_atan2:
1219 v.floating = atan2(args[0].floating, args[1].floating);
1220 break;
1221 case F_exp:
1222 v.floating = exp(args[0].floating);
1223 break;
1224 case F_pow:
1225 v.floating = pow(args[0].floating, args[1].floating);
1226 break;
1227 case F_log:
1228 v.floating = log(args[0].floating);
1229 break;
1230 case F_min:
1231 v.floating = MIN(args[0].floating, args[1].floating);
1232 break;
1233 case F_max:
1234 v.floating = MAX(args[0].floating, args[1].floating);
1235 break;
1236 case F_sys:
1237 v.integer = system(args[0].string);
1238 break;
1239 case F_hasattr:
1240 case F_get: {
1241 objp = int2ptr(args[0].integer);
1242 char *name = args[1].string;
1243 if (!objp) {
1244 exerror("NULL object passed to aget()/hasAttr()");
1245 v.integer = 0;
1246 } else if (!name) {
1247 exerror("NULL name passed to aget()/hasAttr()");
1248 v.integer = 0;
1249 } else {
1250 Agsym_t *gsym = agattrsym(objp, name);
1251 if (sym->index == F_hasattr)
1252 v.integer = (gsym != NULL);
1253 else {
1254 if (!gsym) {
1255 gsym = agattr_text(agroot(agraphof(objp)), AGTYPE(objp), name, "");
1256 agxbuf tmp = {0};
1258 "Using value of %s uninitialized attribute \"%s\" of \"%s\" "
1259 "in aget()",
1260 kindOf(objp), name, nameOf(pgm, objp, &tmp));
1261 agxbfree(&tmp);
1262 }
1263 v.string = agxget(objp, gsym);
1264 }
1265 }
1266 break;
1267 }
1268 case F_set:
1269 objp = int2ptr(args[0].integer);
1270 if (!objp) {
1271 error(ERROR_WARNING, "NULL object passed to aset()");
1272 v.integer = 1;
1273 } else {
1274 char *name = args[1].string;
1275 char *value = args[2].string;
1276 if (!name) {
1277 error(ERROR_WARNING, "NULL name passed to aset()");
1278 v.integer = 1;
1279 } else if (!value) {
1280 error(ERROR_WARNING, "NULL value passed to aset()");
1281 v.integer = 1;
1282 } else {
1283 v.integer = setattr(objp, name, value);
1284 }
1285 }
1286 break;
1287 case F_dset:
1288 gp = int2ptr(args[0].integer);
1289 if (gp) {
1290 char *kind = args[1].string;
1291 char *name = args[2].string;
1292 char *value = args[3].string;
1293 if (!name) {
1294 error(ERROR_WARNING, "NULL name passed to setDflt()");
1295 v.integer = 1;
1296 } else if (!value) {
1297 error(ERROR_WARNING, "NULL value passed to setDflt()");
1298 v.integer = 1;
1299 } else if (!kind) {
1300 error(ERROR_WARNING, "NULL kind passed to setDflt()");
1301 v.integer = 1;
1302 } else {
1303 v.integer = setDfltAttr(gp, kind, name, value);
1304 }
1305 } else {
1306 error(ERROR_WARNING, "NULL graph passed to node()");
1307 v.integer = 0;
1308 }
1309 break;
1310 case F_fstattr:
1311 gp = int2ptr(args[0].integer);
1312 if (gp) {
1313 char *kind = args[1].string;
1314 if (!kind) {
1315 error(ERROR_ERROR, "NULL kind passed to fstAttr()");
1316 v.string = 0;
1317 } else {
1318 v.string = nxtAttr(gp, kind, NULL);
1319 }
1320 } else {
1321 exerror("NULL graph passed to fstAttr()");
1322 v.string = 0;
1323 }
1324 break;
1325 case F_nxtattr:
1326 case F_isattr:
1327 case F_dget:
1328 gp = int2ptr(args[0].integer);
1329 if (gp) {
1330 char *kind = args[1].string;
1331 char *name = args[2].string;
1332 if (!name) {
1333 exerror("NULL name passed to %s", sym->name);
1334 v.string = 0;
1335 } else if (!kind) {
1336 exerror("NULL kind passed to %s", sym->name);
1337 v.string = 0;
1338 } else if (sym->index == F_isattr) {
1339 v.integer = agattr_text(gp, toKind(kind, sym->name), name, 0) != NULL;
1340 } else if (sym->index == F_nxtattr) {
1341 v.string = nxtAttr(gp, kind, name);
1342 } else {
1343 v.string = getDfltAttr(gp, kind, name);
1344 }
1345 } else {
1346 exerror("NULL graph passed to %s", sym->name);
1347 v.string = 0;
1348 }
1349 break;
1350 case F_canon:
1351 v.string = canon(pgm, args[0].string);
1352 break;
1353 case F_ishtml:
1354 v.integer = aghtmlstr(args[0].string);
1355 break;
1356 case F_html:
1357 gp = int2ptr(args[0].integer);
1358 if (gp) {
1359 v.string = toHtml(gp, args[1].string);
1360 } else {
1361 error(ERROR_WARNING, "NULL graph passed to html()");
1362 v.string = 0;
1363 }
1364 break;
1365 case F_tolower:
1366 v.string = toLower(pgm, args[0].string);
1367 break;
1368 case F_colorx:
1369 v.string = colorx(pgm, args[0].string, args[1].string);
1370 break;
1371 case F_strcmp:
1372 if (args[0].string) {
1373 if (args[1].string)
1374 v.integer = strcmp(args[0].string, args[1].string);
1375 else
1376 v.integer = -1;
1377 } else if (args[1].string)
1378 v.integer = 1;
1379 else
1380 v.integer = 0;
1381 break;
1382 case F_toupper:
1383 v.string = toUpper(pgm, args[0].string);
1384 break;
1385 case F_xof:
1386 v.string = xyOf(pgm, args[0].string, true);
1387 break;
1388 case F_yof:
1389 v.string = xyOf(pgm, args[0].string, false);
1390 break;
1391 case F_llof:
1392 v.string = bbOf(pgm, args[0].string, true);
1393 break;
1394 case F_urof:
1395 v.string = bbOf(pgm, args[0].string, false);
1396 break;
1397 case F_length:
1398 v.integer = strlen(args[0].string);
1399 break;
1400 case F_index:
1401 v.integer = indexOf(args[0].string, args[1].string);
1402 break;
1403 case F_rindex:
1404 v.integer = rindexOf(args[0].string, args[1].string);
1405 break;
1406 case F_match: {
1407 const size_t m = match(args[0].string, args[1].string);
1408 if (m == SIZE_MAX) {
1409 v.integer = -1;
1410 } else {
1411 v.integer = (long long)m;
1412 }
1413 break;
1414 }
1415 case F_call:
1416 if ((bp = findBinding(state, args[0].string)))
1417 v.integer = (bp->fn)(args[1].string);
1418 else
1419 v.integer = -1;
1420 break;
1421 default:
1422 v.integer = -1;
1423 exerror("unknown function call: %s", sym->name);
1424 }
1425 return v;
1426 } else if (elt == EX_ARRAY) {
1427 args = env;
1428 state = disc->user;
1429 switch (sym->index) {
1430 case A_ARGV: {
1431 strview_t arg;
1432 if (getArg(args[0].integer, state, &arg) != 0) {
1433 return (Extype_t){0};
1434 }
1435 v.string = exstralloc(pgm, arg.size + 1);
1436 if (arg.size > 0) {
1437 memcpy(v.string, arg.data, arg.size);
1438 }
1439 break;
1440 }
1441 default:
1442 exerror("unknown array name: %s", sym->name);
1443 v.string = 0;
1444 }
1445 return v;
1446 }
1447
1448 state = env;
1449 if (ref) {
1450 objp = deref(pgm, node, ref, 0, state);
1451 if (!objp) {
1452 agxbuf xb = {0};
1453 exerror("null reference in expression %s", deparse(pgm, node, &xb));
1454 agxbfree(&xb);
1455 }
1456 } else if (sym->lex == ID && sym->index <= LAST_V) {
1457 switch (sym->index) {
1458 case V_this:
1459 v.integer = ptr2int(state->curobj);
1460 break;
1461 case V_thisg:
1462 v.integer = ptr2int(state->curgraph);
1463 break;
1464 case V_nextg:
1465 v.integer = ptr2int(state->nextgraph);
1466 break;
1467 case V_targt:
1468 v.integer = ptr2int(state->target);
1469 break;
1470 case V_outgraph:
1471 v.integer = ptr2int(state->outgraph);
1472 break;
1473 case V_tgtname:
1474 v.string = state->tgtname;
1475 break;
1476 case V_infname:
1477 if (state->infname == NULL) {
1478 agxbuf xb = {0};
1479 exerror("current input file is not defined for %s",
1480 deparse(pgm, node, &xb));
1481 agxbfree(&xb);
1482 return (Extype_t){0};
1483 }
1484 v.string = state->infname;
1485 break;
1486 case V_ARGC: {
1487 const size_t size = LIST_SIZE(&state->args);
1488 assert(size <= LLONG_MAX);
1489 v.integer = (long long)size;
1490 break;
1491 }
1492 case V_travtype:
1493 v.integer = state->tvt;
1494 break;
1495 case V_travroot:
1496 v.integer = ptr2int(state->tvroot);
1497 break;
1498 case V_travnext:
1499 v.integer = ptr2int(state->tvnext);
1500 break;
1501 case V_travedge:
1502 v.integer = ptr2int(state->tvedge);
1503 break;
1504 }
1505 return v;
1506 } else {
1507 objp = state->curobj;
1508 if (!objp) {
1509 agxbuf xb = {0};
1510 exerror("current object $ not defined as reference for %s",
1511 deparse(pgm, node, &xb));
1512 agxbfree(&xb);
1513 }
1514 }
1515
1516 if (objp) {
1517 if (lookup(pgm, objp, sym, &v)) {
1518 agxbuf xb = {0};
1519 exerror("in expression %s", deparse(pgm, node, &xb));
1520 agxbfree(&xb);
1521 v.integer = 0;
1522 }
1523 } else
1524 v.integer = 0;
1525
1526 return v;
1527}
1528
1529#define MINTYPE (LAST_M + 1) /* First type occurs after last M_ */
1530
1531static char *typeName(long op) { return typenames[op - MINTYPE]; }
1532
1533/* Set sym to value v.
1534 * Return -1 if not allowed.
1535 * Assume already type correct.
1536 */
1537static int setval(Expr_t *pgm, Exnode_t *x, Exid_t *sym, Exref_t *ref,
1538 void *env, Extype_t v) {
1539 Gpr_t *state;
1540 Agobj_t *objp;
1541 Agnode_t *np;
1542 int rv = 0;
1543
1544 state = env;
1545 if (ref) {
1546 objp = deref(pgm, x, ref, 0, state);
1547 if (!objp) {
1548 agxbuf xb = {0};
1549 exerror("in expression %s.%s", ref->symbol->name, deparse(pgm, x, &xb));
1550 agxbfree(&xb);
1551 return -1;
1552 }
1553 } else if (MINNAME <= sym->index && sym->index <= MAXNAME) {
1554 switch (sym->index) {
1555 case V_outgraph:
1556 state->outgraph = int2ptr(v.integer);
1557 break;
1558 case V_travtype: {
1559 long long iv = v.integer;
1560 if (validTVT(v.integer))
1561 state->tvt = (trav_type)iv;
1562 else
1563 error(1, "unexpected value %lld assigned to %s : ignored", iv,
1564 typeName(T_tvtyp));
1565 break;
1566 }
1567 case V_travroot:
1568 np = int2ptr(v.integer);
1569 if (!np || agroot(np) == state->curgraph)
1570 state->tvroot = np;
1571 else {
1572 error(1, "cannot set $tvroot, node %s not in $G : ignored",
1573 agnameof(np));
1574 }
1575 break;
1576 case V_travnext:
1577 np = int2ptr(v.integer);
1578 if (!np || agroot(np) == state->curgraph) {
1579 state->tvnext = np;
1580 state->flags |= GV_NEXT_SET;
1581 } else {
1582 error(1, "cannot set $tvnext, node %s not in $G : ignored",
1583 agnameof(np));
1584 }
1585 break;
1586 case V_tgtname:
1587 free(state->tgtname);
1588 state->tgtname = strdup(v.string);
1589 state->name_used = 0;
1590 break;
1591 default:
1592 rv = -1;
1593 break;
1594 }
1595 return rv;
1596 } else {
1597 objp = state->curobj;
1598 if (!objp) {
1599 agxbuf xb = {0};
1600 exerror("current object $ undefined in expression %s",
1601 deparse(pgm, x, &xb));
1602 agxbfree(&xb);
1603 return -1;
1604 }
1605 }
1606
1607 assignable(objp, (unsigned char *)sym->name);
1608 return setattr(objp, sym->name, v.string);
1609}
1610
1620 Extype_t v = {0};
1621 switch (rhs->index) {
1622 case A_ARGV: {
1623 Gpr_t *const state = disc->user;
1624 const size_t size = LIST_SIZE(&state->args);
1625 assert(size <= LLONG_MAX);
1626 v.integer = (long long)size;
1627 break;
1628 }
1629 default:
1630 exerror("unknown array name: %s", rhs->name);
1631 break;
1632 }
1633 return v;
1634}
1635
1645static int in(Extype_t lhs, Exid_t *rhs, Exdisc_t *disc) {
1646 switch (rhs->index) {
1647 case A_ARGV: {
1648 Gpr_t *const state = disc->user;
1649 return lhs.integer >= 0 &&
1650 (unsigned long long)lhs.integer < LIST_SIZE(&state->args);
1651 }
1652 default:
1653 exerror("unknown array name: %s", rhs->name);
1654 break;
1655 }
1656 return 0;
1657}
1658
1659static int codePhase;
1660
1661#define haveGraph (1 <= codePhase && codePhase <= 4)
1662#define haveTarget (2 <= codePhase && codePhase <= 4)
1663#define inWalk (2 <= codePhase && codePhase <= 3)
1664
1665/* typeChk:
1666 * Type check input type against implied type of symbol sym.
1667 * If okay, return result type; else return 0.
1668 * For functions, input type set must intersect with function domain.
1669 * This means type errors may occur, but these will be caught at runtime.
1670 * For non-functions, input type must be 0.
1671 */
1672static tctype typeChk(tctype intype, Exid_t *sym) {
1673 tctype dom = 0, rng = 0;
1674
1675 switch (sym->lex) {
1676 case DYNAMIC:
1677 dom = 0;
1678 switch (sym->type) {
1679 case T_obj:
1680 rng = YALL;
1681 break;
1682 case T_node:
1683 rng = Y(V);
1684 break;
1685 case T_graph:
1686 rng = Y(G);
1687 break;
1688 case T_edge:
1689 rng = Y(E);
1690 break;
1691 case INTEGER:
1692 rng = Y(I);
1693 break;
1694 case FLOATING:
1695 rng = Y(F);
1696 break;
1697 case STRING:
1698 rng = Y(S);
1699 break;
1700 default:
1701 exerror("unknown dynamic type %" PRIdMAX " of symbol %s",
1702 (intmax_t)sym->type, sym->name);
1703 break;
1704 }
1705 break;
1706 case ID:
1707 if (sym->index <= LAST_M) {
1708 switch (sym->index) {
1709 case V_travroot:
1710 case V_this:
1711 case V_thisg:
1712 case V_nextg:
1713 if (!haveGraph)
1714 exerror("keyword %s cannot be used in BEGIN/END statements",
1715 sym->name);
1716 break;
1717 case V_targt:
1718 if (!haveTarget)
1719 exerror("keyword %s cannot be used in BEGIN/BEG_G/END statements",
1720 sym->name);
1721 break;
1722 }
1723 dom = tchk[sym->index][0];
1724 rng = tchk[sym->index][1];
1725 } else {
1726 dom = YALL;
1727 rng = Y(S);
1728 }
1729 break;
1730 case NAME:
1731 if (!intype && !haveGraph)
1732 exerror("undeclared, unmodified names like \"%s\" cannot be\nused in "
1733 "BEGIN and END statements",
1734 sym->name);
1735 dom = YALL;
1736 rng = Y(S);
1737 break;
1738 default:
1739 exerror("unexpected symbol in typeChk: name %s, lex %" PRIdMAX, sym->name,
1740 (intmax_t)sym->lex);
1741 break;
1742 }
1743
1744 if (dom) {
1745 if (!intype)
1746 intype = YALL; /* type of $ */
1747 if (!(dom & intype))
1748 rng = 0;
1749 } else if (intype)
1750 rng = 0;
1751 return rng;
1752}
1753
1754// type check variable expression
1755static tctype typeChkExp(Exref_t *ref, Exid_t *sym) {
1756 tctype ty;
1757
1758 if (ref) {
1759 ty = typeChk(0, ref->symbol);
1760 for (ref = ref->next; ty && ref; ref = ref->next)
1761 ty = typeChk(ty, ref->symbol);
1762 if (!ty)
1763 return 0;
1764 } else
1765 ty = 0;
1766 return typeChk(ty, sym);
1767}
1768
1769/* Called during compilation for uses of references: abc.x
1770 * Also for abc.f(..), type abc.v, "abc".x and CONSTANTS.
1771 * The grammar has been altered to disallow the first 3.
1772 * Type check expressions; return value unused.
1773 */
1774static Extype_t refval(Expr_t *pgm, Exnode_t *node, Exid_t *sym, Exref_t *ref) {
1775
1776 Extype_t v;
1777 if (sym->lex == CONSTANT) {
1778 switch (sym->index) {
1779 case C_flat:
1780 v.integer = TV_flat;
1781 break;
1782 case C_ne:
1783 v.integer = TV_ne;
1784 break;
1785 case C_en:
1786 v.integer = TV_en;
1787 break;
1788 case C_bfs:
1789 v.integer = TV_bfs;
1790 break;
1791 case C_dfs:
1792 v.integer = TV_dfs;
1793 break;
1794 case C_fwd:
1795 v.integer = TV_fwd;
1796 break;
1797 case C_rev:
1798 v.integer = TV_rev;
1799 break;
1800 case C_postdfs:
1801 v.integer = TV_postdfs;
1802 break;
1803 case C_postfwd:
1804 v.integer = TV_postfwd;
1805 break;
1806 case C_postrev:
1807 v.integer = TV_postrev;
1808 break;
1809 case C_prepostdfs:
1811 break;
1812 case C_prepostfwd:
1814 break;
1815 case C_prepostrev:
1817 break;
1818 case C_null:
1819 v.integer = 0;
1820 break;
1821 default:
1822 v = exzero(node->type);
1823 break;
1824 }
1825 } else {
1826 if (!typeChkExp(ref, sym)) {
1827 agxbuf xb = {0};
1828 exerror("type error using %s", deparse(pgm, node, &xb));
1829 agxbfree(&xb);
1830 }
1831 v = exzero(node->type);
1832 }
1833 return v;
1834}
1835
1836/* Evaluate (l ex->op r) producing a value of type ex->type,
1837 * stored in l.
1838 * May be unary, with r = NULL
1839 * Return -1 if operation cannot be done, 0 otherwise.
1840 * If arg is != 0, operation unnecessary; just report possibility.
1841 */
1842static int binary(Exnode_t *l, Exnode_t *ex, Exnode_t *r, int arg) {
1843 Agobj_t *lobjp;
1844 Agobj_t *robjp;
1845 int ret = -1;
1846
1847 if (BUILTIN(l->type))
1848 return -1;
1849 if (r && BUILTIN(r->type))
1850 return -1;
1851 if (!INTEGRAL(ex->type))
1852 return -1;
1853
1854 if (l->type == T_tvtyp) {
1855
1856 if (!r)
1857 return -1; /* Assume libexpr handled unary */
1858 if (r->type != T_tvtyp)
1859 return -1;
1860
1861 long long li = l->data.constant.value.integer;
1862 long long ri = r->data.constant.value.integer;
1863 switch (ex->op) {
1864 case EQ:
1865 if (arg)
1866 return 0;
1867 l->data.constant.value.integer = li == ri;
1868 ret = 0;
1869 break;
1870 case NE:
1871 if (arg)
1872 return 0;
1873 l->data.constant.value.integer = li != ri;
1874 ret = 0;
1875 break;
1876 case '<':
1877 if (arg)
1878 return 0;
1879 l->data.constant.value.integer = li < ri;
1880 ret = 0;
1881 break;
1882 case LE:
1883 if (arg)
1884 return 0;
1885 l->data.constant.value.integer = li <= ri;
1886 ret = 0;
1887 break;
1888 case GE:
1889 if (arg)
1890 return 0;
1891 l->data.constant.value.integer = li >= ri;
1892 ret = 0;
1893 break;
1894 case '>':
1895 if (arg)
1896 return 0;
1897 l->data.constant.value.integer = li > ri;
1898 ret = 0;
1899 break;
1900 }
1901 }
1902
1903 /* l is a graph object; make sure r is also */
1904 if (r && r->type == T_tvtyp)
1905 return -1;
1906
1907 lobjp = int2ptr(l->data.constant.value.integer);
1908 if (r)
1909 robjp = int2ptr(r->data.constant.value.integer);
1910 else
1911 robjp = 0;
1912 switch (ex->op) {
1913 case EQ:
1914 if (arg)
1915 return 0;
1916 l->data.constant.value.integer = !compare(lobjp, robjp);
1917 ret = 0;
1918 break;
1919 case NE:
1920 if (arg)
1921 return 0;
1922 l->data.constant.value.integer = compare(lobjp, robjp);
1923 ret = 0;
1924 break;
1925 case '<':
1926 if (arg)
1927 return 0;
1928 l->data.constant.value.integer = compare(lobjp, robjp) < 0;
1929 ret = 0;
1930 break;
1931 case LE:
1932 if (arg)
1933 return 0;
1934 l->data.constant.value.integer = compare(lobjp, robjp) <= 0;
1935 ret = 0;
1936 break;
1937 case GE:
1938 if (arg)
1939 return 0;
1940 l->data.constant.value.integer = compare(lobjp, robjp) >= 0;
1941 ret = 0;
1942 break;
1943 case '>':
1944 if (arg)
1945 return 0;
1946 l->data.constant.value.integer = compare(lobjp, robjp) > 0;
1947 ret = 0;
1948 break;
1949 }
1950
1951 return ret;
1952}
1953
1954static int strToTvtype(char *s) {
1955 int rt = 0;
1956 char *sfx;
1957
1958 if (startswith(s, "TV_")) {
1959 sfx = s + 3;
1960 if (!strcmp(sfx, "flat")) {
1961 rt = TV_flat;
1962 } else if (!strcmp(sfx, "ne")) {
1963 rt = TV_ne;
1964 } else if (!strcmp(sfx, "en")) {
1965 rt = TV_en;
1966 } else if (!strcmp(sfx, "bfs")) {
1967 rt = TV_bfs;
1968 } else if (!strcmp(sfx, "dfs")) {
1969 rt = TV_dfs;
1970 } else if (!strcmp(sfx, "fwd")) {
1971 rt = TV_fwd;
1972 } else if (!strcmp(sfx, "rev")) {
1973 rt = TV_rev;
1974 } else if (!strcmp(sfx, "postdfs")) {
1975 rt = TV_postdfs;
1976 } else if (!strcmp(sfx, "postfwd")) {
1977 rt = TV_postfwd;
1978 } else if (!strcmp(sfx, "postrev")) {
1979 rt = TV_postrev;
1980 } else if (!strcmp(sfx, "prepostdfs")) {
1981 rt = TV_prepostdfs;
1982 } else if (!strcmp(sfx, "prepostfwd")) {
1983 rt = TV_prepostfwd;
1984 } else if (!strcmp(sfx, "prepostrev")) {
1985 rt = TV_prepostrev;
1986 } else
1987 exerror("illegal string \"%s\" for type tvtype_t", s);
1988 } else
1989 exerror("illegal string \"%s\" for type tvtype_t", s);
1990 return rt;
1991}
1992
1993static char *tvtypeToStr(long long v) {
1994 char *s = 0;
1995
1996 switch (v) {
1997 case TV_flat:
1998 s = "TV_flat";
1999 break;
2000 case TV_ne:
2001 s = "TV_ne";
2002 break;
2003 case TV_en:
2004 s = "TV_en";
2005 break;
2006 case TV_bfs:
2007 s = "TV_bfs";
2008 break;
2009 case TV_dfs:
2010 s = "TV_dfs";
2011 break;
2012 case TV_fwd:
2013 s = "TV_fwd";
2014 break;
2015 case TV_rev:
2016 s = "TV_rev";
2017 break;
2018 case TV_postdfs:
2019 s = "TV_postdfs";
2020 break;
2021 case TV_postfwd:
2022 s = "TV_postfwd";
2023 break;
2024 case TV_postrev:
2025 s = "TV_postrev";
2026 break;
2027 case TV_prepostdfs:
2028 s = "TV_prepostdfs";
2029 break;
2030 case TV_prepostfwd:
2031 s = "TV_prepostfwd";
2032 break;
2033 case TV_prepostrev:
2034 s = "TV_prepostrev";
2035 break;
2036 default:
2037 exerror("Unexpected value %lld for type tvtype_t", v);
2038 break;
2039 }
2040 return s;
2041}
2042
2043/* Convert value x to type string.
2044 * Assume x does not have a built-in type
2045 * Return -1 if conversion cannot be done, 0 otherwise.
2046 * If arg is != 0, conversion unnecessary; just report possibility.
2047 */
2048static int stringOf(Expr_t *prog, Exnode_t *x, int arg) {
2049 Agobj_t *objp;
2050 int rv = 0;
2051
2052 if (arg)
2053 return 0;
2054
2055 if (x->type == T_tvtyp) {
2056 if (!(x->data.constant.value.string =
2058 rv = -1;
2059 } else {
2060 objp = int2ptr(x->data.constant.value.integer);
2061 if (!objp) {
2062 exerror("cannot generate name for NULL %s", typeName(x->type));
2063 rv = -1;
2064 } else {
2065 agxbuf tmp = {0};
2066 x->data.constant.value.string = nameOf(prog, objp, &tmp);
2067 agxbfree(&tmp);
2068 }
2069 }
2070 x->type = STRING;
2071 return rv;
2072}
2073
2074/* Convert value x of type x->type to type type.
2075 * Return -1 if conversion cannot be done, 0 otherwise.
2076 * If arg is != 0, conversion unnecessary; just report possibility.
2077 * In particular, assume x != 0 if arg == 0.
2078 */
2079static int convert(Exnode_t *x, long type, int arg) {
2080 Agobj_t *objp;
2081 int ret = -1;
2082
2083 /* If both types are built-in, let libexpr handle */
2084 if (BUILTIN(type) && BUILTIN(x->type))
2085 return -1;
2086 if (type == T_obj && x->type <= T_obj)
2087 ret = 0; /* trivial cast from specific graph object to T_obj */
2088 else if (type <= T_obj && x->type == INTEGER) {
2089 if (x->data.constant.value.integer == 0)
2090 ret = 0; /* allow NULL pointer */
2091 } else if (type == INTEGER) {
2092 ret = 0;
2093 } else if (x->type == T_obj) {
2094 /* check dynamic type */
2095 if (arg) {
2096 if (type != FLOATING && type <= T_obj)
2097 ret = 0;
2098 } else {
2099 objp = int2ptr(x->data.constant.value.integer);
2100 switch (type) {
2101 case T_graph:
2102 if (!objp || AGTYPE(objp) == AGRAPH)
2103 ret = 0;
2104 break;
2105 case T_node:
2106 if (!objp || AGTYPE(objp) == AGNODE)
2107 ret = 0;
2108 break;
2109 case T_edge:
2110 if (!objp || isedge(objp))
2111 ret = 0;
2112 break;
2113 }
2114 }
2115 } else if (type == STRING) {
2116 if (x->type == T_tvtyp) {
2117 ret = 0;
2118 if (!arg) {
2121 }
2122 }
2123 } else if (type == T_tvtyp && x->type == INTEGER) {
2124 if (arg)
2125 ret = 0;
2126 else if (validTVT(x->data.constant.value.integer))
2127 ret = 0;
2128 else
2129 exerror("Integer value %lld not legal for type tvtype_t",
2131 }
2132 /* in case libexpr hands us the trivial case */
2133 else if (x->type == type) {
2134 ret = 0;
2135 } else if (x->type == STRING) {
2136 char *s;
2137 if (type == T_tvtyp) {
2138 if (arg)
2139 ret = 0;
2140 else {
2141 ret = 0;
2142 s = x->data.constant.value.string;
2144 }
2145 }
2146 }
2147 if (!arg && ret == 0)
2148 x->type = type;
2149 return ret;
2150}
2151
2152/* Calculate unique key for object.
2153 * We use this to unify local copies of nodes and edges.
2154 */
2155static Extype_t keyval(Extype_t v, long type) {
2156 if (type <= T_obj) {
2157 v.integer = AGID(int2ptr(v.integer));
2158 }
2159 return v;
2160}
2161
2162// convert type indices to symbolic name
2163static int a2t[] = {0, FLOATING, INTEGER, STRING,
2164 T_node, T_edge, T_graph, T_obj};
2165
2166// create and initialize expr discipline
2167static Exdisc_t *initDisc(Gpr_t *state) {
2168 Exdisc_t *dp = calloc(1, sizeof(Exdisc_t));
2169 if (!dp) {
2170 error(ERROR_ERROR, "could not create libexp discipline: out of memory");
2171 return 0;
2172 }
2173
2174 dp->version = EX_VERSION;
2176 dp->symbols = symbols;
2177 dp->convertf = convert;
2178 dp->stringof = stringOf;
2179 dp->binaryf = binary;
2180 dp->typename = typeName;
2181 if (state->errf)
2182 dp->errorf = state->errf;
2183 else
2184 dp->errorf = errorf;
2185 dp->keyf = keyval;
2186 dp->getf = getval;
2187 dp->reff = refval;
2188 dp->setf = setval;
2189 dp->lengthf = length;
2190 dp->inf = in;
2191 dp->exitf = state->exitf;
2192 dp->types = a2t;
2193 dp->user = state;
2194
2195 state->dp = dp; /* dp is freed when state is freed */
2196
2197 return dp;
2198}
2199
2200/* Compile given string, then extract and return
2201 * typed expression.
2202 */
2203static Exnode_t *compile(Expr_t *prog, char *src, char *input, int line,
2204 const char *lbl, const char *sfx, int kind) {
2205 Exnode_t *e = 0;
2206 int rv;
2207
2208 /* create input stream */
2209 FILE *sf = tmpfile();
2210 assert(sf != NULL);
2211 if (input) {
2212 fputs(input, sf);
2213 }
2214 if (sfx) {
2215 fputs(sfx, sf);
2216 }
2217 if (fseek(sf, 0, SEEK_SET) < 0) {
2218 error(ERROR_ERROR, "failed to seek temporary file");
2219 (void)fclose(sf);
2220 return NULL;
2221 }
2222
2223 /* prefixing label if necessary */
2224 agxbuf label = {0};
2225 if (lbl) {
2226 agxbprint(&label, "%s:\n", lbl);
2227 line--;
2228 }
2229
2230 if (!src)
2231 src = "<command line>";
2232 rv = excomp(prog, src, line, sf, lbl ? agxbdisown(&label) : NULL);
2233 fclose(sf);
2234
2235 if (rv >= 0 && getErrorErrors() == 0)
2236 e = exexpr(prog, lbl, kind);
2237
2238 return e;
2239}
2240
2241// check if guard is an assignment and warn
2242static void checkGuard(Exnode_t *gp, char *src, int line) {
2243 gp = exnoncast(gp);
2244 if (gp && exisAssign(gp)) {
2245 if (src) {
2246 setErrorFileLine(src, line);
2247 }
2248 error(ERROR_WARNING, "assignment used as bool in guard");
2249 }
2250}
2251
2252static case_stmt *mkStmts(Expr_t *prog, char *src, case_infos_t cases,
2253 const char *lbl) {
2254 agxbuf tmp = {0};
2255
2256 case_stmt *cs = gv_calloc(LIST_SIZE(&cases), sizeof(case_stmt));
2257
2258 for (size_t i = 0; i < LIST_SIZE(&cases); i++) {
2259 case_info *sp = LIST_AT(&cases, i);
2260 if (sp->guard) {
2261 agxbprint(&tmp, "%s_g%" PRISIZE_T, lbl, i);
2262 cs[i].guard =
2263 compile(prog, src, sp->guard, sp->gstart, agxbuse(&tmp), 0, INTEGER);
2264 if (getErrorErrors())
2265 break;
2266 checkGuard(cs[i].guard, src, sp->gstart);
2267 }
2268 if (sp->action) {
2269 agxbprint(&tmp, "%s_a%" PRISIZE_T, lbl, i);
2270 cs[i].action =
2271 compile(prog, src, sp->action, sp->astart, agxbuse(&tmp), 0, INTEGER);
2272 if (getErrorErrors())
2273 break;
2274 /* If no error but no compiled action, the input action must
2275 * have been essentially an empty block, which should be
2276 * considered different from a missing block. So, compile a
2277 * trivial block.
2278 */
2279 if (!cs[i].action) {
2280 agxbprint(&tmp, "%s__a%" PRISIZE_T, lbl, i);
2281 cs[i].action =
2282 compile(prog, src, "1", sp->astart, agxbuse(&tmp), 0, INTEGER);
2283 }
2284 }
2285 }
2286 agxbfree(&tmp);
2287 return cs;
2288}
2289
2291static bool mkBlock(comp_block *bp, Expr_t *prog, char *src, parse_block *inp,
2292 size_t i) {
2293 bool has_begin_g = false; // does this block use a `BEG_G` statement?
2294
2295 codePhase = 1;
2296 if (inp->begg_stmt) {
2297 static const char PREFIX[] = "_begin_g_";
2298 agxbuf label = {0};
2299 agxbprint(&label, "%s%" PRISIZE_T, PREFIX, i);
2300 symbols[0].type = T_graph;
2301 tchk[V_this][1] = Y(G);
2302 bp->begg_stmt = compile(prog, src, inp->begg_stmt, inp->l_beging,
2303 agxbuse(&label), 0, VOIDTYPE);
2304 agxbfree(&label);
2305 if (getErrorErrors())
2306 goto finishBlk;
2307 has_begin_g = true;
2308 }
2309
2310 codePhase = 2;
2311 if (!LIST_IS_EMPTY(&inp->node_stmts)) {
2312 static const char PREFIX[] = "_nd";
2313 agxbuf label = {0};
2314 symbols[0].type = T_node;
2315 tchk[V_this][1] = Y(V);
2316 bp->n_nstmts = LIST_SIZE(&inp->node_stmts);
2317 agxbprint(&label, "%s%" PRISIZE_T, PREFIX, i);
2318 bp->node_stmts = mkStmts(prog, src, inp->node_stmts, agxbuse(&label));
2319 agxbfree(&label);
2320 if (getErrorErrors())
2321 goto finishBlk;
2322 bp->does_walk_graph = true;
2323 }
2324
2325 codePhase = 3;
2326 if (!LIST_IS_EMPTY(&inp->edge_stmts)) {
2327 static const char PREFIX[] = "_eg";
2328 agxbuf label = {0};
2329 symbols[0].type = T_edge;
2330 tchk[V_this][1] = Y(E);
2331 bp->n_estmts = LIST_SIZE(&inp->edge_stmts);
2332 agxbprint(&label, "%s%" PRISIZE_T, PREFIX, i);
2333 bp->edge_stmts = mkStmts(prog, src, inp->edge_stmts, agxbuse(&label));
2334 agxbfree(&label);
2335 if (getErrorErrors())
2336 goto finishBlk;
2337 bp->does_walk_graph = true;
2338 }
2339
2340finishBlk:
2341 if (getErrorErrors()) {
2342 free(bp->node_stmts);
2343 free(bp->edge_stmts);
2344 bp->node_stmts = 0;
2345 bp->edge_stmts = 0;
2346 }
2347
2348 return has_begin_g || bp->does_walk_graph;
2349}
2350
2351// convert command line flags to actions in END_G
2352static const char *doFlags(compflags_t flags) {
2353 if (flags.srcout) {
2354 if (flags.induce) {
2355 return "\n$O = $G;\ninduce($O);\n";
2356 }
2357 return "\n$O = $G;\n";
2358 }
2359 if (flags.induce) {
2360 return "\ninduce($O);\n";
2361 }
2362 return "\n";
2363}
2364
2365// convert gpr sections in libexpr program
2367 const char *endg_sfx = NULL;
2368 bool uses_graph = false;
2369
2370 /* Make sure we have enough bits for types */
2371 assert(CHAR_BIT * sizeof(tctype) >= (1 << TBITS));
2372
2373 comp_prog *p = calloc(1, sizeof(comp_prog));
2374 if (!p) {
2375 error(ERROR_ERROR, "could not create compiled program: out of memory");
2376 goto finish;
2377 }
2378
2379 if (flags.srcout || flags.induce || flags.clone) {
2380 endg_sfx = doFlags(flags);
2381 }
2382
2383 if (!initDisc(state))
2384 goto finish;
2385
2386 exinit();
2387 if (!(p->prog = exopen(state->dp)))
2388 goto finish;
2389
2390 codePhase = 0;
2391 if (inp->begin_stmt) {
2392 p->begin_stmt = compile(p->prog, inp->source, inp->begin_stmt, inp->l_begin,
2393 0, 0, VOIDTYPE);
2394 if (getErrorErrors())
2395 goto finish;
2396 }
2397
2398 if (!LIST_IS_EMPTY(&inp->blocks)) {
2399 comp_block *bp;
2400
2401 p->blocks = bp = gv_calloc(LIST_SIZE(&inp->blocks), sizeof(comp_block));
2402
2403 for (size_t i = 0; i < LIST_SIZE(&inp->blocks); bp++, i++) {
2404 parse_block *ibp = LIST_AT(&inp->blocks, i);
2405 uses_graph |= mkBlock(bp, p->prog, inp->source, ibp, i);
2406 if (getErrorErrors())
2407 goto finish;
2408 p->n_blocks++;
2409 }
2410 }
2411 p->uses_graph = uses_graph;
2412
2413 codePhase = 4;
2414 if (inp->endg_stmt || endg_sfx) {
2415 symbols[0].type = T_graph;
2416 tchk[V_this][1] = Y(G);
2417 p->endg_stmt = compile(p->prog, inp->source, inp->endg_stmt, inp->l_endg,
2418 "_end_g", endg_sfx, VOIDTYPE);
2419 if (getErrorErrors())
2420 goto finish;
2421 }
2422
2423 codePhase = 5;
2424 if (inp->end_stmt) {
2425 symbols[0].type = T_obj;
2426 p->end_stmt = compile(p->prog, inp->source, inp->end_stmt, inp->l_end,
2427 "_end_", 0, VOIDTYPE);
2428 if (getErrorErrors())
2429 goto finish;
2430 }
2431 setErrorLine(0); /* execution errors have no line numbers */
2432
2433 if (p->end_stmt)
2434 p->uses_graph = true;
2435
2436finish:
2437 if (getErrorErrors()) {
2438 freeCompileProg(p);
2439 p = 0;
2440 }
2441
2442 return p;
2443}
2444
2446 comp_block *bp;
2447
2448 if (!p)
2449 return;
2450
2451 exclose(p->prog);
2452 for (size_t i = 0; i < p->n_blocks; i++) {
2453 bp = p->blocks + i;
2454 free(bp->node_stmts);
2455 free(bp->edge_stmts);
2456 }
2457 free(p->blocks);
2458 free(p);
2459}
2460
2461/* Read graph from file and initialize
2462 * dynamic data.
2463 */
2464Agraph_t *readG(FILE *fp) {
2465 Agraph_t *g = agread(fp, NULL);
2466 if (g) {
2467 aginit(g, AGRAPH, UDATA, sizeof(gdata), false);
2468 aginit(g, AGNODE, UDATA, sizeof(ndata), false);
2469 aginit(g, AGEDGE, UDATA, sizeof(edata), false);
2470 }
2471 return g;
2472}
2473
2474// open graph and initialize dynamic data
2475Agraph_t *openG(char *name, Agdesc_t desc) {
2476 Agraph_t *g = agopen(name, desc, NULL);
2477 if (g)
2478 agbindrec(g, UDATA, sizeof(gdata), false);
2479 return g;
2480}
2481
2482// open subgraph and initialize dynamic data
2483Agraph_t *openSubg(Agraph_t *g, char *name) {
2484 Agraph_t *sg;
2485
2486 sg = agsubg(g, name, 1);
2487 if (sg && !aggetrec(sg, UDATA, 0))
2488 agbindrec(sg, UDATA, sizeof(gdata), false);
2489 return sg;
2490}
2491
2492// create node and initialize dynamic data
2493Agnode_t *openNode(Agraph_t *g, char *name) {
2494 Agnode_t *np;
2495
2496 np = agnode(g, name, 1);
2497 if (np && !aggetrec(np, UDATA, 0))
2498 agbindrec(np, UDATA, sizeof(ndata), false);
2499 return np;
2500}
2501
2502// create edge and initialize dynamic data
2503Agedge_t *openEdge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *key) {
2504 Agedge_t *ep;
2505 Agraph_t *root;
2506
2507 root = sameG(t, h, "openEdge", "tail and head nodes");
2508 if (!root)
2509 return 0;
2510 if (g) {
2511 if (!sameG(g, root, "openEdge", "subgraph and nodes"))
2512 return 0;
2513 } else
2514 g = root;
2515
2516 ep = agedge(g, t, h, key, 1);
2517 if (ep && !aggetrec(ep, UDATA, 0))
2518 agbindrec(ep, UDATA, sizeof(edata), false);
2519 return ep;
2520}
Agedge_t * isEdge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *key)
Definition actions.c:408
int indexOf(char *s1, char *s2)
return index of leftmost string s2 in string s1, or -1
Definition actions.c:62
Agraph_t * freadFile(Expr_t *ex, long long fd)
Definition actions.c:565
Agobj_t * cloneO(Agraph_t *g, Agobj_t *obj)
Definition actions.c:318
size_t match(char *str, char *pat)
return index of pattern pat in string str, or SIZE_MAX
Definition actions.c:92
int deleteObj(Agraph_t *g, Agobj_t *obj)
Definition actions.c:476
int writeFile(Agraph_t *g, char *f)
Definition actions.c:517
int fwriteFile(Expr_t *ex, Agraph_t *g, long long fd)
Definition actions.c:555
long rindexOf(char *s1, char *s2)
return index of rightmost string s2 in string s1, or -1
Definition actions.c:68
int sfioWrite(Agraph_t *g, FILE *fp)
Definition actions.c:504
Agraph_t * sameG(void *obj1, void *obj2, char *fn, char *msg)
Definition actions.c:48
char * toLower(Expr_t *pgm, char *src)
convert characters to lowercase
Definition actions.c:668
Agraph_t * cloneG(Agraph_t *g, char *name)
Definition actions.c:300
int lockGraph(Agraph_t *g, int v)
Definition actions.c:449
char * toHtml(Agraph_t *g, char *arg)
create a string marked as HTML
Definition actions.c:702
int compare(Agobj_t *l, Agobj_t *r)
lexicographic ordering of objects
Definition actions.c:639
Agraph_t * readFile(char *f)
Definition actions.c:537
Agraph_t * compOf(Agraph_t *g, Agnode_t *n)
return connected component of node
Definition actions.c:387
char * colorx(Expr_t *ex, const char *incolor, char *fmt)
RGB, RGBA, HSV, HSVA.
Definition actions.c:727
char * toUpper(Expr_t *pgm, char *src)
convert characters to uppercase
Definition actions.c:685
char * readLine(Expr_t *ex, long long fd)
Definition actions.c:618
int closeFile(Expr_t *ex, long long fd)
Definition actions.c:593
static void out(agerrlevel_t level, const char *fmt, va_list args)
Report messages using a user-supplied or default write function.
Definition agerror.c:86
Dynamically expanding string buffers.
static void agxbfree(agxbuf *xb)
free any malloced resources
Definition agxbuf.h:97
static int agxbprint(agxbuf *xb, const char *fmt,...)
Printf-style output to an agxbuf.
Definition agxbuf.h:252
static WUR char * agxbuse(agxbuf *xb)
Definition agxbuf.h:325
static int agxbputc(agxbuf *xb, char c)
add character to buffer
Definition agxbuf.h:295
static char * agxbdisown(agxbuf *xb)
Definition agxbuf.h:345
Memory allocation wrappers that exit on failure.
static void * gv_calloc(size_t nmemb, size_t size)
Definition alloc.h:26
static void addNode(block_t *bp, Agnode_t *n)
Definition blocktree.c:20
abstract graph C library, Cgraph API
static char * deparse(Expr_t *ex, Exnode_t *n, agxbuf *xb)
Definition compile.c:194
static tctype typeChkExp(Exref_t *ref, Exid_t *sym)
Definition compile.c:1755
static char * getDfltAttr(Agraph_t *gp, char *k, char *name)
Definition compile.c:545
#define MINTYPE
Definition compile.c:1529
Agedge_t * openEdge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *key)
Definition compile.c:2503
comp_prog * compileProg(parse_prog *inp, Gpr_t *state, compflags_t flags)
Definition compile.c:2366
static bool mkBlock(comp_block *bp, Expr_t *prog, char *src, parse_block *inp, size_t i)
Definition compile.c:2291
#define MIN(a, b)
Definition compile.c:43
static int lookup(Expr_t *pgm, Agobj_t *objp, Exid_t *sym, Extype_t *v)
Definition compile.c:330
static Agdesc_t xargs(char *args)
Definition compile.c:161
static tctype typeChk(tctype intype, Exid_t *sym)
Definition compile.c:1672
static int codePhase
Definition compile.c:1659
static int strToTvtype(char *s)
Definition compile.c:1954
static case_stmt * mkStmts(Expr_t *prog, char *src, case_infos_t cases, const char *lbl)
Definition compile.c:2252
Agraph_t * openG(char *name, Agdesc_t desc)
Definition compile.c:2475
#define haveTarget
Definition compile.c:1662
Agraph_t * openSubg(Agraph_t *g, char *name)
Definition compile.c:2483
static char * tvtypeToStr(long long v)
Definition compile.c:1993
static int stringOf(Expr_t *prog, Exnode_t *x, int arg)
Definition compile.c:2048
static Agobj_t * deref(Expr_t *pgm, Exnode_t *x, Exref_t *ref, Agobj_t *objp, Gpr_t *state)
Definition compile.c:204
static int binary(Exnode_t *l, Exnode_t *ex, Exnode_t *r, int arg)
Definition compile.c:1842
static char * bbOf(Expr_t *pgm, char *pt, bool getll)
Definition compile.c:91
void freeCompileProg(comp_prog *p)
Definition compile.c:2445
static Exdisc_t * initDisc(Gpr_t *state)
Definition compile.c:2167
#define haveGraph
Definition compile.c:1661
static Extype_t getval(Expr_t *pgm, Exnode_t *node, Exid_t *sym, Exref_t *ref, void *env, int elt, Exdisc_t *disc)
Definition compile.c:557
Agnode_t * openNode(Agraph_t *g, char *name)
Definition compile.c:2493
static char * typeName(long op)
Definition compile.c:1531
Agraph_t * readG(FILE *fp)
Definition compile.c:2464
static char * xyOf(Expr_t *pgm, char *pt, bool getx)
Definition compile.c:112
static char * kindToStr(int kind)
Definition compile.c:312
static char * nameOf(Expr_t *ex, Agobj_t *obj, agxbuf *tmps)
Definition compile.c:58
static int a2t[]
Definition compile.c:2163
static int setDfltAttr(Agraph_t *gp, char *k, char *name, char *value)
Definition compile.c:474
static const char * doFlags(compflags_t flags)
Definition compile.c:2352
static Extype_t refval(Expr_t *pgm, Exnode_t *node, Exid_t *sym, Exref_t *ref)
Definition compile.c:1774
static long long ptr2int(const void *p)
Definition compile.c:53
static int setattr(Agobj_t *objp, char *name, char *val)
Definition compile.c:304
static Exnode_t * compile(Expr_t *prog, char *src, char *input, int line, const char *lbl, const char *sfx, int kind)
Definition compile.c:2203
static void assignable(Agobj_t *objp, unsigned char *name)
Definition compile.c:271
static int convert(Exnode_t *x, long type, int arg)
Definition compile.c:2079
static int setval(Expr_t *pgm, Exnode_t *x, Exid_t *sym, Exref_t *ref, void *env, Extype_t v)
Definition compile.c:1537
static char * nxtAttr(Agraph_t *gp, char *k, char *name)
Definition compile.c:521
static Extype_t keyval(Extype_t v, long type)
Definition compile.c:2155
static int getArg(long long n, Gpr_t *state, strview_t *out)
Definition compile.c:464
static char * kindOf(Agobj_t *objp)
string representation of object’s kind
Definition compile.c:325
static void * int2ptr(long long i)
Definition compile.c:51
static void checkGuard(Exnode_t *gp, char *src, int line)
Definition compile.c:2242
static Extype_t length(Exid_t *rhs, Exdisc_t *disc)
Definition compile.c:1619
static int toKind(char *k, char *fn)
Definition compile.c:506
static int isedge(Agobj_t *obj)
Definition compile.c:39
static int in(Extype_t lhs, Exid_t *rhs, Exdisc_t *disc)
Definition compile.c:1645
#define MAX(a, b)
Definition compile.c:44
static int posOf(Agnode_t *np, int idx, double *v)
Definition compile.c:132
#define UDATA
Definition compile.h:30
int getErrorErrors(void)
Definition error.c:33
void errorf(const char *prefix, void *state, int level, const char *s,...)
Definition error.c:90
void setErrorLine(int line)
Definition error.c:26
void setErrorFileLine(char *src, int line)
Definition error.c:27
#define ERROR_WARNING
Definition error.h:35
#define ERROR_ERROR
Definition error.h:36
void exdump(Expr_t *ex, Exnode_t *node, agxbuf *xb)
Definition excc.c:617
void exerror(const char *format,...)
Definition exerror.c:63
char * exstring(Expr_t *ex, char *s)
Definition exeval.c:1909
void * exstralloc(Expr_t *ex, size_t sz)
Definition exeval.c:1917
Exnode_t * exexpr(Expr_t *ex, const char *name, int type)
return the expression for name coerced to type
Definition exexpr.c:24
Expr_t * exopen(Exdisc_t *disc)
Definition exopen.c:40
#define DYNAMIC
Definition exparse.h:167
#define GE
Definition exparse.h:220
#define NE
Definition exparse.h:218
#define FLOATING
Definition exparse.h:155
#define VOIDTYPE
Definition exparse.h:157
#define CONSTANT
Definition exparse.h:163
#define LE
Definition exparse.h:219
#define EQ
Definition exparse.h:217
static Dtdisc_t disc
Definition exparse.y:209
expr procedure type
Definition exparse.y:208
int exisAssign(Exnode_t *)
#define TBITS
Definition expr.h:74
#define EX_VERSION
Definition expr.h:38
#define F
Definition expr.h:70
#define I
Definition expr.h:71
#define EX_CHARSTRING
Definition expr.h:44
Exnode_t * exnoncast(Exnode_t *)
#define EX_CALL
Definition expr.h:48
#define BUILTIN(t)
Definition expr.h:58
void exinit(void)
#define EX_ARRAY
Definition expr.h:47
void exclose(Expr_t *)
#define EX_UNDECLARED
Definition expr.h:45
#define INTEGRAL(t)
Definition expr.h:57
int excomp(Expr_t *p, const char *name, int line, FILE *fp, char *prefix)
#define S
Definition expr.h:72
Extype_t exzero(long int)
Definition exzero.c:26
static int flags
Definition gc.c:63
#define E
Definition gdefs.h:6
static Exid_t symbols[]
Definition gdefs.h:52
@ MAXNAME
Definition gdefs.h:46
static char * typenames[]
Definition gdefs.h:60
@ LAST_V
Definition gdefs.h:19
#define YALL
Definition gdefs.h:10
static tctype tchk[][2]
Definition gdefs.h:78
unsigned short tctype
Definition gdefs.h:76
#define Y(i)
Definition gdefs.h:3
@ LAST_M
Definition gdefs.h:31
#define G
Definition gdefs.h:7
#define V
Definition gdefs.h:5
static double len(glCompPoint p)
Definition glutils.c:138
void free(void *)
#define NAME
Definition gmlparse.h:135
#define ID
Definition gmlparse.h:134
#define STRING
Definition gmlparse.h:133
#define INTEGER
Definition gmlparse.h:131
#define SIZE_MAX
Definition gmlscan.c:347
gvprbinding * findBinding(Gpr_t *state, char *fname)
Definition gprstate.c:72
bool validTVT(long long c)
Definition gprstate.c:30
trav_type
Definition gprstate.h:32
@ TV_flat
Definition gprstate.h:32
@ TV_fwd
Definition gprstate.h:34
@ TV_rev
Definition gprstate.h:34
@ TV_postdfs
Definition gprstate.h:35
@ TV_dfs
Definition gprstate.h:34
@ TV_prepostfwd
Definition gprstate.h:36
@ TV_en
Definition gprstate.h:32
@ TV_prepostrev
Definition gprstate.h:36
@ TV_prepostdfs
Definition gprstate.h:36
@ TV_postfwd
Definition gprstate.h:35
@ TV_bfs
Definition gprstate.h:33
@ TV_ne
Definition gprstate.h:32
@ TV_postrev
Definition gprstate.h:35
#define T_node
Definition grammar.h:113
#define T_edge
Definition grammar.h:114
#define T_graph
Definition grammar.h:112
node NULL
Definition grammar.y:181
int agnedges(Agraph_t *g)
Definition graph.c:169
int agdegree(Agraph_t *g, Agnode_t *n, int in, int out)
Definition graph.c:231
int agnnodes(Agraph_t *g)
Definition graph.c:163
size_t graphviz_node_induce(Agraph_t *g, Agraph_t *edgeset)
Definition node_induce.c:12
Agsym_t * agattr_text(Agraph_t *g, int kind, char *name, const char *value)
creates or looks up text attributes of a graph
Definition attr.c:333
Agsym_t * agattrsym(void *obj, char *name)
looks up a string attribute for a graph object given as an argument
Definition attr.c:146
Agsym_t * agnxtattr(Agraph_t *g, int kind, Agsym_t *attr)
permits traversing the list of attributes of a given type
Definition attr.c:362
int agxset(void *obj, Agsym_t *sym, const char *value)
Definition attr.c:521
char * agxget(void *obj, Agsym_t *sym)
Definition attr.c:457
Agedge_t * agedge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *name, int createflag)
Definition edge.c:252
#define AGMKOUT(e)
Definition cgraph.h:976
Agedge_t * agnxtin(Agraph_t *g, Agedge_t *e)
Definition edge.c:73
Agedge_t * agfstout(Agraph_t *g, Agnode_t *n)
Definition edge.c:28
#define agtail(e)
Definition cgraph.h:982
Agedge_t * agnxtedge(Agraph_t *g, Agedge_t *e, Agnode_t *n)
Definition edge.c:98
#define aghead(e)
Definition cgraph.h:983
Agedge_t * agnxtout(Agraph_t *g, Agedge_t *e)
Definition edge.c:43
Agedge_t * agfstedge(Agraph_t *g, Agnode_t *n)
Definition edge.c:89
#define AGTAIL(e)
Definition cgraph.h:978
Agedge_t * agfstin(Agraph_t *g, Agnode_t *n)
Definition edge.c:59
#define AGHEAD(e)
Definition cgraph.h:979
int agisdirected(Agraph_t *g)
Definition graph.c:184
int agisstrict(Agraph_t *g)
Definition graph.c:194
Agraph_t * agopen(char *name, Agdesc_t desc, Agdisc_t *disc)
creates a new graph with the given name and kind
Definition graph.c:44
Agraph_t * agread(void *chan, Agdisc_t *disc)
constructs a new graph
Definition grammar.c:2052
Agdesc_t Agdirected
directed
Definition graph.c:278
Agnode_t * agnode(Agraph_t *g, char *name, int createflag)
Definition node.c:143
Agnode_t * agnxtnode(Agraph_t *g, Agnode_t *n)
Definition node.c:50
Agnode_t * agfstnode(Agraph_t *g)
Definition node.c:43
int agrelabel_node(Agnode_t *n, char *newname)
Definition node.c:232
Agraph_t * agraphof(void *obj)
Definition obj.c:187
char * agnameof(void *)
returns a string descriptor for the object.
Definition id.c:145
#define AGID(obj)
returns the unique integer ID associated with the object
Definition cgraph.h:221
#define AGTYPE(obj)
returns AGRAPH, AGNODE, or AGEDGE depending on the type of the object
Definition cgraph.h:216
int agcontains(Agraph_t *, void *obj)
returns non-zero if obj is a member of (sub)graph
Definition obj.c:235
int agobjkind(void *obj)
Definition obj.c:254
Agraph_t * agroot(void *obj)
Definition obj.c:170
@ AGOUTEDGE
Definition cgraph.h:207
@ AGEDGE
Definition cgraph.h:207
@ AGNODE
Definition cgraph.h:207
@ AGINEDGE
Definition cgraph.h:207
@ AGRAPH
Definition cgraph.h:207
Agrec_t * aggetrec(void *obj, const char *name, int move_to_front)
find record in circular list and do optional move-to-front and lock
Definition rec.c:43
void aginit(Agraph_t *g, int kind, const char *rec_name, int rec_size, int move_to_front)
attach new records to objects of specified kind
Definition rec.c:172
void * agbindrec(void *obj, const char *name, unsigned int recsize, int move_to_front)
attaches a new record of the given size to the object
Definition rec.c:91
int aghtmlstr(const char *)
Definition refstr.c:440
Agraph_t * agparent(Agraph_t *g)
Definition subg.c:85
Agraph_t * agfstsubg(Agraph_t *g)
Definition subg.c:72
Agraph_t * agnxtsubg(Agraph_t *subg)
Definition subg.c:77
Agraph_t * agsubg(Agraph_t *g, char *name, int cflag)
Definition subg.c:52
agxbput(xb, staging)
#define GV_NEXT_SET
Definition gvpr.h:53
table Syntax error
Definition htmlparse.y:288
static void addEdge(edge_t *de, edge_t *e)
Definition layout.c:342
static void copyAttr(graph_t *g, graph_t *dg, char *attr)
copy given attribute from g to dg
Definition layout.c:356
type-generic dynamically expanding list
#define LIST_AT(list, index)
Definition list.h:210
#define LIST_APPEND(list,...)
Definition list.h:151
#define LIST_SIZE(list)
Definition list.h:92
#define LIST_IS_EMPTY(list)
Definition list.h:102
#define LIST_REMOVE(list, item)
Definition list.h:284
#define LIST_GET(list, index)
Definition list.h:197
static FILE * openFile(const char *argv0, const char *name, const char *mode)
Definition openFile.h:8
static char * canon(graph_t *g, char *s, char *buffer)
Definition output.c:103
#define PRISIZE_T
Definition prisize_t.h:25
static int label(Agnode_t *n, int nodecnt, int *edgecnt)
Definition sccmap.c:163
static bool startswith(const char *s, const char *prefix)
does the string s begin with the string prefix?
Definition startswith.h:11
graph descriptor
Definition cgraph.h:284
unsigned strict
Definition cgraph.h:286
unsigned directed
Definition cgraph.h:285
a generic header of Agraph_s, Agnode_s and Agedge_s
Definition cgraph.h:210
graph or subgraph
Definition cgraph.h:424
string attribute descriptor symbol in Agattr_s.dict
Definition cgraph.h:641
char * name
Definition cgraph.h:643
char * defval
Definition cgraph.h:644
Exid_t * symbols
Definition expr.h:155
char *(* typename)(long)
Definition expr.h:164
Extype_t(* reff)(Expr_t *, Exnode_t *, Exid_t *, Exref_t *)
Definition expr.h:174
int * types
Definition expr.h:202
Exerror_f errorf
Definition expr.h:170
Extype_t(* keyf)(Extype_t, long)
Definition expr.h:168
int(* convertf)(Exnode_t *, long, int)
Definition expr.h:160
int(* binaryf)(Exnode_t *, Exnode_t *, Exnode_t *, int)
Definition expr.h:162
Extype_t(* getf)(Expr_t *, Exnode_t *, Exid_t *, Exref_t *, void *, int, Exdisc_t *)
Definition expr.h:171
uint64_t version
Definition expr.h:153
int(* stringof)(Expr_t *, Exnode_t *, int)
Definition expr.h:166
int(* inf)(Extype_t lhs, Exid_t *rhs, Exdisc_t *disc)
Definition expr.h:199
Exexit_f exitf
Definition expr.h:201
uint64_t flags
Definition expr.h:154
int(* setf)(Expr_t *, Exnode_t *, Exid_t *, Exref_t *, void *, Extype_t)
Definition expr.h:176
void * user
Definition expr.h:203
Extype_t(* lengthf)(Exid_t *rhs, Exdisc_t *disc)
Definition expr.h:187
Definition expr.h:91
long type
Definition expr.h:95
long lex
Definition expr.h:93
char name[EX_NAMELEN]
Definition expr.h:99
long index
Definition expr.h:94
long op
operator
Definition expr.h:142
long type
value type
Definition expr.h:141
Exdata_t data
Definition expr.h:144
Definition expr.h:207
Exid_t * symbol
Definition expr.h:105
Exref_t * next
Definition expr.h:104
Agedge_t * tvedge
Definition gprstate.h:54
char * tgtname
Definition gprstate.h:48
Exexit_f exitf
Definition gprstate.h:47
Agobj_t * curobj
Definition gprstate.h:44
Agraph_t * target
Definition gprstate.h:42
Agraph_t * curgraph
Definition gprstate.h:40
int flags
Definition gprstate.h:57
FILE * outFile
Definition gprstate.h:50
Agraph_t * outgraph
Definition gprstate.h:43
Exdisc_t * dp
Definition gprstate.h:45
Agnode_t * tvroot
Definition gprstate.h:52
Agnode_t * tvnext
Definition gprstate.h:53
char * infname
Definition gprstate.h:49
Agraph_t * nextgraph
Definition gprstate.h:41
Exerror_f errf
Definition gprstate.h:46
int name_used
Definition gprstate.h:55
strviews_t args
Definition gprstate.h:56
trav_type tvt
Definition gprstate.h:51
char * guard
Definition parse.h:24
int astart
Definition parse.h:25
char * action
Definition parse.h:26
int gstart
Definition parse.h:23
Exnode_t * guard
Definition compile.h:26
Exnode_t * action
Definition compile.h:27
case_stmt * node_stmts
Definition compile.h:71
Exnode_t * begg_stmt
Definition compile.h:67
size_t n_nstmts
Definition compile.h:69
size_t n_estmts
Definition compile.h:70
case_stmt * edge_stmts
Definition compile.h:72
bool does_walk_graph
does this block have a node or edge statement?
Definition compile.h:68
Exnode_t * begin_stmt
Definition compile.h:78
comp_block * blocks
Definition compile.h:80
Expr_t * prog
Definition compile.h:77
size_t n_blocks
Definition compile.h:79
bool uses_graph
does this program use the input graph?
Definition compile.h:76
Exnode_t * endg_stmt
Definition compile.h:81
Exnode_t * end_stmt
Definition compile.h:82
gvpruserfn fn
Definition gvpr.h:60
int l_beging
Definition parse.h:32
case_infos_t node_stmts
Definition parse.h:34
case_infos_t edge_stmts
Definition parse.h:35
char * begg_stmt
Definition parse.h:33
int l_end
Definition parse.h:42
int l_begin
Definition parse.h:42
int l_endg
Definition parse.h:42
char * end_stmt
Definition parse.h:46
char * endg_stmt
Definition parse.h:45
char * begin_stmt
Definition parse.h:43
parse_blocks_t blocks
Definition parse.h:44
char * source
Definition parse.h:41
a non-owning string reference
Definition strview.h:20
const char * data
start of the pointed to string
Definition strview.h:21
size_t size
extent of the string in bytes
Definition strview.h:22
Non-owning string references.
static short TFA_State
Definition trieFA.h:49
#define TFA_Init()
Definition trieFA.h:55
#define TFA_Advance(C)
Definition trieFA.h:61
#define TFA_Definition()
Definition trieFA.h:86
long long integer
Definition exparse.h:240
double floating
Definition exparse.h:237
char * string
Definition exparse.h:242
struct Exdata_u::@61 constant
Exnode_t * dyna
Definition expr.h:131
struct Exdata_u::@64 variable
Extype_t value
Definition expr.h:112
Definition grammar.c:90
#define UNREACHABLE()
Definition unreachable.h:30
static void * copy(void *p, Dtdisc_t *disc UNUSED)
Definition write.c:773