2014-03-27 03:24:37 +01:00
|
|
|
// Copyright (c) 2014, Thomas Goyne <plorkyeran@aegisub.org>
|
2013-04-10 05:46:59 +02:00
|
|
|
//
|
|
|
|
// Permission to use, copy, modify, and 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.
|
|
|
|
//
|
|
|
|
// Aegisub Project http://www.aegisub.org/
|
|
|
|
|
2014-03-27 03:24:37 +01:00
|
|
|
#include "libaegisub/lua/utils.h"
|
2013-04-10 05:46:59 +02:00
|
|
|
|
|
|
|
#include <boost/regex/icu.hpp>
|
2014-03-27 03:24:37 +01:00
|
|
|
#include <lauxlib.h>
|
2013-04-10 05:46:59 +02:00
|
|
|
|
|
|
|
namespace {
|
2014-03-27 03:24:37 +01:00
|
|
|
using namespace agi::lua;
|
|
|
|
|
2013-04-10 05:46:59 +02:00
|
|
|
boost::u32regex& get_regex(lua_State *L) {
|
2014-03-27 03:24:37 +01:00
|
|
|
return get<boost::u32regex>(L, 1, "aegisub.regex");
|
2013-04-10 05:46:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
boost::smatch& get_smatch(lua_State *L) {
|
2014-03-27 03:24:37 +01:00
|
|
|
return get<boost::smatch>(L, 1, "aegisub.smatch");
|
2013-04-10 05:46:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
int regex_matches(lua_State *L) {
|
2014-04-27 16:20:11 +02:00
|
|
|
push_value(L, u32regex_match(check_string(L, 2), get_regex(L)));
|
2013-04-10 05:46:59 +02:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int regex_match(lua_State *L) {
|
|
|
|
auto re = get_regex(L);
|
2014-03-27 03:24:37 +01:00
|
|
|
std::string str = check_string(L, 2);
|
2013-04-10 05:46:59 +02:00
|
|
|
int start = lua_tointeger(L, 3);
|
|
|
|
|
2014-03-27 03:24:37 +01:00
|
|
|
auto result = make<boost::smatch>(L, "aegisub.smatch");
|
2013-04-10 05:46:59 +02:00
|
|
|
if (!u32regex_search(str.cbegin() + start, str.cend(), *result, re,
|
|
|
|
start > 0 ? boost::match_prev_avail | boost::match_not_bob : boost::match_default))
|
|
|
|
{
|
|
|
|
lua_pop(L, 1);
|
|
|
|
lua_pushnil(L);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int regex_get_match(lua_State *L) {
|
2014-04-27 16:20:11 +02:00
|
|
|
auto& match = get_smatch(L);
|
|
|
|
auto idx = check_uint(L, 2) - 1;
|
|
|
|
if (idx > match.size() || !match[idx].matched) {
|
2013-04-10 05:46:59 +02:00
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
push_value(L, distance(match.prefix().first, match[idx].first + 1));
|
|
|
|
push_value(L, distance(match.prefix().first, match[idx].second));
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
int regex_search(lua_State *L) {
|
2014-04-27 16:20:11 +02:00
|
|
|
auto& re = get_regex(L);
|
|
|
|
auto str = check_string(L, 2);
|
|
|
|
auto start = check_uint(L, 3) - 1;
|
|
|
|
argcheck(L, start <= str.size(), 3, "out of bounds");
|
2013-04-10 05:46:59 +02:00
|
|
|
boost::smatch result;
|
|
|
|
if (!u32regex_search(str.cbegin() + start, str.cend(), result, re,
|
|
|
|
start > 0 ? boost::match_prev_avail | boost::match_not_bob : boost::match_default))
|
|
|
|
{
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
push_value(L, start + result.position() + 1);
|
|
|
|
push_value(L, start + result.position() + result.length());
|
|
|
|
return 2;
|
|
|
|
}
|
|
|
|
|
|
|
|
int regex_replace(lua_State *L) {
|
2014-04-27 16:20:11 +02:00
|
|
|
auto& re = get_regex(L);
|
2014-03-27 03:24:37 +01:00
|
|
|
const auto replacement = check_string(L, 2);
|
2014-04-27 16:20:11 +02:00
|
|
|
const auto str = check_string(L, 3);
|
|
|
|
int max_count = check_int(L, 4);
|
2013-04-10 05:46:59 +02:00
|
|
|
|
|
|
|
// Can't just use regex_replace here since it can only do one or infinite replacements
|
|
|
|
auto match = boost::u32regex_iterator<std::string::const_iterator>(begin(str), end(str), re);
|
|
|
|
auto end_it = boost::u32regex_iterator<std::string::const_iterator>();
|
|
|
|
|
|
|
|
auto suffix = begin(str);
|
|
|
|
|
|
|
|
std::string ret;
|
|
|
|
auto out = back_inserter(ret);
|
|
|
|
while (match != end_it && max_count > 0) {
|
|
|
|
copy(suffix, match->prefix().second, out);
|
|
|
|
match->format(out, replacement);
|
|
|
|
suffix = match->suffix().first;
|
|
|
|
++match;
|
|
|
|
--max_count;
|
|
|
|
}
|
|
|
|
|
|
|
|
copy(suffix, end(str), out);
|
|
|
|
|
|
|
|
push_value(L, ret);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int regex_compile(lua_State *L) {
|
2014-04-27 16:20:11 +02:00
|
|
|
auto pattern(check_string(L, 1));
|
|
|
|
int flags = check_int(L, 2);
|
2014-03-27 03:24:37 +01:00
|
|
|
auto re = make<boost::u32regex>(L, "aegisub.regex");
|
2013-04-10 05:46:59 +02:00
|
|
|
|
|
|
|
try {
|
|
|
|
*re = boost::make_u32regex(pattern, boost::u32regex::perl | flags);
|
|
|
|
}
|
|
|
|
catch (std::exception const& e) {
|
|
|
|
lua_pop(L, 1);
|
|
|
|
push_value(L, e.what());
|
|
|
|
return 1;
|
|
|
|
// Do the actual triggering of the error in the Lua code as that code
|
|
|
|
// can report the original call site
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int regex_gc(lua_State *L) {
|
|
|
|
using boost::u32regex;
|
|
|
|
get_regex(L).~u32regex();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int smatch_gc(lua_State *L) {
|
|
|
|
using boost::smatch;
|
|
|
|
get_smatch(L).~smatch();
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int regex_process_flags(lua_State *L) {
|
|
|
|
int ret = 0;
|
|
|
|
int nargs = lua_gettop(L);
|
|
|
|
for (int i = 1; i <= nargs; ++i) {
|
|
|
|
if (!lua_islightuserdata(L, i)) {
|
|
|
|
push_value(L, "Flags must follow all non-flag arguments");
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
ret |= (int)(intptr_t)lua_touserdata(L, i);
|
|
|
|
}
|
|
|
|
|
|
|
|
push_value(L, ret);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int regex_init_flags(lua_State *L) {
|
2014-04-27 15:41:28 +02:00
|
|
|
lua_createtable(L, 0, 9);
|
2013-04-10 05:46:59 +02:00
|
|
|
|
|
|
|
set_field(L, "ICASE", (void*)boost::u32regex::icase);
|
|
|
|
set_field(L, "NOSUB", (void*)boost::u32regex::nosubs);
|
|
|
|
set_field(L, "COLLATE", (void*)boost::u32regex::collate);
|
|
|
|
set_field(L, "NEWLINE_ALT", (void*)boost::u32regex::newline_alt);
|
|
|
|
set_field(L, "NO_MOD_M", (void*)boost::u32regex::no_mod_m);
|
|
|
|
set_field(L, "NO_MOD_S", (void*)boost::u32regex::no_mod_s);
|
|
|
|
set_field(L, "MOD_S", (void*)boost::u32regex::mod_s);
|
|
|
|
set_field(L, "MOD_X", (void*)boost::u32regex::mod_x);
|
|
|
|
set_field(L, "NO_EMPTY_SUBEXPRESSIONS", (void*)boost::u32regex::no_empty_expressions);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2014-03-27 03:24:37 +01:00
|
|
|
extern "C" int luaopen_re_impl(lua_State *L) {
|
2013-04-10 05:46:59 +02:00
|
|
|
if (luaL_newmetatable(L, "aegisub.regex")) {
|
2014-04-27 16:20:11 +02:00
|
|
|
set_field<regex_gc>(L, "__gc");
|
2013-04-10 05:46:59 +02:00
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (luaL_newmetatable(L, "aegisub.smatch")) {
|
2014-04-27 16:20:11 +02:00
|
|
|
set_field<smatch_gc>(L, "__gc");
|
2013-04-10 05:46:59 +02:00
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
|
2014-04-27 15:41:28 +02:00
|
|
|
lua_createtable(L, 0, 8);
|
2014-04-27 16:20:11 +02:00
|
|
|
set_field<regex_matches>(L, "matches");
|
|
|
|
set_field<regex_search>(L, "search");
|
|
|
|
set_field<regex_match>(L, "match");
|
|
|
|
set_field<regex_get_match>(L, "get_match");
|
|
|
|
set_field<regex_replace>(L, "replace");
|
|
|
|
set_field<regex_compile>(L, "compile");
|
|
|
|
set_field<regex_process_flags>(L, "process_flags");
|
|
|
|
set_field<regex_init_flags>(L, "init_flags");
|
2013-04-10 05:46:59 +02:00
|
|
|
return 1;
|
|
|
|
}
|