cosmopolitan/tool/net/lre.c

244 lines
7.5 KiB
C
Raw Normal View History

/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/macros.internal.h"
#include "libc/str/str.h"
#include "third_party/lua/lauxlib.h"
#include "third_party/regex/regex.h"
struct ReErrno {
int err;
char doc[64];
};
static void LuaSetIntField(lua_State *L, const char *k, lua_Integer v) {
lua_pushinteger(L, v);
lua_setfield(L, -2, k);
}
static int LuaReReturnError(lua_State *L, regex_t *r, int rc) {
struct ReErrno *e;
lua_pushnil(L);
e = lua_newuserdatauv(L, sizeof(struct ReErrno), 0);
luaL_setmetatable(L, "re.Errno");
e->err = rc;
regerror(rc, r, e->doc, sizeof(e->doc));
return 2;
}
static regex_t *LuaReCompileImpl(lua_State *L, const char *p, int f) {
int rc;
regex_t *r;
r = lua_newuserdatauv(L, sizeof(regex_t), 0);
luaL_setmetatable(L, "re.Regex");
f &= REG_EXTENDED | REG_ICASE | REG_NEWLINE | REG_NOSUB;
f ^= REG_EXTENDED;
if ((rc = regcomp(r, p, f)) == REG_OK) {
return r;
} else {
LuaReReturnError(L, r, rc);
return NULL;
}
}
static int LuaReSearchImpl(lua_State *L, regex_t *r, const char *s, int f) {
int rc, i, n;
regmatch_t *m;
luaL_Buffer tmp;
n = 1 + r->re_nsub;
m = (regmatch_t *)luaL_buffinitsize(L, &tmp, n * sizeof(regmatch_t));
if ((rc = regexec(r, s, n, m, f >> 8)) == REG_OK) {
for (i = 0; i < n; ++i) {
lua_pushlstring(L, s + m[i].rm_so, m[i].rm_eo - m[i].rm_so);
}
return n;
} else {
return LuaReReturnError(L, r, rc);
}
}
////////////////////////////////////////////////////////////////////////////////
// re
static int LuaReSearch(lua_State *L) {
int f;
regex_t *r;
const char *p, *s;
p = luaL_checkstring(L, 1);
s = luaL_checkstring(L, 2);
f = luaL_optinteger(L, 3, 0);
if (f & ~(REG_EXTENDED | REG_ICASE | REG_NEWLINE | REG_NOSUB |
REG_NOTBOL << 8 | REG_NOTEOL << 8)) {
luaL_argerror(L, 3, "invalid flags");
unreachable;
}
if ((r = LuaReCompileImpl(L, p, f))) {
return LuaReSearchImpl(L, r, s, f);
} else {
return 2;
}
}
static int LuaReCompile(lua_State *L) {
int f;
regex_t *r;
const char *p;
p = luaL_checkstring(L, 1);
f = luaL_optinteger(L, 2, 0);
if (f & ~(REG_EXTENDED | REG_ICASE | REG_NEWLINE | REG_NOSUB)) {
luaL_argerror(L, 2, "invalid flags");
unreachable;
}
if ((r = LuaReCompileImpl(L, p, f))) {
return 1;
} else {
return 2;
}
}
////////////////////////////////////////////////////////////////////////////////
// re.Regex
static int LuaReRegexSearch(lua_State *L) {
int f;
regex_t *r;
const char *s;
r = luaL_checkudata(L, 1, "re.Regex");
s = luaL_checkstring(L, 2);
f = luaL_optinteger(L, 3, 0);
if (f & ~(REG_NOTBOL << 8 | REG_NOTEOL << 8)) {
luaL_argerror(L, 3, "invalid flags");
unreachable;
}
return LuaReSearchImpl(L, r, s, f);
}
static int LuaReRegexGc(lua_State *L) {
regex_t *r;
r = luaL_checkudata(L, 1, "re.Regex");
regfree(r);
return 0;
}
static const luaL_Reg kLuaRe[] = {
{"compile", LuaReCompile}, //
{"search", LuaReSearch}, //
{NULL, NULL}, //
};
static const luaL_Reg kLuaReRegexMeth[] = {
{"search", LuaReRegexSearch}, //
{NULL, NULL}, //
};
static const luaL_Reg kLuaReRegexMeta[] = {
{"__gc", LuaReRegexGc}, //
{NULL, NULL}, //
};
static void LuaReRegexObj(lua_State *L) {
luaL_newmetatable(L, "re.Regex");
luaL_setfuncs(L, kLuaReRegexMeta, 0);
luaL_newlibtable(L, kLuaReRegexMeth);
luaL_setfuncs(L, kLuaReRegexMeth, 0);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
}
////////////////////////////////////////////////////////////////////////////////
// re.Errno
static struct ReErrno *GetReErrno(lua_State *L) {
return luaL_checkudata(L, 1, "re.Errno");
}
// re.Errno:errno()
// └─→ errno:int
static int LuaReErrnoErrno(lua_State *L) {
lua_pushinteger(L, GetReErrno(L)->err);
return 1;
}
// re.Errno:doc()
// └─→ description:str
static int LuaReErrnoDoc(lua_State *L) {
lua_pushstring(L, GetReErrno(L)->doc);
return 1;
}
static const luaL_Reg kLuaReErrnoMeth[] = {
{"errno", LuaReErrnoErrno}, //
{"doc", LuaReErrnoDoc}, //
{0}, //
};
static const luaL_Reg kLuaReErrnoMeta[] = {
{"__tostring", LuaReErrnoDoc}, //
{0}, //
};
static void LuaReErrnoObj(lua_State *L) {
luaL_newmetatable(L, "re.Errno");
luaL_setfuncs(L, kLuaReErrnoMeta, 0);
luaL_newlibtable(L, kLuaReErrnoMeth);
luaL_setfuncs(L, kLuaReErrnoMeth, 0);
lua_setfield(L, -2, "__index");
lua_pop(L, 1);
}
////////////////////////////////////////////////////////////////////////////////
_Alignas(1) static const struct thatispacked {
const char s[8];
char x;
} kReMagnums[] = {
{"BASIC", REG_EXTENDED}, // compile flag
{"ICASE", REG_ICASE}, // compile flag
{"NEWLINE", REG_NEWLINE}, // compile flag
{"NOSUB", REG_NOSUB}, // compile flag
{"NOMATCH", REG_NOMATCH}, // error
{"BADPAT", REG_BADPAT}, // error
{"ECOLLATE", REG_ECOLLATE}, // error
{"ECTYPE", REG_ECTYPE}, // error
{"EESCAPE", REG_EESCAPE}, // error
{"ESUBREG", REG_ESUBREG}, // error
{"EBRACK", REG_EBRACK}, // error
{"EPAREN", REG_EPAREN}, // error
{"EBRACE", REG_EBRACE}, // error
{"BADBR", REG_BADBR}, // error
{"ERANGE", REG_ERANGE}, // error
{"ESPACE", REG_ESPACE}, // error
{"BADRPT", REG_BADRPT}, // error
};
int LuaRe(lua_State *L) {
int i;
char buf[9];
luaL_newlib(L, kLuaRe);
LuaSetIntField(L, "NOTBOL", REG_NOTBOL << 8); // search flag
LuaSetIntField(L, "NOTEOL", REG_NOTEOL << 8); // search flag
for (i = 0; i < ARRAYLEN(kReMagnums); ++i) {
memcpy(buf, kReMagnums[i].s, 8);
buf[8] = 0;
LuaSetIntField(L, buf, kReMagnums[i].x);
}
LuaReRegexObj(L);
LuaReErrnoObj(L);
return 1;
}