local a={a="\097\110\099\104\111\114\101\100",i="\099\097\115\101\108\101\115\115";m="\109\117\108\116\105\108\105\110\101";s="\100\111\116\097\108\108";u="\117\110\105\099\111\100\101",U="\117\110\103\114\101\101\100\121",x="\101\120\116\101\110\100\101\100"}local z={alnum=true;alpha=true,ascii=true,blank=true;cntrl=true,digit=true,graph=true,lower=true;print=true;punct=true;space=true,upper=true;word=true,xdigit=true}local d={[68]={"\099\108\097\115\115","\100\105\103\105\116",true};[83]={"\099\108\097\115\115";"\115\112\097\099\101";true};[87]={"\099\108\097\115\115","\119\111\114\100";true},[100]={"\099\108\097\115\115","\100\105\103\105\116",false};[115]={"\099\108\097\115\115","\115\112\097\099\101";false};[119]={"\099\108\097\115\115","\119\111\114\100",false};[72]={"\099\108\097\115\115","\098\108\097\110\107";true};[86]={"\099\108\097\115\115";"\118\101\114\116\105\099\097\108\095\116\097\098";true};[104]={"\099\108\097\115\115","\098\108\097\110\107";false},[118]={"\099\108\097\115\115";"\118\101\114\116\105\099\097\108\095\116\097\098";false};[78]={78};[82]={82},[66]=8;[110]=10,[114]=13,[116]=9}local m={[98]={98;{"\099\108\097\115\115";"\119\111\114\100",false}};[66]={66;{"\099\108\097\115\115";"\119\111\114\100";false}},[75]={75},[71]={71},[74]={74};[90]={90},[122]={122}}local Q={C=true;Cc=true,Cf=true;Cn=true;Co=true,Cs=true;L=true;Ll=true;Lm=true,Lo=true;Lt=true;Lu=true;M=true;Mc=true;Me=true;Mn=true;N=true,Nd=true;Nl=true,No=true;P=true;Pc=true;Pd=true,Pe=true;Pf=true;Pi=true;Po=true,Ps=true,S=true;Sc=true,Sk=true,Sm=true;So=true,Z=true,Zl=true,Zp=true;Zs=true;Xan=true,Xps=true,Xsp=true,Xuc=true;Xwd=true}local E={[33]=true;[34]=true;[35]=true;[36]=true;[37]=true,[38]=true;[39]=true,[40]=true,[41]=true;[42]=true;[43]=true;[44]=true;[45]=true,[46]=true;[47]=true;[58]=true,[59]=true;[60]=true,[61]=true;[62]=true,[63]=true,[64]=true,[91]=true;[92]=true,[93]=true,[94]=true;[95]=true,[96]=true;[123]=true;[124]=true,[125]=true;[126]=true}local W={36}local P={46}local X={94}local v={124}local function L(a,z,d)if a=="\077\097\116\099\104"then return function(...)local a=select("\035",...)if a<1 then error("\109\105\115\115\105\110\103 \097\114\103\117\109\101\110\116 \035\049 \040\077\097\116\099\104 \101\120\112\101\099\116\101\100\041",2)end local m,Q=...if not(proxy[m]and proxy[m].name=="\077\097\116\099\104")then error(string.format("\105\110\118\097\108\105\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \037\113 \040\077\097\116\099\104 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",z,typeof(m)),2)else m=proxy[m]end if z=="\103\114\111\117\112"or z=="\115\112\097\110"then if Q==nil then Q=0 end end return d(m,Q)end end return function(...)local a=select("\035",...)if a<1 then error("\109\105\115\115\105\110\103 \097\114\103\117\109\101\110\116 \035\049 \040\082\101\103\069\120 \101\120\112\101\099\116\101\100\041",2)elseif a<2 then error("\109\105\115\115\105\110\103 \097\114\103\117\109\101\110\116 \035\050 \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\041",2)end local m,Q,E,W,P,X=...if not(proxy[m]and proxy[m].name=="\082\101\103\069\120")then if type(m)~="\115\116\114\105\110\103"and type(m)~="\110\117\109\098\101\114"then error(string.format("\105\110\118\097\108\105\100 \097\114\103\117\109\101\110\116 \035\049 \116\111 \037\113 \040\082\101\103\069\120 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",z,typeof(m)),2)end m=re.fromstring(m)elseif z=="\115\117\098"then if type(E)=="\110\117\109\098\101\114"then E..=""elseif type(E)~="\115\116\114\105\110\103"then error(string.format("\105\110\118\097\108\105\100 \097\114\103\117\109\101\110\116 \035\051 \116\111 \'\115\117\098\' \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",typeof(E)),2)end elseif type(Q)=="\110\117\109\098\101\114"then Q..=""elseif type(Q)~="\115\116\114\105\110\103"then error(string.format("\105\110\118\097\108\105\100 \097\114\103\117\109\101\110\116 \035\050 \116\111 \037\113 \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",z,typeof(Q)),2)end if z~="\115\117\098"and z~="\115\112\108\105\116"then local a=typeof(E)if a~="\110\105\108"then E=tonumber(E)if not E then error(string.format("\105\110\118\097\108\105\100 \097\114\103\117\109\101\110\116 \035\051 \116\111 \037\113 \040\110\117\109\098\101\114 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",z,a),2)elseif E<0 then E=(#Q+math.floor(E+.5))+1 else E=math.max(math.floor(E+.5),1)end end end m=proxy[m]if z=="\109\097\116\099\104"or z=="\109\097\116\099\104\105\116\101\114"then W=...elseif z=="\115\117\098"then X=...end return d(m,Q,E,W,P,X)end end

local flag_map = {
	a = 'anchored', i = 'caseless', m = 'multiline', s = 'dotall', u = 'unicode', U = 'ungreedy', x ='extended',
};

local posix_class_names = {
	alnum = true, alpha = true, ascii = true, blank = true, cntrl = true, digit = true, graph = true, lower = true, print = true, punct = true, space = true, upper = true, word = true, xdigit = true,
};

local escape_chars = {
	-- grouped
	-- digit, spaces and words
	[0x44] = { "class", "digit", true }, [0x53] = { "class", "space", true }, [0x57] = { "class", "word", true },
	[0x64] = { "class", "digit", false }, [0x73] = { "class", "space", false }, [0x77] = { "class", "word", false },
	-- horizontal/vertical whitespace and newline
	[0x48] = { "class", "blank", true }, [0x56] = { "class", "vertical_tab", true },
	[0x68] = { "class", "blank", false }, [0x76] = { "class", "vertical_tab", false },
	[0x4E] = { 0x4E }, [0x52] = { 0x52 },

	-- not grouped
	[0x42] = 0x08,
	[0x6E] = 0x0A, [0x72] = 0x0D, [0x74] = 0x09,
};

local b_escape_chars = {
	-- word boundary and not word boundary
	[0x62] = { 0x62, { "class", "word", false } }, [0x42] = { 0x42, { "class", "word", false } },

	-- keep match out
	[0x4B] = { 0x4B },

	-- start & end of string
	[0x47] = { 0x47 }, [0x4A] = { 0x4A }, [0x5A] = { 0x5A }, [0x7A] = { 0x7A },
};

local valid_categories = {
	C = true, Cc = true, Cf = true, Cn = true, Co = true, Cs = true,
	L = true, Ll = true, Lm = true, Lo = true, Lt = true, Lu = true,
	M = true, Mc = true, Me = true, Mn = true,
	N = true, Nd = true, Nl = true, No = true,
	P = true, Pc = true, Pd = true, Pe = true, Pf = true, Pi = true, Po = true, Ps = true,
	S = true, Sc = true, Sk = true, Sm = true, So = true,
	Z = true, Zl = true, Zp = true, Zs = true,

	Xan = true, Xps = true, Xsp = true, Xuc = true, Xwd = true,
};

local class_ascii_punct = {
	[0x21] = true, [0x22] = true, [0x23] = true, [0x24] = true, [0x25] = true, [0x26] = true, [0x27] = true, [0x28] = true, [0x29] = true, [0x2A] = true, [0x2B] = true, [0x2C] = true, [0x2D] = true, [0x2E] = true, [0x2F] = true,
	[0x3A] = true, [0x3B] = true, [0x3C] = true, [0x3D] = true, [0x3E] = true, [0x3F] = true, [0x40] = true, [0x5B] = true, [0x5C] = true, [0x5D] = true, [0x5E] = true, [0x5F] = true, [0x60] = true, [0x7B] = true, [0x7C] = true,
	[0x7D] = true, [0x7E] = true,
};

local end_str = { 0x24 };
local dot = { 0x2E };
local beginning_str = { 0x5E };
local alternation = { 0x7C };

local function check_re(re_type, name, func)
	if re_type == "Match" then
		return function(...)
			local arg_n = select('#', ...);
			if arg_n < 1 then
				error("missing argument #1 (Match expected)", 2);
			end;
			local arg0, arg1 = ...;
			if not (proxy[arg0] and proxy[arg0].name == "Match") then
				error(string.format("invalid argument #1 to %q (Match expected, got %s)", name, typeof(arg0)), 2);
			else
				arg0 = proxy[arg0];
			end;
			if name == "group" or name == "span" then
				if arg1 == nil then
					arg1 = 0;
				end;
			end;
			return func(arg0, arg1);
		end;
	end;
	return function(...)
		local arg_n = select('#', ...);
		if arg_n < 1 then
			error("missing argument #1 (RegEx expected)", 2);
		elseif arg_n < 2 then
			error("missing argument #2 (string expected)", 2);
		end;
		local arg0, arg1, arg2, arg3, arg4, arg5 = ...;
		if not (proxy[arg0] and proxy[arg0].name == "RegEx") then
			if type(arg0) ~= "string" and type(arg0) ~= "number" then
				error(string.format("invalid argument #1 to %q (RegEx expected, got %s)", name, typeof(arg0)), 2);
			end;
			arg0 = re.fromstring(arg0);
		elseif name == "sub" then
			if type(arg2) == "number" then
				arg2 ..= '';
			elseif type(arg2) ~= "string" then
				error(string.format("invalid argument #3 to 'sub' (string expected, got %s)", typeof(arg2)), 2);
			end;
		elseif type(arg1) == "number" then
			arg1 ..= '';
		elseif type(arg1) ~= "string" then
			error(string.format("invalid argument #2 to %q (string expected, got %s)", name, typeof(arg1)), 2);
		end;
		if name ~= "sub" and name ~= "split" then
			local init_type = typeof(arg2);
			if init_type ~= 'nil' then
				arg2 = tonumber(arg2);
				if not arg2 then
					error(string.format("invalid argument #3 to %q (number expected, got %s)", name, init_type), 2);
				elseif arg2 < 0 then
					arg2 = #arg1 + math.floor(arg2 + 0.5) + 1;
				else
					arg2 = math.max(math.floor(arg2 + 0.5), 1);
				end;
			end;
		end;
		arg0 = proxy[arg0];
		if name == "match" or name == "matchiter" then
			arg3 = ...;
		elseif name == "sub" then
			arg5 = ...;
		end;
		return func(arg0, arg1, arg2, arg3, arg4, arg5);
	end;
end;
