kisiseldwarf

unicode_implmenetation_notmine

Jul 20th, 2019
302
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 8.88 KB | None | 0 0
  1. local m = {} -- the module
  2.  
  3. local ustring = {} -- table to index equivalent string.* functions
  4.  
  5. -- License: MIT
  6.  
  7. m._VERSION  = "utf8string 1.0.0"
  8. m._URL      = "https://github.com/tst2005/lua-utf8string"
  9. m._LICENSE  = 'MIT <http://opensource.org/licenses/MIT>'
  10.  
  11. -- my custom type for Unicode String
  12. local utf8type = "ustring"
  13.  
  14. local typeof = assert(type)
  15. local tostring = assert(tostring)
  16.  
  17. local string = require("string")
  18. local sgmatch = assert(string.gmatch or string.gfind) -- lua 5.1+ or 5.0
  19. local string_find = assert(string.find)
  20. local string_sub = assert(string.sub)
  21. local string_byte = assert(string.byte)
  22.  
  23. local table_concat = table.concat
  24.  
  25. local utf8_object
  26.  
  27. local function utf8_sub(uobj, i, j)
  28.         assert(i, "bad argument #2 to 'sub' (number expected, got no value)")
  29.     if i then assert(type(i) == "number") end
  30.     if j then assert(type(j) == "number") end
  31.  
  32.     if i == 0 then
  33.         i = 1
  34.     elseif i < 0 then
  35.         i = #uobj+i+1
  36.     end
  37.  
  38.     if j and j < 0 then
  39.         j = #uobj+j+1
  40.     end
  41.  
  42.     local b = i <= 1 and 1 or uobj[i-1]+1
  43.     local e = j and uobj[j]
  44.     -- create an new utf8 object from the original one (do not "parse" it again)
  45.     local rel = uobj[i-1] or 0 -- relative position
  46.     local new = {}
  47.     for x=i,j,1 do
  48.         new[#new+1] = uobj[x] -rel
  49.     end
  50.     new.rawstring = string_sub(uobj.rawstring, b, assert( type(e)=="number" and e))
  51.     new.usestring = uobj.usestring
  52.     return utf8_object(new)
  53. end
  54.  
  55. local function utf8_typeof(obj)
  56.     local mt = getmetatable(obj)
  57.     return mt and mt.__type or typeof(obj)
  58. end
  59.  
  60. local function utf8_is_object(obj)
  61.     return not not (utf8_typeof(obj) == utf8type)
  62. end
  63.  
  64. local function utf8_tostring(obj)
  65.     if utf8_is_object(obj) then
  66.         return obj.rawstring
  67.     end
  68.     return obj
  69.     --return tostring(obj)
  70. end
  71.  
  72. local function utf8_clone(self)
  73.     if not utf8_is_object(self) then
  74.         error("it is not a ustring object ! what to do for clonning ?", 2)
  75.     end
  76.     local o = {
  77.         rawstring = self.rawstring,
  78.         usestring = self.usestring,
  79.     }
  80.     return utf8_object(o)
  81. end
  82.  
  83. --local function utf8_is_uchar(uchar)
  84. --  return (uchar:len() > 1) -- len() = string.len()
  85. --end
  86.  
  87. --        %z = 0x00 (\0 not allowed)
  88. --        \1 = 0x01
  89. --      \127 = 0x7F
  90. --      \128 = 0x80
  91. --      \191 = 0xBF
  92.  
  93. -- parse a lua string to split each UTF-8 sequence to separated table item
  94. local function private_string2ustring(unicode_string)
  95.     assert(typeof(unicode_string) == "string", "unicode_string is not a string?!")
  96.  
  97.     local e = 0 -- end of found string
  98.     local o = {}
  99.     while true do
  100.         -- FIXME: how to drop invalid sequence ?!
  101.         local b
  102.         b, e = string_find(unicode_string, "[%z\1-\127\194-\244][\128-\191]*", e+1)
  103.         if not b then break end
  104.         o[#o+1] = e
  105.     end
  106.     o.rawstring = unicode_string
  107.     o.usestring = #unicode_string == #o
  108.     return utf8_object(o)
  109. end
  110.  
  111. local function private_contains_unicode(str)
  112.     return not not str:find("[\128-\193]+")
  113. end
  114.  
  115. local function utf8_auto_convert(unicode_string, i, j)
  116.     assert(typeof(unicode_string) == "string", "unicode_string is not a string: ", typeof(unicode_string))
  117.     local obj, containsutf8 = private_string2ustring(unicode_string)
  118.     --if private_contains_unicode(unicode_string) then
  119.     --  obj = private_string2ustring(unicode_string)
  120.     --else
  121.     --  obj = unicode_string
  122.     --end
  123.     return (i and obj:sub(i,j)) or obj
  124. end
  125.  
  126. local function utf8_op_concat(obj1, obj2)
  127. --  local h
  128. --  local function sethand(o) h = getmetatable(o).__concat end
  129. --  if not pcall(sethand, obj1) then pcall(sethand, obj2) end
  130. --  if h then return h(obj1, obj2) end
  131.     return utf8_auto_convert( tostring(obj1) .. tostring(obj2) )
  132. end
  133.  
  134. local floor = table.floor
  135. local string_char = utf8_char
  136. local table_concat = table.concat
  137.  
  138. -- http://en.wikipedia.org/wiki/Utf8
  139. -- http://developer.coronalabs.com/code/utf-8-conversion-utility
  140. local function utf8_onechar(unicode)
  141.         if unicode <= 0x7F then return string_char(unicode) end
  142.  
  143.         if (unicode <= 0x7FF) then
  144.                 local Byte0 = 0xC0 + floor(unicode / 0x40)
  145.                 local Byte1 = 0x80 + (unicode % 0x40)
  146.                 return string_char(Byte0, Byte1)
  147.         end
  148.  
  149.         if (unicode <= 0xFFFF) then
  150.                 local Byte0 = 0xE0 +  floor(unicode / 0x1000) -- 0x1000 = 0x40 * 0x40
  151.                 local Byte1 = 0x80 + (floor(unicode / 0x40) % 0x40)
  152.                 local Byte2 = 0x80 + (unicode % 0x40)
  153.                 return string_char(Byte0, Byte1, Byte2)
  154.         end
  155.  
  156.         if (unicode <= 0x10FFFF) then
  157.                 local code = unicode
  158.                 local Byte3= 0x80 + (code % 0x40)
  159.                 code       = floor(code / 0x40)
  160.                 local Byte2= 0x80 + (code % 0x40)
  161.                 code       = floor(code / 0x40)
  162.                 local Byte1= 0x80 + (code % 0x40)
  163.                 code       = floor(code / 0x40)
  164.                 local Byte0= 0xF0 + code
  165.  
  166.                 return string_char(Byte0, Byte1, Byte2, Byte3)
  167.         end
  168.  
  169.         error('Unicode cannot be greater than U+10FFFF!', 3)
  170. end
  171.  
  172.  
  173. local function utf8_char(...)
  174.         local r = {}
  175.         for i,v in ipairs({...}) do
  176.                 if type(v) ~= "number" then
  177.                         error("bad argument #"..i.." to 'char' (number expected, got "..type(v)..")", 2)
  178.                 end
  179.                 r[i] = utf8_onechar(v)
  180.         end
  181.         return table_concat(r, "")
  182. end
  183. --for _, n in ipairs{12399, 21560, 12356, 12414, 12377} do print(utf8char(n)) end
  184. --print( lua53_utf8_char( 12399, 21560, 12356, 12414, 12377 ) )
  185.  
  186.  
  187. local function utf8_byte(obj, i, j)
  188.     local i = i or 1
  189.     local j = j or i -- FIXME: 'or i' or 'or -1' ?
  190.     local uobj
  191.     assert(utf8_is_object(obj), "ask utf8_byte() for a non utf8 object?!")
  192. --  if not utf8_is_object(obj) then
  193. --      uobj = utf8_auto_convert(obj, i, j)
  194. --  else
  195.     uobj = obj:sub(i, j)
  196. --  end
  197.     return string_byte(tostring(uobj), 1, -1)
  198. end
  199.  
  200. -- FIXME: what is the lower/upper case of Unicode ?!
  201. -- FIXME: optimisation? the parse is still the same (just change the rawstring ?)
  202. local function utf8_lower(uobj) return utf8_auto_convert( tostring(uobj):lower() ) end
  203. local function utf8_upper(uobj) return utf8_auto_convert( tostring(uobj):upper() ) end
  204.  
  205. -- FIXME: use the already parsed info to generate the reverse info...
  206. local function utf8_reverse(uobj)
  207.     if uobj.usestring then
  208.         return utf8_auto_convert(uobj.rawstring:reverse())
  209.     end
  210.  
  211.     local rawstring = uobj.rawstring
  212.     local tmp = {}
  213.     local e = uobj[#uobj] -- the ending position of uchar
  214. --  local last_value = e
  215. --  local o = {} -- new ustring object
  216.     for n=#uobj-1,1,-1 do
  217.         local b = uobj[n] -- the beginning position of uchar
  218.         tmp[#tmp+1] = string_sub(rawstring, b+1, e) -- the uchar
  219. --      o[#o+1] = last_value-b+1
  220.         e = b
  221.     end
  222.     tmp[#tmp+1] = string_sub(rawstring, 1, e)
  223. --  o[#o+1] = last_value
  224. --  o.rawstring = table_concat(tmp, "")
  225. --  return utf8_object(o)
  226.     return utf8_auto_convert(table_concat(tmp, ""))
  227. end
  228.  
  229.  
  230. local function utf8_rep(uobj, n)
  231.     return utf8_auto_convert(uobj.rawstring:rep(n)) -- :rep() is the string.rep()
  232. end
  233.  
  234. function utf8_object(uobj)
  235.     local mt
  236.     if not uobj then
  237.         uobj = {}
  238.         mt = {}
  239.     else
  240.         mt = getmetatable(uobj) or {}
  241.     end
  242.     mt.__index  = assert(ustring)
  243.     mt.__concat = assert(utf8_op_concat)
  244.     mt.__tostring   = assert(utf8_tostring)
  245.     mt.__type   = assert(utf8type)
  246. --  mt.__call   = function(_self, a1)
  247. --      if a1 == nil then
  248. --          return utf8_clone(_self)
  249. --      end
  250. --      return _self
  251. --  end
  252.     return setmetatable(uobj, mt)
  253. end
  254.  
  255.  
  256. ---- Standard Lua 5.1 string.* ----
  257. ustring.byte    = assert(utf8_byte)
  258. ustring.char    = assert(utf8_char)
  259. ustring.dump    = assert(string.dump)
  260. --ustring.find
  261. ustring.format  = assert(string.format)
  262. --ustring.gmatch
  263. --ustring.gsub
  264. ustring.len = function(uobj) return #uobj end
  265. ustring.lower   = assert(utf8_lower)
  266. --ustring.match
  267. ustring.rep = assert(utf8_rep)
  268. ustring.reverse = assert(utf8_reverse)
  269. ustring.sub = assert(utf8_sub)
  270. ustring.upper   = assert(utf8_upper)
  271.  
  272. ---- custome add-on ----
  273. ustring.type    = assert(utf8_typeof)
  274. ustring.tostring = assert(utf8_tostring)
  275. ustring.clone   = assert(utf8_clone)
  276. --ustring.debugdump = function(self) return table.concat(self, " ") end
  277.  
  278. -- Add fonctions to the module
  279. for k,v in pairs(ustring) do m[k] = v end
  280.  
  281. -- Allow to use the module directly to convert strings
  282. local mt = {
  283.     __call = function(_self, obj, i, j)
  284.         if utf8_is_object(obj) then
  285.             return (i and obj:sub(i,j)) or obj
  286.         end
  287.         local str = obj
  288.         if typeof(str) ~= "string" then
  289.             str = tostring(str)
  290.         end
  291.         return utf8_auto_convert(str, i, j)
  292.     end
  293. }
  294.  
  295. return setmetatable(m,mt)
  296.  
  297. -------------------------------------------------------------------------------
  298. -- begin of the idea : http://lua-users.org/wiki/LuaUnicode
  299. --
  300. -- for uchar in sgmatch(unicode_string, "([%z\1-\127\194-\244][\128-\191]*)") do
  301. --
  302. --local function utf8_strlen(unicode_string)
  303. --  local _, count = string.gsub(unicode_string, "[^\128-\193]", "")
  304. --  return count
  305. --end
  306. -- http://www.unicode.org/reports/tr29/#Grapheme_Cluster_Boundaries
Advertisement
Add Comment
Please, Sign In to add comment