local s={[58]=true;[33]=true;[61]=true;[62]=true,[124]=true}local function u(u,X)if X.unicode and not options.unicodeData then return"\111\112\116\105\111\110\115\046\117\110\105\099\111\100\101\068\097\116\097 \099\097\110\110\111\116 \098\101 \116\117\114\110\101\100 \111\102\102 \119\104\105\108\101 \104\097\118\105\110\103 \117\110\105\099\111\100\101 \102\108\097\103"end local x,y=1,u.n local t=0 local W,l,c={},{},{newline=1;newline_seq=1,not_empty=0}while x<=y do local y=u[x]if y==40 then local X if u[x+1]==42 then x+=2 local s=x while u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=90 or u[x]>=97 and u[x]<=122 or u[x]==95 or u[x]==58)do x+=1 end if u[x]~=41 and u[x-1]~=58 then return"\113\117\097\110\116\105\102\105\101\114 \100\111\101\115\110\'\116 \102\111\108\108\111\119 \097 \114\101\112\101\097\116\097\098\108\101 \112\097\116\116\101\114\110"end local y=utf8_sub(u.s,s,x)if y=="\112\111\115\105\116\105\118\101\095\108\111\111\107\097\104\101\097\100\058"or y=="\110\101\103\097\116\105\118\101\095\108\111\111\107\104\101\097\100\058"or y=="\112\111\115\105\116\105\118\101\095\108\111\111\107\098\101\104\105\110\100\058"or y=="\110\101\103\097\116\105\118\101\095\108\111\111\107\098\101\104\105\110\100\058"or y:find("\094\091\112\110\093\108\091\097\098\093\058\036")then X={40,nil;nil;y:find("\094\110")and 33 or 61,y:find("\098",3,true)and 1}elseif y=="\097\116\111\109\105\099\058"then X={40,nil,nil;62;nil}elseif y=="\065\067\067\069\080\084"or y=="\070\065\073\076"or y=="\070"or y=="\080\082\085\078\069"or y=="\083\075\073\080"then X=y=="\070"and"\070\065\073\076"or y else if line_verbs[y]then c.newline=y elseif y=="\066\083\082\095\065\078\089\067\082\076\070"or y=="\066\083\082\095\085\078\073\067\079\068\069"then c.newline_seq=y=="\066\083\082\095\085\078\073\067\079\068\069"and 1 or 0 elseif y=="\078\079\084\069\077\080\084\089"or y=="\078\079\084\069\077\080\084\089\095\065\084\083\084\065\082\084"then c.not_empty=y=="\078\079\084\069\077\080\084\089"and 1 or 2 else return"\117\110\107\110\111\119\110 \111\114 \109\097\108\102\111\114\109\101\100 \118\101\114\098"end if W[1]then return"\116\104\105\115 \118\101\114\098 \109\117\115\116 \098\101 \112\108\097\099\101\100 \097\116 \116\104\101 \098\101\103\105\110\110\105\110\103 \111\102 \116\104\101 \114\101\103\101\120"end end elseif u[x+1]==63 then x+=2 if u[x]==35 then x=table.find(u,41,x)if not x then return"\117\110\116\101\114\109\105\110\097\116\101\100 \112\097\114\101\110\116\104\101\116\105\099\097\108"end x+=1 continue elseif not u[x]then return"\117\110\116\101\114\109\105\110\097\116\101\100 \112\097\114\101\110\116\104\101\116\105\099\097\108"end X={40;nil;nil,u[x],nil}if u[x]==48 and u[x+1]==41 then X[1],X[2],X[3],X[5]="\114\101\099\117\114\109\097\116\099\104",0,0,nil elseif u[x]>48 and u[x]<=57 then local s=x x+=1 while u[x]>=48 and u[x]<=48 do x+=1 end if u[x]~=41 then return"\105\110\118\097\108\105\100 \103\114\111\117\112 \115\116\114\117\099\116\117\114\101"end X[1],X[2],X[4]="\114\101\099\117\114\109\097\116\099\104",tonumber(utf8_sub(u.s,s,x)),nil elseif u[x]==60 and u[x+1]==33 or u[x+1]==61 then x+=1 X[4],X[5]=u[x],1 elseif u[x]==124 then X[5]=t elseif u[x]==80 or u[x]==60 or u[x]==39 then if u[x]==80 then x+=1 end if u[x]==61 then local s=x+1 while u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=90 or u[x]>=97 and u[x]<=122 or u[x]==95)do x+=1 end if not u[x]then return"\117\110\116\101\114\109\105\110\097\116\101\100 \112\097\114\101\110\116\104\101\116\105\099\097\108"elseif u[x]~=41 or x==s then return"\105\110\118\097\108\105\100 \103\114\111\117\112 \115\116\114\117\099\116\117\114\101"end X={"\098\097\099\107\114\101\102",utf8_sub(u.s,s,x)}elseif u[x]==60 or u[x-1]~=80 and u[x]==39 then local s=u[x]==39 and 39 or 62 local y=x+1 x+=1 if u[x]==41 then return"\109\105\115\115\105\110\103 \099\104\097\114\097\099\116\101\114 \105\110 \115\117\098\112\097\116\116\101\114\110"elseif u[x]>=48 and u[x]<=57 then return"\115\117\098\112\097\116\116\101\114\110 \110\097\109\101 \109\117\115\116 \110\111\116 \098\101\103\105\110 \119\105\116\104 \097 \100\105\103\105\116"elseif not(u[x]>=65 and u[x]<=90 or u[x]>=97 and u[x]<=122 or u[x]==95)then return"\105\110\118\097\108\105\100 \099\104\097\114\097\099\116\101\114 \105\110 \115\117\098\112\097\116\116\101\114\110"end x+=1 while u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=90 or u[x]>=97 and u[x]<=122 or u[x]==95)do x+=1 end if not u[x]then return"\117\110\116\101\114\109\105\110\097\116\101\100 \112\097\114\101\110\116\104\101\116\105\099\097\108"elseif u[x]~=s then return"\105\110\118\097\108\105\100 \099\104\097\114\097\099\116\101\114 \105\110 \115\117\098\112\097\116\116\101\114\110"end local W=utf8_sub(u.s,y,x)t+=1 if(l[W]or t)~=t then return"\115\117\098\112\097\116\116\101\114\110 \110\097\109\101 \097\108\114\101\097\100\121 \101\120\105\115\116\115"end for s,u in pairs(l)do if W~=s and t==u then return"\100\105\102\102\101\114\101\110\116 \110\097\109\101\115 \102\111\114 \115\117\098\112\097\116\116\101\114\110\115 \111\102 \116\104\101 \115\097\109\101 \110\117\109\098\101\114 \097\114\101\110\'\116 \112\101\114\109\105\116\116\101\100"end end l[W]=t X[2],X[4]=t,nil else return"\105\110\118\097\108\105\100 \103\114\111\117\112 \115\116\114\117\099\116\117\114\101"end elseif not s[u[x]]then return"\105\110\118\097\108\105\100 \103\114\111\117\112 \115\116\114\117\099\116\117\114\101"end else t+=1 X={40,t,nil;nil}end if X then table.insert(W,X)end elseif y==41 then local s=#W+1 local u=-1 local X=0 local x,y=0,0 repeat s-=1 local l,c=W[s],type(W[s])=="\116\097\098\108\101"if c and l[1]==40 then y+=1 if X and l.count then X+=l.count end if not l[3]then if l[4]==124 then t=l[5]+math.max(x,y)end if X~=u and u~=-1 then u=nil else u=X end break end elseif l==alternation then if X~=u and u~=-1 then u,X=nil,nil else u,X=X,0 end x,y=math.max(x,y),0 elseif X then if c and l[1]=="\113\117\097\110\116\105\102\105\101\114"then if l[2]==l[3]then X+=l[2]else X=nil end else X+=1 end end until s<1 if s<1 then return"\117\110\109\097\116\099\104\101\100 \041 \105\110 \114\101\103\117\108\097\114 \101\120\112\114\101\115\115\105\111\110"end local l=W[s]local c=#W+1 local C={41;l[2];s;l[4];l[5];count=u}if(l[4]==33 or l[4]==61)and(l[5]and not u)then return"\108\111\111\107\098\101\104\105\110\100 \097\115\115\101\114\116\105\111\110 \105\115 \110\111\116 \102\105\120\101\100 \119\105\100\116\104"end l[3]=c table.insert(W,C)elseif y==46 then table.insert(W,dot)elseif y==91 then local s,y=false,nil x+=1 local t=x if u[x]==94 then s=true x+=1 elseif u[x]==46 or u[x]==58 or u[x]==61 then y=u[x]end local l if u[x]==91 or u[x]==92 then l={}else l={u[x]}x+=1 end while u[x]~=93 do if not u[x]then return"\117\110\116\101\114\109\105\110\097\116\101\100 \099\104\097\114\097\099\116\101\114 \099\108\097\115\115"elseif u[x]==45 and(l[1]and type(l[1])=="\110\117\109\098\101\114")then if u[x+1]==93 then table.insert(l,1,45)else x+=1 local s=u[x]if s==91 then if u[x+1]==46 or u[x+1]==58 or u[x+1]==61 then local s=x+2 repeat s=table.find(u,93,s)until not s or u[s-1]~=92 if not s then return"\117\110\116\101\114\109\105\110\097\116\101\100 \099\104\097\114\097\099\116\101\114 \099\108\097\115\115"elseif u[s-1]==u[x+1]and s-1~=x+1 then return"\105\110\118\097\108\105\100 \114\097\110\103\101 \105\110 \099\104\097\114\097\099\116\101\114 \099\108\097\115\115"end end if l[1]>91 then return"\105\110\118\097\108\105\100 \114\097\110\103\101 \105\110 \099\104\097\114\097\099\116\101\114 \099\108\097\115\115"end elseif s==92 then x+=1 if u[x]==120 then local X,y x+=1 if u[x]and(u[x]>=48 and u[x]<=57)or u[x]>=65 and u[x]<=70 or u[x]>=97 and u[x]<=102 then X=u[x]-((u[x]>=65 and u[x]<=90)and 55 or(u[x]>=97 and u[x]<=122)and 87 or 48)x+=1 if u[x]and(u[x]>=48 and u[x]<=57)or u[x]>=65 and u[x]<=70 or u[x]>=97 and u[x]<=102 then y=u[x]-((u[x]>=65 and u[x]<=90)and 55 or(u[x]>=97 and u[x]<=122)and 87 or 48)else x-=1 end else x-=1 end s=X and(y and 16*X+y or X)or 0 elseif u[x]>=48 and u[x]<=55 then local X,y,t=u[x]-48,nil,nil x+=1 if u[x]and(u[x]>=48 and u[x]<=55)then y=u[x]-48 x+=1 if u[x]and(u[x]>=48 and u[x]<=55)then t=u[x]-48 else x-=1 end else x-=1 end s=y and(t and(64*X+8*y)+t or 8*X+y)or X else s=escape_chars[u[x]]or u[x]if type(s)~="\110\117\109\098\101\114"then return"\105\110\118\097\108\105\100 \114\097\110\103\101 \105\110 \099\104\097\114\097\099\116\101\114 \099\108\097\115\115"end end elseif l[1]>s then return"\105\110\118\097\108\105\100 \114\097\110\103\101 \105\110 \099\104\097\114\097\099\116\101\114 \099\108\097\115\115"end l[1]={"\114\097\110\103\101",l[1],s}end elseif u[x]==91 then if u[x+1]==46 or u[x+1]==58 or u[x+1]==61 then local s=x+2 repeat s=table.find(u,93,s)until not s or u[s-1]~=92 if not s then return"\117\110\116\101\114\109\105\110\097\116\101\100 \099\104\097\114\097\099\116\101\114 \099\108\097\115\115"elseif u[s-1]~=u[x+1]or s-1==x+1 then table.insert(l,1,91)elseif u[s-1]==46 or u[s-1]==61 then return"\080\079\083\073\088 \099\111\108\108\097\116\105\110\103 \101\108\101\109\101\110\116\115 \097\114\101\110\'\116 \115\117\112\112\111\114\116\101\100"elseif u[s-1]==58 then local X=u[x+3]==94 local y=utf8_sub(u.s,x+(X and 3 or 2),s-1)if not posix_class_names[y]then return"\117\110\107\110\111\119\110 \080\079\083\073\088 \099\108\097\115\115 \110\097\109\101"end table.insert(l,1,{"\099\108\097\115\115",y,X})x=s end else table.insert(l,1,91)end elseif u[x]==92 then x+=1 if u[x]==120 then local s,X x+=1 if u[x]==123 then x+=1 local s=x while u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=70 or u[x]>=97 and u[x]<=102)do x+=1 end if u[x]~=125 or x==s then return"\109\097\108\102\111\114\109\101\100 \104\101\120\097\100\101\099\105\109\097\108 \099\104\097\114\097\099\116\101\114"elseif x-s>4 then return"\099\104\097\114\097\099\116\101\114 \111\102\102\115\101\116 \116\111\111 \108\097\114\103\101"end table.insert(l,1,tonumber(utf8_sub(u.s,s,x),16))else if u[x]and(u[x]>=48 and u[x]<=57)or u[x]>=65 and u[x]<=70 or u[x]>=97 and u[x]<=102 then s=u[x]-((u[x]>=65 and u[x]<=90)and 55 or(u[x]>=97 and u[x]<=122)and 87 or 48)x+=1 if u[x]and(u[x]>=48 and u[x]<=57)or u[x]>=65 and u[x]<=70 or u[x]>=97 and u[x]<=102 then X=u[x]-((u[x]>=65 and u[x]<=90)and 55 or(u[x]>=97 and u[x]<=122)and 87 or 48)else x-=1 end else x-=1 end table.insert(l,1,s and(X and 16*s+X or s)or 0)end elseif u[x]>=48 and u[x]<=55 then local s,X,y=u[x]-48,nil,nil x+=1 if u[x]and(u[x]>=48 and u[x]<=55)then X=u[x]-48 x+=1 if u[x]and(u[x]>=48 and u[x]<=55)then y=u[x]-48 else x-=1 end else x-=1 end table.insert(l,1,X and(y and(64*s+8*X)+y or 8*s+X)or s)elseif u[x]==69 then elseif u[x]==81 then local s=x+1 repeat x=table.find(u,92,x+1)until not x or u[x+1]==69 table.move(u,s,x and x-1 or#u,#W+1,W)if not x then break end x+=1 elseif u[x]==78 then if u[x+1]==123 and(u[x+2]==85 and(u[x+3]==43 and X.unicode))then x+=4 local s=x while u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=70 or u[x]>=97 and u[x]<=102)do x+=1 end if u[x]~=125 or x==s then return"\109\097\108\102\111\114\109\101\100 \085\110\105\099\111\100\101 \099\111\100\101 \112\111\105\110\116"end local X=tonumber(utf8_sub(u.s,s,x))table.insert(l,1,X)else return"\105\110\118\097\108\105\100 \101\115\099\097\112\101 \115\101\113\117\101\110\099\101"end elseif u[x]==80 or u[x]==112 then if not options.unicodeData then return"\111\112\116\105\111\110\115\046\117\110\105\099\111\100\101\068\097\116\097 \099\097\110\110\111\116 \098\101 \116\117\114\110\101\100 \111\102\102 \119\104\101\110 \117\115\105\110\103 \092\112"end x+=1 if u[x]~=123 then local s=utf8.char(u[x]or 0)if not valid_categories[s]then return"\117\110\107\110\111\119\110 \111\114 \109\097\108\102\111\114\109\101\100 \115\099\114\105\112\116 \110\097\109\101"end table.insert(l,1,{"\099\097\116\101\103\111\114\121";false;s})else local s=u[x]==80 x+=1 if u[x]==94 then x+=1 s=not s end local X=x while u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=90 or u[x]>=97 and u[x]<=122 or u[x]==95)do x+=1 end if u[x]~=125 then return"\117\110\107\110\111\119\110 \111\114 \109\097\108\102\111\114\109\101\100 \115\099\114\105\112\116 \110\097\109\101"end local y=utf8_sub(u.s,X,x)local t=chr_scripts[y]if t then table.insert(l,1,{"\099\104\097\114\115\101\116",s,t})elseif not valid_categories[y]then return"\117\110\107\110\111\119\110 \111\114 \109\097\108\102\111\114\109\101\100 \115\099\114\105\112\116 \110\097\109\101"else table.insert(l,1,{"\099\097\116\101\103\111\114\121";s;y})end end elseif u[x]==111 then x+=1 if u[x]~=123 then return"\109\097\108\102\111\114\109\101\100 \111\099\116\097\108 \099\111\100\101"end x+=1 local s=x while u[x]and(u[x]>=48 and u[x]<=55)do x+=1 end if u[x]~=125 or x==s then return"\109\097\108\102\111\114\109\101\100 \111\099\116\097\108 \099\111\100\101"end local X=tonumber(utf8_sub(u.s,s,x),8)if X>65535 then return"\099\104\097\114\097\099\116\101\114 \111\102\102\115\101\116 \116\111\111 \108\097\114\103\101"end table.insert(l,1,X)else local s=escape_chars[u[x]]table.insert(l,1,type(s)=="\115\116\114\105\110\103"and{"\099\108\097\115\115";s,false}or s or u[x])end elseif X.ignoreCase and(u[x]>=97 and u[x]<=122)then table.insert(l,1,u[x]-32)else table.insert(l,1,u[x])end x+=1 end if u[x-1]==y and x-1~=t then return y==58 and"\080\079\083\073\088 \110\097\109\101\100 \099\108\097\115\115\101\115 \097\114\101 \111\110\108\121 \115\117\112\112\111\114\116 \119\105\116\104\105\110 \097 \099\104\097\114\097\099\116\101\114 \115\101\116"or"\080\079\083\073\088 \099\111\108\108\097\116\105\110\103 \101\108\101\109\101\110\116\115 \097\114\101\110\'\116 \115\117\112\112\111\114\116\101\100"end if not l[2]and not s then table.insert(W,l[1])else table.insert(W,{"\099\104\097\114\115\101\116",s;l})end elseif y==92 then x+=1 local s=u[x]if not s then return"\112\097\116\116\101\114\110 \109\097\121 \110\111\116 \101\110\100 \119\105\116\104 \097 \116\114\097\105\108\105\110\103 \098\097\099\107\115\108\097\115\104"elseif s>=48 and s<=57 then local s=x while u[x+1]and(u[x+1]>=48 and u[x+1]<=57)do x+=1 end local X=tonumber(utf8_sub(u.s,s,x+1))if X>t and x~=s then x=s local X,y,t if u[x]<=55 then X=u[x]-48 x+=1 if u[x]and(u[x]>=48 and u[x]<=55)then y=u[x]-48 x+=1 if u[x]and(u[x]>=48 and u[x]<=55)then t=u[x]-48 else x-=1 end else x-=1 end end table.insert(W,X and(y and(t and(64*X+8*y)+t or 8*X+y)or X)or u[s])else table.insert(W,{"\098\097\099\107\114\101\102",X})end elseif s==69 then elseif s==81 then local s=x+1 repeat x=table.find(u,92,x+1)until not x or u[x+1]==69 table.move(u,s,x and x-1 or#u,#W+1,W)if not x then break end x+=1 elseif s==78 then if u[x+1]==123 and(u[x+2]==85 and(u[x+3]==43 and X.unicode))then x+=4 local s=x while u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=70 or u[x]>=97 and u[x]<=102)do x+=1 end if u[x]~=125 or x==s then return"\109\097\108\102\111\114\109\101\100 \085\110\105\099\111\100\101 \099\111\100\101 \112\111\105\110\116"end local X=tonumber(utf8_sub(u.s,s,x))table.insert(W,X)else table.insert(W,escape_chars[78])end elseif s==80 or s==112 then if not options.unicodeData then return"\111\112\116\105\111\110\115\046\117\110\105\099\111\100\101\068\097\116\097 \099\097\110\110\111\116 \098\101 \116\117\114\110\101\100 \111\102\102 \119\104\101\110 \117\115\105\110\103 \092\112"end x+=1 if u[x]~=123 then local s=utf8.char(u[x]or 0)if not valid_categories[s]then return"\117\110\107\110\111\119\110 \111\114 \109\097\108\102\111\114\109\101\100 \115\099\114\105\112\116 \110\097\109\101"end table.insert(W,{"\099\097\116\101\103\111\114\121",false,s})else local X=s==80 x+=1 if u[x]==94 then x+=1 X=not X end local y=x while u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=90 or u[x]>=97 and u[x]<=122 or u[x]==95)do x+=1 end if u[x]~=125 then return"\117\110\107\110\111\119\110 \111\114 \109\097\108\102\111\114\109\101\100 \115\099\114\105\112\116 \110\097\109\101"end local t=utf8_sub(u.s,y,x)local l=chr_scripts[t]if l then table.insert(W,{"\099\104\097\114\115\101\116",X;l})elseif not valid_categories[t]then return"\117\110\107\110\111\119\110 \111\114 \109\097\108\102\111\114\109\101\100 \115\099\114\105\112\116 \110\097\109\101"else table.insert(W,{"\099\097\116\101\103\111\114\121",X;t})end end elseif s==103 and(u[x+1]==123 or u[x+1]>=48 and u[x+1]<=57)then local s=false x+=1 if u[x]==123 then x+=1 s=true elseif u[x]<48 or u[x]>57 then return"\109\097\108\102\111\114\109\101\100 \114\101\102\101\114\101\110\099\101 \099\111\100\101"end local X=x while u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=70 or u[x]>=97 and u[x]<=102)do x+=1 end if s and u[x]~=125 then return"\109\097\108\102\111\114\109\101\100 \114\101\102\101\114\101\110\099\101 \099\111\100\101"end local y=tonumber(utf8_sub(u.s,X,x+(s and 0 or 1)))table.insert(W,{"\098\097\099\107\114\101\102",y})if not s then x-=1 end elseif s==111 then x+=1 if u[x+1]~=123 then return"\109\097\108\102\111\114\109\101\100 \111\099\116\097\108 \099\111\100\101"end x+=1 local s=x while u[x]and(u[x]>=48 and u[x]<=55)do x+=1 end if u[x]~=125 or x==s then return"\109\097\108\102\111\114\109\101\100 \111\099\116\097\108 \099\111\100\101"end local X=tonumber(utf8_sub(u.s,s,x),8)if X>65535 then return"\099\104\097\114\097\099\116\101\114 \111\102\102\115\101\116 \116\111\111 \108\097\114\103\101"end table.insert(W,X)elseif s==120 then local s,X x+=1 if u[x]==123 then x+=1 local s=x while u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=70 or u[x]>=97 and u[x]<=102)do x+=1 end if u[x]~=125 or x==s then return"\109\097\108\102\111\114\109\101\100 \104\101\120\097\100\101\099\105\109\097\108 \099\111\100\101"elseif x-s>4 then return"\099\104\097\114\097\099\116\101\114 \111\102\102\115\101\116 \116\111\111 \108\097\114\103\101"end table.insert(W,tonumber(utf8_sub(u.s,s,x),16))else if u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=70 or u[x]>=97 and u[x]<=102)then s=u[x]-((u[x]>=65 and u[x]<=90)and 55 or(u[x]>=97 and u[x]<=122)and 87 or 48)x+=1 if u[x]and(u[x]>=48 and u[x]<=57 or u[x]>=65 and u[x]<=70 or u[x]>=97 and u[x]<=102)then X=u[x]-((u[x]>=65 and u[x]<=90)and 55 or(u[x]>=97 and u[x]<=122)and 87 or 48)else x-=1 end else x-=1 end table.insert(W,s and(X and 16*s+X or s)or 0)end else local u=b_escape_chars[s]or escape_chars[s]table.insert(W,u or s)end elseif y==42 or y==43 or y==63 or y==123 then local s,t if y==123 then local X=x+1 local y while u[x+1]and(u[x+1]>=48 and u[x+1]<=57 or u[x+1]==44 and(not y and x+1~=X))do x+=1 if u[x]==44 then y=x end end if u[x+1]==125 then x+=1 if not y then s=tonumber(utf8_sub(u.s,X,x))t=s else s,t=tonumber(utf8_sub(u.s,X,y)),y+1==x and math.huge or tonumber(utf8_sub(u.s,y+1,x))if t<s then return"\110\117\109\098\101\114\115 \111\117\116 \111\102 \111\114\100\101\114 \105\110 \123\125 \113\117\097\110\116\105\102\105\101\114"end end else table.move(u,X-1,x,#W+1,W)end else s,t=y==43 and 1 or 0,y==63 and 1 or math.huge end if s then local y=X.ungreedy and"\108\097\122\121"or"\103\114\101\101\100\121"if u[x+1]==43 or u[x+1]==63 then x+=1 y=u[x]==43 and"\112\111\115\115\101\115\115\105\118\101"or X.ungreedy and"\103\114\101\101\100\121"or"\108\097\122\121"end local l=#W local c=W[l]if not c or type(c)=="\116\097\098\108\101"and(c[1]=="\113\117\097\110\116\105\102\105\101\114"or c[1]==40 or b_escape_chars[c[1]])or c==alternation or type(c)=="\115\116\114\105\110\103"then return"\113\117\097\110\116\105\102\105\101\114 \100\111\101\115\110\'\116 \102\111\108\108\111\119 \097 \114\101\112\101\097\116\097\098\108\101 \112\097\116\116\101\114\110"end if t==0 then table.remove(W)elseif s~=1 or t~=1 then if type(c)=="\116\097\098\108\101"and c[1]==41 then l=c[3]end W[l]={"\113\117\097\110\116\105\102\105\101\114",s,t,y,W[l]}end end elseif y==124 then table.insert(W,alternation)local s=#W repeat s-=1 local u,X=W[s],type(W[s])=="\116\097\098\108\101"if X and u[1]==41 then s=W[s][3]elseif X and u[1]==40 then if u[4]==124 then t=u[5]end break end until not u elseif y==36 or y==94 then table.insert(W,y==94 and beginning_str or end_str)elseif X.ignoreCase and(y>=97 and y<=122)then table.insert(W,y-32)elseif X.extended and(y>=9 and y<=13 or y==32 or y==35)then if y==35 then repeat x+=1 until not u[x]or u[x]==10 or u[x]==13 end else table.insert(W,y)end x+=1 end local C=0 for s,u in ipairs(W)do if type(u)=="\116\097\098\108\101"and(u[1]==40 or u[1]=="\113\117\097\110\116\105\102\105\101\114"and(type(u[5])=="\116\097\098\108\101"and u[5][1]==40))then if u[1]=="\113\117\097\110\116\105\102\105\101\114"then u=u[5]end if not u[3]then return"\117\110\116\101\114\109\105\110\097\116\101\100 \112\097\114\101\110\116\104\101\116\105\099\097\108"elseif u[2]then C=math.max(C,u[2])end elseif type(u)=="\116\097\098\108\101"and(u[1]=="\098\097\099\107\114\101\102"or u[1]=="\114\101\099\117\114\109\097\116\099\104")then if not l[u[2]]and(type(u[2])~="\110\117\109\098\101\114"or u[2]>t)then return"\114\101\102\101\114\101\110\099\101 \116\111 \097 \110\111\110-\101\120\105\115\116\101\110\116 \111\114 \105\110\118\097\108\105\100 \115\117\098\112\097\116\116\101\114\110"elseif u[1]=="\114\101\099\117\114\109\097\116\099\104"and u[2]~=0 then for s,X in ipairs(W)do if type(X)=="\116\097\098\108\101"and(X[1]==40 and X[2]==u[2])then u[3]=s break end end elseif type(u[2])=="\115\116\114\105\110\103"then u[2]=l[u[2]]end end end W.group_n=C return W,l,c end if not tonumber(options.cacheSize)then error(string.format("\101\120\112\101\099\116\101\100 \110\117\109\098\101\114 \102\111\114 \111\112\116\105\111\110\115\046\099\097\099\104\101\083\105\122\101\044 \103\111\116 \037\115",typeof(options.cacheSize)),2)end local X=math.floor(options.cacheSize or 0)~=0 and tonumber(options.cacheSize)local x,y if not X then elseif X<0 or X~=X then error("\099\097\099\104\101 \115\105\122\101 \099\097\110\110\111\116 \098\101 \097 \110\101\103\097\116\105\118\101 \110\117\109\098\101\114 \111\114 \097 \078\097\078",2)elseif X==math.huge then x,y={nil},{nil}elseif X>=4294967296.0 then error("\099\097\099\104\101 \115\105\122\101 \116\111\111 \108\097\114\103\101",2)else x,y=table.create(options.cacheSize),table.create(options.cacheSize)end if X then function re.pruge()table.clear(y)table.clear(x)end end local function t(s,t,W,l)local c,C,H local T=X and string.format("\037\115\124\037\115",s.s,W)local S=X and x[table.find(y,T)]if S then c,C,H=table.unpack(S,1,3)else c,C,H=u(s,t)if type(c)=="\115\116\114\105\110\103"then error(c,2)end if X and c[1]then table.insert(y,1,T)table.insert(x,1,{c,C;H})if X~=math.huge then table.remove(y,X+1)table.remove(x,X+1)end end end local v=newproxy(true)proxy[v]={name="\082\101\103\069\120";flags=t,flag_repr=W;pattern_repr=l,token=c,group_id=C,verb_flags=H}local K=getmetatable(v)K.__index=setmetatable(t,re_m)K.__tostring=re_tostr K.__metatable=lockmsg return v end local function W(s)return(#s%2==0 and"\092"or"")..(s.."\046")end local function l(s,u)return s:lower()<u:lower()end function re.new(...)if select("\035",...)==0 then error("\109\105\115\115\105\110\103 \097\114\103\117\109\101\110\116 \035\049 \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\041",2)end local s,u=...if type(s)=="\110\117\109\098\101\114"then s..=""elseif type(s)~="\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\049 \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",typeof(s)),2)end if type(u)~="\115\116\114\105\110\103"and(type(u)~="\110\117\109\098\101\114"and u~=nil)then error(string.format("\105\110\118\097\108\105\100 \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\044 \103\111\116 \037\115\041",typeof(u)),2)end local X={anchored=false,caseless=false;multiline=false;dotall=false;unicode=false;ungreedy=false,extended=false}local x={}for s in string.gmatch(u or"",utf8.charpattern)do if X[flag_map[s]]~=false then error("\105\110\118\097\108\105\100 \114\101\103\117\108\097\114 \101\120\112\114\101\115\115\105\111\110 \102\108\097\103 "..s,3)end X[flag_map[s]]=true table.insert(x,s)end table.sort(x,l)x=table.concat(x)return t(to_str_arr(s),X,x,string.format("\047\037\115\047",s:gsub("\040\092\042\041\047",W)))end function re.fromstring(...)if select("\035",...)==0 then error("\109\105\115\115\105\110\103 \097\114\103\117\109\101\110\116 \035\049 \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\041",2)end local s=...if type(s)=="\110\117\109\098\101\114"then s..=""elseif type(s)~="\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\049 \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",typeof(s),2))end local u=to_str_arr(s)local X=u[1]if not X then error("\101\109\112\116\121 \114\101\103\101\120",2)elseif X==92 or X>=48 and X<=57 or X>=65 and X<=90 or X>=97 and X<=122 then error("\100\101\108\105\109\105\116\101\114 \109\117\115\116 \110\111\116 \098\101 \097\108\112\104\097\110\117\109\101\114\105\099 \111\114 \097 \098\097\099\107\115\108\097\115\104",2)end local x=1 repeat x=table.find(u,X,x+1)if not x then error(string.format("\110\111 \101\110\100\105\110\103 \100\101\108\105\109\105\116\101\114 \040\'\037\115\'\041 \102\111\117\110\100",utf8.char(X)),2)end local s=1 while u[x-s]==92 do s+=1 end until s%2==1 local y={anchored=false,caseless=false;multiline=false;dotall=false,unicode=false,ungreedy=false,extended=false}local W={}while u.n>x do local s=utf8.char(table.remove(u))u.n-=1 if y[flag_map[s]]~=false then error("\105\110\118\097\108\105\100 \114\101\103\117\108\097\114 \101\120\112\114\101\115\115\105\111\110 \102\108\097\103 "..s,3)end y[flag_map[s]]=true table.insert(W,s)end table.sort(W,l)W=table.concat(W)table.remove(u,1)table.remove(u)u.n-=2 u.s=string.sub(u.s,2,1+u.n)return t(u,y,W,string.sub(s,1,2+u.n))end local c={["\000"]="\092\120\048\048",["\n"]="\092\110";["\t"]="\092\116",["\r"]="\092\114";["\012"]="\092\102"}function re.escape(...)if select("\035",...)==0 then error("\109\105\115\115\105\110\103 \097\114\103\117\109\101\110\116 \035\049 \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\041",2)end local s,u,X=...if type(s)=="\110\117\109\098\101\114"then s..=""elseif type(s)~="\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\049 \116\111 \'\101\115\099\097\112\101\' \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",typeof(s)),2)end if X==nil then X=""elseif type(X)=="\110\117\109\098\101\114"then X..=""elseif type(X)~="\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 \'\101\115\099\097\112\101\' \040\115\116\114\105\110\103 \101\120\112\101\099\116\101\100\044 \103\111\116 \037\115\041",typeof(X)),2)end if utf8.len(X)>1 or X:match("\094\091\037\097\092\093\036")then error("\100\101\108\105\109\105\116\101\114 \104\097\118\101 \110\111\116 \098\101 \097\108\112\104\097\110\117\109\101\114\105\099",2)end return(string.gsub(s,"\091\000\012\n\r\t\093",c)):gsub(string.format("\091\092\037\115\035\040\041\037\037\037\037\042\043\046\063\091\037\037\093\094\123\124\037\115\093",u and"\037\115"or"",(X:find("\094\091\037\037\037\093\093\036")and"\037"or"")..X),"\092\037\049")end function re.type(...)if select("\035",...)==0 then error("\109\105\115\115\105\110\103 \097\114\103\117\109\101\110\116 \035\049",2)end return proxy[...]and proxy[...].name end for s,u in pairs(re_m)do re[s]=u end re_m={__index=re_m}lockmsg=re.fromstring("\047\084\104\101\092\115\042\109\101\116\097\116\097\098\108\101\092\115\042\105\115\092\115\042\040\063\058\108\111\099\107\101\100\124\105\110\097\099\099\101\115\115\105\098\108\101\041\040\063\035\078\105\099\101 \116\114\121 \058\093\041\047\105");(getmetatable(lockmsg)).__metatable=lockmsg local function C()error("\065\116\116\101\109\112\116 \116\111 \109\111\100\105\102\121 \097 \114\101\097\100\111\110\108\121 \116\097\098\108\101",2)end match_m={__index=match_m;__metatable=lockmsg;__newindex=C}re.Match=setmetatable({},match_m)return setmetatable({},{__index=re,__metatable=lockmsg;__newindex=C})


