utf8.lua 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. -- $Id: utf8.lua,v 1.12 2016/11/07 13:11:28 roberto Exp $
  2. -- See Copyright Notice in file all.lua
  3. print "testing UTF-8 library"
  4. local utf8 = require'utf8'
  5. local function checkerror (msg, f, ...)
  6. local s, err = pcall(f, ...)
  7. assert(not s and string.find(err, msg))
  8. end
  9. local function len (s)
  10. return #string.gsub(s, "[\x80-\xBF]", "")
  11. end
  12. local justone = "^" .. utf8.charpattern .. "$"
  13. -- 't' is the list of codepoints of 's'
  14. local function checksyntax (s, t)
  15. local ts = {"return '"}
  16. for i = 1, #t do ts[i + 1] = string.format("\\u{%x}", t[i]) end
  17. ts[#t + 2] = "'"
  18. ts = table.concat(ts)
  19. assert(assert(load(ts))() == s)
  20. end
  21. assert(utf8.offset("alo", 5) == nil)
  22. assert(utf8.offset("alo", -4) == nil)
  23. -- 't' is the list of codepoints of 's'
  24. local function check (s, t)
  25. local l = utf8.len(s)
  26. assert(#t == l and len(s) == l)
  27. assert(utf8.char(table.unpack(t)) == s)
  28. assert(utf8.offset(s, 0) == 1)
  29. checksyntax(s, t)
  30. local t1 = {utf8.codepoint(s, 1, -1)}
  31. assert(#t == #t1)
  32. for i = 1, #t do assert(t[i] == t1[i]) end
  33. for i = 1, l do
  34. local pi = utf8.offset(s, i) -- position of i-th char
  35. local pi1 = utf8.offset(s, 2, pi) -- position of next char
  36. assert(string.find(string.sub(s, pi, pi1 - 1), justone))
  37. assert(utf8.offset(s, -1, pi1) == pi)
  38. assert(utf8.offset(s, i - l - 1) == pi)
  39. assert(pi1 - pi == #utf8.char(utf8.codepoint(s, pi)))
  40. for j = pi, pi1 - 1 do
  41. assert(utf8.offset(s, 0, j) == pi)
  42. end
  43. for j = pi + 1, pi1 - 1 do
  44. assert(not utf8.len(s, j))
  45. end
  46. assert(utf8.len(s, pi, pi) == 1)
  47. assert(utf8.len(s, pi, pi1 - 1) == 1)
  48. assert(utf8.len(s, pi) == l - i + 1)
  49. assert(utf8.len(s, pi1) == l - i)
  50. assert(utf8.len(s, 1, pi) == i)
  51. end
  52. local i = 0
  53. for p, c in utf8.codes(s) do
  54. i = i + 1
  55. assert(c == t[i] and p == utf8.offset(s, i))
  56. assert(utf8.codepoint(s, p) == c)
  57. end
  58. assert(i == #t)
  59. i = 0
  60. for p, c in utf8.codes(s) do
  61. i = i + 1
  62. assert(c == t[i] and p == utf8.offset(s, i))
  63. end
  64. assert(i == #t)
  65. i = 0
  66. for c in string.gmatch(s, utf8.charpattern) do
  67. i = i + 1
  68. assert(c == utf8.char(t[i]))
  69. end
  70. assert(i == #t)
  71. for i = 1, l do
  72. assert(utf8.offset(s, i) == utf8.offset(s, i - l - 1, #s + 1))
  73. end
  74. end
  75. do -- error indication in utf8.len
  76. local function check (s, p)
  77. local a, b = utf8.len(s)
  78. assert(not a and b == p)
  79. end
  80. check("abc\xE3def", 4)
  81. check("汉字\x80", #("汉字") + 1)
  82. check("\xF4\x9F\xBF", 1)
  83. check("\xF4\x9F\xBF\xBF", 1)
  84. end
  85. -- error in utf8.codes
  86. checkerror("invalid UTF%-8 code",
  87. function ()
  88. local s = "ab\xff"
  89. for c in utf8.codes(s) do assert(c) end
  90. end)
  91. -- error in initial position for offset
  92. checkerror("position out of range", utf8.offset, "abc", 1, 5)
  93. checkerror("position out of range", utf8.offset, "abc", 1, -4)
  94. checkerror("position out of range", utf8.offset, "", 1, 2)
  95. checkerror("position out of range", utf8.offset, "", 1, -1)
  96. checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
  97. checkerror("continuation byte", utf8.offset, "𦧺", 1, 2)
  98. checkerror("continuation byte", utf8.offset, "\x80", 1)
  99. local s = "hello World"
  100. local t = {string.byte(s, 1, -1)}
  101. for i = 1, utf8.len(s) do assert(t[i] == string.byte(s, i)) end
  102. check(s, t)
  103. check("汉字/漢字", {27721, 23383, 47, 28450, 23383,})
  104. do
  105. local s = "áéí\128"
  106. local t = {utf8.codepoint(s,1,#s - 1)}
  107. assert(#t == 3 and t[1] == 225 and t[2] == 233 and t[3] == 237)
  108. checkerror("invalid UTF%-8 code", utf8.codepoint, s, 1, #s)
  109. checkerror("out of range", utf8.codepoint, s, #s + 1)
  110. t = {utf8.codepoint(s, 4, 3)}
  111. assert(#t == 0)
  112. checkerror("out of range", utf8.codepoint, s, -(#s + 1), 1)
  113. checkerror("out of range", utf8.codepoint, s, 1, #s + 1)
  114. end
  115. assert(utf8.char() == "")
  116. assert(utf8.char(97, 98, 99) == "abc")
  117. assert(utf8.codepoint(utf8.char(0x10FFFF)) == 0x10FFFF)
  118. checkerror("value out of range", utf8.char, 0x10FFFF + 1)
  119. local function invalid (s)
  120. checkerror("invalid UTF%-8 code", utf8.codepoint, s)
  121. assert(not utf8.len(s))
  122. end
  123. -- UTF-8 representation for 0x11ffff (value out of valid range)
  124. invalid("\xF4\x9F\xBF\xBF")
  125. -- overlong sequences
  126. invalid("\xC0\x80") -- zero
  127. invalid("\xC1\xBF") -- 0x7F (should be coded in 1 byte)
  128. invalid("\xE0\x9F\xBF") -- 0x7FF (should be coded in 2 bytes)
  129. invalid("\xF0\x8F\xBF\xBF") -- 0xFFFF (should be coded in 3 bytes)
  130. -- invalid bytes
  131. invalid("\x80") -- continuation byte
  132. invalid("\xBF") -- continuation byte
  133. invalid("\xFE") -- invalid byte
  134. invalid("\xFF") -- invalid byte
  135. -- empty string
  136. check("", {})
  137. -- minimum and maximum values for each sequence size
  138. s = "\0 \x7F\z
  139. \xC2\x80 \xDF\xBF\z
  140. \xE0\xA0\x80 \xEF\xBF\xBF\z
  141. \xF0\x90\x80\x80 \xF4\x8F\xBF\xBF"
  142. s = string.gsub(s, " ", "")
  143. check(s, {0,0x7F, 0x80,0x7FF, 0x800,0xFFFF, 0x10000,0x10FFFF})
  144. x = "日本語a-4\0éó"
  145. check(x, {26085, 26412, 35486, 97, 45, 52, 0, 233, 243})
  146. -- Supplementary Characters
  147. check("𣲷𠜎𠱓𡁻𠵼ab𠺢",
  148. {0x23CB7, 0x2070E, 0x20C53, 0x2107B, 0x20D7C, 0x61, 0x62, 0x20EA2,})
  149. check("𨳊𩶘𦧺𨳒𥄫𤓓\xF4\x8F\xBF\xBF",
  150. {0x28CCA, 0x29D98, 0x269FA, 0x28CD2, 0x2512B, 0x244D3, 0x10ffff})
  151. local i = 0
  152. for p, c in string.gmatch(x, "()(" .. utf8.charpattern .. ")") do
  153. i = i + 1
  154. assert(utf8.offset(x, i) == p)
  155. assert(utf8.len(x, p) == utf8.len(x) - i + 1)
  156. assert(utf8.len(c) == 1)
  157. for j = 1, #c - 1 do
  158. assert(utf8.offset(x, 0, p + j - 1) == p)
  159. end
  160. end
  161. print'ok'