1 /*=========================================================================*\
3 * Graph support for Lua.
9 \*=========================================================================*/
11 /*=========================================================================*\
13 \*=========================================================================*/
20 /*=========================================================================* \
22 \*=========================================================================*/
23 static unsigned int localid
= 0;
25 unsigned int newid(void){
26 localid
= localid
+ 1;
31 * Convert numerical kind indication into readable text string.
33 char *agobjkindstr(struct Agobj_s
*obj
)
35 int kind
= agobjkind(obj
);
36 return kind
== AGRAPH
? "graph" : kind
== AGNODE
? "node" : kind
== AGEDGE
? "edge" : "unknown";
40 * Insertion callback: called when an object is inserted by cgraph.
41 * Used to register proxy object on top of stack in Lua registry using cgraph object as key.
43 void cb_insert(struct Agraph_s
*g
, struct Agobj_s
*obj
, void *L
)
45 /* register user data on top of stack */
46 TRACE(" cb_insert(): register object '%s' type: %s\n", agnameof(obj
), agobjkindstr(obj
));
47 set_object((lua_State
*)L
, (void *) obj
);
51 * Deletion callback: called when an object is inserted by cgraph.
52 * De-register proxy object.
54 void cb_delete(Agraph_t
*g
, Agobj_t
*obj
, void *L
)
58 TRACE(" cb_delete() : unregister object '%s' kind=%s ptr=%p\n", agnameof(obj
),
59 agobjkind(obj
) == AGRAPH
? "graph" : agobjkind(obj
) == AGNODE
? "node" :
60 agobjkind(obj
) == AGEDGE
? "edge" : "unknown", obj
);
61 skey
= agget(obj
, "__attrib__");
62 if (skey
&& (strlen(skey
) != 0)) {
63 printf(" cb_delete(): nulling attrib\n");
64 lua_pushstring(L
, skey
);
66 lua_rawset(L
, LUA_REGISTRYINDEX
);
67 agset(obj
, "__attrib__", NULL
);
68 agstrfree(agroot(obj
), skey
);
74 * Modification callback. Not used by LuaGRAPH
76 void cb_modify(Agraph_t
*g
, Agobj_t
*obj
, void *L
, Agsym_t
*sym
)
78 TRACE(" cb_modify(): modify object '%s'\n", agnameof(obj
));
82 * Query Lua stack for an object
84 gr_object_t
*toobject(lua_State
*L
, int narg
, const char *type
, int strict
)
89 ud
= luaL_checkudata(L
, narg
, type
);
91 ud
= lua_touserdata(L
, narg
);
94 luaL_error(L
, "bad argument %d (valid userdata `%s' expected; null received)",
95 narg
, (type
) ? type
: "???");
98 if (strict
&& (ud
->p
.p
== NULL
)){
99 luaL_error(L
, "bad argument #%d (valid userdata `%s' %d expected: invalid received)",
100 narg
, (type
) ? type
: "???", ud
->p
.status
);
108 * Register an object in Lua registry.
110 int set_object(lua_State
*L
, void *key
)
112 TRACE(" set_object(): key=%p (%s %d)\n", key
, __FILE__
, __LINE__
);
113 lua_pushlightuserdata(L
, key
); /* ud, key */
114 lua_pushvalue(L
, -2); /* ud, key, ud */
115 lua_rawset(L
, LUA_REGISTRYINDEX
); /* ud */
119 int set_status(lua_State
*L
, void *key
, int status
)
123 lua_pushlightuserdata(L
, key
); /* ?, key */
124 lua_rawget(L
, LUA_REGISTRYINDEX
);
125 ud
= lua_touserdata(L
, -1);
127 ud
->p
.status
= status
;
131 * Delete an object from Lua registry.
133 int del_object(lua_State
*L
, void *key
)
135 TRACE(" del_object(): key=%p '%s' (%s %d)\n", key
, agnameof(key
), __FILE__
, __LINE__
);
136 set_status(L
, key
, DEAD
);
137 lua_pushlightuserdata(L
, key
);
138 lua_pushnil(L
); /* ?, key, nil */
139 lua_rawset(L
, LUA_REGISTRYINDEX
); /* ? */
143 /*-------------------------------------------------------------------------*\
144 * Property: status [string]
145 * Returns the string "alive" or "dead" for the given graph.
146 * Use to test whether a reference to a graph object kept in a lua variable
150 \*-------------------------------------------------------------------------*/
151 int gr_status(lua_State
*L
)
153 gr_object_t
*ud
= toobject(L
, 1, NULL
, NONSTRICT
);
155 if (ud
->p
.status
== ALIVE
)
156 lua_pushstring(L
, "alive");
158 lua_pushstring(L
, "dead");
160 lua_pushstring(L
, "???");
165 int gr_collect(lua_State
*L
)
167 gr_object_t
*ud
= lua_touserdata(L
, 1);
168 TRACE(" gr_collect(): %s ud=%p ptr=%p\n", ud
->p
.name
, ud
, ud
->p
.p
);
176 * Get an object from Lua registry.
178 int get_object(lua_State
*L
, void *key
)
181 lua_pushlightuserdata(L
, key
); /* ?, key */
182 lua_rawget(L
, LUA_REGISTRYINDEX
); /* ?, ud or nil */
183 ud
= lua_touserdata(L
, -1);
185 TRACE(" get_object(): key = %p not found (%s %d)\n", key
, __FILE__
, __LINE__
);
186 lua_pushstring(L
, "object not found in registry"); /* ?, nil, err */
189 TRACE(" get_object(): key=%p ud=%p '%s' (%s %d)\n", key
, ud
, ud
->p
.name
, __FILE__
,__LINE__
);
190 return 1; /* ?, ud */
194 * Get the value of a graphviz object attribute
196 int getval(lua_State
*L
)
198 gr_object_t
*ud
= toobject(L
, 1, NULL
, STRICT
);
199 char *key
= (char *) luaL_checkstring(L
, 2);
200 char *value
= agget(ud
->p
.p
, key
);
201 if (!value
|| strlen(value
) == 0){
205 lua_pushstring(L
, value
);
210 * Set a value to a graphviz object attribute
212 static int setval(lua_State
*L
)
214 gr_object_t
*ud
= toobject(L
, 1, NULL
, STRICT
);
215 char *key
= (char *) luaL_checkstring(L
, 2);
216 char *value
= (char *) luaL_checkstring(L
, 3);
217 // return agset(ud->p.p, key, value);
218 return agsafeset(ud
->p
.p
, key
, value
, "");
222 * Generic object index metamethod handler
223 * This closure has 2 upvalues:
224 * 1. table with read members (this is the metatable)
225 * 2. metatable.__metatable contains the methods
228 int object_index_handler(lua_State
*L
)
233 /* Read member lookup in first upvalue */
234 lua_pushvalue(L
, 2); /* ud, key, key */
235 lua_rawget(L
, lua_upvalueindex(1)); /* ud, key, getfunc (member) or nil (method) */
236 if (!lua_isfunction(L
, -1)){
237 /* Member not found - try methods */
238 lua_pop(L
, 1); /* ud, key */
239 lua_pushvalue(L
, 2); /* ud, key, key */
240 lua_gettable(L
, lua_upvalueindex(2)); /* ud, key, method */
241 if (lua_isnil(L
, -1)){
242 /* Try generic get method */
243 if (lua_isstring(L
, 2)){
244 lua_settop(L
, 2); /* ud, key */
245 lua_pushcfunction(L
, getval
); /* ud, key, gr_get */
246 lua_pushvalue(L
, 1); /* ud, key, gr_get, ud */
247 lua_pushvalue(L
, 2); /* ud, key, gr_get, ud, key */
248 lua_call(L
, 2, 1); /* ud, key, value or nil */
250 if (lua_isnil(L
, -1)){
251 /* Try generic storage */
252 lua_settop(L
, 2); /* ud, key */
253 ud
= toobject(L
, 1, NULL
, STRICT
);
254 skey
= agget(ud
->p
.p
, "__attrib__");
255 if (skey
&& strlen(skey
) > 0){
256 lua_pushstring(L
, skey
); /* ud, key, skey */
257 lua_rawget(L
, LUA_REGISTRYINDEX
); /* ud, key, stab */
258 lua_pushvalue(L
, 2); /* ud, key, stab, key */
259 lua_rawget(L
, -2); /* ud, key, stab, value */
260 lua_remove(L
, -2); /* ud, key, value */
262 lua_pushnil(L
); /* ud, key, nil */
267 /* Call the member's access function */
268 lua_pushvalue(L
, 1); /* ud, key, getfunc, ud */
269 lua_pushvalue(L
, 2); /* ud, key, getfunc, ud, key */
270 lua_call(L
, 2, 1); /* ud, key, value */
275 * Generic object newindex metamethod handler.
276 * Lua Stack: ud, key, value
278 int object_newindex_handler(lua_State
*L
)
280 char sskey
[16], *skey
;
281 TRACE(" newindex(): key='%s' value='%s'\n", lua_tostring(L
, 2), lua_tostring(L
, 3));
282 if ((!lua_isstring(L
, 2)) || (!lua_isstring(L
, 3))){
283 gr_object_t
*ud
= toobject(L
, 1, NULL
, STRICT
);
284 skey
= agget(ud
->p
.p
, "__attrib__");
285 if (!skey
|| (strlen(skey
) == 0)){
286 /* Let's create an attrib table on the fly if none exists */
287 sprintf(sskey
, "%p", ud
->p
.p
);
288 skey
= agstrdup(agroot(ud
->p
.p
), sskey
);
289 agset(ud
->p
.p
, "__attrib__", skey
);
290 lua_pushstring(L
, skey
); /* ud, key, value, skey */
291 lua_newtable(L
); /* ud, key, value, skey, stab */
292 lua_rawset(L
, LUA_REGISTRYINDEX
); /* ud, key, value, */
294 lua_pushstring(L
, skey
); /* ud, key, value, skey */
295 lua_rawget(L
, LUA_REGISTRYINDEX
); /* ud, key, value, stab */
296 lua_pushvalue(L
, 2); /* ud, key, value, stab, key */
297 lua_pushvalue(L
, 3); /* ud, key, value, stab, key, value */
298 lua_rawset(L
, -3); /* ud, key, value, stab */
299 lua_pop(L
, -1); /* ud, key, value */
306 * Create the graph object with member and method access.
307 * Lua entry stack: ud
310 int new_object(lua_State
*L
, const char *kind
, const luaL_Reg
*reg_rmembers
,
311 const luaL_Reg
*reg_methods
, const luaL_Reg
*reg_metamethods
,
312 index_handler_t
*index_handler
)
314 int methods
, metatable
;
315 /* Put methods in a table */
316 lua_newtable(L
); /* ud, mtab */
318 register_metainfo(L
, reg_methods
);
319 methods
= lua_gettop(L
);
321 /* Put metamethods in a new metatable */
322 luaL_newmetatable(L
, kind
); /* ud, mtab, mt */
324 register_metainfo(L
, reg_metamethods
);
325 metatable
= lua_gettop(L
);
327 /* Keep a reference to methods in the new metatable */
328 lua_pushstring(L
, "__metatable"); /* ud, mtab, mt, "__metatable" */
329 lua_pushvalue(L
, methods
); /* ud, mtab, mt, "__metatable", mtab */
330 lua_rawset(L
, -3); /* ud, mtab, mt */
332 lua_pushstring(L
, "__index"); /* ud, mtab, mt, "__index" */
334 /* First upvalue: metatable => members access */
335 lua_pushvalue(L
, metatable
); /* ud, mtab, mt, "__index", mt(upv 1) */
337 /* Put members into the metatable */
338 register_metainfo(L
, reg_rmembers
);
340 /* Second upvalue, methods => methods access */
341 lua_pushvalue(L
, methods
); /* ud, mtab, mt, "__index", mt(upv 1), mtab(upv 2) */
343 /* Set index_handler with 2 upvalues as __index metamethod */
344 lua_pushcclosure(L
, index_handler
, 2); /* ud, mtab, mt, "__index", func */
345 lua_rawset(L
, metatable
); /* ud, mtab, mt */
347 lua_setmetatable(L
, -3); /* ud, mtab */
348 lua_pop(L
,1); /* ud */
354 * Check an object type.
356 void *chk_object(lua_State
*L
, void *obj
)
358 gr_object_t
*ud
= (gr_object_t
*) obj
;
359 if (ud
->p
.p
== NULL
){
360 lua_pushfstring(L
, "invalid userdata of type `%s' detected",
361 ud
->p
.type
== AGRAPH
? "graph" :
362 ud
->p
.type
== AGNODE
? "node" :
363 ud
->p
.type
== AGEDGE
? "edge" : "unknown");
370 /*-------------------------------------------------------------------------*\
371 * Generic method: type = obj.type(self)
372 * Returns type of a graph object.
373 * Returns the type as string: 'graph', 'node' or 'edge'.
376 \*-------------------------------------------------------------------------*/
377 int get_object_type(lua_State
*L
)
379 gr_object_t
*ud
= toobject(L
, 1, NULL
, STRICT
);
380 lua_pushstring(L
, AGTYPE(ud
->p
.p
) == AGRAPH
? "graph" :
381 AGTYPE(ud
->p
.p
) == AGNODE
? "node" :
382 AGTYPE(ud
->p
.p
) == AGEDGE
? "edge" : "unkown");