aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am28
-rw-r--r--src/call.c86
-rw-r--r--src/compose.c106
-rw-r--r--src/lspipat.c336
-rw-r--r--src/lspipat.h149
-rw-r--r--src/lspipat.lua155
-rw-r--r--src/misc.c89
-rw-r--r--src/render.c138
-rw-r--r--src/simple.c57
-rw-r--r--src/string.c131
-rw-r--r--src/uint.c128
-rw-r--r--src/unary.c182
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;
+}