local other_valid_group_char = {
	-- non-capturing group
	[0x3A] = true,
	-- lookarounds
	[0x21] = true, [0x3D] = true,
	-- atomic
	[0x3E] = true,
	-- branch reset
	[0x7C] = true,
};

local function tokenize_ptn(codes, flags)
	if flags.unicode and not options.unicodeData then
		return "options.unicodeData cannot be turned off while having unicode flag";
	end;
	local i, len = 1, codes.n;
	local group_n = 0;
	local outln, group_id, verb_flags = { }, { }, {
		newline = 1, newline_seq = 1, not_empty = 0,
	};
	while i <= len do
		local c = codes[i];
		if c == 0x28 then
			-- Match
			local ret;
			if codes[i + 1] == 0x2A then
				i += 2;
				local start_i = i;
				while codes[i]
					and (codes[i] >= 0x30 and codes[i] <= 0x39
					or codes[i] >= 0x41 and codes[i] <= 0x5A
					or codes[i] >= 0x61 and codes[i] <= 0x7A
					or codes[i] == 0x5F or codes[i] == 0x3A) do
					i += 1;
				end;
				if codes[i] ~= 0x29 and codes[i - 1] ~= 0x3A then
					-- fallback as normal and ( can't be repeated
					return "quantifier doesn't follow a repeatable pattern";
				end;
				local selected_verb = utf8_sub(codes.s, start_i, i);
				if selected_verb == "positive_lookahead:" or selected_verb == "negative_lookhead:"
					or selected_verb == "positive_lookbehind:" or selected_verb == "negative_lookbehind:"
					or selected_verb:find("^[pn]l[ab]:$") then
					ret = { 0x28, nil, nil, selected_verb:find('^n') and 0x21 or 0x3D, selected_verb:find('b', 3, true) and 1 };
				elseif selected_verb == "atomic:" then
					ret = { 0x28, nil, nil, 0x3E, nil };
				elseif selected_verb == "ACCEPT" or selected_verb == "FAIL" or selected_verb == 'F' or selected_verb == "PRUNE" or selected_verb == "SKIP" then
					ret = selected_verb == 'F' and "FAIL" or selected_verb;
				else
					if line_verbs[selected_verb] then
						verb_flags.newline = selected_verb;
					elseif selected_verb == "BSR_ANYCRLF" or selected_verb == "BSR_UNICODE" then
						verb_flags.newline_seq = selected_verb == "BSR_UNICODE" and 1 or 0;
					elseif selected_verb == "NOTEMPTY" or selected_verb == "NOTEMPTY_ATSTART" then
						verb_flags.not_empty = selected_verb == "NOTEMPTY" and 1 or 2;
					else
						return "unknown or malformed verb";
					end;
					if outln[1] then
						return "this verb must be placed at the beginning of the regex";
					end;
				end;
			elseif codes[i + 1] == 0x3F then
				-- ? syntax
				i += 2;
				if codes[i] == 0x23 then
					-- comments
					i = table.find(codes, 0x29, i);
					if not i then
						return "unterminated parenthetical";
					end;
					i += 1;
					continue;
				elseif not codes[i] then
					return "unterminated parenthetical";
				end;
				ret = { 0x28, nil, nil, codes[i], nil };
				if codes[i] == 0x30 and codes[i + 1] == 0x29 then
					-- recursive match entire pattern
					ret[1], ret[2], ret[3], ret[5] = "recurmatch", 0, 0, nil;
				elseif codes[i] > 0x30 and codes[i] <= 0x39 then
					-- recursive match
					local org_i = i;
					i += 1;
					while codes[i] >= 0x30 and codes[i] <= 0x30 do
						i += 1;
					end;
					if codes[i] ~= 0x29 then
						return "invalid group structure";
					end;
					ret[1], ret[2], ret[4] = "recurmatch", tonumber(utf8_sub(codes.s, org_i, i)), nil;
				elseif codes[i] == 0x3C and codes[i + 1] == 0x21 or codes[i + 1] == 0x3D then
					-- lookbehinds
					i += 1;
					ret[4], ret[5] = codes[i], 1;
				elseif codes[i] == 0x7C then
					-- branch reset
					ret[5] = group_n;
				elseif codes[i] == 0x50 or codes[i] == 0x3C or codes[i] == 0x27 then
					if codes[i] == 0x50 then
						i += 1;
					end;
					if codes[i] == 0x3D then
						-- backref
						local start_i = i + 1;
						while codes[i] and
							(codes[i] >= 0x30 and codes[i] <= 0x39
								or codes[i] >= 0x41 and codes[i] <= 0x5A
								or codes[i] >= 0x61 and codes[i] <= 0x7A
								or codes[i] == 0x5F) do
							i += 1;
						end;
						if not codes[i] then
							return "unterminated parenthetical";
						elseif codes[i] ~= 0x29 or i == start_i then
							return "invalid group structure";
						end;
						ret = { "backref", utf8_sub(codes.s, start_i, i) };
					elseif codes[i] == 0x3C or codes[i - 1] ~= 0x50 and codes[i] == 0x27 then
						-- named capture
						local delimiter = codes[i] == 0x27 and 0x27 or 0x3E;
						local start_i = i + 1;
						i += 1;
						if codes[i] == 0x29 then
							return "missing character in subpattern";
						elseif codes[i] >= 0x30 and codes[i] <= 0x39 then
							return "subpattern name must not begin with a digit";
						elseif not (codes[i] >= 0x41 and codes[i] <= 0x5A or codes[i] >= 0x61 and codes[i] <= 0x7A or codes[i] == 0x5F) then
							return "invalid character in subpattern";
						end;
						i += 1;
						while codes[i] and
							(codes[i] >= 0x30 and codes[i] <= 0x39
								or codes[i] >= 0x41 and codes[i] <= 0x5A
								or codes[i] >= 0x61 and codes[i] <= 0x7A
								or codes[i] == 0x5F) do
							i += 1;
						end;
						if not codes[i] then
							return "unterminated parenthetical";
						elseif codes[i] ~= delimiter then
							return "invalid character in subpattern";
						end;
						local name = utf8_sub(codes.s, start_i, i);
						group_n += 1;
						if (group_id[name] or group_n) ~= group_n then
							return "subpattern name already exists";
						end;
						for name1, group_n1 in pairs(group_id) do
							if name ~= name1 and group_n == group_n1 then
								return "different names for subpatterns of the same number aren't permitted";
							end;
						end;
						group_id[name] = group_n;
						ret[2], ret[4] = group_n, nil;
					else
						return "invalid group structure";
					end;
				elseif not other_valid_group_char[codes[i]] then
					return "invalid group structure";
				end;
			else
				group_n += 1;
				ret = { 0x28, group_n, nil, nil };
			end;
			if ret then
				table.insert(outln, ret);
			end;
		elseif c == 0x29 then
			-- Close parenthesis
			local i1 = #outln + 1;
			local lookbehind_c = -1;
			local current_lookbehind_c = 0;
			local max_c, group_c = 0, 0;
			repeat
				i1 -= 1;
				local v, is_table = outln[i1], type(outln[i1]) == "table";
				if is_table and v[1] == 0x28 then
					group_c += 1;
					if current_lookbehind_c and v.count then
						current_lookbehind_c += v.count;
					end;
					if not v[3] then
						if v[4] == 0x7C then
							group_n = v[5] + math.max(max_c, group_c);
						end;
						if current_lookbehind_c ~= lookbehind_c and lookbehind_c ~= -1 then
							lookbehind_c = nil;
						else
							lookbehind_c = current_lookbehind_c;
						end;
						break;
					end;
				elseif v == alternation then
					if current_lookbehind_c ~= lookbehind_c and lookbehind_c ~= -1 then
						lookbehind_c, current_lookbehind_c = nil, nil;
					else
						lookbehind_c, current_lookbehind_c = current_lookbehind_c, 0;
					end;
					max_c, group_c = math.max(max_c, group_c), 0;
				elseif current_lookbehind_c then
					if is_table and v[1] == "quantifier" then
						if v[2] == v[3] then
							current_lookbehind_c += v[2];
						else
							current_lookbehind_c = nil;
						end;
					else
						current_lookbehind_c += 1;
					end;
				end;
			until i1 < 1;
			if i1 < 1 then
				return "unmatched ) in regular expression";
			end;
			local v = outln[i1];
			local outln_len_p_1 = #outln + 1;
			local ret = { 0x29, v[2], i1, v[4], v[5], count = lookbehind_c };
			if (v[4] == 0x21 or v[4] == 0x3D) and v[5] and not lookbehind_c then
				return "lookbehind assertion is not fixed width";
			end;
			v[3] = outln_len_p_1;
			table.insert(outln, ret);
		elseif c == 0x2E then
			table.insert(outln, dot);
		elseif c == 0x5B then
			-- Character set
			local negate, char_class = false, nil;
			i += 1;
			local start_i = i;
			if codes[i] == 0x5E then
				negate = true;
				i += 1;
			elseif codes[i] == 0x2E or codes[i] == 0x3A or codes[i] == 0x3D then
				-- POSIX character classes
				char_class = codes[i];
			end;
			local ret;
			if codes[i] == 0x5B or codes[i] == 0x5C then
				ret = { };
			else
				ret = { codes[i] };
				i += 1;
			end;
			while codes[i] ~= 0x5D do
				if not codes[i] then
					return "unterminated character class";
				elseif codes[i] == 0x2D and ret[1] and type(ret[1]) == "number" then
					if codes[i + 1] == 0x5D then
						table.insert(ret, 1, 0x2D);
					else
						i += 1;
						local ret_c = codes[i];
						if ret_c == 0x5B then
							if codes[i + 1] == 0x2E or codes[i + 1] == 0x3A or codes[i + 1] == 0x3D then
								-- Check for POSIX character class, name does not matter
								local i1 = i + 2;
								repeat
									i1 = table.find(codes, 0x5D, i1);
								until not i1 or codes[i1 - 1] ~= 0x5C;
								if not i1 then
									return "unterminated character class";
								elseif codes[i1 - 1] == codes[i + 1] and i1 - 1 ~= i + 1 then
									return "invalid range in character class";
								end;
							end;
							if ret[1] > 0x5B then
								return "invalid range in character class";
							end;
						elseif ret_c == 0x5C then
							i += 1;
							if codes[i] == 0x78 then
								local radix0, radix1;
								i += 1;
								if codes[i] and codes[i] >= 0x30 and codes[i] <= 0x39 or codes[i] >= 0x41 and codes[i] <= 0x46 or codes[i] >= 0x61 and codes[i] <= 0x66 then
									radix0 = codes[i] - ((codes[i] >= 0x41 and codes[i] <= 0x5A) and 0x37 or (codes[i] >= 0x61 and codes[i] <= 0x7A) and 0x57 or 0x30);
									i += 1;
									if codes[i] and codes[i] >= 0x30 and codes[i] <= 0x39 or codes[i] >= 0x41 and codes[i] <= 0x46 or codes[i] >= 0x61 and codes[i] <= 0x66 then
										radix1 = codes[i] - ((codes[i] >= 0x41 and codes[i] <= 0x5A) and 0x37 or (codes[i] >= 0x61 and codes[i] <= 0x7A) and 0x57 or 0x30);
									else
										i -= 1;
									end;
								else
									i -= 1;
								end;
								ret_c = radix0 and (radix1 and 16 * radix0 + radix1 or radix0) or 0;
							elseif codes[i] >= 0x30 and codes[i] <= 0x37 then
								local radix0, radix1, radix2 = codes[i] - 0x30, nil, nil;
								i += 1;
								if codes[i] and codes[i] >= 0x30 and codes[i] <= 0x37 then
									radix1 = codes[i] - 0x30;
									i += 1;
									if codes[i] and codes[i] >= 0x30 and codes[i] <= 0x37 then
										radix2 = codes[i] - 0x30;
									else
										i -= 1;
									end;
								else
									i -= 1;
								end;
								ret_c = radix1 and (radix2 and 64 * radix0 + 8 * radix1 + radix2 or 8 * radix0 + radix1) or radix0;
							else
								ret_c = escape_chars[codes[i]] or codes[i];
								if type(ret_c) ~= "number" then
									return "invalid range in character class";
								end;
							end;
						elseif ret[1] > ret_c then
							return "invalid range in character class";
						end;
						ret[1] = { "range", ret[1], ret_c };
					end;
				elseif codes[i] == 0x5B then
					if codes[i + 1] == 0x2E or codes[i + 1] == 0x3A or codes[i + 1] == 0x3D then
						local i1 = i + 2;
						repeat
							i1 = table.find(codes, 0x5D, i1);
						until not i1 or codes[i1 - 1] ~= 0x5C;
						if not i1 then
							return "unterminated character class";
						elseif codes[i1 - 1] ~= codes[i + 1] or i1 - 1 == i + 1 then
							table.insert(ret, 1, 0x5B);
						elseif codes[i1 - 1] == 0x2E or codes[i1 - 1] == 0x3D then
							return "POSIX collating elements aren't supported";
						elseif codes[i1 - 1] == 0x3A then
							-- I have no plans to support escape codes (\) in character class names
							local negate = codes[i + 3] == 0x5E;
							local class_name = utf8_sub(codes.s, i + (negate and 3 or 2), i1 - 1);
							--  If not valid then throw an error
							if not posix_class_names[class_name] then
								return "unknown POSIX class name";
							end;
							table.insert(ret, 1, { "class", class_name, negate });
							i = i1;
						end;
					else
						table.insert(ret, 1, 0x5B);
					end;
				elseif codes[i] == 0x5C then
					i += 1;
					if codes[i] == 0x78 then
						local radix0, radix1;
						i += 1;
						if codes[i] == 0x7B then
							i += 1;
							local org_i = i;
							while codes[i] and
								(codes[i] >= 0x30 and codes[i] <= 0x39
									or codes[i] >= 0x41 and codes[i] <= 0x46
									or codes[i] >= 0x61 and codes[i] <= 0x66) do
								i += 1;
							end;
							if codes[i] ~= 0x7D or i == org_i then
								return "malformed hexadecimal character";
							elseif i - org_i > 4 then
								return "character offset too large";
							end;
							table.insert(ret, 1, tonumber(utf8_sub(codes.s, org_i, i), 16));
						else
							if codes[i] and codes[i] >= 0x30 and codes[i] <= 0x39 or codes[i] >= 0x41 and codes[i] <= 0x46 or codes[i] >= 0x61 and codes[i] <= 0x66 then
								radix0 = codes[i] - ((codes[i] >= 0x41 and codes[i] <= 0x5A) and 0x37 or (codes[i] >= 0x61 and codes[i] <= 0x7A) and 0x57 or 0x30);
								i += 1;
								if codes[i] and codes[i] >= 0x30 and codes[i] <= 0x39 or codes[i] >= 0x41 and codes[i] <= 0x46 or codes[i] >= 0x61 and codes[i] <= 0x66 then
									radix1 = codes[i] - ((codes[i] >= 0x41 and codes[i] <= 0x5A) and 0x37 or (codes[i] >= 0x61 and codes[i] <= 0x7A) and 0x57 or 0x30);
								else
									i -= 1;
								end;
							else
								i -= 1;
							end;
							table.insert(ret, 1, radix0 and (radix1 and 16 * radix0 + radix1 or radix0) or 0);
						end;
					elseif codes[i] >= 0x30 and codes[i] <= 0x37 then
						local radix0, radix1, radix2 = codes[i] - 0x30, nil, nil;
						i += 1;
						if codes[i] and codes[i] >= 0x30 and codes[i] <= 0x37 then
							radix1 = codes[i] - 0x30;
							i += 1;
							if codes[i] and codes[i] >= 0x30 and codes[i] <= 0x37 then
								radix2 = codes[i] - 0x30;
							else
								i -= 1;
							end;
						else
							i -= 1;
						end;
						table.insert(ret, 1, radix1 and (radix2 and 64 * radix0 + 8 * radix1 + radix2 or 8 * radix0 + radix1) or radix0);
					elseif codes[i] == 0x45 then
						-- intentionally left blank, \E that's not preceded \Q is ignored
					elseif codes[i] == 0x51 then
						local start_i = i + 1;
						repeat
							i = table.find(codes, 0x5C, i + 1);
						until not i or codes[i + 1] == 0x45;
						table.move(codes, start_i, i and i - 1 or #codes, #outln + 1, outln);
						if not i then
							break;
						end;
						i += 1;
					elseif codes[i] == 0x4E then
						if codes[i + 1] == 0x7B and codes[i + 2] == 0x55 and codes[i + 3] == 0x2B and flags.unicode then
							i += 4;
							local start_i = i;
							while codes[i] and
								(codes[i] >= 0x30 and codes[i] <= 0x39
									or codes[i] >= 0x41 and codes[i] <= 0x46
									or codes[i] >= 0x61 and codes[i] <= 0x66) do
								i += 1;
							end;
							if codes[i] ~= 0x7D or i == start_i then
								return "malformed Unicode code point";
							end;
							local code_point = tonumber(utf8_sub(codes.s, start_i, i));
							table.insert(ret, 1, code_point);
						else
							return "invalid escape sequence";
						end;
					elseif codes[i] == 0x50 or codes[i] == 0x70 then
						if not options.unicodeData then
							return "options.unicodeData cannot be turned off when using \\p";
						end;
						i += 1;
						if codes[i] ~= 0x7B then
							local c_name = utf8.char(codes[i] or 0);
							if not valid_categories[c_name] then
								return "unknown or malformed script name";
							end;
							table.insert(ret, 1, { "category", false, c_name });
						else
							local negate = codes[i] == 0x50;
							i += 1;
							if codes[i] == 0x5E then
								i += 1;
								negate = not negate;
							end;
							local start_i = i;
							while codes[i] and
								(codes[i] >= 0x30 and codes[i] <= 0x39
									or codes[i] >= 0x41 and codes[i] <= 0x5A
									or codes[i] >= 0x61 and codes[i] <= 0x7A
									or codes[i] == 0x5F) do
								i += 1;
							end;
							if codes[i] ~= 0x7D then
								return "unknown or malformed script name";
							end;
							local c_name = utf8_sub(codes.s, start_i, i);
							local script_set = chr_scripts[c_name];
							if script_set then
								table.insert(ret, 1, { "charset", negate, script_set });
							elseif not valid_categories[c_name] then
								return "unknown or malformed script name";
							else
								table.insert(ret, 1, { "category", negate, c_name });
							end;
						end;
					elseif codes[i] == 0x6F then
						i += 1;
						if codes[i] ~= 0x7B then
							return "malformed octal code";
						end;
						i += 1;
						local org_i = i;
						while codes[i] and codes[i] >= 0x30 and codes[i] <= 0x37 do
							i += 1;
						end;
						if codes[i] ~= 0x7D or i == org_i then
							return "malformed octal code";
						end;
						local ret_chr = tonumber(utf8_sub(codes.s, org_i, i), 8);
						if ret_chr > 0xFFFF then
							return "character offset too large";
						end;
						table.insert(ret, 1, ret_chr);
					else
						local esc_char = escape_chars[codes[i]];
						table.insert(ret, 1, type(esc_char) == "string" and { "class", esc_char, false } or esc_char or codes[i]);
					end;
				elseif flags.ignoreCase and codes[i] >= 0x61 and codes[i] <= 0x7A then
					table.insert(ret, 1, codes[i] - 0x20);
				else
					table.insert(ret, 1, codes[i]);
				end;
				i += 1;
			end;
			if codes[i - 1] == char_class and i - 1 ~= start_i then
				return char_class == 0x3A and "POSIX named classes are only support within a character set" or "POSIX collating elements aren't supported";
			end;
			if not ret[2] and not negate then
				table.insert(outln, ret[1]);
			else
				table.insert(outln, { "charset", negate, ret });
			end;
		elseif c == 0x5C then
			-- Escape char
			i += 1;
			local escape_c = codes[i];
			if not escape_c then
				return "pattern may not end with a trailing backslash";
			elseif escape_c >= 0x30 and escape_c <= 0x39 then
				local org_i = i;
				while codes[i + 1] and codes[i + 1] >= 0x30 and codes[i + 1] <= 0x39 do
					i += 1;
				end;
				local escape_d = tonumber(utf8_sub(codes.s, org_i, i + 1));
				if escape_d > group_n and i ~= org_i then
					i = org_i;
					local radix0, radix1, radix2;
					if codes[i] <= 0x37 then
						radix0 = codes[i] - 0x30;
						i += 1;
						if codes[i] and codes[i] >= 0x30 and codes[i] <= 0x37 then
							radix1 = codes[i] - 0x30;
							i += 1;
							if codes[i] and codes[i] >= 0x30 and codes[i] <= 0x37 then
								radix2 = codes[i] - 0x30;
							else
								i -= 1;
							end;
						else
							i -= 1;
						end;
					end;
					table.insert(outln, radix0 and (radix1 and (radix2 and 64 * radix0 + 8 * radix1 + radix2 or 8 * radix0 + radix1) or radix0) or codes[org_i]);
				else
					table.insert(outln, { "backref", escape_d });
				end;
			elseif escape_c == 0x45 then
				-- intentionally left blank, \E that's not preceded \Q is ignored
			elseif escape_c == 0x51 then
				local start_i = i + 1;
				repeat
					i = table.find(codes, 0x5C, i + 1);
				until not i or codes[i + 1] == 0x45;
				table.move(codes, start_i, i and i - 1 or #codes, #outln + 1, outln);
				if not i then
					break;
				end;
				i += 1;
			elseif escape_c == 0x4E then
				if codes[i + 1] == 0x7B and codes[i + 2] == 0x55 and codes[i + 3] == 0x2B and flags.unicode then
					i += 4;
					local start_i = i;
					while codes[i] and
						(codes[i] >= 0x30 and codes[i] <= 0x39
							or codes[i] >= 0x41 and codes[i] <= 0x46
							or codes[i] >= 0x61 and codes[i] <= 0x66) do
						i += 1;
					end;
					if codes[i] ~= 0x7D or i == start_i then
						return "malformed Unicode code point";
					end;
					local code_point = tonumber(utf8_sub(codes.s, start_i, i));
					table.insert(outln, code_point);
				else
					table.insert(outln, escape_chars[0x4E]);
				end;
			elseif escape_c == 0x50 or escape_c == 0x70 then
				if not options.unicodeData then
					return "options.unicodeData cannot be turned off when using \\p";
				end;
				i += 1;
				if codes[i] ~= 0x7B then
					local c_name = utf8.char(codes[i] or 0);
					if not valid_categories[c_name] then
						return "unknown or malformed script name";
					end;
					table.insert(outln, { "category", false, c_name });
				else
					local negate = escape_c == 0x50;
					i += 1;
					if codes[i] == 0x5E then
						i += 1;
						negate = not negate;
					end;
					local start_i = i;
					while codes[i] and
						(codes[i] >= 0x30 and codes[i] <= 0x39
							or codes[i] >= 0x41 and codes[i] <= 0x5A
							or codes[i] >= 0x61 and codes[i] <= 0x7A
							or codes[i] == 0x5F) do
						i += 1;
					end;
					if codes[i] ~= 0x7D then
						return "unknown or malformed script name";
					end;
					local c_name = utf8_sub(codes.s, start_i, i);
					local script_set = chr_scripts[c_name];
					if script_set then
						table.insert(outln, { "charset", negate, script_set });
					elseif not valid_categories[c_name] then
						return "unknown or malformed script name";
					else
						table.insert(outln, { "category", negate, c_name });
					end;
				end;
			elseif escape_c == 0x67 and (codes[i + 1] == 0x7B or codes[i + 1] >= 0x30 and codes[i + 1] <= 0x39) then
				local is_grouped = false;
				i += 1;
				if codes[i] == 0x7B then
					i += 1;
					is_grouped = true;
				elseif codes[i] < 0x30 or codes[i] > 0x39 then
					return "malformed reference code";
				end;
				local org_i = i;
				while codes[i] and
					(codes[i] >= 0x30 and codes[i] <= 0x39
						or codes[i] >= 0x41 and codes[i] <= 0x46
						or codes[i] >= 0x61 and codes[i] <= 0x66) do
					i += 1;
				end;
				if is_grouped and codes[i] ~= 0x7D then
					return "malformed reference code";
				end;
				local ref_name = tonumber(utf8_sub(codes.s, org_i, i + (is_grouped and 0 or 1)));
				table.insert(outln, { "backref", ref_name });
				if not is_grouped then
					i -= 1;
				end;
			elseif escape_c == 0x6F then
				i += 1;
				if codes[i + 1] ~= 0x7B then
					return "malformed octal code";
				end
				i += 1;
				local org_i = i;
				while codes[i] and codes[i] >= 0x30 and codes[i] <= 0x37 do
					i += 1;
				end;
				if codes[i] ~= 0x7D or i == org_i then
					return "malformed octal code";
				end;
				local ret_chr = tonumber(utf8_sub(codes.s, org_i, i), 8);
				if ret_chr > 0xFFFF then
					return "character offset too large";
				end;
				table.insert(outln, ret_chr);
			elseif escape_c == 0x78 then
				local radix0, radix1;
				i += 1;
				if codes[i] == 0x7B then
					i += 1;
					local org_i = i;
					while codes[i] and
						(codes[i] >= 0x30 and codes[i] <= 0x39
							or codes[i] >= 0x41 and codes[i] <= 0x46
							or codes[i] >= 0x61 and codes[i] <= 0x66) do
						i += 1;
					end;
					if codes[i] ~= 0x7D or i == org_i then
						return "malformed hexadecimal code";
					elseif i - org_i > 4 then
						return "character offset too large";
					end;
					table.insert(outln, tonumber(utf8_sub(codes.s, org_i, i), 16));
				else
					if codes[i] and (codes[i] >= 0x30 and codes[i] <= 0x39 or codes[i] >= 0x41 and codes[i] <= 0x46 or codes[i] >= 0x61 and codes[i] <= 0x66) then
						radix0 = codes[i] - ((codes[i] >= 0x41 and codes[i] <= 0x5A) and 0x37 or (codes[i] >= 0x61 and codes[i] <= 0x7A) and 0x57 or 0x30);
						i += 1;
						if codes[i] and (codes[i] >= 0x30 and codes[i] <= 0x39 or codes[i] >= 0x41 and codes[i] <= 0x46 or codes[i] >= 0x61 and codes[i] <= 0x66) then
							radix1 = codes[i] - ((codes[i] >= 0x41 and codes[i] <= 0x5A) and 0x37 or (codes[i] >= 0x61 and codes[i] <= 0x7A) and 0x57 or 0x30);
						else
							i -= 1;
						end;
					else
						i -= 1;
					end;
					table.insert(outln, radix0 and (radix1 and 16 * radix0 + radix1 or radix0) or 0);
				end;
			else
				local esc_char = b_escape_chars[escape_c] or escape_chars[escape_c];
				table.insert(outln, esc_char or escape_c);
			end;
		elseif c == 0x2A or c == 0x2B or c == 0x3F or c == 0x7B then
			-- Quantifier
			local start_q, end_q;
			if c == 0x7B then
				local org_i = i + 1;
				local start_i;
				while codes[i + 1] and (codes[i + 1] >= 0x30 and codes[i + 1] <= 0x39 or codes[i + 1] == 0x2C and not start_i and i + 1 ~= org_i) do
					i += 1;
					if codes[i] == 0x2C then
						start_i = i;
					end;
				end;
				if codes[i + 1] == 0x7D then
					i += 1;
					if not start_i then
						start_q = tonumber(utf8_sub(codes.s, org_i, i));
						end_q = start_q;
					else
						start_q, end_q = tonumber(utf8_sub(codes.s, org_i, start_i)), start_i + 1 == i and math.huge or tonumber(utf8_sub(codes.s, start_i + 1, i));
						if end_q < start_q then
							return "numbers out of order in {} quantifier";
						end;
					end;
				else
					table.move(codes, org_i - 1, i, #outln + 1, outln);
				end;
			else
				start_q, end_q = c == 0x2B and 1 or 0, c == 0x3F and 1 or math.huge;
			end;
			if start_q then
				local quantifier_type = flags.ungreedy and "lazy" or "greedy";
				if codes[i + 1] == 0x2B or codes[i + 1] == 0x3F then
					i += 1;
					quantifier_type = codes[i] == 0x2B and "possessive" or flags.ungreedy and "greedy" or "lazy";
				end;
				local outln_len = #outln;
				local last_outln_value = outln[outln_len];
				if not last_outln_value or type(last_outln_value) == "table" and (last_outln_value[1] == "quantifier" or last_outln_value[1] == 0x28 or b_escape_chars[last_outln_value[1]])
					or last_outln_value == alternation or type(last_outln_value) == "string" then
					return "quantifier doesn't follow a repeatable pattern";
				end;
				if end_q == 0 then
					table.remove(outln);
				elseif start_q ~= 1 or end_q ~= 1 then
					if type(last_outln_value) == "table" and last_outln_value[1] == 0x29 then
						outln_len = last_outln_value[3];
					end;
					outln[outln_len] = { "quantifier", start_q, end_q, quantifier_type, outln[outln_len] };
				end;
			end;
		elseif c == 0x7C then
			-- Alternation
			table.insert(outln, alternation);
			local i1 = #outln;
			repeat
				i1 -= 1;
				local v1, is_table = outln[i1], type(outln[i1]) == "table";
				if is_table and v1[1] == 0x29 then
					i1 = outln[i1][3];
				elseif is_table and v1[1] == 0x28 then
					if v1[4] == 0x7C then
						group_n = v1[5];
					end;
					break;
				end;
			until not v1;
		elseif c == 0x24 or c == 0x5E then
			table.insert(outln, c == 0x5E and beginning_str or end_str);
		elseif flags.ignoreCase and c >= 0x61 and c <= 0x7A then
			table.insert(outln, c - 0x20);
		elseif flags.extended and (c >= 0x09 and c <= 0x0D or c == 0x20 or c == 0x23) then
			if c == 0x23 then
				repeat
					i += 1;
				until not codes[i] or codes[i] == 0x0A or codes[i] == 0x0D;
			end;
		else
			table.insert(outln, c);
		end;
		i += 1;
	end;
	local max_group_n = 0;
	for i, v in ipairs(outln) do
		if type(v) == "table" and (v[1] == 0x28 or v[1] == "quantifier" and type(v[5]) == "table" and v[5][1] == 0x28) then
			if v[1] == "quantifier" then
				v = v[5];
			end;
			if not v[3] then
				return "unterminated parenthetical";
			elseif v[2] then
				max_group_n = math.max(max_group_n, v[2]);
			end;
		elseif type(v) == "table" and (v[1] == "backref" or v[1] == "recurmatch") then
			if not group_id[v[2]] and (type(v[2]) ~= "number" or v[2] > group_n) then
				return "reference to a non-existent or invalid subpattern";
			elseif v[1] == "recurmatch" and v[2] ~= 0 then
				for i1, v1 in ipairs(outln) do
					if type(v1) == "table" and v1[1] == 0x28 and v1[2] == v[2] then
						v[3] = i1;
						break;
					end;
				end;
			elseif type(v[2]) == "string" then
				v[2] = group_id[v[2]];
			end;
		end;
	end;
	outln.group_n = max_group_n;
	return outln, group_id, verb_flags;
end;

if not tonumber(options.cacheSize) then
	error(string.format("expected number for options.cacheSize, got %s", typeof(options.cacheSize)), 2);
end;
local cacheSize = math.floor(options.cacheSize or 0) ~= 0 and tonumber(options.cacheSize);
local cache_pattern, cache_pattern_names;
if not cacheSize then
elseif cacheSize < 0 or cacheSize ~= cacheSize then
	error("cache size cannot be a negative number or a NaN", 2);
elseif cacheSize == math.huge then
	cache_pattern, cache_pattern_names = { nil }, { nil };
elseif cacheSize >= 2 ^ 32 then
	error("cache size too large", 2);
else
	cache_pattern, cache_pattern_names = table.create(options.cacheSize), table.create(options.cacheSize);
end;
if cacheSize then
	function re.pruge()
		table.clear(cache_pattern_names);
		table.clear(cache_pattern);
	end;
end;

local function new_re(str_arr, flags, flag_repr, pattern_repr)
	local tokenized_ptn, group_id, verb_flags;
	local cache_format = cacheSize and string.format("%s|%s", str_arr.s, flag_repr);
	local cached_token = cacheSize and cache_pattern[table.find(cache_pattern_names, cache_format)];
	if cached_token then
		tokenized_ptn, group_id, verb_flags = table.unpack(cached_token, 1, 3);
	else
		tokenized_ptn, group_id, verb_flags = tokenize_ptn(str_arr, flags);
		if type(tokenized_ptn) == "string" then
			error(tokenized_ptn, 2);
		end;
		if cacheSize and tokenized_ptn[1] then
			table.insert(cache_pattern_names, 1, cache_format);
			table.insert(cache_pattern, 1, { tokenized_ptn, group_id, verb_flags });
			if cacheSize ~= math.huge then
				table.remove(cache_pattern_names, cacheSize + 1);
				table.remove(cache_pattern, cacheSize + 1);
			end;
		end;
	end;

	local object = newproxy(true);
	proxy[object] = { name = "RegEx", flags = flags, flag_repr = flag_repr, pattern_repr = pattern_repr, token = tokenized_ptn, group_id = group_id, verb_flags = verb_flags };
	local object_mt = getmetatable(object);
	object_mt.__index = setmetatable(flags, re_m);
	object_mt.__tostring = re_tostr;
	object_mt.__metatable = lockmsg;

	return object;
end;

local function escape_fslash(pre)
	return (#pre % 2 == 0 and '\\' or '') .. pre .. '.';
end;

local function sort_flag_chr(a, b)
	return a:lower() < b:lower();
end;

function re.new(...)
	if select('#', ...) == 0 then
		error("missing argument #1 (string expected)", 2);
	end;
	local ptn, flags_str = ...;
	if type(ptn) == "number" then
		ptn ..= '';
	elseif type(ptn) ~= "string" then
		error(string.format("invalid argument #1 (string expected, got %s)", typeof(ptn)), 2);
	end;
	if type(flags_str) ~= "string" and type(flags_str) ~= "number" and flags_str ~= nil then
		error(string.format("invalid argument #2 (string expected, got %s)", typeof(flags_str)), 2);
	end;

	local flags = {
		anchored = false, caseless = false, multiline = false, dotall = false, unicode = false, ungreedy = false, extended = false,
	};
	local flag_repr = { };
	for f in string.gmatch(flags_str or '', utf8.charpattern) do
		if flags[flag_map[f]] ~= false then
			error("invalid regular expression flag " .. f, 3);
		end;
		flags[flag_map[f]] = true;
		table.insert(flag_repr, f);
	end;
	table.sort(flag_repr, sort_flag_chr);
	flag_repr = table.concat(flag_repr);
	return new_re(to_str_arr(ptn), flags, flag_repr, string.format("/%s/", ptn:gsub("(\\*)/", escape_fslash)));
end;

function re.fromstring(...)
	if select('#', ...) == 0 then
		error("missing argument #1 (string expected)", 2);
	end;
	local ptn = ...;
	if type(ptn) == "number" then
		ptn ..= '';
	elseif type(ptn) ~= "string" then
		error(string.format("invalid argument #1 (string expected, got %s)", typeof(ptn), 2));
	end;
	local str_arr = to_str_arr(ptn);
	local delimiter = str_arr[1];
	if not delimiter then
		error("empty regex", 2);
	elseif delimiter == 0x5C or (delimiter >= 0x30 and delimiter <= 0x39) or (delimiter >= 0x41 and delimiter <= 0x5A) or (delimiter >= 0x61 and delimiter <= 0x7A) then
		error("delimiter must not be alphanumeric or a backslash", 2);
	end;

	local i0 = 1;
	repeat
		i0 = table.find(str_arr, delimiter, i0 + 1);
		if not i0 then
			error(string.format("no ending delimiter ('%s') found", utf8.char(delimiter)), 2);
		end;
		local escape_count = 1;
		while str_arr[i0 - escape_count] == 0x5C do
			escape_count += 1;
		end;
	until escape_count % 2 == 1;

	local flags = {
		anchored = false, caseless = false, multiline = false, dotall = false, unicode = false, ungreedy = false, extended = false,
	};
	local flag_repr = { };
	while str_arr.n > i0 do
		local f = utf8.char(table.remove(str_arr));
		str_arr.n -= 1;
		if flags[flag_map[f]] ~= false then
			error("invalid regular expression flag " .. f, 3);
		end;
		flags[flag_map[f]] = true;
		table.insert(flag_repr, f);
	end;
	table.sort(flag_repr, sort_flag_chr);
	flag_repr = table.concat(flag_repr);
	table.remove(str_arr, 1);
	table.remove(str_arr);
	str_arr.n -= 2;
	str_arr.s = string.sub(str_arr.s, 2, 1 + str_arr.n);
	return new_re(str_arr, flags, flag_repr, string.sub(ptn, 1, 2 + str_arr.n));
end;

local re_escape_line_chrs = {
	['\0'] = '\\x00', ['\n'] = '\\n', ['\t'] = '\\t', ['\r'] = '\\r', ['\f'] = '\\f',
};

function re.escape(...)
	if select('#', ...) == 0 then
		error("missing argument #1 (string expected)", 2);
	end;
	local str, extended, delimiter = ...;
	if type(str) == "number" then
		str ..= '';
	elseif type(str) ~= "string" then
		error(string.format("invalid argument #1 to 'escape' (string expected, got %s)", typeof(str)), 2);
	end;
	if delimiter == nil then
		delimiter = '';
	elseif type(delimiter) == "number" then
		delimiter ..= '';
	elseif type(delimiter) ~= "string" then
		error(string.format("invalid argument #3 to 'escape' (string expected, got %s)", typeof(delimiter)), 2);
	end;
	if utf8.len(delimiter) > 1 or delimiter:match("^[%a\\]$") then
		error("delimiter have not be alphanumeric", 2);
	end;
	return (string.gsub(str, "[\0\f\n\r\t]", re_escape_line_chrs):gsub(string.format("[\\%s#()%%%%*+.?[%%]^{|%s]", extended and '%s' or '', (delimiter:find'^[%%%]]$' and '%' or '') .. delimiter), "\\%1"));
end;

function re.type(...)
	if select('#', ...) == 0 then
		error("missing argument #1", 2);
	end;
	return proxy[...] and proxy[...].name;
end;

for k, f in pairs(re_m) do
	re[k] = f;
end;

re_m = { __index = re_m };

lockmsg = re.fromstring([[/The\s*metatable\s*is\s*(?:locked|inaccessible)(?#Nice try :])/i]]);
getmetatable(lockmsg).__metatable = lockmsg;

local function readonly_table()
	error("Attempt to modify a readonly table", 2);
end;

match_m = {
	__index = match_m,
	__metatable = lockmsg,
	__newindex = readonly_table,
};

re.Match = setmetatable({ }, match_m);

return setmetatable({ }, {
	__index = re,
	__metatable = lockmsg,
	__newindex = readonly_table,
});
