1 /*=========================================================================*\
3 * Graph support for Lua.
7 * Node related functionality.
9 \*=========================================================================*/
11 /*=========================================================================*\
13 \*=========================================================================*/
22 /*=========================================================================* \
24 \*=========================================================================*/
26 /*=========================================================================*\
28 \*=========================================================================*/
29 static int gr_nameof(lua_State
*L
);
30 static int gr_id(lua_State
*L
);
31 static int gr_equal(lua_State
*L
);
32 static int gr_delete(lua_State
*L
);
33 static int gr_degree(lua_State
*L
);
34 static int gr_graphof(lua_State
*L
);
35 static int gr_edge(lua_State
*L
);
36 static int gr_nextedge(lua_State
*L
);
37 static int gr_walkedges(lua_State
*L
);
38 static int gr_nextinput(lua_State
*L
);
39 static int gr_walkinputs(lua_State
*L
);
40 static int gr_nextoutput(lua_State
*L
);
41 static int gr_walkoutputs(lua_State
*L
);
42 static int gr_info(lua_State
*L
);
43 static int gr_tostring(lua_State
*L
);
45 /*=========================================================================*\
47 \*=========================================================================*/
48 static const luaL_Reg reg_methods
[] = {
49 {"delete", gr_delete
},
50 {"degree", gr_degree
},
52 {"nextedge", gr_nextedge
},
53 {"walkedges", gr_walkedges
},
54 {"nextinput", gr_nextinput
},
55 {"walkinputs", gr_walkinputs
},
56 {"nextoutput", gr_nextoutput
},
57 {"walkoutputs", gr_walkoutputs
},
58 {"type", get_object_type
},
64 static const luaL_Reg reg_rmembers
[] = {
67 {"status", gr_status
},
68 {"graph", gr_graphof
},
72 static const luaL_Reg reg_metamethods
[] = {
75 {"__newindex", object_newindex_handler
},
76 {"__concat", gr_edge
},
78 {"__tostring", gr_tostring
},
81 /*=========================================================================*\
83 \*=========================================================================*/
84 /*-------------------------------------------------------------------------*\
86 \*-------------------------------------------------------------------------*/
89 * Create a new Lua userdata object and configure metatable.
91 int new_node(lua_State
*L
){
92 return new_object(L
, "node", reg_rmembers
, reg_methods
, reg_metamethods
,
93 object_index_handler
);
96 /*-------------------------------------------------------------------------*\
97 * Metamethod: __eq [boolean]
100 * n1 == n2 or n1 ~= n2 with metamethods
101 \*-------------------------------------------------------------------------*/
102 static int gr_equal(lua_State
*L
)
104 gr_node_t
*ud1
= tonode(L
, 1, STRICT
);
105 gr_node_t
*ud2
= tonode(L
, 2, STRICT
);
106 lua_pushboolean(L
, (AGID(ud1
->n
) == AGID(ud2
->n
)));
110 /*-------------------------------------------------------------------------*\
111 * Property: name [string]
112 * Provides the name of a node.
115 \*-------------------------------------------------------------------------*/
116 static int gr_nameof(lua_State
*L
)
118 gr_node_t
*ud
= tonode(L
, 1, STRICT
);
119 if (ud
->status
!= ALIVE
){
120 luaL_error(L
, "deleted");
123 lua_pushstring(L
, agnameof(ud
->n
));
127 /*-------------------------------------------------------------------------*\
128 * Property: id [number]
132 \*-------------------------------------------------------------------------*/
133 static int gr_id(lua_State
*L
)
135 gr_node_t
*ud
= tonode(L
, 1, STRICT
);
136 lua_pushnumber(L
, AGID(ud
->n
));
140 /*-------------------------------------------------------------------------*\
141 * Write info about a node to stdout.
144 \*-------------------------------------------------------------------------*/
145 static int gr_info(lua_State
*L
)
148 gr_node_t
*ud
= tonode(L
, 1, STRICT
);
153 printf("INFO NODE '%s' '%s' id=%lu seq=%d\n", agnameof(ud
->n
), ud
->name
, (unsigned long) AGID(ud
->n
), AGSEQ(ud
->n
));
154 printf(" ptr: %p\n", ud
->n
);
155 printf(" Symbols:\n");
156 se
= agfstout(g
, ud
->n
);
158 while ((sym
= agnxtattr(g
,AGNODE
,sym
))!=NULL
)
159 printf(" %s = '%s'\n",sym
->name
,sym
->defval
);
161 printf(" Out edges: d-out=%d u-out=%d\n", agdegree(g
, ud
->n
, 0, 1), agcountuniqedges(g
, ud
->n
, 0, 1));
164 printf(" name: '%s', head: '%s', tail: '%s' id=%lud, seq=%d %p\n",
165 agnameof(se
), agnameof(aghead(se
)), agnameof(agtail(se
)), (unsigned long) AGID(se
), AGSEQ(se
), (void*)se
);
166 se
= agnxtout(g
, se
);
169 printf(" In edges: d-in=%d u-in=%d\n", agdegree(g
, ud
->n
, 1, 0), agcountuniqedges(g
, ud
->n
, 1, 0));
171 se
= agfstin(g
, ud
->n
);
173 printf(" name: '%s', head: '%s', tail: '%s' îd=%lu seq=%d %p\n",
174 agnameof(se
), agnameof(aghead(se
)), agnameof(agtail(se
)), (unsigned long) AGID(se
), AGSEQ(se
), (void*)se
);
178 printf(" Edges: d-io=%d u-io=%d\n", agdegree(g
, ud
->n
, 1, 1), agcountuniqedges(g
, ud
->n
, 1, 1));
180 se
= agfstedge(g
, ud
->n
);
182 printf(" name: '%s', head: '%s', tail: '%s' id=%lud seq=%d %p\n",
183 agnameof(se
), agnameof(aghead(se
)), agnameof(agtail(se
)), (unsigned long) AGID(se
), AGSEQ(se
), (void*)se
);
184 se
= agnxtedge(g
, se
, ud
->n
);
189 /*-------------------------------------------------------------------------* \
190 * Method: n.delete(self)
191 * Delete a node. All associated edges are deleted as well.
192 * Returns non-nil on success.
194 * rv, err = n:delete(h)
195 \*-------------------------------------------------------------------------*/
196 static int gr_delete(lua_State
*L
)
199 gr_node_t
*ud
= tonode(L
, 1, NONSTRICT
);
202 /* Delete all associated edges with tail on this node */
204 if (ud
->status
== ALIVE
){
205 TRACE(" n.delete(): deleting node '%s' ud=%p id=0x%0lx (%s %d) \n",
206 agnameof(ud
->n
), (void *) ud
, (unsigned long) AGID(ud
->n
), __FILE__
, __LINE__
);
210 TRACE(" n:delete(): ud=%p already closed (%s %d)\n", (void *)ud
, __FILE__
, __LINE__
);
212 lua_pushnumber(L
, 0);
217 * Provided for external access to gr_delete function.
219 int gr_delete_node(lua_State
*L
)
224 /*-------------------------------------------------------------------------*\
225 * Method: n.degree(self, what)
226 * Determines degree of a node.
227 * Returns number of in-edges (what='*i'), out-edges (what='*o') or the sum
228 * of all edges (what='*a') to/from/of a node.
230 * rv, err = n:degree("*i")
231 \*-------------------------------------------------------------------------*/
232 int gr_degree(lua_State
*L
)
238 gr_node_t
*ud
= tonode(L
, 1, STRICT
);
239 char *flag
= (char *) luaL_optstring(L
, 2, "*a");
245 luaL_error(L
, "invalid format specifier");
249 case 'i': outdeg
= FALSE
; break;
250 case 'o': indeg
= FALSE
; break;
253 for (e
= agfstin(g
, n
); e
; e
= agnxtin(g
, e
)){
258 for (e
= agfstout(g
, n
); e
; e
= agnxtout(g
, e
)){
262 lua_pushnumber(L
, count
);
266 /*-------------------------------------------------------------------------*\
268 * Determines the graph to which of a node belongs.
269 * Returns graph userdata.
272 \*-------------------------------------------------------------------------*/
273 int gr_graphof(lua_State
*L
)
275 gr_node_t
*ud
= tonode(L
, 1, STRICT
);
276 Agraph_t
*g
= agraphof(ud
->n
);
279 lua_pushstring(L
, "no graph");
282 return get_object(L
, g
);
285 /*-------------------------------------------------------------------------*\
286 * Method: e, tail, head = n.edge(self, node, label, flag)
287 * Finds or creates an edge. The given node is the tail of the edge.
288 * The created node is the the edge.
290 * The optional flag nocreate=true inhibits auto-creation of the edge.
291 * Any node given by name is implicitly created and it's userdata returned
292 * as additional results - even if nocreate is not set.
294 * e, tail, head = n.edge(head, "edge-1", true | false)
295 * e, tail, head = e:node("headname", "edge-1", true | false)
296 \*-------------------------------------------------------------------------*/
297 static int gr_edge(lua_State
*L
)
306 gr_node_t
*tail
= tonode(L
, 1, STRICT
);
310 if (lua_isuserdata(L
, 2))
311 head
= tonode(L
, 2, STRICT
);
313 lua_pushcfunction(L
, gr_create_node
); /* tail, nhead, (label), (nocreate), func */
314 get_object(L
, agroot(tail
->n
)); /* tail, nhead, (label), (nocreate), func, graph */
315 lua_pushvalue(L
, 2); /* ... func, graph, nhead */
316 if (lua_isboolean(L
, 4))
317 lua_pushvalue(L
, 4); /* ... func, graph, nhead, (nocreate) */
319 lua_pushboolean(L
, 0); /* ... func, graph, nhead, false */
320 lua_call(L
, 3, 1); /* tail, nhead, (label), head */
321 if (lua_isnil(L
, -1))
323 head
= tonode(L
, -1, STRICT
);
324 lua_pop(L
,1); /* tail, nhead, (label) */
328 if (g
!= agroot(head
->n
)){
329 luaL_error(L
, "head/tail not in same graph");
331 label
= (char *) luaL_optstring(L
, 3, NULL
); /* ud, peer, name, (flag) */
332 if ((e
= agedge(g
, tail
->n
, head
->n
, label
, 0)) != NULL
){
333 rv
= get_object(L
, e
); /* ud, peer, name, (flag), edge */
334 if (lua_isnil(L
, -rv
)){
335 /* not yet registered */
336 lua_pop(L
, rv
); /* ud, peer, name, (flag) */
337 edge
= lua_newuserdata(L
, sizeof(gr_edge_t
)); /* ud, peer, name, (flag), edge */
340 agset(e
, "label", label
);
341 edge
->name
= strdup(agnameof(e
));
343 edge
->status
= ALIVE
;
345 lua_pushlightuserdata(L
, tail
);
346 lua_pushlightuserdata(L
, head
); /* ud, peer, name, (flag), edge, tail, head */
349 /* Edge already registered */
350 lua_pushlightuserdata(L
, tail
);
351 lua_pushlightuserdata(L
, head
);
352 return rv
+ 2; /* ud, peer, name, (flag), edge, tail, head */
355 /* Edge does not exist */
356 if (lua_toboolean(L
, 4)){
358 lua_pushstring(L
, "edge not found");
361 edge
= lua_newuserdata(L
, sizeof(gr_edge_t
));
362 sprintf(ename
, "edge@%u", newid());
363 if ((edge
->e
= agedge(g
, tail
->n
, head
->n
, ename
, 1)) == NULL
){
364 luaL_error(L
, "agedge failed");
368 agset(edge
->e
, "label", label
);
369 edge
->name
= strdup(agnameof(edge
->e
));
371 edge
->status
= ALIVE
;
373 lua_pushlightuserdata(L
, tail
);
374 lua_pushlightuserdata(L
, head
);
379 /*-------------------------------------------------------------------------*\
380 * Method: e = nextedge(self, prev)
381 * Retrieves next edge of a node.
382 * Returns the next edge of the given node. With nil as prev the first
385 * first = n:nextedge(nil)
386 * second = n:nextedge(first)
387 * third = n:nextedge(second)
388 \*-------------------------------------------------------------------------*/
389 static int gr_nextedge(lua_State
*L
)
396 gr_node_t
*ud_n
= tonode(L
, 1, STRICT
);
399 if (lua_isnil(L
, 2)){
400 e
= agfstedge(g
, ud_n
->n
);
402 ud_e
= toedge(L
, 2, STRICT
);
403 e
= agnxtedge(g
, ud_e
->e
, ud_n
->n
);
410 /* Check whether userdata exists .. */
411 rv
= get_object(L
, e
);
413 /* .. yes: return it */
416 /* .. no: create it */
418 ud_e
= lua_newuserdata(L
, sizeof(gr_edge_t
));
420 sprintf(sbuf
, "edge@%lu", (unsigned long) AGID(e
));
421 ud_e
->name
= strdup(sbuf
);
423 ud_e
->status
= ALIVE
;
430 /*-------------------------------------------------------------------------*\
431 * Iterator: walkedges()
432 * Iterator over all edges of a given node.
434 * for n in g:walkedges() do ... end
435 \*-------------------------------------------------------------------------*/
436 static int gr_walkedges(lua_State
*L
)
438 lua_pushcfunction(L
, gr_nextedge
); /* ud, nextedge */
439 lua_pushvalue(L
, -2); /* ud, nextedge, n */
440 lua_pushnil(L
); /* ud, nextedge, n, nil */
444 /*-------------------------------------------------------------------------*\
445 * Method: e = nextinput(self, prev)
446 * e = nextoutput(self, prev)
447 * Same behaviour as nextedge, but only input or output edges are returned.
449 * first = n:nextinput(nil)
450 * second = n:nextinput(first)
451 * third = n:nextinput(second)
452 \*-------------------------------------------------------------------------*/
453 typedef Agedge_t
* (edge_first_iter_t
)(Agraph_t
*g
, Agnode_t
*n
);
454 typedef Agedge_t
* (edge_next_iter_t
)(Agraph_t
*g
, Agedge_t
*e
);
456 static int gr_nextinout(lua_State
*L
, edge_first_iter_t
*fst
, edge_next_iter_t
*nxt
)
462 gr_node_t
*ud_n
= tonode(L
, 1, STRICT
);
463 Agraph_t
*g
= agroot(ud_n
->n
);
468 ud_e
= toedge(L
, 2, STRICT
);
476 /* Check whether userdata exists .. */
477 rv
= get_object(L
, e
);
479 /* .. yes: return it */
482 /* .. no: create it */
484 ud_e
= lua_newuserdata(L
, sizeof(gr_edge_t
));
486 sprintf(sbuf
, "edge@%lu", (unsigned long) AGID(e
));
487 ud_e
->name
= strdup(sbuf
);
495 static int gr_nextinput(lua_State
*L
)
497 return gr_nextinout(L
, agfstin
, agnxtin
);
500 static int gr_nextoutput(lua_State
*L
)
502 return gr_nextinout(L
, agfstout
, agnxtout
);
504 /*-------------------------------------------------------------------------*\
505 * Iterator: e = n.walkinputs(self)
506 * Iterator over all input edges of a given node.
508 * for e in n:walkinputs() do ... end
509 \*-------------------------------------------------------------------------*/
510 static int gr_walkinputs(lua_State
*L
)
512 lua_pushcfunction(L
, gr_nextinput
); /* ud, nextedge */
513 lua_pushvalue(L
, -2); /* ud, nextedge, n */
514 lua_pushnil(L
); /* ud, nextedge, n, nil */
518 /*-------------------------------------------------------------------------*\
519 * Iterator: e = walkoutputs(self)
520 * Iterator over all input edges of a given node.
522 * for e in n:walkoutputs() do ... end
523 \*-------------------------------------------------------------------------*/
524 static int gr_walkoutputs(lua_State
*L
)
526 lua_pushcfunction(L
, gr_nextoutput
); /* ud, nextedge */
527 lua_pushvalue(L
, -2); /* ud, nextedge, n */
528 lua_pushnil(L
); /* ud, nextedge, n, nil */
532 /*-------------------------------------------------------------------------*\
533 * Metamethod: __tostring [boolean]
534 * Return a string presentation of a graph object.
536 * g == h or g != h with metamethods
537 \*-------------------------------------------------------------------------*/
538 static int gr_tostring(lua_State
*L
)
540 gr_object_t
*ud
= toobject(L
, 1, NULL
, NONSTRICT
);
541 lua_pushfstring(L
, "node: %p (%s)", ud
, ud
->p
.status
== ALIVE
? "alive" : "dead");