#include "lua_lsup.h" #include "stackdump.h" #define check_term(L) \ *(LSUP_Term **)luaL_checkudata(L, 1, "LSUP.Term") LSUP_Term **allocate_term (lua_State *L) { LSUP_Term **tp = lua_newuserdatauv (L, sizeof (*tp), 1); luaL_getmetatable (L, "LSUP.Term"); lua_setmetatable (L, -2); return tp; } LSUP_TermSet **allocate_tset (lua_State *L) { LSUP_TermSet **ts_p = lua_newuserdatauv (L, sizeof (*ts_p), 1); luaL_getmetatable (L, "LSUP.TermSet"); lua_setmetatable (L, -2); return ts_p; } /* * Factory methods. */ static int l_new_iriref (lua_State *L) { const char *data = luaL_checkstring (L, 1); // TODO handle nsm. LSUP_NSMap *nsm = lua_touserdata (L, 2); LSUP_Term **tp = allocate_term (L); *tp = LSUP_iriref_new (data, nsm); LUA_NLCHECK (*tp, "Error creating term."); return 1; } static int l_new_iriref_abs (lua_State *L) { LSUP_Term *root = check_term (L), *iri = *(LSUP_Term **)luaL_checkudata (L, 2, "LSUP.Term"); LSUP_Term **tp = allocate_term (L); *tp = LSUP_iriref_absolute (root, iri); LUA_NLCHECK (*tp, "Error creating term."); return 1; } static int l_new_iriref_rel (lua_State *L) { LSUP_Term *root = check_term (L), *iri = *(LSUP_Term **)luaL_checkudata (L, 2, "LSUP.Term"); LSUP_Term **tp = allocate_term (L); *tp = LSUP_iriref_relative (root, iri); LUA_NLCHECK (*tp, "Error creating term."); return 1; } /** @brief create a new literal. * * Argument 2 (data type) and 3 (lang) are exclusive. If both are specified, * datatype has precedence. If both are nil, datatype is set to the default * (xsd:string). */ static int l_new_lit (lua_State *L) { const char *data = luaL_checkstring (L, 1), *dtype_str = lua_tostring (L, 2); const char *lang = lua_tostring (L, 3); LSUP_Term **tp = allocate_term (L); LSUP_Term *dtype; if (dtype_str) { // TODO check memory leak. dtype = LSUP_iriref_new (dtype_str, LSUP_default_nsm); *tp = LSUP_literal_new (data, dtype); } else if (lang) *tp = LSUP_lt_literal_new (data, (char *)lang); else *tp = LSUP_literal_new (data, NULL); LUA_NLCHECK (*tp, "Error creating term."); return 1; } static int l_new_bnode (lua_State *L) { const char *data = lua_tostring (L, 1); LSUP_Term **tp = allocate_term (L); *tp = LSUP_bnode_new (data); LUA_NLCHECK (*tp, "Error creating term."); return 1; } static int l_new_copy (lua_State *L) { LSUP_Term *src = check_term (L); LSUP_Term **tp = allocate_term (L); *tp = LSUP_term_copy (src); LUA_NLCHECK (*tp, "Error creating term."); return 1; } /* * Class methods. */ static int l_term_equals (lua_State *L) { LSUP_Term *t1 = check_term (L), *t2 = *(LSUP_Term **)luaL_checkudata (L, 2, "LSUP.Term"); lua_pushboolean (L, LSUP_term_equals (t1, t2)); return 1; } static int l_term_gc (lua_State *L) { LSUP_Term **tp = luaL_checkudata(L, 1, "LSUP.Term"); LOG_TRACE ("Garbage collecting term @%p", *tp); LSUP_term_free (*tp); *tp = NULL; return 0; } /* static int l_term_to_string (lua_State *L) { LSUP_Term *t = check_term (L); char *nt_term = NULL; CHECK (nt_codec.encode_term (t, NULL, &nt_term), fail); lua_pushfstring (L, "LSUP.Term @%p %s", t, nt_term); return 1; fail: if (nt_term) free (nt_term); return luaL_error (L, "Error encoding term for display!"); } */ static int l_term_to_n3 (lua_State *L) { LSUP_Term *t = check_term (L); char *nt_term = NULL; CHECK (nt_codec.encode_term (t, NULL, &nt_term), fail); lua_pushstring (L, nt_term); free (nt_term); return 1; fail: if (nt_term) free (nt_term); return luaL_error (L, "Error encoding term for display."); } /* * Getters. */ // Forward declaration. static const luaL_Reg term_getters []; static int l_term_get_attr (lua_State *L) { const char *attr = luaL_checkstring (L, 2); //printf ("Got attr: %s\n", attr); if (luaL_getmetafield (L, 1, attr) != LUA_TNIL) return 1; // Return metamethod to be called as a function. luaL_getmetafield (L, 1, "getters"); if (lua_getfield (L, -1, attr) != LUA_TNIL) return lua_tocfunction (L, -1)(L); return 0; } static int get_data (lua_State *L) { LSUP_Term *t = check_term (L); lua_pushstring (L, t->data); return 1; } static int get_type (lua_State *L) { LSUP_Term *t = check_term (L); lua_pushinteger (L, t->type); return 1; } static int get_iriref_nsm (lua_State *L) { LSUP_Term *t = check_term (L); // TODO lua_pushlightuserdata (L, LSUP_iriref_nsm (t)); return 1; } static int get_iriref_prefix (lua_State *L) { LSUP_Term *t = check_term (L); lua_pushstring (L, LSUP_iriref_prefix (t)); return 1; } static int get_iriref_path (lua_State *L) { LSUP_Term *t = check_term (L); lua_pushstring (L, LSUP_iriref_path (t)); return 1; } static int get_iriref_frag (lua_State *L) { LSUP_Term *t = check_term (L); lua_pushstring (L, LSUP_iriref_frag (t)); return 1; } static int get_lit_datatype (lua_State *L) { printf ("Getting datatype.\n"); LSUP_Term *t = check_term (L); if (!LSUP_IS_LITERAL (t)) return luaL_error (L, "Term is not a literal.", 2); lua_pushstring (L, t->datatype->data); return 1; } static int get_lit_lang (lua_State *L) { LSUP_Term *t = check_term (L); if (!LSUP_IS_LITERAL (t)) return luaL_error (L, "Term is not a literal.", 2); lua_pushstring (L, t->lang); return 1; } static int get_hash (lua_State *L) { LSUP_Term *t = check_term (L); lua_pushinteger (L, LSUP_term_hash (t)); return 1; } /* * Setters. */ // Very simple for now. //static const luaL_Reg term_setters []; static int l_term_set_attr (lua_State *L) { return luaL_error (L, "Direct setting is not allowed for this type.", 2); } /* * Ancillary types */ /* * Term set. */ static int term_set_iter_next (lua_State *L) { LSUP_TermSet *ts = *(LSUP_TermSet **)lua_touserdata (L, lua_upvalueindex (1)); size_t *ip = lua_touserdata (L, lua_upvalueindex (2)); LSUP_Term *tmp; LSUP_rc rc = LSUP_term_set_next (ts, ip, &tmp); LUA_PCHECK (rc, "Error iterating over term set"); if (rc == LSUP_END) { free (ip); return 0; } LSUP_Term **tp = allocate_term (L); *tp = LSUP_term_copy (tmp); if (!*tp) { free (ip); luaL_error (L, "Error allocating term."); } return 1; } static int l_term_set_iter_init (lua_State *L) { size_t *ip = malloc (sizeof (*ip)); LUA_NLCHECK (ip, "Error allocating tset iterator."); *ip = 0; lua_pushlightuserdata (L, ip); STACK_DUMP(L, "Before pushing tset next closure"); lua_pushcclosure (L, term_set_iter_next, 2); return 1; } static int term_set_gc (lua_State *L) { LSUP_TermSet **ts_p = lua_touserdata (L, 1); LSUP_term_set_free (*ts_p); *ts_p = NULL; return 0; } /* * Link map. */ static int lmap_iter_next (lua_State *L) { LSUP_LinkMapIterator *it = *(LSUP_LinkMapIterator **)lua_touserdata (L, lua_upvalueindex (1)); LSUP_Term **link_p = (LSUP_Term **)lua_newuserdata (L, sizeof *link_p); *link_p = NULL; luaL_getmetatable (L, "LSUP.Term"); lua_setmetatable (L, -2); LSUP_TermSet *ts = NULL; LSUP_Term *tmp = NULL; LSUP_rc rc = LSUP_link_map_next (it, &tmp, &ts); if (rc != LSUP_OK) { if (rc == LSUP_END) return 0; else LUA_PCHECK (rc, "Error iterating over link map"); } *link_p = LSUP_term_copy (tmp); LUA_NLCHECK (*link_p, "Error allocating term."); size_t i = 0; tmp = NULL; lua_newtable (L); while ((rc = LSUP_term_set_next (ts, &i, &tmp)) == LSUP_OK) { LSUP_Term **t2_p = (LSUP_Term **)lua_newuserdata (L, sizeof *t2_p); luaL_getmetatable (L, "LSUP.Term"); lua_setmetatable (L, -2); *t2_p = LSUP_term_copy (tmp); lua_pushboolean (L, true); lua_rawset (L, -3); } LUA_PCHECK (rc, "Error iterating over term set"); // linked term + term set. return 2; } /** * Internally this function creates a LMapIterator, which is used as the * upvalue for lmap_iter_next(). The iterator is garbage collected at the end * of the iteration loop, the link map can be reused. */ static int l_lmap_iter_init (lua_State *L) { STACK_DUMP (L, "beginning of LMap iter init fn"); LSUP_LinkMap *lm = *(LSUP_LinkMap **)luaL_checkudata(L, 1, "LSUP.LinkMap"); LSUP_LinkMapIterator **lmit_p = (LSUP_LinkMapIterator **)lua_newuserdata (L, sizeof *lmit_p); *lmit_p = LSUP_link_map_iter_new (lm); luaL_getmetatable (L, "LSUP.LMapIterator"); lua_setmetatable (L, -2); lua_pushcclosure (L, lmap_iter_next, 1); STACK_DUMP (L, "After pushing iter closure"); return 1; } static int link_map_gc (lua_State *L) { LSUP_LinkMap **lm_p = lua_touserdata (L, 1); LOG_DEBUG ("Garbage collecting link map @%p", *lm_p); // FIXME This is to prevent a double-free on shutdown. // Must find the culprit instead. if (UNLIKELY (!lm_p || !*lm_p)) return 0; LSUP_link_map_free (*lm_p); *lm_p = NULL; return 0; } static int lmap_iter_gc (lua_State *L) { LSUP_LinkMapIterator *it = *(LSUP_LinkMapIterator **)lua_touserdata (L, 1); LOG_DEBUG ("Garbage collecting link map iterator @%p", it); LSUP_link_map_iter_free (it); return 0; } /* * Library setup. */ static const luaL_Reg term_lib_fn [] = { {"new_iriref", l_new_iriref}, {"new_iriref_abs", l_new_iriref_abs}, {"new_iriref_rel", l_new_iriref_rel}, {"new_lit", l_new_lit}, {"new_bnode", l_new_bnode}, {"new_copy", l_new_copy}, {NULL} }; static const luaL_Reg term_getters [] = { // General getters. {"data", get_data}, {"type", get_type}, // IRIRef getters. {"nsm", get_iriref_nsm}, {"prefix", get_iriref_prefix}, {"path", get_iriref_path}, {"frag", get_iriref_frag}, // Literal getters. {"datatype", get_lit_datatype}, {"lang", get_lit_lang}, // Generic getters. {"hash", get_hash}, {NULL} }; /* static const luaL_Reg term_setters [] = { {NULL} }; */ static const luaL_Reg term_lib_meth [] = { {"__eq", l_term_equals}, {"__gc", l_term_gc}, {"__index", l_term_get_attr}, {"__tostring", l_term_to_n3}, {"__newindex", l_term_set_attr}, {NULL} }; static const LEnumConst term_enums[] = { {"TYPE_IRIREF", LSUP_TERM_IRIREF}, {"TYPE_NS_IRIREF", LSUP_TERM_NS_IRIREF}, {"TYPE_LITERAL", LSUP_TERM_LITERAL}, {"TYPE_LT_LITERAL", LSUP_TERM_LT_LITERAL}, {"TYPE_BNODE", LSUP_TERM_BNODE}, {"LINK_INBOUND", LSUP_LINK_INBOUND}, {"LINK_OUTBOUND", LSUP_LINK_OUTBOUND}, {"LINK_EDGE", LSUP_LINK_EDGE}, {NULL, 0} }; static const LStringConst term_strings[] = { {"RDF_TYPE", LSUP_RDF_TYPE}, {"RDF_TYPE_NS", LSUP_RDF_TYPE_NS}, {"DEFAULT_DTYPE", DEFAULT_DTYPE}, {"DEFAULT_DTYPE_NS", DEFAULT_DTYPE_NS}, {NULL, 0} }; int luaopen_lsup_term (lua_State *L) { LSUP_init(); // This is idempotent: no problem calling it multiple times. luaL_newmetatable (L, "LSUP.Term"); luaL_setfuncs (L, term_lib_meth, 0); // Getters table. lua_newtable (L); for (int i = 0; term_getters[i].name != NULL; i++) { lua_pushcfunction (L, term_getters[i].func); lua_setfield (L, -2, term_getters[i].name); } // Set getters table as a value for the Term metatable. lua_setfield (L, -2, "getters"); /* * Metatables for ancillary types. */ // Term set. luaL_newmetatable (L, "LSUP.TermSet"); lua_pushcfunction (L, term_set_gc); lua_setfield (L, -2, "__gc"); lua_pushcfunction (L, l_term_set_iter_init); lua_setfield (L, -2, "__pairs"); // Link map. luaL_newmetatable (L, "LSUP.LinkMap"); lua_pushcfunction (L, link_map_gc); lua_setfield (L, -2, "__gc"); lua_pushcfunction (L, l_lmap_iter_init); lua_setfield (L, -2, "__pairs"); // Link map iterator. luaL_newmetatable (L, "LSUP.LMapIterator"); lua_pushcfunction (L, lmap_iter_gc); lua_setfield (L, -2, "__gc"); luaL_newlib (L, term_lib_fn); // Module-level constants. push_string_const (L, term_strings); push_int_const (L, term_enums); return 1; }