grammar.y 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. /**
  2. * @file
  3. * @ingroup cgraph_core
  4. */
  5. /*************************************************************************
  6. * Copyright (c) 2011 AT&T Intellectual Property
  7. * All rights reserved. This program and the accompanying materials
  8. * are made available under the terms of the Eclipse Public License v1.0
  9. * which accompanies this distribution, and is available at
  10. * https://www.eclipse.org/legal/epl-v10.html
  11. *
  12. * Contributors: Details at https://graphviz.org
  13. *************************************************************************/
  14. %require "3.0"
  15. /* By default, Bison emits a parser using symbols prefixed with "yy". Graphviz
  16. * contains multiple Bison-generated parsers, so we alter this prefix to avoid
  17. * symbol clashes.
  18. */
  19. %define api.prefix {aag}
  20. %code requires {
  21. #include <cghdr.h>
  22. #include <util/agxbuf.h>
  23. struct gstack_s;
  24. struct aagextra_s {
  25. int dummy; /* struct must not be empty */
  26. /* Common */
  27. /* Parser */
  28. /* Lexer */
  29. };
  30. }
  31. %{
  32. #include <stdbool.h>
  33. #include <stdio.h>
  34. #include <cghdr.h>
  35. #include <stddef.h>
  36. #include <util/alloc.h>
  37. #include <util/streq.h>
  38. #include <util/unreachable.h>
  39. extern void aagerror(const char*);
  40. static const char Key[] = "key";
  41. static int SubgraphDepth = 0;
  42. typedef union s { /* possible items in generic list */
  43. Agnode_t *n;
  44. Agraph_t *subg;
  45. Agedge_t *e;
  46. Agsym_t *asym; /* bound attribute */
  47. char *name; /* unbound attribute */
  48. struct item_s *list; /* list-of-lists (for edgestmt) */
  49. } val_t;
  50. typedef struct item_s { /* generic list */
  51. int tag; /* T_node, T_subgraph, T_edge, T_attr */
  52. val_t u; /* primary element */
  53. char *str; /* secondary value - port or attr value */
  54. struct item_s *next;
  55. } item;
  56. typedef struct list_s { /* maintain head and tail ptrs for fast append */
  57. item *first;
  58. item *last;
  59. } list_t;
  60. typedef struct gstack_s {
  61. Agraph_t *g;
  62. Agraph_t *subg;
  63. list_t nodelist,edgelist,attrlist;
  64. struct gstack_s *down;
  65. } gstack_t;
  66. /* functions */
  67. static void appendnode(char *name, char *port, char *sport);
  68. static void attrstmt(int tkind, char *macroname);
  69. static void startgraph(char *name, bool directed, bool strict);
  70. static void getedgeitems(void);
  71. static void newedge(Agnode_t *t, char *tport, Agnode_t *h, char *hport, char *key);
  72. static void edgerhs(Agnode_t *n, char *tport, item *hlist, char *key);
  73. static void appendattr(char *name, char *value);
  74. static void bindattrs(int kind);
  75. static void applyattrs(void *obj);
  76. static void endgraph(void);
  77. static void endnode(void);
  78. static void endedge(void);
  79. static void freestack(void);
  80. static char* concat(char*, char*);
  81. static char* concatPort(char*, char*);
  82. static void opensubg(char *name);
  83. static void closesubg(void);
  84. /* global */
  85. static Agraph_t *G; /* top level graph */
  86. static Agdisc_t *Disc; /* discipline passed to agread or agconcat */
  87. static gstack_t *S;
  88. %}
  89. %union {
  90. int i;
  91. char *str;
  92. struct Agnode_s *n;
  93. }
  94. %token <i> T_graph T_node T_edge T_digraph T_subgraph T_strict T_edgeop
  95. /* T_list, T_attr are internal tags, not really tokens */
  96. %token T_list T_attr
  97. %token <str> T_atom T_qatom
  98. %type <i> optstrict graphtype rcompound attrtype
  99. %type <str> optsubghdr optgraphname optmacroname atom qatom
  100. %%
  101. graph : hdr body {freestack(); endgraph();}
  102. | error {if (G) {freestack(); endgraph(); agclose(G); G = Ag_G_global = NULL;}}
  103. | /* empty */
  104. ;
  105. body : '{' optstmtlist '}' ;
  106. hdr : optstrict graphtype optgraphname {startgraph($3,$2 != 0,$1 != 0);}
  107. ;
  108. optgraphname: atom {$$=$1;} | /* empty */ {$$=0;} ;
  109. optstrict : T_strict {$$=1;} | /* empty */ {$$=0;} ;
  110. graphtype : T_graph {$$ = 0;} | T_digraph {$$ = 1;} ;
  111. optstmtlist : stmtlist | /* empty */ ;
  112. stmtlist : stmtlist stmt | stmt ;
  113. optsemi : ';' | ;
  114. stmt : attrstmt optsemi
  115. | compound optsemi
  116. ;
  117. compound : simple rcompound optattr
  118. {if ($2) endedge(); else endnode();}
  119. ;
  120. simple : nodelist | subgraph ;
  121. rcompound : T_edgeop {getedgeitems();} simple {getedgeitems();} rcompound {$$ = 1;}
  122. | /* empty */ {$$ = 0;}
  123. ;
  124. nodelist : node | nodelist ',' node ;
  125. node : atom {appendnode($1,NULL,NULL);}
  126. | atom ':' atom {appendnode($1,$3,NULL);}
  127. | atom ':' atom ':' atom {appendnode($1,$3,$5);}
  128. ;
  129. attrstmt : attrtype optmacroname attrlist {attrstmt($1,$2);}
  130. | graphattrdefs {attrstmt(T_graph,NULL);}
  131. ;
  132. attrtype : T_graph {$$ = T_graph;}
  133. | T_node {$$ = T_node;}
  134. | T_edge {$$ = T_edge;}
  135. ;
  136. optmacroname : atom '=' {$$ = $1;}
  137. | /* empty */ {$$ = NULL; }
  138. ;
  139. optattr : attrlist | /* empty */ ;
  140. attrlist : optattr '[' optattrdefs ']' ;
  141. optattrdefs : optattrdefs attrdefs
  142. | /* empty */ ;
  143. attrdefs : attrassignment optseparator
  144. ;
  145. attrassignment : atom '=' atom {appendattr($1,$3);}
  146. ;
  147. graphattrdefs : attrassignment
  148. ;
  149. subgraph : optsubghdr {opensubg($1);} body {closesubg();}
  150. ;
  151. optsubghdr : T_subgraph atom {$$=$2;}
  152. | T_subgraph {$$=NULL;}
  153. | /* empty */ {$$=NULL;}
  154. ;
  155. optseparator : ';' | ',' | /*empty*/ ;
  156. atom : T_atom {$$ = $1;}
  157. | qatom {$$ = $1;}
  158. ;
  159. qatom : T_qatom {$$ = $1;}
  160. | qatom '+' T_qatom {$$ = concat($1,$3);}
  161. ;
  162. %%
  163. static item *newitem(int tag, void *p0, char *p1)
  164. {
  165. item *rv = agalloc(G,sizeof(item));
  166. rv->tag = tag;
  167. rv->u.name = p0;
  168. rv->str = p1;
  169. return rv;
  170. }
  171. static item *cons_node(Agnode_t *n, char *port)
  172. { return newitem(T_node,n,port); }
  173. static item *cons_attr(char *name, char *value)
  174. { return newitem(T_atom,name,value); }
  175. static item *cons_list(item *list)
  176. { return newitem(T_list,list,NULL); }
  177. static item *cons_subg(Agraph_t *subg)
  178. { return newitem(T_subgraph,subg,NULL); }
  179. static gstack_t *push(gstack_t *s, Agraph_t *subg) {
  180. gstack_t *rv;
  181. rv = agalloc(G,sizeof(gstack_t));
  182. rv->down = s;
  183. rv->g = subg;
  184. return rv;
  185. }
  186. static gstack_t *pop(gstack_t *s)
  187. {
  188. gstack_t *rv;
  189. rv = s->down;
  190. agfree(G,s);
  191. return rv;
  192. }
  193. static void delete_items(item *ilist)
  194. {
  195. item *p,*pn;
  196. for (p = ilist; p; p = pn) {
  197. pn = p->next;
  198. if (p->tag == T_list) delete_items(p->u.list);
  199. if (p->tag == T_atom) agstrfree(G,p->str);
  200. agfree(G,p);
  201. }
  202. }
  203. static void deletelist(list_t *list)
  204. {
  205. delete_items(list->first);
  206. list->first = list->last = NULL;
  207. }
  208. static void listapp(list_t *list, item *v)
  209. {
  210. if (list->last) list->last->next = v;
  211. list->last = v;
  212. if (list->first == NULL) list->first = v;
  213. }
  214. /* attrs */
  215. static void appendattr(char *name, char *value)
  216. {
  217. item *v;
  218. assert(value != NULL);
  219. v = cons_attr(name,value);
  220. listapp(&S->attrlist, v);
  221. }
  222. static void bindattrs(int kind)
  223. {
  224. item *aptr;
  225. char *name;
  226. for (aptr = S->attrlist.first; aptr; aptr = aptr->next) {
  227. assert(aptr->tag == T_atom); /* signifies unbound attr */
  228. name = aptr->u.name;
  229. if (kind == AGEDGE && streq(name,Key)) continue;
  230. if ((aptr->u.asym = agattr(S->g,kind,name,NULL)) == NULL)
  231. aptr->u.asym = agattr(S->g,kind,name,"");
  232. aptr->tag = T_attr; /* signifies bound attr */
  233. agstrfree(G,name);
  234. }
  235. }
  236. /* attach node/edge specific attributes */
  237. static void applyattrs(void *obj)
  238. {
  239. item *aptr;
  240. for (aptr = S->attrlist.first; aptr; aptr = aptr->next) {
  241. if (aptr->tag == T_attr) {
  242. if (aptr->u.asym) {
  243. agxset(obj,aptr->u.asym,aptr->str);
  244. }
  245. }
  246. else {
  247. assert(AGTYPE(obj) == AGINEDGE || AGTYPE(obj) == AGOUTEDGE);
  248. assert(aptr->tag == T_atom);
  249. assert(streq(aptr->u.name,Key));
  250. }
  251. }
  252. }
  253. static void nomacros(void)
  254. {
  255. agwarningf("attribute macros not implemented");
  256. }
  257. /* attrstmt:
  258. * First argument is always attrtype, so switch covers all cases.
  259. * This function is used to handle default attribute value assignment.
  260. */
  261. static void attrstmt(int tkind, char *macroname)
  262. {
  263. item *aptr;
  264. int kind = 0;
  265. Agsym_t* sym;
  266. /* creating a macro def */
  267. if (macroname) nomacros();
  268. /* invoking a macro def */
  269. for (aptr = S->attrlist.first; aptr; aptr = aptr->next)
  270. if (aptr->str == NULL) nomacros();
  271. switch(tkind) {
  272. case T_graph: kind = AGRAPH; break;
  273. case T_node: kind = AGNODE; break;
  274. case T_edge: kind = AGEDGE; break;
  275. default: UNREACHABLE();
  276. }
  277. bindattrs(kind); /* set up defaults for new attributes */
  278. for (aptr = S->attrlist.first; aptr; aptr = aptr->next) {
  279. /* If the tag is still T_atom, aptr->u.asym has not been set */
  280. if (aptr->tag == T_atom) continue;
  281. if (!aptr->u.asym->fixed || S->g != G)
  282. sym = agattr(S->g,kind,aptr->u.asym->name,aptr->str);
  283. else
  284. sym = aptr->u.asym;
  285. if (S->g == G)
  286. sym->print = true;
  287. }
  288. deletelist(&S->attrlist);
  289. }
  290. /* nodes */
  291. static void appendnode(char *name, char *port, char *sport)
  292. {
  293. item *elt;
  294. if (sport) {
  295. port = concatPort (port, sport);
  296. }
  297. elt = cons_node(agnode(S->g, name, 1), port);
  298. listapp(&S->nodelist, elt);
  299. agstrfree(G,name);
  300. }
  301. /* apply current optional attrs to nodelist and clean up lists */
  302. /* what's bad is that this could also be endsubg. also, you can't
  303. clean up S->subg in closesubg() because S->subg might be needed
  304. to construct edges. these are the sort of notes you write to yourself
  305. in the future. */
  306. static void endnode(void)
  307. {
  308. item *ptr;
  309. bindattrs(AGNODE);
  310. for (ptr = S->nodelist.first; ptr; ptr = ptr->next)
  311. applyattrs(ptr->u.n);
  312. deletelist(&S->nodelist);
  313. deletelist(&S->attrlist);
  314. deletelist(&S->edgelist);
  315. S->subg = 0; /* notice a pattern here? :-( */
  316. }
  317. /* edges - store up node/subg lists until optional edge key can be seen */
  318. static void getedgeitems(void)
  319. {
  320. item *v = 0;
  321. if (S->nodelist.first) {
  322. v = cons_list(S->nodelist.first);
  323. S->nodelist.first = S->nodelist.last = NULL;
  324. }
  325. else {if (S->subg) v = cons_subg(S->subg); S->subg = 0;}
  326. /* else nil append */
  327. if (v) listapp(&S->edgelist, v);
  328. }
  329. static void endedge(void)
  330. {
  331. char *key;
  332. item *aptr,*tptr,*p;
  333. Agnode_t *t;
  334. Agraph_t *subg;
  335. bindattrs(AGEDGE);
  336. /* look for "key" pseudo-attribute */
  337. key = NULL;
  338. for (aptr = S->attrlist.first; aptr; aptr = aptr->next) {
  339. if (aptr->tag == T_atom && streq(aptr->u.name,Key))
  340. key = aptr->str;
  341. }
  342. /* can make edges with node lists or subgraphs */
  343. for (p = S->edgelist.first; p->next; p = p->next) {
  344. if (p->tag == T_subgraph) {
  345. subg = p->u.subg;
  346. for (t = agfstnode(subg); t; t = agnxtnode(subg,t))
  347. edgerhs(agsubnode(S->g, t, 0), NULL, p->next, key);
  348. }
  349. else {
  350. for (tptr = p->u.list; tptr; tptr = tptr->next)
  351. edgerhs(tptr->u.n,tptr->str,p->next,key);
  352. }
  353. }
  354. deletelist(&S->nodelist);
  355. deletelist(&S->edgelist);
  356. deletelist(&S->attrlist);
  357. S->subg = 0;
  358. }
  359. /* concat:
  360. */
  361. static char*
  362. concat (char* s1, char* s2)
  363. {
  364. char* s;
  365. char buf[BUFSIZ];
  366. char* sym;
  367. size_t len = strlen(s1) + strlen(s2) + 1;
  368. if (len <= BUFSIZ) sym = buf;
  369. else sym = gv_alloc(len);
  370. strcpy(sym,s1);
  371. strcat(sym,s2);
  372. s = agstrdup (G,sym);
  373. agstrfree (G,s1);
  374. agstrfree (G,s2);
  375. if (sym != buf) free (sym);
  376. return s;
  377. }
  378. static char*
  379. concatPort (char* s1, char* s2)
  380. {
  381. agxbuf buf = {0};
  382. agxbprint(&buf, "%s:%s", s1, s2);
  383. char *s = agstrdup(G, agxbuse(&buf));
  384. agstrfree (G,s1);
  385. agstrfree (G,s2);
  386. agxbfree(&buf);
  387. return s;
  388. }
  389. static void edgerhs(Agnode_t *tail, char *tport, item *hlist, char *key)
  390. {
  391. Agnode_t *head;
  392. Agraph_t *subg;
  393. item *hptr;
  394. if (hlist->tag == T_subgraph) {
  395. subg = hlist->u.subg;
  396. for (head = agfstnode(subg); head; head = agnxtnode(subg,head))
  397. newedge(tail, tport, agsubnode(S->g, head, 0), NULL, key);
  398. }
  399. else {
  400. for (hptr = hlist->u.list; hptr; hptr = hptr->next)
  401. newedge(tail, tport, agsubnode(S->g, hptr->u.n, 0), hptr->str, key);
  402. }
  403. }
  404. static void mkport(Agedge_t *e, char *name, char *val)
  405. {
  406. Agsym_t *attr;
  407. if (val) {
  408. if ((attr = agattr(S->g,AGEDGE,name,NULL)) == NULL)
  409. attr = agattr(S->g,AGEDGE,name,"");
  410. agxset(e,attr,val);
  411. }
  412. }
  413. static void newedge(Agnode_t *t, char *tport, Agnode_t *h, char *hport, char *key)
  414. {
  415. Agedge_t *e;
  416. e = agedge(S->g, t, h, key, 1);
  417. if (e) { /* can fail if graph is strict and t==h */
  418. char *tp = tport;
  419. char *hp = hport;
  420. if (agtail(e) != aghead(e) && aghead(e) == t) {
  421. /* could happen with an undirected edge */
  422. char *temp;
  423. temp = tp; tp = hp; hp = temp;
  424. }
  425. mkport(e,TAILPORT_ID,tp);
  426. mkport(e,HEADPORT_ID,hp);
  427. applyattrs(e);
  428. }
  429. }
  430. /* graphs and subgraphs */
  431. static void startgraph(char *name, bool directed, bool strict)
  432. {
  433. if (G == NULL) {
  434. SubgraphDepth = 0;
  435. Agdesc_t req = {.directed = directed, .strict = strict, .maingraph = true};
  436. Ag_G_global = G = agopen(name,req,Disc);
  437. }
  438. else {
  439. Ag_G_global = G;
  440. }
  441. S = push(S,G);
  442. agstrfree(NULL,name);
  443. }
  444. static void endgraph(void)
  445. {
  446. aglexeof();
  447. aginternalmapclearlocalnames(G);
  448. }
  449. static void opensubg(char *name)
  450. {
  451. if (++SubgraphDepth >= YYMAXDEPTH/2) {
  452. agerrorf("subgraphs nested more than %d deep", YYMAXDEPTH);
  453. }
  454. S = push(S, agsubg(S->g, name, 1));
  455. agstrfree(G,name);
  456. }
  457. static void closesubg(void)
  458. {
  459. Agraph_t *subg = S->g;
  460. --SubgraphDepth;
  461. S = pop(S);
  462. S->subg = subg;
  463. assert(subg);
  464. }
  465. static void freestack(void)
  466. {
  467. while (S) {
  468. deletelist(&S->nodelist);
  469. deletelist(&S->attrlist);
  470. deletelist(&S->edgelist);
  471. S = pop(S);
  472. }
  473. }
  474. extern FILE *aagin;
  475. Agraph_t *agconcat(Agraph_t *g, void *chan, Agdisc_t *disc)
  476. {
  477. aagin = chan;
  478. G = g;
  479. Ag_G_global = NULL;
  480. Disc = (disc? disc : &AgDefaultDisc);
  481. aglexinit(Disc, chan);
  482. aagparse();
  483. if (Ag_G_global == NULL) aglexbad();
  484. return Ag_G_global;
  485. }
  486. Agraph_t *agread(void *fp, Agdisc_t *disc) {return agconcat(NULL,fp,disc); }