diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile.am | 28 | ||||
-rw-r--r-- | src/call.c | 86 | ||||
-rw-r--r-- | src/compose.c | 106 | ||||
-rw-r--r-- | src/lspipat.c | 336 | ||||
-rw-r--r-- | src/lspipat.h | 149 | ||||
-rw-r--r-- | src/lspipat.lua | 155 | ||||
-rw-r--r-- | src/misc.c | 89 | ||||
-rw-r--r-- | src/render.c | 138 | ||||
-rw-r--r-- | src/simple.c | 57 | ||||
-rw-r--r-- | src/string.c | 131 | ||||
-rw-r--r-- | src/uint.c | 128 | ||||
-rw-r--r-- | src/unary.c | 182 |
12 files changed, 1585 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..de6160b --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,28 @@ +# Main lspipat Automake file +# processed automatically + +AM_CFLAGS = -std=c99 -Wall + +lualib_lspipat_LTLIBRARIES = core.la +core_la_SOURCES = lspipat.c lspipat.h \ + call.c compose.c unary.c render.c misc.c \ + simple.c string.c uint.c +core_la_LDFLAGS = -module + +if LUA_PRECOMPILE + +lualib_DATA = lspipat.out +CLEANFILES = $(lualib_DATA) +EXTRA_DIST = lspipat.lua + +lspipat.out : lspipat.lua + @LUAC@ @LUAC_FLAGS@ -o $@ $< + +install-data-hook : + mv -f $(DESTDIR)$(lualibdir)/lspipat.out $(DESTDIR)$(lualibdir)/lspipat.lua + +else + +dist_lualib_DATA = lspipat.lua + +endif diff --git a/src/call.c b/src/call.c new file mode 100644 index 0000000..818ebdb --- /dev/null +++ b/src/call.c @@ -0,0 +1,86 @@ +/* + * LSPIPAT - LUA SPIPAT WRAPPER + * Copyright (C) 2010, Robin Haberkorn + * License: LGPL + * + * CORE: CALL OPERATIONS + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "lspipat.h" + +static void +callFncHandler(VString matched, void *global __attribute__((unused)), void *local) +{ + struct callRefs *call = local; + lua_State *L = call->cb.L; + + lua_rawgeti(L, LUA_REGISTRYINDEX, call->cb.function); + lua_pushlstring(L, matched.ptr, matched.len); + lua_rawgeti(L, LUA_REGISTRYINDEX, call->cb.cookie); +#if 0 + lua_rawgeti(L, LUA_REGISTRYINDEX, *(int *)global); +#endif + + lua_call(L, 2, 0); +} + +struct callOperator { + struct pat *(*call)(struct pat *, void (*)(VString, void *, void *), void *); +}; + + /* TODO: local cookie support, this would also allow helper functions for assignment to global variables */ + /* at least one parameter is a pattern, the lvalue has to be it */ +static int +genericCallOperator(lua_State *L, struct callOperator spipat) +{ + PATTERN_WRAPPER *new; + struct callRefs *call; + + PATTERN_WRAPPER *lvalue = luaL_checkudata(L, 1, PATTERN_MT); + if (!lvalue->pattern) + L_ERROR(L_FREED); + if (!lua_isfunction(L, 2)) + L_ERROR(L_TYPE); + + if (!(new = lua_newuserdata(L, sizeof(PATTERN_WRAPPER)))) + L_ERROR(L_ALLOC); + memset(new, 0, sizeof(PATTERN_WRAPPER)); + lua_insert(L, 1); /* move wrapper below lvalue */ + + new->type = PATTERN_CALL; + + call = &new->u.call; + call->cb.L = L; + call->cb.cookie = LUA_REFNIL; + call->cb.function = luaL_ref(L, LUA_REGISTRYINDEX); + call->pattern = luaL_ref(L, LUA_REGISTRYINDEX); + /* wrapper at top again */ + + new->pattern = spipat.call(lvalue->pattern, callFncHandler, call); + if (!new->pattern) + L_ERROR(L_ALLOC); + + luaL_getmetatable(L, PATTERN_MT); + lua_setmetatable(L, -2); + + return 1; +} + +#define STDCALLOP(LFNC, SPIFNC) \ + LUA_SIG(LFNC) \ + { \ + return genericCallOperator(L, (struct callOperator) { \ + .call = SPIFNC \ + }); \ + } + +STDCALLOP(l_op_call_immed, spipat_call_immed) +STDCALLOP(l_op_call_onmatch, spipat_call_onmatch) + +#undef STDCALLOP diff --git a/src/compose.c b/src/compose.c new file mode 100644 index 0000000..b8be248 --- /dev/null +++ b/src/compose.c @@ -0,0 +1,106 @@ +/* + * LSPIPAT - LUA SPIPAT WRAPPER + * Copyright (C) 2010, Robin Haberkorn + * License: LGPL + * + * CORE: COMPOSITION OPERATIONS + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "lspipat.h" + +struct composeOperator { + struct pat *(*str_pat)(VString, struct pat *); + struct pat *(*pat_str)(struct pat *, VString); + struct pat *(*chr_pat)(Character, struct pat *); + struct pat *(*pat_chr)(struct pat *, Character); + struct pat *(*pat_pat)(struct pat *, struct pat *); +}; + + /* at least one parameter must be a pattern, both are only allowed to be numbers, strings or patterns */ + +static int +genericComposeOperator(lua_State *L, struct composeOperator spipat) +{ + VString str = VSTRING_INITIALIZER; + PATTERN_WRAPPER *new; + + if (!(new = lua_newuserdata(L, sizeof(PATTERN_WRAPPER)))) + L_ERROR(L_ALLOC); + memset(new, 0, sizeof(PATTERN_WRAPPER)); + lua_insert(L, 1); + + if (lua_isstring(L, 2)) { /* lvalue number/string, rvalue is pattern */ + PATTERN_WRAPPER *rvalue = lua_touserdata(L, 3); + + if (!rvalue->pattern) + L_ERROR(L_FREED); + str.ptr = lua_tolstring(L, 2, (size_t *)&str.len); + + new->type = PATTERN_ONESUBPAT; + new->u.onesubpat.pattern = luaL_ref(L, LUA_REGISTRYINDEX); + + new->pattern = str.len == 1 ? spipat.chr_pat(*str.ptr, rvalue->pattern) + : spipat.str_pat(str, rvalue->pattern); + + lua_pop(L, 1); /* `new' at stack top */ + } else { /* lvalue must be pattern */ + PATTERN_WRAPPER *lvalue = luaL_checkudata(L, 2, PATTERN_MT); + + if (!lvalue->pattern) + L_ERROR(L_FREED); + + if (lua_isstring(L, 3)) { /* rvalue number/string */ + str.ptr = lua_tolstring(L, 3, (size_t *)&str.len); + + new->pattern = str.len == 1 ? spipat.pat_chr(lvalue->pattern, *str.ptr) + : spipat.pat_str(lvalue->pattern, str); + + lua_pop(L, 1); + + new->type = PATTERN_ONESUBPAT; + new->u.onesubpat.pattern = luaL_ref(L, LUA_REGISTRYINDEX); + } else { /* rvalue must be pattern */ + PATTERN_WRAPPER *rvalue = luaL_checkudata(L, 3, PATTERN_MT); + + if (!rvalue->pattern) + L_ERROR(L_FREED); + + new->type = PATTERN_TWOSUBPAT; + new->u.twosubpat.pattern2 = luaL_ref(L, LUA_REGISTRYINDEX); + new->u.twosubpat.pattern1 = luaL_ref(L, LUA_REGISTRYINDEX); + + new->pattern = spipat.pat_pat(lvalue->pattern, rvalue->pattern); + } + } + + if (!new->pattern) + L_ERROR(L_ALLOC); + + luaL_getmetatable(L, PATTERN_MT); + lua_setmetatable(L, -2); + + return 1; +} + +#define STDCOMPOSEOP(LFNC, SPIFNC) \ + LUA_SIG(LFNC) \ + { \ + return genericComposeOperator(L, (struct composeOperator) { \ + .str_pat = SPIFNC##_str_pat, \ + .pat_str = SPIFNC##_pat_str, \ + .chr_pat = SPIFNC##_chr_pat, \ + .pat_chr = SPIFNC##_pat_chr, \ + .pat_pat = SPIFNC##_pat_pat \ + }); \ + } + +STDCOMPOSEOP(l_op_and, spipat_and) +STDCOMPOSEOP(l_op_or, spipat_or) + +#undef STDCOMPOSEOP 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 diff --git a/src/lspipat.h b/src/lspipat.h new file mode 100644 index 0000000..dbae4b2 --- /dev/null +++ b/src/lspipat.h @@ -0,0 +1,149 @@ +/* + * LSPIPAT - LUA SPIPAT WRAPPER + * Copyright (C) 2010, Robin Haberkorn + * License: LGPL + */ + +#ifndef _LSPIPAT_H +#define _LSPIPAT_H + +#ifdef HAVE_LUA5_1_LUA_H +#include <lua5.1/lua.h> +#include <lua5.1/lauxlib.h> +#include <lua5.1/lualib.h> +#else +#include <lua.h> +#include <lauxlib.h> +#include <lualib.h> +#endif + +#include <stdint.h> +#include <stdbool.h> +#include <spipat.h> + +#if defined(HAVE_SPIPAT_IMPL_H) && defined(HAVE_SPIPAT_IMAGE_H) +#define USE_SPIPAT_IMAGE_CUSTOM + +#include <spipat_impl.h> +#include <spipat_image.h> + +#endif + +#define VSTRING_INITIALIZER {NULL, 0, NULL, NULL} + + /* Lua error raising */ + +#define L_ALLOC "Allocation error" +#define L_MISC "Miscellaneous error" +#define L_TYPE "Invalid type" +#define L_NUMBER "Invalid number of parameters" +#define L_VALUE "Invalid value for this parameter" +#define L_FREED "Pattern already freed" +#define L_RETURN "Invalid return value" + +#define L_ERROR(MSG, ...) do { \ + luaL_error(L, MSG "\n", ##__VA_ARGS__); \ +} while (0) /* return omitted, so it works for all functions */ + + /* metatables */ + +#define PATTERN_MT "SPIPAT.PATTERN_MT" + + /* structures */ + +typedef struct { + const char *lua; + int c; +} LUA_CONSTANT; + +struct cbRefs { /* wraps references necessary for callbacks */ + lua_State *L; + + int function; + int cookie; /* local cookie */ +}; + +typedef struct { + struct pat *pattern; + + enum { /* Lua reference classes of patterns */ + PATTERN_OTHER = 0, + PATTERN_ONESUBPAT, + PATTERN_TWOSUBPAT, + PATTERN_CALL, + PATTERN_RETFNC, + PATTERN_SIMPLEFNC + } type; + + union { /* references to control garbage collection */ + struct onesubpatRefs { + int pattern; + } onesubpat; + + struct twosubpatRefs { + int pattern1; + int pattern2; + } twosubpat; + + struct callRefs { + int pattern; + struct cbRefs cb; + } call; + + struct retfncRefs { + struct cbRefs cb; + int ret; + } retfnc; + + struct simplefncRefs { + struct cbRefs cb; + } simplefnc; + } u; +} PATTERN_WRAPPER; + + /* Lua functions */ + +#define LUA_SIG(FNC) \ + int FNC(lua_State *L) + +LUA_SIG(l_smatch); +LUA_SIG(l_topattern); +LUA_SIG(l_dump); + +LUA_SIG(l_tostring); + +LUA_SIG(l_op_and); +LUA_SIG(l_op_or); + +LUA_SIG(l_op_call_immed); +LUA_SIG(l_op_call_onmatch); + +LUA_SIG(l_setcur); +LUA_SIG(l_pred); + +LUA_SIG(l_primitive_any); +LUA_SIG(l_primitive_break); +LUA_SIG(l_primitive_breakx); +LUA_SIG(l_primitive_notany); +LUA_SIG(l_primitive_nspan); +LUA_SIG(l_primitive_span); + +LUA_SIG(l_primitive_len); +LUA_SIG(l_primitive_pos); +LUA_SIG(l_primitive_rpos); +LUA_SIG(l_primitive_rtab); +LUA_SIG(l_primitive_tab); + +LUA_SIG(l_primitive_abort); +LUA_SIG(l_primitive_arb); +LUA_SIG(l_primitive_bal); +LUA_SIG(l_primitive_fail); +LUA_SIG(l_primitive_rem); +LUA_SIG(l_primitive_succeed); + +LUA_SIG(l_primitive_arbno); +LUA_SIG(l_primitive_fence); + +void retfncUnrefRet(void *); + +#endif
\ No newline at end of file diff --git a/src/lspipat.lua b/src/lspipat.lua new file mode 100644 index 0000000..9db2082 --- /dev/null +++ b/src/lspipat.lua @@ -0,0 +1,155 @@ +-- +-- LSPIPAT - LUA SPIPAT WRAPPER +-- Copyright (C) 2010, Robin Haberkorn +-- License: LGPL +-- +-- ADDITIONAL METHODS IMPLEMENTED IN LUA +-- + +module("spipat", package.seeall) + +-- +-- Module and Pattern methods +-- + +function ssub(str, pattern, repl, n, flags) + assert(type(repl) == "string" or type(repl) == "function", + "Invalid replacement specified!") + assert(type(n) == "nil" or type(n) == "number", + "Invalid repeat value specified!") + + local cMatches = 0 + repeat + -- cares about the remaining checks + local s, e = smatch(str, pattern, flags) + if not s then break end + + local res = type(repl) == "string" and repl or repl(s, e) + assert(type(res) == "nil" or type(res) == "string", + "Replacement function returned invalid value!") + + if res then str = str:sub(1, s - 1)..res..str:sub(e + 1) end + + if type(n) == "number" then n = n - 1 end + cMatches = cMatches + 1 + until n == 0 + + return str, cMatches +end + +function siter(str, pattern, flags) + local endPos = 0 + pattern = Pos(function() return endPos end) * Arb() * + #function(p) startPos = p + 1 end * pattern * #function(p) endPos = p end + + return function() + if not smatch(str, pattern, flags) then return end + return startPos, endPos + end +end + +-- +-- Primitives (shortcuts for deferring global variables) +-- + +local function genericSetGlobal(val, name) _G[name] = val end + +function _Setcur(name) return Setcur(genericSetGlobal, name) end +_G._Setcur = _Setcur +-- unfortunately, we can't register this as __len to strings... + + -- NOTE: if global `name' is of an invalid type, + -- lspipat will raise an error automatically +local function genericGetGlobal(name) return _G[name] end + +for _, prim in ipairs{ + "Pred", -- _Pred will be registered as __unm to strings + "Any", "Break", "BreakX", "NotAny", "NSpan", "Span", -- string primitives + "Len", "Pos", "RPos", "RTab", "Tab" -- number primitives +} do + local _prim = "_"..prim + + spipat[_prim] = function(name) return spipat[prim](genericGetGlobal, name) end + _G[_prim] = spipat[_prim] +end + +-- FIXME: local cookie support for assignments -> shortcuts for assignment of global variables + +-- +-- POSIX Extended Regular Expressions To SPITBOL Pattern Compiler +-- + +function RegExp(str, captures) + assert(type(captures) == "nil" or type(captures) == "table", + "Invalid captures table given!") + + local stack = {} + local function push(v) table.insert(stack, v) end + local function pop() return table.remove(stack) end + local r2p = {["."] = Len(1), ["^"] = Pos(0), ["$"] = RPos(0)} + + local set + local function add(c) table.insert(set, c) return c end + + local classes = { + blank = " \t", + punct = [[-!"#$%&'()*+,./:;<=>?@[\]^_`{|}~]], + lower = "abcdefghijklmnopqrstuvwxyz", + digit = "0123456789" + } + classes.upper = classes.lower:upper() + classes.alpha = classes.upper..classes.lower + classes.alnum = classes.alpha..classes.digit + classes.word = classes.alnum.."_" + classes.xdigit = classes.upper:sub(1, 6)..classes.lower:sub(1, 6)..classes.digit + classes.space = classes.blank.."\r\n\v\f" + -- TODO: some character classes are still missing... + + local function exp() return exp end + local function seq() return seq end + local atom = ( "\\" * (Len(1) % push) + + NotAny(".[]^$()*+?|{}") % push + + Any(".^$") % function(r) push(r2p[r]) end + + "[" * ( "^" * -function() push(NotAny) set = {} end + + -function() push(Any) set = {} end ) + * (topattern("]") % add + "") + * Arbno( "[:" * (Break(":") % push) * ":]" * -function() return add(classes[pop()]) ~= nil end + + Len(1) * "-" * Len(1) + % function(range) for c = range:byte(), range:byte(3) do add(string.char(c)) end end + + Len(1) % add ) + * "]" * -function() push(pop()(table.concat(set))) end + + "(" * -exp * ")" + * -function() if captures then + push(topattern(pop()) / function(cap) table.insert(captures, cap) end) end end ) + * ( "*" * ( "?" * -function() push(Arbno(pop())) end + + -function() local r; r = pop() * -function() return r end + "" + push(r) end ) + + "+" * -function() local r; r = pop() * (-function() return r end + "") + push(r) end + + "?" * -function() push(topattern("") + pop()) end + + "{" * ( Span(classes.digit) % push ) * "," + * ( Span(classes.digit) + % function(max) local min, c = pop() + local r; r = pop() * -function() c = c + 1 + return c >= tonumber(max) or r end + "" + push(-function() c = 0 end * r * -function() return c >= tonumber(min) end) end ) + * "}" + + "" ) + seq = ( atom * -function() local rvalue, lvalue = pop(), pop() + push(type(lvalue) == "string" and type(rvalue) == "string" and + lvalue..rvalue or lvalue * rvalue) end + * (-seq + "") + "" ) + * ( "|" * -exp * -function() local pat = pop() push(pop() + topattern(pat)) end + + "" ) + exp = atom * seq + + assert(smatch(str, exp * RPos(0), match_anchored), + "Invalid regular expression!") + + return stack[1] +end +_G.RegExp = RegExp + + -- load C core, also registers Lua functions into metatables we cannot + -- access from Lua +require "lspipat.core"
\ No newline at end of file diff --git a/src/misc.c b/src/misc.c new file mode 100644 index 0000000..2bea4c8 --- /dev/null +++ b/src/misc.c @@ -0,0 +1,89 @@ +/* + * LSPIPAT - LUA SPIPAT WRAPPER + * Copyright (C) 2010, Robin Haberkorn + * License: LGPL + * + * CORE: MISCELLANEOUS PRIMITIVES/CONSTRUCTORS + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "lspipat.h" + +LUA_SIG(l_primitive_arbno) +{ + int top = lua_gettop(L); + + VString str = VSTRING_INITIALIZER; + PATTERN_WRAPPER *new; + + luaL_argcheck(L, top == 1, top, L_NUMBER); + + if (!(new = lua_newuserdata(L, sizeof(PATTERN_WRAPPER)))) + L_ERROR(L_ALLOC); + memset(new, 0, sizeof(PATTERN_WRAPPER)); + + if (lua_isstring(L, 1)) { + str.ptr = lua_tolstring(L, 1, (size_t *)&str.len); + + new->pattern = str.len == 1 ? spipat_arbno_chr(*str.ptr) + : spipat_arbno_str(str); + } else { + PATTERN_WRAPPER *wrapper = luaL_checkudata(L, 1, PATTERN_MT); + luaL_argcheck(L, wrapper->pattern, 1, L_FREED); + + lua_insert(L, 1); /* move wrapper to bottom */ + new->type = PATTERN_ONESUBPAT; + new->u.onesubpat.pattern = luaL_ref(L, LUA_REGISTRYINDEX); + /* wrapper at top again */ + + new->pattern = spipat_arbno(wrapper->pattern); + } + + if (!new->pattern) + L_ERROR(L_ALLOC); + + luaL_getmetatable(L, PATTERN_MT); + lua_setmetatable(L, -2); + + return 1; + +} + +LUA_SIG(l_primitive_fence) +{ + int top = lua_gettop(L); + PATTERN_WRAPPER *new; + + luaL_argcheck(L, top < 2, top, L_NUMBER); + + if (!(new = lua_newuserdata(L, sizeof(PATTERN_WRAPPER)))) + L_ERROR(L_ALLOC); + memset(new, 0, sizeof(PATTERN_WRAPPER)); + + if (!top) { + new->pattern = spipat_fence_simple(); + } else { + PATTERN_WRAPPER *wrapper = luaL_checkudata(L, 1, PATTERN_MT); + luaL_argcheck(L, wrapper->pattern, 1, L_FREED); + + lua_insert(L, 1); /* move wrapper to bottom */ + new->type = PATTERN_ONESUBPAT; + new->u.onesubpat.pattern = luaL_ref(L, LUA_REGISTRYINDEX); + /* wrapper at top again */ + + new->pattern = spipat_fence_function(wrapper->pattern); + } + + if (!new->pattern) + L_ERROR(L_ALLOC); + + luaL_getmetatable(L, PATTERN_MT); + lua_setmetatable(L, -2); + + return 1; +} diff --git a/src/render.c b/src/render.c new file mode 100644 index 0000000..28c96ce --- /dev/null +++ b/src/render.c @@ -0,0 +1,138 @@ +/* + * LSPIPAT - LUA SPIPAT WRAPPER + * Copyright (C) 2010, Robin Haberkorn + * License: LGPL + * + * CORE: RENDER-TO-STRING OPERATION + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdlib.h> + +#include "lspipat.h" + +#ifdef USE_SPIPAT_IMAGE_CUSTOM + +static const char *lspipat_strs[] = { /* left out elements that can't be constructed with lspipat */ + [PC_Abort] = "Abort", + [PC_Alt] = " + ", + [PC_Any_CH] = "Any", + [PC_Any_CS] = "Any", + [PC_Any_VF] = "Any", + [PC_Arb_X] = "Arb", + [PC_Arbno_S] = "Arbno", + [PC_Arbno_X] = "Arbno", + [PC_Bal] = "Bal", + [PC_BreakX_CH] = "BreakX", + [PC_BreakX_CS] = "BreakX", + [PC_BreakX_VF] = "BreakX", + [PC_Break_CH] = "Break", + [PC_Break_CS] = "Break", + [PC_Break_VF] = "Break", + [PC_Call_Imm] = " % ", + [PC_Call_OnM] = " / ", + [PC_Fail] = "Fail", + [PC_Fence] = "Fence", + [PC_Fence_X] = "Fence", + [PC_Len_NF] = "Len", + [PC_Len_Nat] = "Len", + [PC_NSpan_CH] = "NSpan", + [PC_NSpan_CS] = "NSpan", + [PC_NSpan_VF] = "NSpan", + [PC_NotAny_CH] = "NotAny", + [PC_NotAny_CS] = "NotAny", + [PC_NotAny_VF] = "NotAny", + [PC_Null] = "\"\"", + [PC_Pos_NF] = "Pos", + [PC_Pos_Nat] = "Pos", + [PC_RPos_NF] = "RPos", + [PC_RPos_Nat] = "RPos", + [PC_RTab_NF] = "RTab", + [PC_RTab_Nat] = "RTab", + [PC_Rem] = "Rem", + [PC_Setcur_Func] = "#", /* also: Setcur */ + [PC_Span_CH] = "Span", + [PC_Span_CS] = "Span", + [PC_Span_VF] = "Span", + [PC_Succeed] = "Succeed", + [PC_Tab_NF] = "Tab", + [PC_Tab_Nat] = "Tab", + [PC_Dynamic_Func] = "-" /* also: Pred */ +}; + +/* TODO: Define some custom Append functions */ + +LUA_SIG(l_tostring) +{ + char buf[1024], *bigbuf; + unsigned len; + + struct state state = { + .ptr = buf, + .size = sizeof(buf) + }; + + PATTERN_WRAPPER *wrapper = lua_touserdata(L, 1); /* parameter is definitely a pattern */ + + luaL_argcheck(L, wrapper->pattern, 1, L_FREED); + + spipat_image_init_state(&state); + state.cquote = "\""; + state.concat = " * "; + state.strings = lspipat_strs; + + len = spipat_image_custom(&state, wrapper->pattern); + if (len < sizeof(buf)) { + lua_pushlstring(L, buf, len); + return 1; + } + + /* sizeof(buf) was too small */ + + state.size = len + 1; + if (!(bigbuf = malloc(state.size))) + L_ERROR(L_ALLOC); + state.ptr = bigbuf; + + spipat_image_custom(&state, wrapper->pattern); + lua_pushlstring(L, bigbuf, len); + + free(bigbuf); + + return 1; +} + +#else + +LUA_SIG(l_tostring) +{ + char buf[1024], *bigbuf; + unsigned len; + + PATTERN_WRAPPER *wrapper = lua_touserdata(L, 1); /* parameter is definitely a pattern */ + + luaL_argcheck(L, wrapper->pattern, 1, L_FREED); + + len = spipat_image(wrapper->pattern, buf, sizeof(buf)); + if (len < sizeof(buf)) { + lua_pushlstring(L, buf, len); + return 1; + } + + /* sizeof(buf) was too small */ + + if (!(bigbuf = malloc(len + 1))) + L_ERROR(L_ALLOC); + + spipat_image(wrapper->pattern, bigbuf, len + 1); + lua_pushlstring(L, bigbuf, len); + + free(bigbuf); + + return 1; +} + +#endif diff --git a/src/simple.c b/src/simple.c new file mode 100644 index 0000000..d407129 --- /dev/null +++ b/src/simple.c @@ -0,0 +1,57 @@ +/* + * LSPIPAT - LUA SPIPAT WRAPPER + * Copyright (C) 2010, Robin Haberkorn + * License: LGPL + * + * CORE: SIMPLE PRIMITIVES/CONSTRUCTORS + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "lspipat.h" + +struct simplePrimitive { + struct pat *(*simple)(void); +}; + +static int +genericSimplePrimitive(lua_State *L, struct simplePrimitive spipat) +{ + int top = lua_gettop(L); + PATTERN_WRAPPER *new; + + luaL_argcheck(L, !top, top, L_NUMBER); + + if (!(new = lua_newuserdata(L, sizeof(PATTERN_WRAPPER)))) + L_ERROR(L_ALLOC); + memset(new, 0, sizeof(PATTERN_WRAPPER)); + + if (!(new->pattern = spipat.simple())) + L_ERROR(L_ALLOC); + + luaL_getmetatable(L, PATTERN_MT); + lua_setmetatable(L, -2); + + return 1; +} + +#define STDSIMPLEPRIM(LFNC, SPIFNC) \ + LUA_SIG(LFNC) \ + { \ + return genericSimplePrimitive(L, (struct simplePrimitive) { \ + .simple = SPIFNC \ + }); \ + } + +STDSIMPLEPRIM(l_primitive_abort, spipat_abort) +STDSIMPLEPRIM(l_primitive_arb, spipat_arb) +STDSIMPLEPRIM(l_primitive_bal, spipat_bal) +STDSIMPLEPRIM(l_primitive_fail, spipat_fail) +STDSIMPLEPRIM(l_primitive_rem, spipat_rem) +STDSIMPLEPRIM(l_primitive_succeed, spipat_succeed) + +#undef STDSIMPLEPRIM diff --git a/src/string.c b/src/string.c new file mode 100644 index 0000000..43f266c --- /dev/null +++ b/src/string.c @@ -0,0 +1,131 @@ +/* + * LSPIPAT - LUA SPIPAT WRAPPER + * Copyright (C) 2010, Robin Haberkorn + * License: LGPL + * + * CORE: STRING PRIMITIVES/CONSTRUCTORS + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "lspipat.h" + +static VString +stringFncHandler(void *global __attribute__((unused)), void *local) +{ + struct retfncRefs *retfnc = local; + lua_State *L = retfnc->cb.L; + + VString ret; + + lua_rawgeti(L, LUA_REGISTRYINDEX, retfnc->cb.function); + lua_rawgeti(L, LUA_REGISTRYINDEX, retfnc->cb.cookie); +#if 0 + lua_rawgeti(L, LUA_REGISTRYINDEX, *(int *)global); +#endif + + lua_call(L, 1, 1); + + if (!lua_isstring(L, -1)) { + lua_pop(L, 1); + L_ERROR(L_RETURN); /* FIXME: is it safe to raise errors? */ + } + + ret.ptr = lua_tolstring(L, -1, (size_t *)&ret.len); + ret.release = retfncUnrefRet; + ret.cookie = retfnc; + + /* + * Register value so Lua doesn't free it until spipat + * doesn't need it anymore (value has to be popped now) + */ + retfnc->ret = luaL_ref(L, LUA_REGISTRYINDEX); + return ret; +} + +struct stringPrimitive { + struct pat *(*chr)(Character); + struct pat *(*str)(VString); + struct pat *(*fnc)(VString (*)(void *, void*), void *); +}; + +static int +genericStringPrimitive(lua_State *L, struct stringPrimitive spipat) +{ + int top = lua_gettop(L); + + VString str = VSTRING_INITIALIZER; + PATTERN_WRAPPER *new; + + luaL_argcheck(L, top, top, L_NUMBER); + + if (!(new = lua_newuserdata(L, sizeof(PATTERN_WRAPPER)))) + L_ERROR(L_ALLOC); + memset(new, 0, sizeof(PATTERN_WRAPPER)); + + switch (lua_type(L, 1)) { + case LUA_TNUMBER: + case LUA_TSTRING: + luaL_argcheck(L, top == 1, top, L_NUMBER); + + str.ptr = lua_tolstring(L, 1, (size_t *)&str.len); + + new->pattern = str.len == 1 ? spipat.chr(*str.ptr) + : spipat.str(str); + break; + + case LUA_TFUNCTION: { + struct retfncRefs *retfnc; + + luaL_argcheck(L, top == 1 || top == 2, top, L_NUMBER); + + lua_insert(L, 1); /* move wrapper to bottom */ + if (top == 1) + lua_pushnil(L); /* cookie will be LUA_REFNIL */ + + new->type = PATTERN_RETFNC; + + retfnc = &new->u.retfnc; + retfnc->cb.L = L; + retfnc->cb.cookie = luaL_ref(L, LUA_REGISTRYINDEX); + retfnc->cb.function = luaL_ref(L, LUA_REGISTRYINDEX); + /* wrapper at top again */ + + new->pattern = spipat.fnc(stringFncHandler, retfnc); + break; + } + default: + return luaL_argerror(L, 1, L_TYPE); + } + + if (!new->pattern) + L_ERROR(L_ALLOC); + + luaL_getmetatable(L, PATTERN_MT); + lua_setmetatable(L, -2); + + return 1; +} + +#define STDSTRPRIM(LFNC, SPIFNC) \ + LUA_SIG(LFNC) \ + { \ + return genericStringPrimitive(L, (struct stringPrimitive) { \ + .chr = SPIFNC##_chr, \ + .str = SPIFNC##_str, \ + .fnc = SPIFNC##_fnc \ + }); \ + } + +STDSTRPRIM(l_primitive_any, spipat_any) +STDSTRPRIM(l_primitive_break, spipat_break) +STDSTRPRIM(l_primitive_breakx, spipat_breakx) +STDSTRPRIM(l_primitive_notany, spipat_notany) +STDSTRPRIM(l_primitive_nspan, spipat_nspan) +STDSTRPRIM(l_primitive_span, spipat_span) + +#undef STDSTRPRIM diff --git a/src/uint.c b/src/uint.c new file mode 100644 index 0000000..1a0530b --- /dev/null +++ b/src/uint.c @@ -0,0 +1,128 @@ +/* + * LSPIPAT - LUA SPIPAT WRAPPER + * Copyright (C) 2010, Robin Haberkorn + * License: LGPL + * + * CORE: UNSIGNED INTEGER PRIMITIVES/CONSTRUCTORS + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <string.h> + +#include "lspipat.h" + +static unsigned +uintFncHandler(void *global __attribute__((unused)), void *local) +{ + struct simplefncRefs *simplefnc = local; + lua_State *L = simplefnc->cb.L; + + int val; + + lua_rawgeti(L, LUA_REGISTRYINDEX, simplefnc->cb.function); + lua_rawgeti(L, LUA_REGISTRYINDEX, simplefnc->cb.cookie); +#if 0 + lua_rawgeti(L, LUA_REGISTRYINDEX, *(int *)global); +#endif + + lua_call(L, 1, 1); + + if (lua_isnil(L, -1)) { + lua_pop(L, 1); + return 0; /* default value */ + } + + if (!lua_isnumber(L, -1)) { + lua_pop(L, 1); + L_ERROR(L_RETURN); /* FIXME: is it safe to raise errors? */ + } + + val = lua_tointeger(L, -1); + lua_pop(L, 1); + if (val < 0) + L_ERROR(L_RETURN); + + return (unsigned)val; +} + +struct uintPrimitive { + struct pat *(*uint)(unsigned); + struct pat *(*fnc)(unsigned (*)(void *, void *), void *); +}; + +static int +genericUIntPrimitive(lua_State *L, struct uintPrimitive spipat) +{ + int top = lua_gettop(L); + PATTERN_WRAPPER *new; + + if (!(new = lua_newuserdata(L, sizeof(PATTERN_WRAPPER)))) + L_ERROR(L_ALLOC); + memset(new, 0, sizeof(PATTERN_WRAPPER)); + + switch (lua_type(L, 1)) { + case LUA_TNONE: + case LUA_TNIL: + case LUA_TNUMBER: + case LUA_TSTRING: { + int val; + + luaL_argcheck(L, top < 2, top, L_NUMBER); + val = luaL_optint(L, 1, 0); + luaL_argcheck(L, val >= 0, 1, L_VALUE); + + new->pattern = spipat.uint((unsigned)val); + break; + } + case LUA_TFUNCTION: { + struct simplefncRefs *simplefnc; + + luaL_argcheck(L, top == 1 || top == 2, top, L_NUMBER); + + lua_insert(L, 1); /* move wrapper to bottom */ + if (top == 1) + lua_pushnil(L); /* cookie will be LUA_REFNIL */ + + new->type = PATTERN_SIMPLEFNC; + + simplefnc = &new->u.simplefnc; + simplefnc->cb.L = L; + simplefnc->cb.cookie = luaL_ref(L, LUA_REGISTRYINDEX); + simplefnc->cb.function = luaL_ref(L, LUA_REGISTRYINDEX); + /* wrapper at top again */ + + new->pattern = spipat.fnc(uintFncHandler, simplefnc); + break; + } + default: + return luaL_argerror(L, 1, L_TYPE); + } + + if (!new->pattern) + L_ERROR(L_ALLOC); + + luaL_getmetatable(L, PATTERN_MT); + lua_setmetatable(L, -2); + + return 1; +} + +#define STDUINTPRIM(LFNC, SPIFNC) \ + LUA_SIG(LFNC) \ + { \ + return genericUIntPrimitive(L, (struct uintPrimitive) { \ + .uint = SPIFNC, \ + .fnc = SPIFNC##_fnc \ + }); \ + } + +STDUINTPRIM(l_primitive_len, spipat_len) +STDUINTPRIM(l_primitive_pos, spipat_pos) +STDUINTPRIM(l_primitive_rpos, spipat_rpos) +STDUINTPRIM(l_primitive_rtab, spipat_rtab) +STDUINTPRIM(l_primitive_tab, spipat_tab) + +#undef STDUINTPRIM diff --git a/src/unary.c b/src/unary.c new file mode 100644 index 0000000..b3f40ef --- /dev/null +++ b/src/unary.c @@ -0,0 +1,182 @@ +/* + * LSPIPAT - LUA SPIPAT WRAPPER + * Copyright (C) 2010, Robin Haberkorn + * License: LGPL + * + * CORE: UNARY OPERATORS (ALSO USED AS PRIMITIVES) + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdbool.h> +#include <string.h> + +#include "lspipat.h" + +static void +setcurFncHandler(unsigned pos, void *global __attribute__((unused)), void *local) +{ + struct simplefncRefs *simplefnc = local; + lua_State *L = simplefnc->cb.L; + + lua_rawgeti(L, LUA_REGISTRYINDEX, simplefnc->cb.function); + lua_pushinteger(L, pos); + lua_rawgeti(L, LUA_REGISTRYINDEX, simplefnc->cb.cookie); +#if 0 + lua_rawgeti(L, LUA_REGISTRYINDEX, *(int *)global); +#endif + + lua_call(L, 2, 0); +} + + /* + * if called as an operator, there will be a nil on top of the stack + */ +LUA_SIG(l_setcur) +{ + int top = lua_gettop(L); + + PATTERN_WRAPPER *new; + struct simplefncRefs *simplefnc; + + luaL_argcheck(L, top == 1 || top == 2, top, L_NUMBER); + luaL_argcheck(L, lua_isfunction(L, 1), 1, L_TYPE); + + if (!(new = lua_newuserdata(L, sizeof(PATTERN_WRAPPER)))) + L_ERROR(L_ALLOC); + memset(new, 0, sizeof(PATTERN_WRAPPER)); + + lua_insert(L, 1); /* move wrapper to bottom */ + if (top == 1) + lua_pushnil(L); /* cookie will be LUA_REFNIL */ + + new->type = PATTERN_SIMPLEFNC; + + simplefnc = &new->u.simplefnc; + simplefnc->cb.L = L; + simplefnc->cb.cookie = luaL_ref(L, LUA_REGISTRYINDEX); + simplefnc->cb.function = luaL_ref(L, LUA_REGISTRYINDEX); + /* wrapper at top again */ + + new->pattern = spipat_setcur_fnc(setcurFncHandler, simplefnc); + if (!new->pattern) + L_ERROR(L_ALLOC); + + luaL_getmetatable(L, PATTERN_MT); + lua_setmetatable(L, -2); + + return 1; +} + +static void +predFncHandler(void *global __attribute__((unused)), void *local, struct dynamic *ret) +{ + struct retfncRefs *retfnc = local; + lua_State *L = retfnc->cb.L; + + lua_rawgeti(L, LUA_REGISTRYINDEX, retfnc->cb.function); + lua_rawgeti(L, LUA_REGISTRYINDEX, retfnc->cb.cookie); +#if 0 + lua_rawgeti(L, LUA_REGISTRYINDEX, *(int *)global); +#endif + + lua_call(L, 1, 1); + + switch (lua_type(L, -1)) { + case LUA_TNUMBER: + case LUA_TSTRING: { + VString *str = &ret->val.str; + + ret->type = DY_VSTR; + + str->ptr = lua_tolstring(L, -1, (size_t *)&str->len); + str->release = retfncUnrefRet; + str->cookie = retfnc; + + /* + * Register value so Lua doesn't free it until spipat + * doesn't need it anymore (value has to be popped now) + */ + retfnc->ret = luaL_ref(L, LUA_REGISTRYINDEX); + return; + } + case LUA_TNIL: /* default behaviour: continue matching (Succeed) */ + ret->type = DY_BOOL; + + ret->val.pred = true; + + lua_pop(L, 1); + return; + + case LUA_TBOOLEAN: + ret->type = DY_BOOL; + + ret->val.pred = lua_toboolean(L, -1); + + lua_pop(L, 1); + return; + + case LUA_TUSERDATA: { /* FIXME: check whether it's really a Pattern */ + PATTERN_WRAPPER *wrapper = lua_touserdata(L, -1); + if (!wrapper->pattern) { + lua_pop(L, 1); + L_ERROR(L_RETURN); + } + + ret->type = DY_PAT; + + ret->val.pat.p = wrapper->pattern; + ret->val.pat.release = retfncUnrefRet; + ret->val.pat.cookie = retfnc; + + /* + * Register value so Lua doesn't free it until spipat + * doesn't need it anymore (value has to be popped now) + */ + retfnc->ret = luaL_ref(L, LUA_REGISTRYINDEX); + return; + } + default: + lua_pop(L, 1); + L_ERROR(L_RETURN); + } + + /* not reached */ +} + +LUA_SIG(l_pred) +{ + int top = lua_gettop(L); + + PATTERN_WRAPPER *new; + struct retfncRefs *retfnc; + + luaL_argcheck(L, top == 1 || top == 2, top, L_NUMBER); + luaL_argcheck(L, lua_isfunction(L, 1), 1, L_TYPE); + + if (!(new = lua_newuserdata(L, sizeof(PATTERN_WRAPPER)))) + L_ERROR(L_ALLOC); + memset(new, 0, sizeof(PATTERN_WRAPPER)); + + lua_insert(L, 1); /* move wrapper to bottom */ + if (top == 1) + lua_pushnil(L); /* cookie will be LUA_REFNIL */ + + new->type = PATTERN_RETFNC; + retfnc = &new->u.retfnc; + retfnc->cb.L = L; + retfnc->cb.cookie = luaL_ref(L, LUA_REGISTRYINDEX); + retfnc->cb.function = luaL_ref(L, LUA_REGISTRYINDEX); + /* wrapper at top again */ + + new->pattern = spipat_dynamic_fnc(predFncHandler, retfnc); + if (!new->pattern) + L_ERROR(L_ALLOC); + + luaL_getmetatable(L, PATTERN_MT); + lua_setmetatable(L, -2); + + return 1; +} |