/******************************************************************************\ String support \******************************************************************************/ // TODO: is string.left() faster than s:left()? // TODO: is string.sub faster than both left and right? // TODO: these return bad results when used with negative numbers! // TODO: benchmarks! local string = string -- optimization /******************************************************************************/ registerType("string", "s", "", nil, nil, function(retval) if type(retval) ~= "string" then error("Return value is not a string, but a "..type(retval).."!",0) end end, function(v) return type(v) ~= "string" end ) /******************************************************************************/ __e2setcost(3) -- temporary registerOperator("ass", "s", "s", function(self, args) local op1, op2 = args[2], args[3] local rv2 = op2[1](self, op2) self.vars[op1] = rv2 self.vclk[op1] = true return rv2 end) /******************************************************************************/ registerOperator("is", "s", "n", function(self, args) local op1 = args[2] local rv1 = op1[1](self, op1) if rv1 ~= "" then return 1 else return 0 end end) registerOperator("eq", "ss", "n", function(self, args) local op1, op2 = args[2], args[3] local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) if rv1 == rv2 then return 1 else return 0 end end) registerOperator("neq", "ss", "n", function(self, args) local op1, op2 = args[2], args[3] local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) if rv1 != rv2 then return 1 else return 0 end end) /******************************************************************************/ __e2setcost(10) -- temporary registerOperator("add", "ss", "s", function(self, args) local op1, op2 = args[2], args[3] return op1[1](self, op1) .. op2[1](self, op2) end) /******************************************************************************/ registerOperator("add", "sn", "s", function(self, args) local op1, op2 = args[2], args[3] return op1[1](self, op1) .. tostring(op2[1](self, op2)) end) registerOperator("add", "ns", "s", function(self, args) local op1, op2 = args[2], args[3] return tostring(op1[1](self, op1)) .. op2[1](self, op2) end) /******************************************************************************/ registerOperator("add", "sv", "s", function(self, args) local op1, op2 = args[2], args[3] local rv2 = op2[1](self, op2) return ("%s[%s,%s,%s]"):format( op1[1](self, op1),rv2[1],rv2[2],rv2[3] ) end) registerOperator("add", "vs", "s", function(self, args) local op1, op2 = args[2], args[3] local rv1 = op1[1](self, op1) return ("[%s,%s,%s]%s"):format( rv1[1],rv1[2],rv1[3],op2[1](self, op2)) end) /******************************************************************************/ registerOperator("add", "sa", "s", function(self, args) local op1, op2 = args[2], args[3] local rv2 = op2[1](self, op2) return ("%s[%s,%s,%s]"):format( op1[1](self, op1),rv2[1],rv2[2],rv2[3] ) end) registerOperator("add", "as", "s", function(self, args) local op1, op2 = args[2], args[3] local rv1 = op1[1](self, op1) return ("[%s,%s,%s]%s"):format( rv1[1],rv1[2],rv1[3],op2[1](self, op2)) end) /******************************************************************************/ __e2setcost(20) -- temporary e2function number string:toNumber() local ret = tonumber(this) if ret == nil then return 0 end return ret end e2function number string:toNumber(number base) local ret = tonumber(this, base) if ret == nil then return 0 end return ret end local string_char = string.char local string_byte = string.byte local string_len = string.len registerFunction("toChar", "n", "s", function(self, args) local op1 = args[2] local rv1 = op1[1](self, op1) if rv1 < 1 then return "" end if rv1 > 255 then return "" end return string_char(rv1) end) registerFunction("toByte", "s", "n", function(self, args) local op1 = args[2] local rv1 = op1[1](self, op1) if rv1 == "" then return -1 end return string_byte(rv1) end) registerFunction("toByte", "sn", "n", function(self, args) local op1, op2 = args[2], args[3] local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) if rv2 < 1 || rv2 > string_len(rv1) then return -1 end return string_byte(rv1, rv2) end) local math_floor = math.floor registerFunction("toUnicodeChar", "n", "s", function(self, args) local op1 = args[2] local rv1 = op1[1](self, op1) local utf8 = "" if rv1 < 1 then return "" elseif rv1 <= 127 then utf8 = string_char (rv1) elseif rv1 < 2048 then utf8 = ("%c%c"):format( 192 + math_floor (rv1 / 64), 128 + (rv1 & 63)) elseif rv1 < 65536 then utf8 = ("%c%c%c"):format( 224 + math_floor (rv1 / 4096), 128 + (math_floor (rv1 / 64) & 63), 128 + (rv1 & 63)) elseif rv1 < 2097152 then utf8 = ("%c%c%c%c"):format( 240 + math_floor (rv1 / 262144), 128 + (math_floor(rv1 / 4096) & 63), 128 + (math_floor (rv1 / 64) & 63), 128 + (rv1 & 63)) end return utf8 end) registerFunction("toUnicodeByte", "s", "n", function(self, args) local op1 = args[2] local rv1 = op1[1](self, op1) if rv1 == "" then return -1 end local byte = string_byte(rv1) if byte >= 128 then if byte >= 240 then -- 4 byte sequence if string_len (rv1) < 4 then return -1 end byte = (byte & 7) * 262144 byte = byte + (string_byte (rv1, 2) & 63) * 4096 byte = byte + (string_byte (rv1, 3) & 63) * 64 byte = byte + (string_byte (rv1, 4) & 63) elseif byte >= 224 then -- 3 byte sequence if string_len (rv1) < 3 then return -1 end byte = (byte & 15) * 4096 byte = byte + (string_byte (rv1, 2) & 63) * 64 byte = byte + (string_byte (rv1, 3) & 63) elseif byte >= 192 then -- 2 byte sequence if string_len (rv1) < 2 then return -1 end byte = (byte & 31) * 64 byte = byte + (string_byte (rv1, 2) & 63) else -- invalid sequence byte = -1 end end return byte end) /******************************************************************************/ registerFunction("index", "s:n", "s", function(self, args) local op1, op2 = args[2], args[3] local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) return rv1:sub(rv2, rv2) end) registerFunction("left", "s:n", "s", function(self, args) local op1, op2 = args[2], args[3] local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) return rv1:Left(rv2) end) registerFunction("right", "s:n", "s", function(self, args) local op1, op2 = args[2], args[3] local rv1, rv2 = op1[1](self, op1), op2[1](self, op2) return rv1:Right(rv2) end) registerFunction("sub", "s:nn", "s", function(self, args) local op1, op2, op3 = args[2], args[3], args[4] local rv1, rv2, rv3 = op1[1](self, op1), op2[1](self, op2), op3[1](self, op3) return rv1:sub(rv2, rv3) end) e2function string string:sub(start) return this:sub(start) end e2function string string:operator[](index) return this:sub(index,index) end registerFunction("upper", "s:", "s", function(self, args) local op1 = args[2], args[3] local rv1 = op1[1](self, op1) return rv1:upper() end) registerFunction("lower", "s:", "s", function(self, args) local op1 = args[2], args[3] local rv1 = op1[1](self, op1) return rv1:lower() end) registerFunction("length", "s:", "n", function(self, args) local op1 = args[2], args[3] local rv1 = op1[1](self, op1) return rv1:len() end) registerFunction("unicodeLength", "s:", "n", function(self, args) local op1 = args[2], args[3] local rv1 = op1[1](self, op1) -- the string.gsub method is inconsistent with how writeUnicodeString and toUnicodeByte handles badly-formed sequences. -- local _, length = string.gsub (rv1, "[^\128-\191]", "") local length = 0 local i = 1 while i <= #rv1 do local byte = string_byte (rv1, i) if byte >= 240 then i = i + 4 elseif byte >= 224 then i = i + 3 elseif byte >= 192 then i = i + 2 else i = i + 1 end length = length + 1 end self.prf = self.prf + length * 0.1 return length end) /******************************************************************************/ registerFunction("repeat", "s:n", "s", function(self,args) local op1, op2 = args[2], args[3] local rv1, rv2 = op1[1](self, op1),op2[1](self, op2) return rv1:rep(rv2) end) registerFunction("trim", "s:", "s", function(self,args) local op1 = args[2] local rv1 = op1[1](self, op1) return string.Trim(rv1) end) registerFunction("trimLeft", "s:", "s", function(self,args) local op1 = args[2] local rv1 = op1[1](self, op1) return rv1:match( "^ *(.-)$") end) registerFunction("trimRight", "s:", "s", function(self,args) local op1 = args[2] local rv1 = op1[1](self, op1) return rv1:TrimRight() end) /******************************************************************************/ local sub = string.sub local gsub = string.gsub local find = string.find --- Returns the 1st occurrence of the string , returns 0 if not found. Prints malformed string errors to the chat area. e2function number string:findRE(string pattern) local OK, Ret = pcall(string.find, this, pattern) if not OK then self.player:ChatPrint(Ret) return 0 else return Ret or 0 end end --- Returns the 1st occurrence of the string starting at and going to the end of the string, returns 0 if not found. Prints malformed string errors to the chat area. e2function number string:findRE(string pattern, start) local OK, Ret = pcall(find, this, pattern, start) if not OK then self.player:ChatPrint(Ret) return 0 else return Ret or 0 end end --- Returns the 1st occurrence of the string , returns 0 if not found. Does not use LUA patterns. e2function number string:find(string needle) return this:find( needle, 1, true) or 0 end --- Returns the 1st occurrence of the string starting at and going to the end of the string, returns 0 if not found. Does not use LUA patterns. e2function number string:find(string needle, start) return this:find( needle, start, true) or 0 end --- Finds and replaces every occurrence of with without regular expressions e2function string string:replace(string needle, string new) if needle == "" then return "" end -- prevent crashes. stupid garry... return this:Replace( needle, new) end --- Finds and replaces every occurrence of with using regular expressions. Prints malformed string errors to the chat area. e2function string string:replaceRE(string pattern, string new) local OK, NewStr = pcall(gsub, this, pattern, new) if not OK then self.player:ChatPrint(NewStr) return "" else return NewStr or "" end end local specialchars = { ["%"] = true, ["^"] = true, ["$"] = true, ["("] = true, [")"] = true, ["."] = true, ["["] = true, ["]"] = true, ["*"] = true, ["+"] = true, ["-"] = true, ["?"] = true } __e2setcost(5) --- Splits the string into an array, along the boundaries formed by the string . See also [[string.Explode]] local string_Explode = string.Explode e2function array string:explode(string delim) local ret = string_Explode( delim, this ) self.prf = self.prf + #ret * 0.3 + #this * 0.1 return ret end e2function array string:explodeRE( string delim ) local ret = string_Explode( delim, this, true ) self.prf = self.prf + #ret * 0.3 + #this * 0.1 return ret end __e2setcost(10) --- Returns a reversed version of e2function string string:reverse() return this:reverse() end /******************************************************************************/ local string_format = string.format local gmatch = string.gmatch local Right = string.Right --- Formats a values exactly like Lua's [http://www.lua.org/manual/5.1/manual.html#pdf-string.format string.format]. Any number and type of parameter can be passed through the "...". Prints errors to the chat area. e2function string format(string fmt, ...) -- TODO: call toString for table-based types local ok, ret = pcall(string_format, fmt, ...) if not ok then self.player:ChatPrint(ret) return "" end return ret end /******************************************************************************/ -- string.match wrappers by Jeremydeath, 2009-08-30 local string_match = string.match local table_remove = table.remove --- runs [[string.match]](, ) and returns the sub-captures as an array. Prints malformed pattern errors to the chat area. e2function array string:match(string pattern) local args = {pcall(string_match, this, pattern)} if not args[1] then self.player:ChatPrint(args[2] or "Unknown error in str:match") return {} else table_remove( args, 1 ) -- Remove "OK" boolean return args or {} end end --- runs [[string.match]](, , ) and returns the sub-captures as an array. Prints malformed pattern errors to the chat area. e2function array string:match(string pattern, position) local args = {pcall(string_match, this, pattern, position)} if not args[1] then self.player:ChatPrint(args[2] or "Unknown error in str:match") return {} else table_remove( args, 1 ) -- Remove "OK" boolean return args or {} end end local table_Copy = table.Copy -- Helper function for gmatch (below) -- (By Divran) local DEFAULT = {n={},ntypes={},s={},stypes={},size=0,istable=true,depth=0} local function gmatch( self, this, pattern ) local ret = table_Copy( DEFAULT ) local num = 0 local iter = this:gmatch( pattern ) local v while true do v = {iter()} if (!v or #v==0) then break end num = num + 1 ret.n[num] = v ret.ntypes[num] = "r" end self.prf = self.prf + num ret.size = num return ret end --- runs [[string.gmatch]](, ) and returns the captures in an array in a table. Prints malformed pattern errors to the chat area. -- (By Divran) e2function table string:gmatch(string pattern) local OK, ret = pcall( gmatch, self, this, pattern ) if (!OK) then self.player:ChatPrint( ret or "Unknown error in str:gmatch" ) return table_Copy( DEFAULT ) else return ret end end --- runs [[string.gmatch]](, , ) and returns the captures in an array in a table. Prints malformed pattern errors to the chat area. -- (By Divran) e2function table string:gmatch(string pattern, position) this = this:Right( -position-1 ) local OK, ret = pcall( gmatch, self, this, pattern ) if (!OK) then self.player:ChatPrint( ret or "Unknown error in str:gmatch" ) return table_Copy( DEFAULT ) else return ret end end --- runs [[string.match]](, ) and returns the first match or an empty string if the match failed. Prints malformed pattern errors to the chat area. e2function string string:matchFirst(string pattern) local OK, Ret = pcall(string_match, this, pattern) if not OK then self.player:ChatPrint(Ret) return "" else return Ret or "" end end --- runs [[string.match]](, , ) and returns the first match or an empty string if the match failed. Prints malformed pattern errors to the chat area. e2function string string:matchFirst(string pattern, position) local OK, Ret = pcall(string_match, this, pattern, position) if not OK then self.player:ChatPrint(Ret) return "" else return Ret or "" end end __e2setcost(nil)