Lua IRC bot.

serialization.lua 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133
  1. local serialization = {}
  2. -- Important: pretty formatting will allow presenting non-serializable values
  3. -- but may generate output that cannot be unserialized back.
  4. function serialization.serialize(value, pretty)
  5. local kw = {["and"]=true, ["break"]=true, ["do"]=true, ["else"]=true,
  6. ["elseif"]=true, ["end"]=true, ["false"]=true, ["for"]=true,
  7. ["function"]=true, ["goto"]=true, ["if"]=true, ["in"]=true,
  8. ["local"]=true, ["nil"]=true, ["not"]=true, ["or"]=true,
  9. ["repeat"]=true, ["return"]=true, ["then"]=true, ["true"]=true,
  10. ["until"]=true, ["while"]=true}
  11. local id = "^[%a_][%w_]*$"
  12. local ts = {}
  13. local function s(v, l)
  14. local t = type(v)
  15. if t == "nil" then
  16. return "nil"
  17. elseif t == "boolean" then
  18. return v and "true" or "false"
  19. elseif t == "number" then
  20. if v ~= v then
  21. return "0/0"
  22. elseif v == math.huge then
  23. return "math.huge"
  24. elseif v == -math.huge then
  25. return "-math.huge"
  26. else
  27. return tostring(v)
  28. end
  29. elseif t == "string" then
  30. return string.format("%q", v):gsub("\\\n","\\n")
  31. elseif t == "table" and pretty and getmetatable(v) and getmetatable(v).__tostring then
  32. return tostring(v)
  33. elseif t == "table" then
  34. if ts[v] then
  35. if pretty then
  36. return "recursion"
  37. else
  38. error("tables with cycles are not supported")
  39. end
  40. end
  41. ts[v] = true
  42. local i, r = 1, nil
  43. local f
  44. if pretty then
  45. local ks, sks, oks = {}, {}, {}
  46. for k in pairs(v) do
  47. if type(k) == "number" then
  48. table.insert(ks, k)
  49. elseif type(k) == "string" then
  50. table.insert(sks, k)
  51. else
  52. table.insert(oks, k)
  53. end
  54. end
  55. table.sort(ks)
  56. table.sort(sks)
  57. for _, k in ipairs(sks) do
  58. table.insert(ks, k)
  59. end
  60. for _, k in ipairs(oks) do
  61. table.insert(ks, k)
  62. end
  63. local n = 0
  64. f = table.pack(function()
  65. n = n + 1
  66. local k = ks[n]
  67. if k ~= nil then
  68. return k, v[k]
  69. else
  70. return nil
  71. end
  72. end)
  73. else
  74. f = table.pack(pairs(v))
  75. end
  76. for k, v in table.unpack(f) do
  77. if r then
  78. r = r .. "," .. (pretty and ("\n" .. string.rep(" ", l)) or "")
  79. else
  80. r = "{"
  81. end
  82. local tk = type(k)
  83. if tk == "number" and k == i then
  84. i = i + 1
  85. r = r .. s(v, l + 1)
  86. else
  87. if tk == "string" and not kw[k] and string.match(k, id) then
  88. r = r .. k
  89. else
  90. r = r .. "[" .. s(k, l + 1) .. "]"
  91. end
  92. r = r .. "=" .. s(v, l + 1)
  93. end
  94. end
  95. ts[v] = nil -- allow writing same table more than once
  96. return (r or "{") .. "}"
  97. else
  98. if pretty then
  99. return tostring(t)
  100. else
  101. error("unsupported type: " .. t)
  102. end
  103. end
  104. end
  105. local result = s(value, 1)
  106. local limit = type(pretty) == "number" and pretty or 10
  107. if pretty then
  108. local truncate = 0
  109. while limit > 0 and truncate do
  110. truncate = string.find(result, "\n", truncate + 1, true)
  111. limit = limit - 1
  112. end
  113. if truncate then
  114. return result:sub(1, truncate) .. "..."
  115. end
  116. end
  117. return result
  118. end
  119. function serialization.unserialize(data)
  120. local result, reason = load("return " .. data, "=data", _, {math={huge=math.huge}})
  121. if not result then
  122. return nil, reason
  123. end
  124. local ok, output = pcall(result)
  125. if not ok then
  126. return nil, output
  127. end
  128. return output
  129. end
  130. return serialization