Graphviz 16.1.1~dev.20261017.1520
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 break;
444 }
445 } else {
446 Agsym_t *gsym = agattrsym(objp, sym->name);
447 if (!gsym) {
448 gsym = agattr_text(agroot(agraphof(objp)), AGTYPE(objp), sym->name, "");
449 agxbuf tmp = {0};
451 "Using value of uninitialized %s attribute \"%s\" of \"%s\"",
452 kindOf(objp), sym->name, nameOf(pgm, objp, &tmp));
453 agxbfree(&tmp);
454 }
455 v->string = agxget(objp, gsym);
456 }
457
458 return 0;
459}
460
465static int getArg(long long n, Gpr_t *state, strview_t *out) {
466 assert(out != NULL);
467 if (n < 0 || (unsigned long long)n >= LIST_SIZE(&state->args)) {
468 exerror("program references ARGV[%lld] - undefined", n);
469 return -1;
470 }
471 *out = LIST_GET(&state->args, (size_t)n);
472 return 0;
473}
474
475static int setDfltAttr(Agraph_t *gp, char *k, char *name, char *value) {
476 int kind;
477
478 switch (*k) {
479 case 'G':
480 kind = AGRAPH;
481 break;
482 case 'E':
483 kind = AGEDGE;
484 break;
485 case 'N':
486 kind = AGNODE;
487 break;
488 default:
489 error(ERROR_WARNING, "Unknown kind \"%s\" passed to setDflt()", k);
490 return 1;
491 }
492
493 // make the implicit default on the root graph explicit in order to avoid the
494 // next `agattr_text` thinking its assignment should be hoisted to the root
495 {
496 Agraph_t *const root = agroot(gp);
497 if (agattr_text(root, kind, name, NULL) == NULL) {
498 agattr_text(root, kind, name, "");
499 }
500 }
501
502 agattr_text(gp, kind, name, value);
503 return 0;
504}
505
506// map string to object kind
507static int toKind(char *k, char *fn) {
508 switch (*k) {
509 case 'G':
510 return AGRAPH;
511 case 'E':
512 return AGEDGE;
513 case 'N':
514 return AGNODE;
515 default:
516 exerror("Unknown kind \"%s\" passed to %s()", k, fn);
517 break;
518 }
519 return 0;
520}
521
522static char *nxtAttr(Agraph_t *gp, char *k, char *name) {
523 char *fn = name ? "nxtAttr" : "fstAttr";
524 int kind = toKind(k, fn);
525 Agsym_t *sym;
526
527 if (name) {
528 sym = agattr_text(gp, kind, name, 0);
529 if (!sym) {
530 exerror("Third argument \"%s\" in nxtAttr() must be the name of an "
531 "existing attribute",
532 name);
533 return "";
534 }
535
536 } else
537 sym = NULL;
538
539 sym = agnxtattr(gp, kind, sym);
540 if (sym)
541 return sym->name;
542 else
543 return "";
544}
545
546static char *getDfltAttr(Agraph_t *gp, char *k, char *name) {
547 int kind = toKind(k, "getDflt");
548 Agsym_t *sym = agattr_text(gp, kind, name, 0);
549 if (!sym) {
550 sym = agattr_text(gp, kind, name, "");
551 error(ERROR_WARNING, "Uninitialized %s attribute \"%s\" in %s",
552 kindToStr(kind), name, "getDflt");
553 }
554 return sym->defval;
555}
556
557// return value associated with gpr identifier
558static Extype_t getval(Expr_t *pgm, Exnode_t *node, Exid_t *sym, Exref_t *ref,
559 void *env, int elt, Exdisc_t *disc) {
560 Extype_t v;
561 Gpr_t *state;
562 Extype_t *args;
563 Agobj_t *objp;
564 Agobj_t *objp1;
565 char *key;
566 Agraph_t *gp;
567 Agnode_t *np;
568 Agnode_t *hp;
569 Agedge_t *ep;
570 gvprbinding *bp;
571
572 assert(sym->lex != CONSTANT);
573 if (elt == EX_CALL) {
574 args = env;
575 state = disc->user;
576 switch (sym->index) {
577 case F_graph:
578 gp = openG(args[0].string, xargs(args[1].string));
579 if (gp != NULL) {
580 LIST_APPEND(&state->open_graphs, gp);
581 }
582 v.integer = ptr2int(gp);
583 break;
584 case F_subg:
585 gp = int2ptr(args[0].integer);
586 if (gp) {
587 gp = openSubg(gp, args[1].string);
588 v.integer = ptr2int(gp);
589 } else {
590 error(ERROR_WARNING, "NULL graph passed to subg()");
591 v.integer = 0;
592 }
593 break;
594 case F_issubg:
595 gp = int2ptr(args[0].integer);
596 if (gp) {
597 v.integer = ptr2int(agsubg(gp, args[1].string, 0));
598 } else {
599 error(ERROR_WARNING, "NULL graph passed to isSubg()");
600 v.integer = 0;
601 }
602 break;
603 case F_fstsubg:
604 gp = int2ptr(args[0].integer);
605 if (gp) {
606 gp = agfstsubg(gp);
607 v.integer = ptr2int(gp);
608 } else {
609 error(ERROR_WARNING, "NULL graph passed to fstsubg()");
610 v.integer = 0;
611 }
612 break;
613 case F_nxtsubg:
614 gp = int2ptr(args[0].integer);
615 if (gp) {
616 gp = agnxtsubg(gp);
617 v.integer = ptr2int(gp);
618 } else {
619 error(ERROR_WARNING, "NULL graph passed to nxtsubg()");
620 v.integer = 0;
621 }
622 break;
623 case F_node:
624 gp = int2ptr(args[0].integer);
625 if (gp) {
626 np = openNode(gp, args[1].string);
627 v.integer = ptr2int(np);
628 } else {
629 error(ERROR_WARNING, "NULL graph passed to node()");
630 v.integer = 0;
631 }
632 break;
633 case F_addnode:
634 gp = int2ptr(args[0].integer);
635 np = int2ptr(args[1].integer);
636 if (!gp) {
637 error(ERROR_WARNING, "NULL graph passed to addNode()");
638 v.integer = 0;
639 } else if (!np) {
640 error(ERROR_WARNING, "NULL node passed to addNode()");
641 v.integer = 0;
642 } else
643 v.integer = ptr2int(addNode(gp, np, 1));
644 break;
645 case F_fstnode:
646 gp = int2ptr(args[0].integer);
647 if (gp) {
648 np = agfstnode(gp);
649 v.integer = ptr2int(np);
650 } else {
651 error(ERROR_WARNING, "NULL graph passed to fstnode()");
652 v.integer = 0;
653 }
654 break;
655 case F_nxtnode:
656 np = int2ptr(args[0].integer);
657 if (np) {
658 np = agnxtnode(agroot(np), np);
659 v.integer = ptr2int(np);
660 } else {
661 error(ERROR_WARNING, "NULL node passed to nxtnode()");
662 v.integer = 0;
663 }
664 break;
665 case F_nxtnodesg:
666 gp = int2ptr(args[0].integer);
667 np = int2ptr(args[1].integer);
668 if (!gp)
669 gp = agroot(np);
670 if (np) {
671 np = agnxtnode(gp, np);
672 v.integer = ptr2int(np);
673 } else {
674 error(ERROR_WARNING, "NULL node passed to nxtnode_sg()");
675 v.integer = 0;
676 }
677 break;
678 case F_isnode:
679 gp = int2ptr(args[0].integer);
680 if (gp) {
681 v.integer = ptr2int(agnode(gp, args[1].string, 0));
682 } else {
683 error(ERROR_WARNING, "NULL graph passed to isNode()");
684 v.integer = 0;
685 }
686 break;
687 case F_issubnode:
688 gp = int2ptr(args[0].integer);
689 np = int2ptr(args[1].integer);
690 if (!gp)
691 gp = agroot(np);
692 if (np) {
693 v.integer = ptr2int(addNode(gp, np, 0));
694 } else {
695 error(ERROR_WARNING, "NULL node passed to isSubnode()");
696 v.integer = 0;
697 }
698 break;
699 case F_indegree:
700 gp = int2ptr(args[0].integer);
701 np = int2ptr(args[1].integer);
702 if (!gp)
703 gp = agroot(np);
704 if (np) {
705 v.integer = agdegree(gp, np, 1, 0);
706 } else {
707 error(ERROR_WARNING, "NULL node passed to indegreeOf()");
708 v.integer = 0;
709 }
710 break;
711 case F_outdegree:
712 gp = int2ptr(args[0].integer);
713 np = int2ptr(args[1].integer);
714 if (!gp)
715 gp = agroot(np);
716 if (np) {
717 v.integer = agdegree(gp, np, 0, 1);
718 } else {
719 error(ERROR_WARNING, "NULL node passed to outdegreeOf()");
720 v.integer = 0;
721 }
722 break;
723 case F_degree:
724 gp = int2ptr(args[0].integer);
725 np = int2ptr(args[1].integer);
726 if (!gp)
727 gp = agroot(np);
728 if (np) {
729 v.integer = agdegree(gp, np, 1, 1);
730 } else {
731 error(ERROR_WARNING, "NULL node passed to degreeOf()");
732 v.integer = 0;
733 }
734 break;
735 case F_isin:
736 gp = int2ptr(args[0].integer);
737 objp = int2ptr(args[1].integer);
738 if (!gp) {
739 error(ERROR_WARNING, "NULL graph passed to isIn()");
740 v.integer = 0;
741 } else if (!objp) {
742 error(ERROR_WARNING, "NULL object passed to isIn()");
743 v.integer = 0;
744 } else
745 v.integer = agcontains(gp, objp);
746 break;
747 case F_compof:
748 gp = int2ptr(args[0].integer);
749 np = int2ptr(args[1].integer);
750 if (!gp) {
751 error(ERROR_WARNING, "NULL graph passed to compOf()");
752 v.integer = 0;
753 } else if (!np) {
754 error(ERROR_WARNING, "NULL node passed to compOf()");
755 v.integer = 0;
756 } else
757 v.integer = ptr2int(compOf(gp, np));
758 break;
759 case F_kindof:
760 objp = int2ptr(args[0].integer);
761 if (!objp) {
762 exerror("NULL object passed to kindOf()");
763 v.string = 0;
764 } else
765 switch (AGTYPE(objp)) {
766 case AGRAPH:
767 v.string = "G";
768 break;
769 case AGNODE:
770 v.string = "N";
771 break;
772 case AGINEDGE:
773 case AGOUTEDGE:
774 v.string = "E";
775 break;
776 default:
777 UNREACHABLE();
778 }
779 break;
780 case F_edge:
781 key = args[2].string;
782 if (*key == '\0')
783 key = 0;
784 np = int2ptr(args[0].integer);
785 hp = int2ptr(args[1].integer);
786 if (!np) {
787 error(ERROR_WARNING, "NULL tail node passed to edge()");
788 v.integer = 0;
789 } else if (!hp) {
790 error(ERROR_WARNING, "NULL head node passed to edge()");
791 v.integer = 0;
792 } else {
793 ep = openEdge(0, np, hp, key);
794 v.integer = ptr2int(ep);
795 }
796 break;
797 case F_edgesg:
798 key = args[3].string;
799 if (*key == '\0')
800 key = 0;
801 gp = int2ptr(args[0].integer);
802 np = int2ptr(args[1].integer);
803 hp = int2ptr(args[2].integer);
804 if (!np) {
805 error(ERROR_WARNING, "NULL tail node passed to edge_sg()");
806 v.integer = 0;
807 } else if (!hp) {
808 error(ERROR_WARNING, "NULL head node passed to edge_sg()");
809 v.integer = 0;
810 } else {
811 ep = openEdge(gp, np, hp, key);
812 v.integer = ptr2int(ep);
813 }
814 break;
815 case F_addedge:
816 gp = int2ptr(args[0].integer);
817 ep = int2ptr(args[1].integer);
818 if (!gp) {
819 error(ERROR_WARNING, "NULL graph passed to addEdge()");
820 v.integer = 0;
821 } else if (!ep) {
822 error(ERROR_WARNING, "NULL edge passed to addEdge()");
823 v.integer = 0;
824 } else
825 v.integer = ptr2int(addEdge(gp, ep, 1));
826 break;
827 case F_opp:
828 ep = int2ptr(args[0].integer);
829 np = int2ptr(args[1].integer);
830 if (!ep) {
831 error(ERROR_WARNING, "NULL edge passed to opp()");
832 v.integer = 0;
833 } else if (!np) {
834 error(ERROR_WARNING, "NULL node passed to opp()");
835 v.integer = 0;
836 } else {
837 if (aghead(ep) == np)
838 np = agtail(ep);
839 else
840 np = aghead(ep);
841 v.integer = ptr2int(np);
842 }
843 break;
844 case F_isedge:
845 key = args[2].string;
846 if (*key == '\0')
847 key = 0;
848 np = int2ptr(args[0].integer);
849 hp = int2ptr(args[1].integer);
850 if (!np) {
851 error(ERROR_WARNING, "NULL tail node passed to isEdge()");
852 v.integer = 0;
853 } else if (!hp) {
854 error(ERROR_WARNING, "NULL head node passed to isEdge()");
855 v.integer = 0;
856 } else
857 v.integer = ptr2int(isEdge(agroot(np), np, hp, key));
858 break;
859 case F_isedgesg:
860 key = args[3].string;
861 if (*key == '\0')
862 key = 0;
863 gp = int2ptr(args[0].integer);
864 np = int2ptr(args[1].integer);
865 hp = int2ptr(args[2].integer);
866 if (!gp)
867 gp = agroot(np);
868 if (!np) {
869 error(ERROR_WARNING, "NULL tail node passed to isEdge_sg()");
870 v.integer = 0;
871 } else if (!hp) {
872 error(ERROR_WARNING, "NULL head node passed to isEdge_sg()");
873 v.integer = 0;
874 } else
875 v.integer = ptr2int(isEdge(gp, np, hp, key));
876 break;
877 case F_issubedge:
878 gp = int2ptr(args[0].integer);
879 ep = int2ptr(args[1].integer);
880 if (!gp)
881 gp = agroot(ep);
882 if (ep) {
883 v.integer = ptr2int(addEdge(gp, ep, 0));
884 } else {
885 error(ERROR_WARNING, "NULL edge passed to isSubedge()");
886 v.integer = 0;
887 }
888 break;
889 case F_fstout:
890 np = int2ptr(args[0].integer);
891 if (np) {
892 ep = agfstout(agroot(np), np);
893 v.integer = ptr2int(ep);
894 } else {
895 error(ERROR_WARNING, "NULL node passed to fstout()");
896 v.integer = 0;
897 }
898 break;
899 case F_fstoutsg:
900 gp = int2ptr(args[0].integer);
901 np = int2ptr(args[1].integer);
902 if (!gp)
903 gp = agroot(np);
904 if (np) {
905 ep = agfstout(gp, np);
906 v.integer = ptr2int(ep);
907 } else {
908 error(ERROR_WARNING, "NULL node passed to fstout_sg()");
909 v.integer = 0;
910 }
911 break;
912 case F_nxtout:
913 ep = int2ptr(args[0].integer);
914 if (ep) {
915 ep = agnxtout(agroot(ep), ep);
916 v.integer = ptr2int(ep);
917 } else {
918 error(ERROR_WARNING, "NULL edge passed to nxtout()");
919 v.integer = 0;
920 }
921 break;
922 case F_nxtoutsg:
923 gp = int2ptr(args[0].integer);
924 ep = int2ptr(args[1].integer);
925 if (!gp)
926 gp = agroot(ep);
927 if (ep) {
928 ep = agnxtout(gp, ep);
929 v.integer = ptr2int(ep);
930 } else {
931 error(ERROR_WARNING, "NULL edge passed to nxtout_sg()");
932 v.integer = 0;
933 }
934 break;
935 case F_fstin:
936 np = int2ptr(args[0].integer);
937 if (np) {
938 ep = agfstin(agroot(np), np);
939 v.integer = ptr2int(ep);
940 } else {
941 error(ERROR_WARNING, "NULL node passed to fstin()");
942 v.integer = 0;
943 }
944 break;
945 case F_fstinsg:
946 gp = int2ptr(args[0].integer);
947 np = int2ptr(args[1].integer);
948 if (!gp)
949 gp = agroot(np);
950 if (np) {
951 ep = agfstin(gp, np);
952 v.integer = ptr2int(ep);
953 } else {
954 error(ERROR_WARNING, "NULL node passed to fstin_sg()");
955 v.integer = 0;
956 }
957 break;
958 case F_nxtin:
959 ep = int2ptr(args[0].integer);
960 if (ep) {
961 ep = agnxtin(agroot(ep), ep);
962 v.integer = ptr2int(ep);
963 } else {
964 error(ERROR_WARNING, "NULL edge passed to nxtin()");
965 v.integer = 0;
966 }
967 break;
968 case F_nxtinsg:
969 gp = int2ptr(args[0].integer);
970 ep = int2ptr(args[1].integer);
971 if (!gp)
972 gp = agroot(ep);
973 if (ep) {
974 ep = agnxtin(gp, ep);
975 v.integer = ptr2int(ep);
976 } else {
977 error(ERROR_WARNING, "NULL edge passed to nxtin_sg()");
978 v.integer = 0;
979 }
980 break;
981 case F_fstedge:
982 np = int2ptr(args[0].integer);
983 if (np) {
984 ep = agfstedge(agroot(np), np);
985 v.integer = ptr2int(ep);
986 } else {
987 error(ERROR_WARNING, "NULL node passed to fstedge()");
988 v.integer = 0;
989 }
990 break;
991 case F_fstedgesg:
992 gp = int2ptr(args[0].integer);
993 np = int2ptr(args[1].integer);
994 if (!gp)
995 gp = agroot(np);
996 if (np) {
997 ep = agfstedge(gp, np);
998 v.integer = ptr2int(ep);
999 } else {
1000 error(ERROR_WARNING, "NULL node passed to fstedge_sg()");
1001 v.integer = 0;
1002 }
1003 break;
1004 case F_nxtedge:
1005 ep = int2ptr(args[0].integer);
1006 np = int2ptr(args[1].integer);
1007 if (!ep) {
1008 error(ERROR_WARNING, "NULL edge passed to nxtedge()");
1009 v.integer = 0;
1010 } else if (!np) {
1011 error(ERROR_WARNING, "NULL node passed to nxtedge()");
1012 v.integer = 0;
1013 } else {
1014 ep = agnxtedge(agroot(np), ep, np);
1015 v.integer = ptr2int(ep);
1016 }
1017 break;
1018 case F_nxtedgesg:
1019 gp = int2ptr(args[0].integer);
1020 ep = int2ptr(args[1].integer);
1021 np = int2ptr(args[2].integer);
1022 if (!gp)
1023 gp = agroot(np);
1024 if (!ep) {
1025 error(ERROR_WARNING, "NULL edge passed to nxtedge_sg()");
1026 v.integer = 0;
1027 } else if (!np) {
1028 error(ERROR_WARNING, "NULL node passed to nxtedge_sg()");
1029 v.integer = 0;
1030 } else {
1031 ep = agnxtedge(gp, ep, np);
1032 v.integer = ptr2int(ep);
1033 }
1034 break;
1035 case F_copy:
1036 gp = int2ptr(args[0].integer);
1037 objp = int2ptr(args[1].integer);
1038 if (!objp) {
1039 error(ERROR_WARNING, "NULL object passed to clone()");
1040 v.integer = 0;
1041 } else
1042 v.integer = ptr2int(copy(gp, objp));
1043 break;
1044 case F_clone:
1045 gp = int2ptr(args[0].integer);
1046 objp = int2ptr(args[1].integer);
1047 if (!objp) {
1048 error(ERROR_WARNING, "NULL object passed to clone()");
1049 v.integer = 0;
1050 } else
1051 v.integer = ptr2int(cloneO(gp, objp));
1052 break;
1053 case F_cloneG:
1054 gp = int2ptr(args[0].integer);
1055 if (gp) {
1056 gp = cloneG(gp, args[1].string);
1057 if (gp != NULL) {
1058 LIST_APPEND(&state->open_graphs, gp);
1059 }
1060 v.integer = ptr2int(gp);
1061 } else {
1062 error(ERROR_WARNING, "NULL graph passed to cloneG()");
1063 v.integer = 0;
1064 }
1065 break;
1066 case F_copya:
1067 objp = int2ptr(args[0].integer);
1068 objp1 = int2ptr(args[1].integer);
1069 if (!(objp && objp1)) {
1070 error(ERROR_WARNING, "NULL object passed to copyA()");
1071 v.integer = 0;
1072 } else
1073 v.integer = copyAttr(objp, objp1);
1074 break;
1075 case F_rename:
1076 objp = int2ptr(args[0].integer);
1077 if (!objp) {
1078 error(ERROR_WARNING, "NULL object passed to rename()");
1079 v.integer = -1;
1080 } else
1081 v.integer = agrelabel_node((Agnode_t *)objp, args[1].string);
1082 break;
1083 case F_induce:
1084 gp = int2ptr(args[0].integer);
1085 if (!gp) {
1086 error(ERROR_WARNING, "NULL graph passed to induce()");
1087 v.integer = 1;
1088 } else {
1089 (void)graphviz_node_induce(gp, NULL);
1090 v.integer = 0;
1091 }
1092 break;
1093 case F_write:
1094 gp = int2ptr(args[0].integer);
1095 if (!gp) {
1096 error(ERROR_WARNING, "NULL graph passed to write()");
1097 v.integer = 1;
1098 } else
1099 v.integer = sfioWrite(gp, state->outFile);
1100 break;
1101 case F_writeg:
1102 gp = int2ptr(args[0].integer);
1103 if (!gp) {
1104 error(ERROR_WARNING, "NULL graph passed to writeG()");
1105 v.integer = 1;
1106 } else
1107 v.integer = writeFile(gp, args[1].string);
1108 break;
1109 case F_readg:
1110 gp = readFile(args[0].string);
1111 v.integer = ptr2int(gp);
1112 break;
1113 case F_fwriteg:
1114 gp = int2ptr(args[0].integer);
1115 if (!gp) {
1116 error(ERROR_WARNING, "NULL graph passed to fwriteG()");
1117 v.integer = 1;
1118 } else
1119 v.integer = fwriteFile(pgm, gp, args[1].integer);
1120 break;
1121 case F_freadg:
1122 gp = freadFile(pgm, args[0].integer);
1123 v.integer = ptr2int(gp);
1124 break;
1125 case F_openf:
1126 v.integer = openFile(pgm, args[0].string, args[1].string);
1127 break;
1128 case F_closef:
1129 v.integer = closeFile(pgm, args[0].integer);
1130 break;
1131 case F_readl:
1132 v.string = readLine(pgm, args[0].integer);
1133 break;
1134 case F_isdirect:
1135 gp = int2ptr(args[0].integer);
1136 if (!gp) {
1137 error(ERROR_WARNING, "NULL graph passed to isDirect()");
1138 v.integer = 0;
1139 } else {
1140 v.integer = agisdirected(gp);
1141 }
1142 break;
1143 case F_isstrict:
1144 gp = int2ptr(args[0].integer);
1145 if (!gp) {
1146 error(ERROR_WARNING, "NULL graph passed to isStrict()");
1147 v.integer = 0;
1148 } else {
1149 v.integer = agisstrict(gp);
1150 }
1151 break;
1152 case F_delete:
1153 gp = int2ptr(args[0].integer);
1154 objp = int2ptr(args[1].integer);
1155 if (!objp) {
1156 error(ERROR_WARNING, "NULL object passed to delete()");
1157 v.integer = 1;
1158 } else if (objp == (Agobj_t *)state->curgraph) {
1159 error(ERROR_WARNING, "cannot delete current graph $G");
1160 v.integer = 1;
1161 } else if (objp == (Agobj_t *)state->target) {
1162 error(ERROR_WARNING, "cannot delete target graph $T");
1163 v.integer = 1;
1164 } else if (objp == state->curobj) {
1165 if (!(v.integer = deleteObj(gp, objp)))
1166 state->curobj = NULL;
1167 } else {
1168 // Drop this from the list of managed open graphs. If it was not open,
1169 // let this silently fail.
1170 if (AGTYPE(objp) == AGRAPH) {
1171 LIST_REMOVE(&state->open_graphs, (Agraph_t *)objp);
1172 }
1173 v.integer = deleteObj(gp, objp);
1174 }
1175 break;
1176 case F_lock:
1177 gp = int2ptr(args[0].integer);
1178 if (!gp) {
1179 error(ERROR_WARNING, "NULL graph passed to lock()");
1180 v.integer = -1;
1181 } else {
1182 const int op = args[1].integer > 0 ? 1 : args[1].integer < 0 ? -1 : 0;
1183 v.integer = lockGraph(gp, op);
1184 }
1185 break;
1186 case F_nnodes:
1187 gp = int2ptr(args[0].integer);
1188 if (!gp) {
1189 error(ERROR_WARNING, "NULL graph passed to nNodes()");
1190 v.integer = 0;
1191 } else {
1192 v.integer = agnnodes(gp);
1193 }
1194 break;
1195 case F_nedges:
1196 gp = int2ptr(args[0].integer);
1197 if (!gp) {
1198 error(ERROR_WARNING, "NULL graph passed to nEdges()");
1199 v.integer = 0;
1200 } else {
1201 v.integer = agnedges(gp);
1202 }
1203 break;
1204 case F_atoi:
1205 v.integer = atoi(args[0].string);
1206 break;
1207 case F_atof:
1208 v.floating = atof(args[0].string);
1209 break;
1210 case F_sqrt:
1211 v.floating = sqrt(args[0].floating);
1212 break;
1213 case F_cos:
1214 v.floating = cos(args[0].floating);
1215 break;
1216 case F_sin:
1217 v.floating = sin(args[0].floating);
1218 break;
1219 case F_atan2:
1220 v.floating = atan2(args[0].floating, args[1].floating);
1221 break;
1222 case F_exp:
1223 v.floating = exp(args[0].floating);
1224 break;
1225 case F_pow:
1226 v.floating = pow(args[0].floating, args[1].floating);
1227 break;
1228 case F_log:
1229 v.floating = log(args[0].floating);
1230 break;
1231 case F_min:
1232 v.floating = MIN(args[0].floating, args[1].floating);
1233 break;
1234 case F_max:
1235 v.floating = MAX(args[0].floating, args[1].floating);
1236 break;
1237 case F_sys:
1238 v.integer = system(args[0].string);
1239 break;
1240 case F_hasattr:
1241 case F_get: {
1242 objp = int2ptr(args[0].integer);
1243 char *name = args[1].string;
1244 if (!objp) {
1245 exerror("NULL object passed to aget()/hasAttr()");
1246 v.integer = 0;
1247 } else if (!name) {
1248 exerror("NULL name passed to aget()/hasAttr()");
1249 v.integer = 0;
1250 } else {
1251 Agsym_t *gsym = agattrsym(objp, name);
1252 if (sym->index == F_hasattr)
1253 v.integer = (gsym != NULL);
1254 else {
1255 if (!gsym) {
1256 gsym = agattr_text(agroot(agraphof(objp)), AGTYPE(objp), name, "");
1257 agxbuf tmp = {0};
1259 "Using value of %s uninitialized attribute \"%s\" of \"%s\" "
1260 "in aget()",
1261 kindOf(objp), name, nameOf(pgm, objp, &tmp));
1262 agxbfree(&tmp);
1263 }
1264 v.string = agxget(objp, gsym);
1265 }
1266 }
1267 break;
1268 }
1269 case F_set:
1270 objp = int2ptr(args[0].integer);
1271 if (!objp) {
1272 error(ERROR_WARNING, "NULL object passed to aset()");
1273 v.integer = 1;
1274 } else {
1275 char *name = args[1].string;
1276 char *value = args[2].string;
1277 if (!name) {
1278 error(ERROR_WARNING, "NULL name passed to aset()");
1279 v.integer = 1;
1280 } else if (!value) {
1281 error(ERROR_WARNING, "NULL value passed to aset()");
1282 v.integer = 1;
1283 } else {
1284 v.integer = setattr(objp, name, value);
1285 }
1286 }
1287 break;
1288 case F_dset:
1289 gp = int2ptr(args[0].integer);
1290 if (gp) {
1291 char *kind = args[1].string;
1292 char *name = args[2].string;
1293 char *value = args[3].string;
1294 if (!name) {
1295 error(ERROR_WARNING, "NULL name passed to setDflt()");
1296 v.integer = 1;
1297 } else if (!value) {
1298 error(ERROR_WARNING, "NULL value passed to setDflt()");
1299 v.integer = 1;
1300 } else if (!kind) {
1301 error(ERROR_WARNING, "NULL kind passed to setDflt()");
1302 v.integer = 1;
1303 } else {
1304 v.integer = setDfltAttr(gp, kind, name, value);
1305 }
1306 } else {
1307 error(ERROR_WARNING, "NULL graph passed to node()");
1308 v.integer = 0;
1309 }
1310 break;
1311 case F_fstattr:
1312 gp = int2ptr(args[0].integer);
1313 if (gp) {
1314 char *kind = args[1].string;
1315 if (!kind) {
1316 error(ERROR_ERROR, "NULL kind passed to fstAttr()");
1317 v.string = 0;
1318 } else {
1319 v.string = nxtAttr(gp, kind, NULL);
1320 }
1321 } else {
1322 exerror("NULL graph passed to fstAttr()");
1323 v.string = 0;
1324 }
1325 break;
1326 case F_nxtattr:
1327 case F_isattr:
1328 case F_dget:
1329 gp = int2ptr(args[0].integer);
1330 if (gp) {
1331 char *kind = args[1].string;
1332 char *name = args[2].string;
1333 if (!name) {
1334 exerror("NULL name passed to %s", sym->name);
1335 v.string = 0;
1336 } else if (!kind) {
1337 exerror("NULL kind passed to %s", sym->name);
1338 v.string = 0;
1339 } else if (sym->index == F_isattr) {
1340 v.integer = agattr_text(gp, toKind(kind, sym->name), name, 0) != NULL;
1341 } else if (sym->index == F_nxtattr) {
1342 v.string = nxtAttr(gp, kind, name);
1343 } else {
1344 v.string = getDfltAttr(gp, kind, name);
1345 }
1346 } else {
1347 exerror("NULL graph passed to %s", sym->name);
1348 v.string = 0;
1349 }
1350 break;
1351 case F_canon:
1352 v.string = canon(pgm, args[0].string);
1353 break;
1354 case F_ishtml:
1355 v.integer = aghtmlstr(args[0].string);
1356 break;
1357 case F_html:
1358 gp = int2ptr(args[0].integer);
1359 if (gp) {
1360 v.string = toHtml(gp, args[1].string);
1361 } else {
1362 error(ERROR_WARNING, "NULL graph passed to html()");
1363 v.string = 0;
1364 }
1365 break;
1366 case F_tolower:
1367 v.string = toLower(pgm, args[0].string);
1368 break;
1369 case F_colorx:
1370 v.string = colorx(pgm, args[0].string, args[1].string);
1371 break;
1372 case F_strcmp:
1373 if (args[0].string) {
1374 if (args[1].string)
1375 v.integer = strcmp(args[0].string, args[1].string);
1376 else
1377 v.integer = -1;
1378 } else if (args[1].string)
1379 v.integer = 1;
1380 else
1381 v.integer = 0;
1382 break;
1383 case F_toupper:
1384 v.string = toUpper(pgm, args[0].string);
1385 break;
1386 case F_xof:
1387 v.string = xyOf(pgm, args[0].string, true);
1388 break;
1389 case F_yof:
1390 v.string = xyOf(pgm, args[0].string, false);
1391 break;
1392 case F_llof:
1393 v.string = bbOf(pgm, args[0].string, true);
1394 break;
1395 case F_urof:
1396 v.string = bbOf(pgm, args[0].string, false);
1397 break;
1398 case F_length:
1399 v.integer = strlen(args[0].string);
1400 break;
1401 case F_index:
1402 v.integer = indexOf(args[0].string, args[1].string);
1403 break;
1404 case F_rindex:
1405 v.integer = rindexOf(args[0].string, args[1].string);
1406 break;
1407 case F_match: {
1408 const size_t m = match(args[0].string, args[1].string);
1409 if (m == SIZE_MAX) {
1410 v.integer = -1;
1411 } else {
1412 v.integer = (long long)m;
1413 }
1414 break;
1415 }
1416 case F_call:
1417 if ((bp = findBinding(state, args[0].string)))
1418 v.integer = (bp->fn)(args[1].string);
1419 else
1420 v.integer = -1;
1421 break;
1422 default:
1423 v.integer = -1;
1424 exerror("unknown function call: %s", sym->name);
1425 }
1426 return v;
1427 } else if (elt == EX_ARRAY) {
1428 args = env;
1429 state = disc->user;
1430 switch (sym->index) {
1431 case A_ARGV: {
1432 strview_t arg;
1433 if (getArg(args[0].integer, state, &arg) != 0) {
1434 return (Extype_t){0};
1435 }
1436 v.string = exstralloc(pgm, arg.size + 1);
1437 if (arg.size > 0) {
1438 memcpy(v.string, arg.data, arg.size);
1439 }
1440 break;
1441 }
1442 default:
1443 exerror("unknown array name: %s", sym->name);
1444 v.string = 0;
1445 }
1446 return v;
1447 }
1448
1449 state = env;
1450 if (ref) {
1451 objp = deref(pgm, node, ref, 0, state);
1452 if (!objp) {
1453 agxbuf xb = {0};
1454 exerror("null reference in expression %s", deparse(pgm, node, &xb));
1455 agxbfree(&xb);
1456 }
1457 } else if (sym->lex == ID && sym->index <= LAST_V) {
1458 switch (sym->index) {
1459 case V_this:
1460 v.integer = ptr2int(state->curobj);
1461 break;
1462 case V_thisg:
1463 v.integer = ptr2int(state->curgraph);
1464 break;
1465 case V_nextg:
1466 v.integer = ptr2int(state->nextgraph);
1467 break;
1468 case V_targt:
1469 v.integer = ptr2int(state->target);
1470 break;
1471 case V_outgraph:
1472 v.integer = ptr2int(state->outgraph);
1473 break;
1474 case V_tgtname:
1475 v.string = state->tgtname;
1476 break;
1477 case V_infname:
1478 if (state->infname == NULL) {
1479 agxbuf xb = {0};
1480 exerror("current input file is not defined for %s",
1481 deparse(pgm, node, &xb));
1482 agxbfree(&xb);
1483 return (Extype_t){0};
1484 }
1485 v.string = state->infname;
1486 break;
1487 case V_ARGC: {
1488 const size_t size = LIST_SIZE(&state->args);
1489 assert(size <= LLONG_MAX);
1490 v.integer = (long long)size;
1491 break;
1492 }
1493 case V_travtype:
1494 v.integer = state->tvt;
1495 break;
1496 case V_travroot:
1497 v.integer = ptr2int(state->tvroot);
1498 break;
1499 case V_travnext:
1500 v.integer = ptr2int(state->tvnext);
1501 break;
1502 case V_travedge:
1503 v.integer = ptr2int(state->tvedge);
1504 break;
1505 }
1506 return v;
1507 } else {
1508 objp = state->curobj;
1509 if (!objp) {
1510 agxbuf xb = {0};
1511 exerror("current object $ not defined as reference for %s",
1512 deparse(pgm, node, &xb));
1513 agxbfree(&xb);
1514 }
1515 }
1516
1517 if (objp) {
1518 if (lookup(pgm, objp, sym, &v)) {
1519 agxbuf xb = {0};
1520 exerror("in expression %s", deparse(pgm, node, &xb));
1521 agxbfree(&xb);
1522 v.integer = 0;
1523 }
1524 } else
1525 v.integer = 0;
1526
1527 return v;
1528}
1529
1530#define MINTYPE (LAST_M + 1) /* First type occurs after last M_ */
1531
1532static char *typeName(long op) { return typenames[op - MINTYPE]; }
1533
1534/* Set sym to value v.
1535 * Return -1 if not allowed.
1536 * Assume already type correct.
1537 */
1538static int setval(Expr_t *pgm, Exnode_t *x, Exid_t *sym, Exref_t *ref,
1539 void *env, Extype_t v) {
1540 Gpr_t *state;
1541 Agobj_t *objp;
1542 Agnode_t *np;
1543 int rv = 0;
1544
1545 state = env;
1546 if (ref) {
1547 objp = deref(pgm, x, ref, 0, state);
1548 if (!objp) {
1549 agxbuf xb = {0};
1550 exerror("in expression %s.%s", ref->symbol->name, deparse(pgm, x, &xb));
1551 agxbfree(&xb);
1552 return -1;
1553 }
1554 } else if (MINNAME <= sym->index && sym->index <= MAXNAME) {
1555 switch (sym->index) {
1556 case V_outgraph:
1557 state->outgraph = int2ptr(v.integer);
1558 break;
1559 case V_travtype: {
1560 long long iv = v.integer;
1561 if (validTVT(v.integer))
1562 state->tvt = (trav_type)iv;
1563 else
1564 error(1, "unexpected value %lld assigned to %s : ignored", iv,
1565 typeName(T_tvtyp));
1566 break;
1567 }
1568 case V_travroot:
1569 np = int2ptr(v.integer);
1570 if (!np || agroot(np) == state->curgraph)
1571 state->tvroot = np;
1572 else {
1573 error(1, "cannot set $tvroot, node %s not in $G : ignored",
1574 agnameof(np));
1575 }
1576 break;
1577 case V_travnext:
1578 np = int2ptr(v.integer);
1579 if (!np || agroot(np) == state->curgraph) {
1580 state->tvnext = np;
1581 state->flags |= GV_NEXT_SET;
1582 } else {
1583 error(1, "cannot set $tvnext, node %s not in $G : ignored",
1584 agnameof(np));
1585 }
1586 break;
1587 case V_tgtname:
1588 free(state->tgtname);
1589 state->tgtname = strdup(v.string);
1590 state->name_used = 0;
1591 break;
1592 default:
1593 rv = -1;
1594 break;
1595 }
1596 return rv;
1597 } else {
1598 objp = state->curobj;
1599 if (!objp) {
1600 agxbuf xb = {0};
1601 exerror("current object $ undefined in expression %s",
1602 deparse(pgm, x, &xb));
1603 agxbfree(&xb);
1604 return -1;
1605 }
1606 }
1607
1608 assignable(objp, (unsigned char *)sym->name);
1609 return setattr(objp, sym->name, v.string);
1610}
1611
1621 Extype_t v = {0};
1622 switch (rhs->index) {
1623 case A_ARGV: {
1624 Gpr_t *const state = disc->user;
1625 const size_t size = LIST_SIZE(&state->args);
1626 assert(size <= LLONG_MAX);
1627 v.integer = (long long)size;
1628 break;
1629 }
1630 default:
1631 exerror("unknown array name: %s", rhs->name);
1632 break;
1633 }
1634 return v;
1635}
1636
1646static int in(Extype_t lhs, Exid_t *rhs, Exdisc_t *disc) {
1647 switch (rhs->index) {
1648 case A_ARGV: {
1649 Gpr_t *const state = disc->user;
1650 return lhs.integer >= 0 &&
1651 (unsigned long long)lhs.integer < LIST_SIZE(&state->args);
1652 }
1653 default:
1654 exerror("unknown array name: %s", rhs->name);
1655 break;
1656 }
1657 return 0;
1658}
1659
1660static int codePhase;
1661
1662#define haveGraph (1 <= codePhase && codePhase <= 4)
1663#define haveTarget (2 <= codePhase && codePhase <= 4)
1664#define inWalk (2 <= codePhase && codePhase <= 3)
1665
1666/* typeChk:
1667 * Type check input type against implied type of symbol sym.
1668 * If okay, return result type; else return 0.
1669 * For functions, input type set must intersect with function domain.
1670 * This means type errors may occur, but these will be caught at runtime.
1671 * For non-functions, input type must be 0.
1672 */
1673static tctype typeChk(tctype intype, Exid_t *sym) {
1674 tctype dom = 0, rng = 0;
1675
1676 switch (sym->lex) {
1677 case DYNAMIC:
1678 dom = 0;
1679 switch (sym->type) {
1680 case T_obj:
1681 rng = YALL;
1682 break;
1683 case T_node:
1684 rng = Y(V);
1685 break;
1686 case T_graph:
1687 rng = Y(G);
1688 break;
1689 case T_edge:
1690 rng = Y(E);
1691 break;
1692 case INTEGER:
1693 rng = Y(I);
1694 break;
1695 case FLOATING:
1696 rng = Y(F);
1697 break;
1698 case STRING:
1699 rng = Y(S);
1700 break;
1701 default:
1702 exerror("unknown dynamic type %" PRIdMAX " of symbol %s",
1703 (intmax_t)sym->type, sym->name);
1704 break;
1705 }
1706 break;
1707 case ID:
1708 if (sym->index <= LAST_M) {
1709 switch (sym->index) {
1710 case V_travroot:
1711 case V_this:
1712 case V_thisg:
1713 case V_nextg:
1714 if (!haveGraph)
1715 exerror("keyword %s cannot be used in BEGIN/END statements",
1716 sym->name);
1717 break;
1718 case V_targt:
1719 if (!haveTarget)
1720 exerror("keyword %s cannot be used in BEGIN/BEG_G/END statements",
1721 sym->name);
1722 break;
1723 }
1724 dom = tchk[sym->index][0];
1725 rng = tchk[sym->index][1];
1726 } else {
1727 dom = YALL;
1728 rng = Y(S);
1729 }
1730 break;
1731 case NAME:
1732 if (!intype && !haveGraph)
1733 exerror("undeclared, unmodified names like \"%s\" cannot be\nused in "
1734 "BEGIN and END statements",
1735 sym->name);
1736 dom = YALL;
1737 rng = Y(S);
1738 break;
1739 default:
1740 exerror("unexpected symbol in typeChk: name %s, lex %" PRIdMAX, sym->name,
1741 (intmax_t)sym->lex);
1742 break;
1743 }
1744
1745 if (dom) {
1746 if (!intype)
1747 intype = YALL; /* type of $ */
1748 if (!(dom & intype))
1749 rng = 0;
1750 } else if (intype)
1751 rng = 0;
1752 return rng;
1753}
1754
1755// type check variable expression
1756static tctype typeChkExp(Exref_t *ref, Exid_t *sym) {
1757 tctype ty;
1758
1759 if (ref) {
1760 ty = typeChk(0, ref->symbol);
1761 for (ref = ref->next; ty && ref; ref = ref->next)
1762 ty = typeChk(ty, ref->symbol);
1763 if (!ty)
1764 return 0;
1765 } else
1766 ty = 0;
1767 return typeChk(ty, sym);
1768}
1769
1770/* Called during compilation for uses of references: abc.x
1771 * Also for abc.f(..), type abc.v, "abc".x and CONSTANTS.
1772 * The grammar has been altered to disallow the first 3.
1773 * Type check expressions; return value unused.
1774 */
1775static Extype_t refval(Expr_t *pgm, Exnode_t *node, Exid_t *sym, Exref_t *ref) {
1776
1777 Extype_t v;
1778 if (sym->lex == CONSTANT) {
1779 switch (sym->index) {
1780 case C_flat:
1781 v.integer = TV_flat;
1782 break;
1783 case C_ne:
1784 v.integer = TV_ne;
1785 break;
1786 case C_en:
1787 v.integer = TV_en;
1788 break;
1789 case C_bfs:
1790 v.integer = TV_bfs;
1791 break;
1792 case C_dfs:
1793 v.integer = TV_dfs;
1794 break;
1795 case C_fwd:
1796 v.integer = TV_fwd;
1797 break;
1798 case C_rev:
1799 v.integer = TV_rev;
1800 break;
1801 case C_postdfs:
1802 v.integer = TV_postdfs;
1803 break;
1804 case C_postfwd:
1805 v.integer = TV_postfwd;
1806 break;
1807 case C_postrev:
1808 v.integer = TV_postrev;
1809 break;
1810 case C_prepostdfs:
1812 break;
1813 case C_prepostfwd:
1815 break;
1816 case C_prepostrev:
1818 break;
1819 case C_null:
1820 v.integer = 0;
1821 break;
1822 default:
1823 v = exzero(node->type);
1824 break;
1825 }
1826 } else {
1827 if (!typeChkExp(ref, sym)) {
1828 agxbuf xb = {0};
1829 exerror("type error using %s", deparse(pgm, node, &xb));
1830 agxbfree(&xb);
1831 }
1832 v = exzero(node->type);
1833 }
1834 return v;
1835}
1836
1837/* Evaluate (l ex->op r) producing a value of type ex->type,
1838 * stored in l.
1839 * May be unary, with r = NULL
1840 * Return -1 if operation cannot be done, 0 otherwise.
1841 * If arg is != 0, operation unnecessary; just report possibility.
1842 */
1843static int binary(Exnode_t *l, Exnode_t *ex, Exnode_t *r, int arg) {
1844 Agobj_t *lobjp;
1845 Agobj_t *robjp;
1846 int ret = -1;
1847
1848 if (BUILTIN(l->type))
1849 return -1;
1850 if (r && BUILTIN(r->type))
1851 return -1;
1852 if (!INTEGRAL(ex->type))
1853 return -1;
1854
1855 if (l->type == T_tvtyp) {
1856
1857 if (!r)
1858 return -1; /* Assume libexpr handled unary */
1859 if (r->type != T_tvtyp)
1860 return -1;
1861
1862 long long li = l->data.constant.value.integer;
1863 long long ri = r->data.constant.value.integer;
1864 switch (ex->op) {
1865 case EQ:
1866 if (arg)
1867 return 0;
1868 l->data.constant.value.integer = li == ri;
1869 ret = 0;
1870 break;
1871 case NE:
1872 if (arg)
1873 return 0;
1874 l->data.constant.value.integer = li != ri;
1875 ret = 0;
1876 break;
1877 case '<':
1878 if (arg)
1879 return 0;
1880 l->data.constant.value.integer = li < ri;
1881 ret = 0;
1882 break;
1883 case LE:
1884 if (arg)
1885 return 0;
1886 l->data.constant.value.integer = li <= ri;
1887 ret = 0;
1888 break;
1889 case GE:
1890 if (arg)
1891 return 0;
1892 l->data.constant.value.integer = li >= ri;
1893 ret = 0;
1894 break;
1895 case '>':
1896 if (arg)
1897 return 0;
1898 l->data.constant.value.integer = li > ri;
1899 ret = 0;
1900 break;
1901 }
1902 }
1903
1904 /* l is a graph object; make sure r is also */
1905 if (r && r->type == T_tvtyp)
1906 return -1;
1907
1908 lobjp = int2ptr(l->data.constant.value.integer);
1909 if (r)
1910 robjp = int2ptr(r->data.constant.value.integer);
1911 else
1912 robjp = 0;
1913 switch (ex->op) {
1914 case EQ:
1915 if (arg)
1916 return 0;
1917 l->data.constant.value.integer = !compare(lobjp, robjp);
1918 ret = 0;
1919 break;
1920 case NE:
1921 if (arg)
1922 return 0;
1923 l->data.constant.value.integer = compare(lobjp, robjp);
1924 ret = 0;
1925 break;
1926 case '<':
1927 if (arg)
1928 return 0;
1929 l->data.constant.value.integer = compare(lobjp, robjp) < 0;
1930 ret = 0;
1931 break;
1932 case LE:
1933 if (arg)
1934 return 0;
1935 l->data.constant.value.integer = compare(lobjp, robjp) <= 0;
1936 ret = 0;
1937 break;
1938 case GE:
1939 if (arg)
1940 return 0;
1941 l->data.constant.value.integer = compare(lobjp, robjp) >= 0;
1942 ret = 0;
1943 break;
1944 case '>':
1945 if (arg)
1946 return 0;
1947 l->data.constant.value.integer = compare(lobjp, robjp) > 0;
1948 ret = 0;
1949 break;
1950 }
1951
1952 return ret;
1953}
1954
1955static int strToTvtype(char *s) {
1956 int rt = 0;
1957 char *sfx;
1958
1959 if (startswith(s, "TV_")) {
1960 sfx = s + 3;
1961 if (!strcmp(sfx, "flat")) {
1962 rt = TV_flat;
1963 } else if (!strcmp(sfx, "ne")) {
1964 rt = TV_ne;
1965 } else if (!strcmp(sfx, "en")) {
1966 rt = TV_en;
1967 } else if (!strcmp(sfx, "bfs")) {
1968 rt = TV_bfs;
1969 } else if (!strcmp(sfx, "dfs")) {
1970 rt = TV_dfs;
1971 } else if (!strcmp(sfx, "fwd")) {
1972 rt = TV_fwd;
1973 } else if (!strcmp(sfx, "rev")) {
1974 rt = TV_rev;
1975 } else if (!strcmp(sfx, "postdfs")) {
1976 rt = TV_postdfs;
1977 } else if (!strcmp(sfx, "postfwd")) {
1978 rt = TV_postfwd;
1979 } else if (!strcmp(sfx, "postrev")) {
1980 rt = TV_postrev;
1981 } else if (!strcmp(sfx, "prepostdfs")) {
1982 rt = TV_prepostdfs;
1983 } else if (!strcmp(sfx, "prepostfwd")) {
1984 rt = TV_prepostfwd;
1985 } else if (!strcmp(sfx, "prepostrev")) {
1986 rt = TV_prepostrev;
1987 } else
1988 exerror("illegal string \"%s\" for type tvtype_t", s);
1989 } else
1990 exerror("illegal string \"%s\" for type tvtype_t", s);
1991 return rt;
1992}
1993
1994static char *tvtypeToStr(long long v) {
1995 char *s = 0;
1996
1997 switch (v) {
1998 case TV_flat:
1999 s = "TV_flat";
2000 break;
2001 case TV_ne:
2002 s = "TV_ne";
2003 break;
2004 case TV_en:
2005 s = "TV_en";
2006 break;
2007 case TV_bfs:
2008 s = "TV_bfs";
2009 break;
2010 case TV_dfs:
2011 s = "TV_dfs";
2012 break;
2013 case TV_fwd:
2014 s = "TV_fwd";
2015 break;
2016 case TV_rev:
2017 s = "TV_rev";
2018 break;
2019 case TV_postdfs:
2020 s = "TV_postdfs";
2021 break;
2022 case TV_postfwd:
2023 s = "TV_postfwd";
2024 break;
2025 case TV_postrev:
2026 s = "TV_postrev";
2027 break;
2028 case TV_prepostdfs:
2029 s = "TV_prepostdfs";
2030 break;
2031 case TV_prepostfwd:
2032 s = "TV_prepostfwd";
2033 break;
2034 case TV_prepostrev:
2035 s = "TV_prepostrev";
2036 break;
2037 default:
2038 exerror("Unexpected value %lld for type tvtype_t", v);
2039 break;
2040 }
2041 return s;
2042}
2043
2044/* Convert value x to type string.
2045 * Assume x does not have a built-in type
2046 * Return -1 if conversion cannot be done, 0 otherwise.
2047 * If arg is != 0, conversion unnecessary; just report possibility.
2048 */
2049static int stringOf(Expr_t *prog, Exnode_t *x, int arg) {
2050 Agobj_t *objp;
2051 int rv = 0;
2052
2053 if (arg)
2054 return 0;
2055
2056 if (x->type == T_tvtyp) {
2057 if (!(x->data.constant.value.string =
2059 rv = -1;
2060 } else {
2061 objp = int2ptr(x->data.constant.value.integer);
2062 if (!objp) {
2063 exerror("cannot generate name for NULL %s", typeName(x->type));
2064 rv = -1;
2065 } else {
2066 agxbuf tmp = {0};
2067 x->data.constant.value.string = nameOf(prog, objp, &tmp);
2068 agxbfree(&tmp);
2069 }
2070 }
2071 x->type = STRING;
2072 return rv;
2073}
2074
2075/* Convert value x of type x->type to type type.
2076 * Return -1 if conversion cannot be done, 0 otherwise.
2077 * If arg is != 0, conversion unnecessary; just report possibility.
2078 * In particular, assume x != 0 if arg == 0.
2079 */
2080static int convert(Exnode_t *x, long type, int arg) {
2081 Agobj_t *objp;
2082 int ret = -1;
2083
2084 /* If both types are built-in, let libexpr handle */
2085 if (BUILTIN(type) && BUILTIN(x->type))
2086 return -1;
2087 if (type == T_obj && x->type <= T_obj)
2088 ret = 0; /* trivial cast from specific graph object to T_obj */
2089 else if (type <= T_obj && x->type == INTEGER) {
2090 if (x->data.constant.value.integer == 0)
2091 ret = 0; /* allow NULL pointer */
2092 } else if (type == INTEGER) {
2093 ret = 0;
2094 } else if (x->type == T_obj) {
2095 /* check dynamic type */
2096 if (arg) {
2097 if (type != FLOATING && type <= T_obj)
2098 ret = 0;
2099 } else {
2100 objp = int2ptr(x->data.constant.value.integer);
2101 switch (type) {
2102 case T_graph:
2103 if (!objp || AGTYPE(objp) == AGRAPH)
2104 ret = 0;
2105 break;
2106 case T_node:
2107 if (!objp || AGTYPE(objp) == AGNODE)
2108 ret = 0;
2109 break;
2110 case T_edge:
2111 if (!objp || isedge(objp))
2112 ret = 0;
2113 break;
2114 }
2115 }
2116 } else if (type == STRING) {
2117 if (x->type == T_tvtyp) {
2118 ret = 0;
2119 if (!arg) {
2122 }
2123 }
2124 } else if (type == T_tvtyp && x->type == INTEGER) {
2125 if (arg)
2126 ret = 0;
2127 else if (validTVT(x->data.constant.value.integer))
2128 ret = 0;
2129 else
2130 exerror("Integer value %lld not legal for type tvtype_t",
2132 }
2133 /* in case libexpr hands us the trivial case */
2134 else if (x->type == type) {
2135 ret = 0;
2136 } else if (x->type == STRING) {
2137 char *s;
2138 if (type == T_tvtyp) {
2139 if (arg)
2140 ret = 0;
2141 else {
2142 ret = 0;
2143 s = x->data.constant.value.string;
2145 }
2146 }
2147 }
2148 if (!arg && ret == 0)
2149 x->type = type;
2150 return ret;
2151}
2152
2153/* Calculate unique key for object.
2154 * We use this to unify local copies of nodes and edges.
2155 */
2156static Extype_t keyval(Extype_t v, long type) {
2157 if (type <= T_obj) {
2158 v.integer = AGID(int2ptr(v.integer));
2159 }
2160 return v;
2161}
2162
2163// convert type indices to symbolic name
2164static int a2t[] = {0, FLOATING, INTEGER, STRING,
2165 T_node, T_edge, T_graph, T_obj};
2166
2167// create and initialize expr discipline
2168static Exdisc_t *initDisc(Gpr_t *state) {
2169 Exdisc_t *dp = calloc(1, sizeof(Exdisc_t));
2170 if (!dp) {
2171 error(ERROR_ERROR, "could not create libexp discipline: out of memory");
2172 return 0;
2173 }
2174
2175 dp->version = EX_VERSION;
2177 dp->symbols = symbols;
2178 dp->convertf = convert;
2179 dp->stringof = stringOf;
2180 dp->binaryf = binary;
2181 dp->typename = typeName;
2182 if (state->errf)
2183 dp->errorf = state->errf;
2184 else
2185 dp->errorf = errorf;
2186 dp->keyf = keyval;
2187 dp->getf = getval;
2188 dp->reff = refval;
2189 dp->setf = setval;
2190 dp->lengthf = length;
2191 dp->inf = in;
2192 dp->exitf = state->exitf;
2193 dp->types = a2t;
2194 dp->user = state;
2195
2196 state->dp = dp; /* dp is freed when state is freed */
2197
2198 return dp;
2199}
2200
2201/* Compile given string, then extract and return
2202 * typed expression.
2203 */
2204static Exnode_t *compile(Expr_t *prog, char *src, char *input, int line,
2205 const char *lbl, const char *sfx, int kind) {
2206 Exnode_t *e = 0;
2207 int rv;
2208
2209 /* create input stream */
2210 FILE *sf = tmpfile();
2211 assert(sf != NULL);
2212 if (input) {
2213 fputs(input, sf);
2214 }
2215 if (sfx) {
2216 fputs(sfx, sf);
2217 }
2218 if (fseek(sf, 0, SEEK_SET) < 0) {
2219 error(ERROR_ERROR, "failed to seek temporary file");
2220 (void)fclose(sf);
2221 return NULL;
2222 }
2223
2224 /* prefixing label if necessary */
2225 agxbuf label = {0};
2226 if (lbl) {
2227 agxbprint(&label, "%s:\n", lbl);
2228 line--;
2229 }
2230
2231 if (!src)
2232 src = "<command line>";
2233 rv = excomp(prog, src, line, sf, lbl ? agxbdisown(&label) : NULL);
2234 fclose(sf);
2235
2236 if (rv >= 0 && getErrorErrors() == 0)
2237 e = exexpr(prog, lbl, kind);
2238
2239 return e;
2240}
2241
2242// check if guard is an assignment and warn
2243static void checkGuard(Exnode_t *gp, char *src, int line) {
2244 gp = exnoncast(gp);
2245 if (gp && exisAssign(gp)) {
2246 if (src) {
2247 setErrorFileLine(src, line);
2248 }
2249 error(ERROR_WARNING, "assignment used as bool in guard");
2250 }
2251}
2252
2253static case_stmt *mkStmts(Expr_t *prog, char *src, case_infos_t cases,
2254 const char *lbl) {
2255 agxbuf tmp = {0};
2256
2257 case_stmt *cs = gv_calloc(LIST_SIZE(&cases), sizeof(case_stmt));
2258
2259 for (size_t i = 0; i < LIST_SIZE(&cases); i++) {
2260 case_info *sp = LIST_AT(&cases, i);
2261 if (sp->guard) {
2262 agxbprint(&tmp, "%s_g%" PRISIZE_T, lbl, i);
2263 cs[i].guard =
2264 compile(prog, src, sp->guard, sp->gstart, agxbuse(&tmp), 0, INTEGER);
2265 if (getErrorErrors())
2266 break;
2267 checkGuard(cs[i].guard, src, sp->gstart);
2268 }
2269 if (sp->action) {
2270 agxbprint(&tmp, "%s_a%" PRISIZE_T, lbl, i);
2271 cs[i].action =
2272 compile(prog, src, sp->action, sp->astart, agxbuse(&tmp), 0, INTEGER);
2273 if (getErrorErrors())
2274 break;
2275 /* If no error but no compiled action, the input action must
2276 * have been essentially an empty block, which should be
2277 * considered different from a missing block. So, compile a
2278 * trivial block.
2279 */
2280 if (!cs[i].action) {
2281 agxbprint(&tmp, "%s__a%" PRISIZE_T, lbl, i);
2282 cs[i].action =
2283 compile(prog, src, "1", sp->astart, agxbuse(&tmp), 0, INTEGER);
2284 }
2285 }
2286 }
2287 agxbfree(&tmp);
2288 return cs;
2289}
2290
2292static bool mkBlock(comp_block *bp, Expr_t *prog, char *src, parse_block *inp,
2293 size_t i) {
2294 bool has_begin_g = false; // does this block use a `BEG_G` statement?
2295
2296 codePhase = 1;
2297 if (inp->begg_stmt) {
2298 static const char PREFIX[] = "_begin_g_";
2299 agxbuf label = {0};
2300 agxbprint(&label, "%s%" PRISIZE_T, PREFIX, i);
2301 symbols[0].type = T_graph;
2302 tchk[V_this][1] = Y(G);
2303 bp->begg_stmt = compile(prog, src, inp->begg_stmt, inp->l_beging,
2304 agxbuse(&label), 0, VOIDTYPE);
2305 agxbfree(&label);
2306 if (getErrorErrors())
2307 goto finishBlk;
2308 has_begin_g = true;
2309 }
2310
2311 codePhase = 2;
2312 if (!LIST_IS_EMPTY(&inp->node_stmts)) {
2313 static const char PREFIX[] = "_nd";
2314 agxbuf label = {0};
2315 symbols[0].type = T_node;
2316 tchk[V_this][1] = Y(V);
2317 bp->n_nstmts = LIST_SIZE(&inp->node_stmts);
2318 agxbprint(&label, "%s%" PRISIZE_T, PREFIX, i);
2319 bp->node_stmts = mkStmts(prog, src, inp->node_stmts, agxbuse(&label));
2320 agxbfree(&label);
2321 if (getErrorErrors())
2322 goto finishBlk;
2323 bp->does_walk_graph = true;
2324 }
2325
2326 codePhase = 3;
2327 if (!LIST_IS_EMPTY(&inp->edge_stmts)) {
2328 static const char PREFIX[] = "_eg";
2329 agxbuf label = {0};
2330 symbols[0].type = T_edge;
2331 tchk[V_this][1] = Y(E);
2332 bp->n_estmts = LIST_SIZE(&inp->edge_stmts);
2333 agxbprint(&label, "%s%" PRISIZE_T, PREFIX, i);
2334 bp->edge_stmts = mkStmts(prog, src, inp->edge_stmts, agxbuse(&label));
2335 agxbfree(&label);
2336 if (getErrorErrors())
2337 goto finishBlk;
2338 bp->does_walk_graph = true;
2339 }
2340
2341finishBlk:
2342 if (getErrorErrors()) {
2343 free(bp->node_stmts);
2344 free(bp->edge_stmts);
2345 bp->node_stmts = 0;
2346 bp->edge_stmts = 0;
2347 }
2348
2349 return has_begin_g || bp->does_walk_graph;
2350}
2351
2352// convert command line flags to actions in END_G
2353static const char *doFlags(compflags_t flags) {
2354 if (flags.srcout) {
2355 if (flags.induce) {
2356 return "\n$O = $G;\ninduce($O);\n";
2357 }
2358 return "\n$O = $G;\n";
2359 }
2360 if (flags.induce) {
2361 return "\ninduce($O);\n";
2362 }
2363 return "\n";
2364}
2365
2366// convert gpr sections in libexpr program
2368 const char *endg_sfx = NULL;
2369 bool uses_graph = false;
2370
2371 /* Make sure we have enough bits for types */
2372 assert(CHAR_BIT * sizeof(tctype) >= (1 << TBITS));
2373
2374 comp_prog *p = calloc(1, sizeof(comp_prog));
2375 if (!p) {
2376 error(ERROR_ERROR, "could not create compiled program: out of memory");
2377 goto finish;
2378 }
2379
2380 if (flags.srcout || flags.induce || flags.clone) {
2381 endg_sfx = doFlags(flags);
2382 }
2383
2384 if (!initDisc(state))
2385 goto finish;
2386
2387 exinit();
2388 if (!(p->prog = exopen(state->dp)))
2389 goto finish;
2390
2391 codePhase = 0;
2392 if (inp->begin_stmt) {
2393 p->begin_stmt = compile(p->prog, inp->source, inp->begin_stmt, inp->l_begin,
2394 0, 0, VOIDTYPE);
2395 if (getErrorErrors())
2396 goto finish;
2397 }
2398
2399 if (!LIST_IS_EMPTY(&inp->blocks)) {
2400 comp_block *bp;
2401
2402 p->blocks = bp = gv_calloc(LIST_SIZE(&inp->blocks), sizeof(comp_block));
2403
2404 for (size_t i = 0; i < LIST_SIZE(&inp->blocks); bp++, i++) {
2405 parse_block *ibp = LIST_AT(&inp->blocks, i);
2406 uses_graph |= mkBlock(bp, p->prog, inp->source, ibp, i);
2407 if (getErrorErrors())
2408 goto finish;
2409 p->n_blocks++;
2410 }
2411 }
2412 p->uses_graph = uses_graph;
2413
2414 codePhase = 4;
2415 if (inp->endg_stmt || endg_sfx) {
2416 symbols[0].type = T_graph;
2417 tchk[V_this][1] = Y(G);
2418 p->endg_stmt = compile(p->prog, inp->source, inp->endg_stmt, inp->l_endg,
2419 "_end_g", endg_sfx, VOIDTYPE);
2420 if (getErrorErrors())
2421 goto finish;
2422 }
2423
2424 codePhase = 5;
2425 if (inp->end_stmt) {
2426 symbols[0].type = T_obj;
2427 p->end_stmt = compile(p->prog, inp->source, inp->end_stmt, inp->l_end,
2428 "_end_", 0, VOIDTYPE);
2429 if (getErrorErrors())
2430 goto finish;
2431 }
2432 setErrorLine(0); /* execution errors have no line numbers */
2433
2434 if (p->end_stmt)
2435 p->uses_graph = true;
2436
2437finish:
2438 if (getErrorErrors()) {
2439 freeCompileProg(p);
2440 p = 0;
2441 }
2442
2443 return p;
2444}
2445
2447 comp_block *bp;
2448
2449 if (!p)
2450 return;
2451
2452 exclose(p->prog);
2453 for (size_t i = 0; i < p->n_blocks; i++) {
2454 bp = p->blocks + i;
2455 free(bp->node_stmts);
2456 free(bp->edge_stmts);
2457 }
2458 free(p->blocks);
2459 free(p);
2460}
2461
2462/* Read graph from file and initialize
2463 * dynamic data.
2464 */
2465Agraph_t *readG(FILE *fp) {
2466 Agraph_t *g = agread(fp, NULL);
2467 if (g) {
2468 aginit(g, AGRAPH, UDATA, sizeof(gdata), false);
2469 aginit(g, AGNODE, UDATA, sizeof(ndata), false);
2470 aginit(g, AGEDGE, UDATA, sizeof(edata), false);
2471 }
2472 return g;
2473}
2474
2475// open graph and initialize dynamic data
2476Agraph_t *openG(char *name, Agdesc_t desc) {
2477 Agraph_t *g = agopen(name, desc, NULL);
2478 if (g)
2479 agbindrec(g, UDATA, sizeof(gdata), false);
2480 return g;
2481}
2482
2483// open subgraph and initialize dynamic data
2484Agraph_t *openSubg(Agraph_t *g, char *name) {
2485 Agraph_t *sg;
2486
2487 sg = agsubg(g, name, 1);
2488 if (sg && !aggetrec(sg, UDATA, 0))
2489 agbindrec(sg, UDATA, sizeof(gdata), false);
2490 return sg;
2491}
2492
2493// create node and initialize dynamic data
2494Agnode_t *openNode(Agraph_t *g, char *name) {
2495 Agnode_t *np;
2496
2497 np = agnode(g, name, 1);
2498 if (np && !aggetrec(np, UDATA, 0))
2499 agbindrec(np, UDATA, sizeof(ndata), false);
2500 return np;
2501}
2502
2503// create edge and initialize dynamic data
2504Agedge_t *openEdge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *key) {
2505 Agedge_t *ep;
2506 Agraph_t *root;
2507
2508 root = sameG(t, h, "openEdge", "tail and head nodes");
2509 if (!root)
2510 return 0;
2511 if (g) {
2512 if (!sameG(g, root, "openEdge", "subgraph and nodes"))
2513 return 0;
2514 } else
2515 g = root;
2516
2517 ep = agedge(g, t, h, key, 1);
2518 if (ep && !aggetrec(ep, UDATA, 0))
2519 agbindrec(ep, UDATA, sizeof(edata), false);
2520 return ep;
2521}
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:1756
static char * getDfltAttr(Agraph_t *gp, char *k, char *name)
Definition compile.c:546
#define MINTYPE
Definition compile.c:1530
Agedge_t * openEdge(Agraph_t *g, Agnode_t *t, Agnode_t *h, char *key)
Definition compile.c:2504
comp_prog * compileProg(parse_prog *inp, Gpr_t *state, compflags_t flags)
Definition compile.c:2367
static bool mkBlock(comp_block *bp, Expr_t *prog, char *src, parse_block *inp, size_t i)
Definition compile.c:2292
#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:1673
static int codePhase
Definition compile.c:1660
static int strToTvtype(char *s)
Definition compile.c:1955
static case_stmt * mkStmts(Expr_t *prog, char *src, case_infos_t cases, const char *lbl)
Definition compile.c:2253
Agraph_t * openG(char *name, Agdesc_t desc)
Definition compile.c:2476
#define haveTarget
Definition compile.c:1663
Agraph_t * openSubg(Agraph_t *g, char *name)
Definition compile.c:2484
static char * tvtypeToStr(long long v)
Definition compile.c:1994
static int stringOf(Expr_t *prog, Exnode_t *x, int arg)
Definition compile.c:2049
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:1843
static char * bbOf(Expr_t *pgm, char *pt, bool getll)
Definition compile.c:91
void freeCompileProg(comp_prog *p)
Definition compile.c:2446
static Exdisc_t * initDisc(Gpr_t *state)
Definition compile.c:2168
#define haveGraph
Definition compile.c:1662
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:558
Agnode_t * openNode(Agraph_t *g, char *name)
Definition compile.c:2494
static char * typeName(long op)
Definition compile.c:1532
Agraph_t * readG(FILE *fp)
Definition compile.c:2465
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:2164
static int setDfltAttr(Agraph_t *gp, char *k, char *name, char *value)
Definition compile.c:475
static const char * doFlags(compflags_t flags)
Definition compile.c:2353
static Extype_t refval(Expr_t *pgm, Exnode_t *node, Exid_t *sym, Exref_t *ref)
Definition compile.c:1775
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:2204
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:2080
static int setval(Expr_t *pgm, Exnode_t *x, Exid_t *sym, Exref_t *ref, void *env, Extype_t v)
Definition compile.c:1538
static char * nxtAttr(Agraph_t *gp, char *k, char *name)
Definition compile.c:522
static Extype_t keyval(Extype_t v, long type)
Definition compile.c:2156
static int getArg(long long n, Gpr_t *state, strview_t *out)
Definition compile.c:465
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:2243
static Extype_t length(Exid_t *rhs, Exdisc_t *disc)
Definition compile.c:1620
static int toKind(char *k, char *fn)
Definition compile.c:507
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:1646
#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:1951
void * exstralloc(Expr_t *ex, size_t sz)
Definition exeval.c:1959
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