diff options
Diffstat (limited to 'src/lspipat.c')
-rw-r--r-- | src/lspipat.c | 336 |
1 files changed, 336 insertions, 0 deletions
diff --git a/src/lspipat.c b/src/lspipat.c new file mode 100644 index 0000000..5075961 --- /dev/null +++ b/src/lspipat.c @@ -0,0 +1,336 @@ +/* + * LSPIPAT - LUA SPIPAT WRAPPER + * Copyright (C) 2010, Robin Haberkorn + * License: LGPL + * + * CORE: LIBSPIPAT <-> LUA INTERACTION + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "lspipat.h" + +/* + * Module and Pattern methods + */ + +/* TODO: support global cookies */ + +LUA_SIG(l_smatch) +{ + int top = lua_gettop(L); + + struct spipat_match match; + enum spipat_match_ret ret; + + luaL_argcheck(L, top == 2 || top == 3, top, L_NUMBER); + + memset(&match, 0, sizeof(match)); + match.subject.ptr = luaL_checklstring(L, 1, (size_t *)&match.subject.len); + match.flags = luaL_optint(L, 3, 0); + + if (lua_isstring(L, 2)) { + VString str = VSTRING_INITIALIZER; + str.ptr = lua_tolstring(L, 2, (size_t *)&str.len); + + match.pattern = str.len == 1 ? spipat_char(*str.ptr) + : spipat_string(str); + if (!match.pattern) + L_ERROR(L_ALLOC); + } else { + PATTERN_WRAPPER *wrapper = luaL_checkudata(L, 2, PATTERN_MT); + luaL_argcheck(L, wrapper->pattern, 2, L_FREED); + + match.pattern = wrapper->pattern; + spipat_hold(match.pattern); + } + + ret = spipat_match2(&match); + spipat_free(match.pattern); /* only frees the temporary pattern for string params */ + if (ret == SPIPAT_MATCH_EXCEPTION) + L_ERROR("%s", match.exception); + + if (ret == SPIPAT_MATCH_FAILURE) { + lua_pushnil(L); + return 1; + } + + /* SPIPAT_MATCH_SUCCESS */ + lua_pushinteger(L, match.start); + lua_pushinteger(L, match.stop); + return 2; +} + + /* should we check __topattern operations in types metatables just like tostring does? */ +LUA_SIG(l_topattern) +{ + int top = lua_gettop(L); + + luaL_argcheck(L, top == 1, top, L_NUMBER); + + switch (lua_type(L, 1)) { + case LUA_TNUMBER: + case LUA_TSTRING: { + PATTERN_WRAPPER *wrapper; + VString str = VSTRING_INITIALIZER; + + if (!(wrapper = lua_newuserdata(L, sizeof(PATTERN_WRAPPER)))) + L_ERROR(L_ALLOC); + memset(wrapper, 0, sizeof(PATTERN_WRAPPER)); + + str.ptr = lua_tolstring(L, 1, (size_t *)&str.len); + + wrapper->pattern = str.len == 1 ? spipat_char(*str.ptr) + : spipat_string(str); + if (!wrapper->pattern) + L_ERROR(L_ALLOC); + + luaL_getmetatable(L, PATTERN_MT); + lua_setmetatable(L, -2); + + return 1; + } + case LUA_TUSERDATA: + /* FIXME: check whether it's a PATTERN_MT (without raising an error) */ + return 1; + + default: + return 0; + } + + /* not reached */ +} + +LUA_SIG(l_dump) +{ + PATTERN_WRAPPER *wrapper; + int top = lua_gettop(L); + + luaL_argcheck(L, top == 1, top, L_NUMBER); + wrapper = luaL_checkudata(L, 1, PATTERN_MT); + luaL_argcheck(L, wrapper->pattern, 1, L_FREED); + + spipat_dump(wrapper->pattern); + return 0; +} + +/* + * Finalizer + */ + +static inline void +unrefCallback(struct cbRefs *cb) +{ + luaL_unref(cb->L, LUA_REGISTRYINDEX, cb->function); + luaL_unref(cb->L, LUA_REGISTRYINDEX, cb->cookie); +} + +LUA_SIG(l_finalize_pattern) +{ + int top = lua_gettop(L); + PATTERN_WRAPPER *wrapper; + + luaL_argcheck(L, top == 1, top, L_NUMBER); + wrapper = luaL_checkudata(L, 1, PATTERN_MT); + + if (!wrapper->pattern) + return 0; /* already freed */ + + spipat_free(wrapper->pattern); /* should also release any strings/patterns */ + wrapper->pattern = NULL; /* (remove from registry using release functions) returned by some callback */ + + switch (wrapper->type) { + case PATTERN_OTHER: + break; + case PATTERN_ONESUBPAT: + luaL_unref(L, LUA_REGISTRYINDEX, wrapper->u.onesubpat.pattern); + break; + case PATTERN_TWOSUBPAT: + luaL_unref(L, LUA_REGISTRYINDEX, wrapper->u.twosubpat.pattern1); + luaL_unref(L, LUA_REGISTRYINDEX, wrapper->u.twosubpat.pattern2); + break; + case PATTERN_CALL: + luaL_unref(L, LUA_REGISTRYINDEX, wrapper->u.call.pattern); + unrefCallback(&wrapper->u.call.cb); + break; + case PATTERN_RETFNC: + unrefCallback(&wrapper->u.retfnc.cb); + break; + case PATTERN_SIMPLEFNC: + unrefCallback(&wrapper->u.simplefnc.cb); + break; + default: + L_ERROR(L_MISC); + } + + return 0; +} + +/* + * Cookie release function for function return values + */ + +void +retfncUnrefRet(void *arg) +{ + struct retfncRefs *retfnc = arg; + + luaL_unref(retfnc->cb.L, LUA_REGISTRYINDEX, retfnc->ret); +} + +/* + * Loader + */ + +int +luaopen_lspipat_core(lua_State *L) +{ + static const luaL_Reg spipat[] = { + {"smatch", l_smatch}, + + {"topattern", l_topattern}, + {"dump", l_dump}, + + {"free", l_finalize_pattern}, + {NULL, NULL} + }; + + static const luaL_Reg primitives[] = { + /* string primitives */ + {"Any", l_primitive_any}, + {"Break", l_primitive_break}, + {"BreakX", l_primitive_breakx}, + {"NotAny", l_primitive_notany}, + {"NSpan", l_primitive_nspan}, + {"Span", l_primitive_span}, + + /* unsigned integer primitives */ + {"Len", l_primitive_len}, + {"Pos", l_primitive_pos}, + {"RPos", l_primitive_rpos}, + {"RTab", l_primitive_rtab}, + {"Tab", l_primitive_tab}, + + /* simple primitives */ + {"Abort", l_primitive_abort}, + {"Arb", l_primitive_arb}, + {"Bal", l_primitive_bal}, + {"Fail", l_primitive_fail}, + {"Rem", l_primitive_rem}, + {"Succeed", l_primitive_succeed}, + + /* misc. primitives */ + {"Arbno", l_primitive_arbno}, + {"Fence", l_primitive_fence}, + + /* primitives for unary operators */ + {"Setcur", l_setcur}, + {"Pred", l_pred}, + {NULL, NULL} + }; + + static const luaL_Reg methods[] = { + {"free", l_finalize_pattern}, + {NULL, NULL} + }; + + static const luaL_Reg operations[] = { + {"__mul", l_op_and}, + {"__add", l_op_or}, + {"__mod", l_op_call_immed}, + {"__div", l_op_call_onmatch}, + + {"__tostring", l_tostring}, + + {"__gc", l_finalize_pattern}, + {NULL, NULL} + }; + + static const LUA_CONSTANT mapping[] = { + {"match_debug", SPIPAT_DEBUG}, + {"match_anchored", SPIPAT_ANCHORED}, + {NULL, 0} + }; + + /* module methods, primitives & constants */ + + luaL_register(L, "spipat", spipat); + luaL_register(L, NULL, primitives); + + for (const LUA_CONSTANT *m = mapping; m->lua; m++) { + lua_pushinteger(L, m->c); + lua_setfield(L, -2, m->lua); + } + /* module table should be at stack index 2 */ + + /* global methods & primitives */ + /* FIXME: make it optional (function or submodule) */ + + for (const luaL_Reg *p = primitives; p->name; p++) { + lua_pushcfunction(L, p->func); + lua_setglobal(L, p->name); + } + lua_pushcfunction(L, l_topattern); + lua_setglobal(L, "topattern"); + + /* "patch" string meta table with some methods */ + + lua_pushstring(L, "foo"); /* FIXME: use luaL_getmetatable */ + lua_getmetatable(L, -1); + lua_getfield(L, 2, "_Pred"); /* ok, this is hairy: will only be available if string cannot be converted to a number */ + lua_setfield(L, -2, "__unm"); + lua_getfield(L, -1, "__index"); + lua_pushcfunction(L, l_smatch); /* maybe split "spipat" and use luaL_register */ + lua_setfield(L, -2, "smatch"); + lua_getfield(L, 2, "ssub"); /* maybe write aux function to register Lua functions */ + lua_setfield(L, -2, "ssub"); + lua_getfield(L, 2, "siter"); + lua_setfield(L, -2, "siter"); + lua_pushcfunction(L, l_topattern); + lua_setfield(L, -2, "topattern"); + lua_pop(L, 3); + /* TODO: maybe also set the pattern-specific operations - adapt l_op_or/and to cope with two strings + however, arithmetic ops are already defined for strings if they can be converted to numbers */ + + /* "patch" number meta table with some methods */ + + lua_pushinteger(L, 23); /* FIXME: use luaL_getmetatable */ + if (!lua_getmetatable(L, -1)) { + lua_newtable(L); + lua_newtable(L); + } else + lua_getfield(L, -1, "__index"); + lua_pushcfunction(L, l_topattern); + lua_setfield(L, -2, "topattern"); + lua_setfield(L, -2, "__index"); + lua_setmetatable(L, -2); + lua_pop(L, 1); + + /* "patch" function meta table with operators */ + + lua_pushcfunction(L, l_smatch); /* FIXME: use luaL_getmetatable */ + if (!lua_getmetatable(L, -1)) + lua_newtable(L); + lua_pushcfunction(L, l_setcur); + lua_setfield(L, -2, "__len"); + lua_pushcfunction(L, l_pred); + lua_setfield(L, -2, "__unm"); + lua_setmetatable(L, -2); + lua_pop(L, 1); + + /* pattern metatable: methods & operations/events */ + + luaL_newmetatable(L, PATTERN_MT); + luaL_register(L, NULL, operations); + lua_newtable(L); + luaL_register(L, NULL, methods); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + + /* module table should be on top of the stack again */ + return 1; +}
\ No newline at end of file |