MINOR: hlua: Add regex class

This patch simply brings HAProxy internal regex system to the Lua API.
Lua doesn't embed regexes, now it inherits from the regexes compiled
with haproxy.
This commit is contained in:
Thierry FOURNIER 2017-10-25 12:59:51 +02:00 committed by Willy Tarreau
parent 46c72551f3
commit 31904278dc
3 changed files with 168 additions and 0 deletions

View File

@ -1890,6 +1890,70 @@ Socket class
:param class_socket socket: Is the manipulated Socket.
:param integer value: The timeout value.
.. _regex_class:
Regex class
===========
.. js:class:: Regex
This class allows the usage of HAProxy regexes because classic lua doesn't
provides regexes. This class inherits the HAProxy compilation options, so the
regexes can be libc regex, pcre regex or pcre JIT regex.
The expression matching number is limited to 20 per regex. The only available
option is case sensitive.
Because regexes compilation is a heavy process, it is better to define all
your regexes in the **body context** and use it during the runtime.
.. code-block:: lua
-- Create the regex
st, regex = Regex.new("needle (..) (...)", true);
-- Check compilation errors
if st == false then
print "error: " .. regex
end
-- Match the regexes
print(regex:exec("Looking for a needle in the haystack")) -- true
print(regex:exec("Lokking for a cat in the haystack")) -- false
-- Extract words
st, list = regex:match("Looking for a needle in the haystack")
print(st) -- true
print(list[1]) -- needle in the
print(list[2]) -- in
print(list[3]) -- the
.. js:function:: Regex.new(regex, case_sensitive)
Create and compile a regex.
:param string regex: The regular expression according with the libc or pcre
standard
:param boolean case_sensitive: Match is case sensitive or not.
:returns: boolean status and :ref:`regex_class` or string containing fail reason.
.. js:function:: Regex.exec(regex, str)
Execute the regex.
:param class_regex regex: A :ref:`regex_class` object.
:param string str: The input string will be compared with the compiled regex.
:returns: a boolean status according with the match result.
.. js:function:: Regex.match(regex, str)
Execute the regex and return matched expressions.
:param class_map map: A :ref:`regex_class` object.
:param string str: The input string will be compared with the compiled regex.
:returns: a boolean status according with the match result, and
a table containing all the string matched in order of declaration.
.. _map_class:
Map class

View File

@ -24,6 +24,7 @@
#define CLASS_PROXY "Proxy"
#define CLASS_SERVER "Server"
#define CLASS_LISTENER "Listener"
#define CLASS_REGEX "Regex"
struct stream;

View File

@ -36,6 +36,7 @@ static int class_concat_ref;
static int class_proxy_ref;
static int class_server_ref;
static int class_listener_ref;
static int class_regex_ref;
#define STATS_LEN (MAX((int)ST_F_TOTAL_FIELDS, (int)INF_TOTAL_FIELDS))
@ -1076,6 +1077,90 @@ int hlua_match_addr(lua_State *L)
return 1;
}
static struct my_regex *hlua_check_regex(lua_State *L, int ud)
{
return (hlua_checkudata(L, ud, class_regex_ref));
}
static int hlua_regex_comp(struct lua_State *L)
{
struct my_regex *regex;
const char *str;
int cs;
char *err;
str = luaL_checkstring(L, 1);
luaL_argcheck(L, lua_isboolean(L, 2), 2, NULL);
cs = lua_toboolean(L, 2);
regex = lua_newuserdata(L, sizeof(*regex));
err = NULL;
if (!regex_comp(str, regex, cs, 1, &err)) {
lua_pushboolean(L, 0); /* status error */
lua_pushstring(L, err); /* Reason */
free(err);
return 2;
}
lua_pushboolean(L, 1); /* Status ok */
/* Create object */
lua_newtable(L);
lua_pushvalue(L, -3); /* Get the userdata pointer. */
lua_rawseti(L, -2, 0);
lua_rawgeti(L, LUA_REGISTRYINDEX, class_regex_ref);
lua_setmetatable(L, -2);
return 2;
}
static int hlua_regex_exec(struct lua_State *L)
{
struct my_regex *regex;
const char *str;
size_t len;
regex = hlua_check_regex(L, 1);
str = luaL_checklstring(L, 2, &len);
lua_pushboolean(L, regex_exec2(regex, (char *)str, len));
return 1;
}
static int hlua_regex_match(struct lua_State *L)
{
struct my_regex *regex;
const char *str;
size_t len;
regmatch_t pmatch[20];
int ret;
int i;
regex = hlua_check_regex(L, 1);
str = luaL_checklstring(L, 2, &len);
ret = regex_exec_match2(regex, (char *)str, len, 20, pmatch, 0);
lua_pushboolean(L, ret);
lua_newtable(L);
if (ret) {
for (i = 0; i < 20 && pmatch[i].rm_so != -1; i++) {
lua_pushlstring(L, str + pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so);
lua_rawseti(L, -2, i + 1);
}
}
return 2;
}
static int hlua_regex_free(struct lua_State *L)
{
struct my_regex *regex;
regex = hlua_check_regex(L, 1);
regex_free(regex);
return 0;
}
int hlua_fcn_reg_core_fcn(lua_State *L)
{
if (!hlua_concat_init(L))
@ -1092,6 +1177,24 @@ int hlua_fcn_reg_core_fcn(lua_State *L)
hlua_class_function(L, "match_addr", hlua_match_addr);
hlua_class_function(L, "tokenize", hlua_tokenize);
/* Create regex object. */
lua_newtable(L);
hlua_class_function(L, "new", hlua_regex_comp);
lua_newtable(L); /* The metatable. */
lua_pushstring(L, "__index");
lua_newtable(L);
hlua_class_function(L, "exec", hlua_regex_exec);
hlua_class_function(L, "match", hlua_regex_match);
lua_rawset(L, -3); /* -> META["__index"] = TABLE */
hlua_class_function(L, "__gc", hlua_regex_free);
lua_pushvalue(L, -1); /* Duplicate the metatable reference. */
class_regex_ref = hlua_register_metatable(L, CLASS_REGEX);
lua_setmetatable(L, -2);
lua_setglobal(L, CLASS_REGEX); /* Create global object called Regex */
/* Create listener object. */
lua_newtable(L);
lua_pushstring(L, "__index");