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