%{ /* This software may only be used by you under license from AT&T Corp. ("AT&T"). A copy of AT&T's Source Code Agreement is available at AT&T's Internet website having the URL: If you received this software without first entering into a license with AT&T, you have an infringing copy of this software and cannot use it without violating AT&T's intellectual property rights. */ #pragma prototyped #include "libgraph.h" #ifdef DMALLOC #include "dmalloc.h" #endif #ifndef HAVE_STRDUP extern char *strdup(const char*); #endif static char Port[SMALLBUF],*Symbol; static char In_decl,In_edge_stmt; static int Current_class,Agraph_type; static Agraph_t *G; static Agnode_t *N; static Agedge_t *E; static objstack_t *SP; static Agraph_t *Gstack[32]; static int GSP; static int override; /* agoverride: * If override == 1, initial attr_stmt is ignored if the attribute is * already defined. * If override > 2, any attr_stmt is ignored if the attribute is * already defined with non-trivial default. */ void agoverride(int ov) { override = (ov > 0 ? ov : 0); } static void push_subg(Agraph_t *g) { G = Gstack[GSP++] = g; } static Agraph_t *pop_subg(void) { Agraph_t *g; if (GSP == 0) { agerr (AGERR, "Gstack underflow in graph parser\n"); exit(1); } g = Gstack[--GSP]; /* graph being popped off */ if (GSP > 0) G = Gstack[GSP - 1]; /* current graph */ else G = 0; return g; } static objport_t pop_gobj(void) { objport_t rv; rv.obj = pop_subg(); rv.port = NULL; return rv; } static void anonname(char* buf) { static int anon_id = 0; sprintf(buf,"_anonymous_%d",anon_id++); } static void begin_graph(char *name) { Agraph_t *g; char buf[SMALLBUF]; if (!name) { anonname(buf); name = buf; } g = AG.parsed_g = agopen(name,Agraph_type); push_subg(g); In_decl = TRUE; } static void end_graph(void) { pop_subg(); } static Agnode_t *bind_node(char *name) { Agnode_t *n = agnode(G,name); In_decl = FALSE; return n; } static void anonsubg(void) { char buf[SMALLBUF]; Agraph_t *subg; In_decl = FALSE; anonname(buf); subg = agsubg(G,buf); push_subg(subg); } #if 0 /* NOT USED */ static int isanonsubg(Agraph_t *g) { return (strncmp("_anonymous_",g->name,11) == 0); } #endif static void begin_edgestmt(objport_t objp) { struct objstack_t *new_sp; new_sp = NEW(objstack_t); new_sp->link = SP; SP = new_sp; SP->list = SP->last = NEW(objlist_t); SP->list->data = objp; SP->list->link = NULL; SP->in_edge_stmt = In_edge_stmt; SP->subg = G; agpushproto(G); In_edge_stmt = TRUE; } static void mid_edgestmt(objport_t objp) { SP->last->link = NEW(objlist_t); SP->last = SP->last->link; SP->last->data = objp; SP->last->link = NULL; } static void end_edgestmt(void) { objstack_t *old_SP; objlist_t *tailptr,*headptr,*freeptr; Agraph_t *t_graph,*h_graph; Agnode_t *t_node,*h_node,*t_first,*h_first; Agedge_t *e; char *tport,*hport; for (tailptr = SP->list; tailptr->link; tailptr = tailptr->link) { headptr = tailptr->link; tport = tailptr->data.port; hport = headptr->data.port; if (TAG_OF(tailptr->data.obj) == TAG_NODE) { t_graph = NULL; t_first = (Agnode_t*)(tailptr->data.obj); } else { t_graph = (Agraph_t*)(tailptr->data.obj); t_first = agfstnode(t_graph); } if (TAG_OF(headptr->data.obj) == TAG_NODE) { h_graph = NULL; h_first = (Agnode_t*)(headptr->data.obj); } else { h_graph = (Agraph_t*)(headptr->data.obj); h_first = agfstnode(h_graph); } for (t_node = t_first; t_node; t_node = t_graph ? agnxtnode(t_graph,t_node) : NULL) { for (h_node = h_first; h_node; h_node = h_graph ? agnxtnode(h_graph,h_node) : NULL ) { e = agedge(G,t_node,h_node); if (e) { char *tp = tport; char *hp = hport; if ((e->tail != e->head) && (e->head == t_node)) { /* could happen with an undirected edge */ char *temp; temp = tp; tp = hp; hp = temp; } if (tp && tp[0]) agxset(e,TAILX,tp); if (hp && hp[0]) agxset(e,HEADX,hp); } } } } tailptr = SP->list; while (tailptr) { freeptr = tailptr; tailptr = tailptr->link; if (TAG_OF(freeptr->data.obj) == TAG_NODE) free(freeptr->data.port); free(freeptr); } if (G != SP->subg) abort(); agpopproto(G); In_edge_stmt = SP->in_edge_stmt; old_SP = SP; SP = SP->link; In_decl = FALSE; free(old_SP); Current_class = TAG_GRAPH; } #if 0 /* NOT USED */ static Agraph_t *parent_of(Agraph_t *g) { Agraph_t *rv; rv = agusergraph(agfstin(g->meta_node->graph,g->meta_node)->tail); return rv; } #endif static void attr_set(char *name, char *value) { Agsym_t *ap = NULL; char *defval = ""; if (In_decl && (G->root == G)) defval = value; switch (Current_class) { case TAG_NODE: ap = agfindattr(G->proto->n,name); if (ap == NULL) ap = agnodeattr(AG.parsed_g,name,defval); else if (override && (In_decl || ((override > 1) && (N == G->proto->n) && *(ap->value)))) return; agxset(N,ap->index,value); break; case TAG_EDGE: ap = agfindattr(G->proto->e,name); if (ap == NULL) ap = agedgeattr(AG.parsed_g,name,defval); else if (override && (In_decl || ((override > 1) && (E == G->proto->e) && *(ap->value)))) return; agxset(E,ap->index,value); break; case 0: /* default */ case TAG_GRAPH: ap = agfindattr(G,name); if (ap == NULL) ap = agraphattr(AG.parsed_g,name,defval); else if (override && (In_decl || ((override > 1) && *(ap->value)))) return; agxset(G,ap->index,value); break; } } /* concat: */ static char* concat (char* s1, char* s2) { char* s; char buf[BUFSIZ]; char* sym; int len = strlen(s1) + strlen(s2) + 1; if (len <= BUFSIZ) sym = buf; else sym = (char*)malloc(len); strcpy(sym,s1); strcat(sym,s2); s = agstrdup (sym); if (sym != buf) free (sym); return s; } %} %union { int i; char *str; struct objport_t obj; struct Agnode_t *n; } %token T_graph T_digraph T_strict %token T_node T_edge T_edgeop %token T_symbol T_qsymbol %type symbol qsymbol optgraphname %type node_name %type node_id subg_stmt %left T_subgraph /* to avoid subgraph hdr shift/reduce conflict */ %left '{' %% file : graph_type optgraphname {begin_graph($2); agstrfree($2);} '{' stmt_list '}' {AG.accepting_state = TRUE; end_graph();} | error { if (AG.parsed_g) agclose(AG.parsed_g); AG.parsed_g = NULL; /*exit(1);*/ } | /* empty*/ {AG.parsed_g = NULL;} ; optgraphname: symbol {$$=$1;} | /* empty */ {$$=0;} ; /* it is safe to change graph type and name before contents appear */ graph_type : T_graph {Agraph_type = AGRAPH; AG.edge_op = "--";} | T_strict T_graph {Agraph_type = AGRAPHSTRICT; AG.edge_op = "--";} | T_digraph {Agraph_type = AGDIGRAPH; AG.edge_op = "->";} | T_strict T_digraph {Agraph_type = AGDIGRAPHSTRICT; AG.edge_op = "->";} ; attr_class : T_graph {Current_class = TAG_GRAPH;} | T_node {Current_class = TAG_NODE; N = G->proto->n;} | T_edge {Current_class = TAG_EDGE; E = G->proto->e;} ; inside_attr_list : iattr_set optcomma inside_attr_list | /* empty */ ; optcomma : /* empty */ | ',' ; attr_list : '[' inside_attr_list ']' ; rec_attr_list : rec_attr_list attr_list | /* empty */ ; opt_attr_list : rec_attr_list ; attr_set : symbol '=' symbol {attr_set($1,$3); agstrfree($1); agstrfree($3);} ; iattr_set : attr_set | symbol {attr_set($1,"true"); agstrfree($1); } ; stmt_list : stmt_list1 | /* empty */ ; stmt_list1 : stmt | stmt_list1 stmt ; stmt : stmt1 | stmt1 ';' | error {agerror("syntax error, statement skipped");} ; stmt1 : node_stmt | edge_stmt | attr_stmt | subg_stmt {} ; attr_stmt : attr_class attr_list {Current_class = TAG_GRAPH; /* reset */} | attr_set {Current_class = TAG_GRAPH;} ; node_id : node_name node_port { objport_t rv; rv.obj = $1; rv.port = strdup(Port); Port[0] = '\0'; $$ = rv; } ; node_name : symbol {$$ = bind_node($1); agstrfree($1);} ; node_port : /* empty */ | port_location | port_angle /* undocumented */ | port_angle port_location /* undocumented */ | port_location port_angle /* undocumented */ ; port_location : ':' symbol {strcat(Port,":"); strcat(Port,$2);} | ':' '(' symbol {Symbol = strdup($3);} ',' symbol ')' { char buf[SMALLBUF]; sprintf(buf,":(%s,%s)",Symbol,$6); strcat(Port,buf); free(Symbol); } ; port_angle : '@' symbol { char buf[SMALLBUF]; sprintf(buf,"@%s",$2); strcat(Port,buf); } ; node_stmt : node_id {Current_class = TAG_NODE; N = (Agnode_t*)($1.obj);} opt_attr_list {Current_class = TAG_GRAPH; /* reset */} ; edge_stmt : node_id {begin_edgestmt($1);} edgeRHS { E = SP->subg->proto->e; Current_class = TAG_EDGE; } opt_attr_list {end_edgestmt();} | subg_stmt {begin_edgestmt($1);} edgeRHS { E = SP->subg->proto->e; Current_class = TAG_EDGE; } opt_attr_list {end_edgestmt();} ; edgeRHS : T_edgeop node_id {mid_edgestmt($2);} | T_edgeop node_id {mid_edgestmt($2);} edgeRHS | T_edgeop subg_stmt {mid_edgestmt($2);} | T_edgeop subg_stmt {mid_edgestmt($2);} edgeRHS ; subg_stmt : subg_hdr '{' stmt_list '}'%prec '{' {$$ = pop_gobj();} | T_subgraph '{' { anonsubg(); } stmt_list '}' {$$ = pop_gobj();} | '{' { anonsubg(); } stmt_list '}' {$$ = pop_gobj();} | subg_hdr %prec T_subgraph {$$ = pop_gobj();} ; subg_hdr : T_subgraph symbol { Agraph_t *subg; if ((subg = agfindsubg(AG.parsed_g,$2))) aginsert(G,subg); else subg = agsubg(G,$2); push_subg(subg); In_decl = FALSE; agstrfree($2); } ; symbol : T_symbol {$$ = $1; } | qsymbol {$$ = $1; } ; qsymbol : T_qsymbol {$$ = $1; } | qsymbol '+' T_qsymbol {$$ = concat($1,$3); agstrfree($1); agstrfree($3);} ;