PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 1ms

/mod_couchdb/couchdb/json.lib.lua

https://code.google.com/p/prosody-modules/
Lua | 354 lines | 348 code | 5 blank | 1 comment | 45 complexity | 04ff798c797fadd2cf3677b6714d7310 MD5 | raw file
  1. local type = type;
  2. local t_insert, t_concat, t_remove = table.insert, table.concat, table.remove;
  3. local s_char = string.char;
  4. local tostring, tonumber = tostring, tonumber;
  5. local pairs, ipairs = pairs, ipairs;
  6. local next = next;
  7. local error = error;
  8. local newproxy, getmetatable = newproxy, getmetatable;
  9. local print = print;
  10. --module("json")
  11. local _M = {};
  12. local null = newproxy and newproxy(true) or {};
  13. if getmetatable and getmetatable(null) then
  14. getmetatable(null).__tostring = function() return "null"; end;
  15. end
  16. _M.null = null;
  17. local escapes = {
  18. ["\""] = "\\\"", ["\\"] = "\\\\", ["\b"] = "\\b",
  19. ["\f"] = "\\f", ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t"};
  20. local unescapes = {
  21. ["\""] = "\"", ["\\"] = "\\", ["/"] = "/",
  22. b = "\b", f = "\f", n = "\n", r = "\r", t = "\t"};
  23. for i=0,31 do
  24. local ch = s_char(i);
  25. if not escapes[ch] then escapes[ch] = ("\\u%.4X"):format(i); end
  26. end
  27. local valid_types = {
  28. number = true,
  29. string = true,
  30. table = true,
  31. boolean = true
  32. };
  33. local special_keys = {
  34. __array = true;
  35. __hash = true;
  36. };
  37. local simplesave, tablesave, arraysave, stringsave;
  38. function stringsave(o, buffer)
  39. -- FIXME do proper utf-8 and binary data detection
  40. t_insert(buffer, "\""..(o:gsub(".", escapes)).."\"");
  41. end
  42. function arraysave(o, buffer)
  43. t_insert(buffer, "[");
  44. if next(o) then
  45. for i,v in ipairs(o) do
  46. simplesave(v, buffer);
  47. t_insert(buffer, ",");
  48. end
  49. t_remove(buffer);
  50. end
  51. t_insert(buffer, "]");
  52. end
  53. function tablesave(o, buffer)
  54. local __array = {};
  55. local __hash = {};
  56. local hash = {};
  57. for i,v in ipairs(o) do
  58. __array[i] = v;
  59. end
  60. for k,v in pairs(o) do
  61. local ktype, vtype = type(k), type(v);
  62. if valid_types[vtype] or v == null then
  63. if ktype == "string" and not special_keys[k] then
  64. hash[k] = v;
  65. elseif (valid_types[ktype] or k == null) and __array[k] == nil then
  66. __hash[k] = v;
  67. end
  68. end
  69. end
  70. if next(__hash) ~= nil or next(hash) ~= nil or next(__array) == nil then
  71. t_insert(buffer, "{");
  72. local mark = #buffer;
  73. for k,v in pairs(hash) do
  74. stringsave(k, buffer);
  75. t_insert(buffer, ":");
  76. simplesave(v, buffer);
  77. t_insert(buffer, ",");
  78. end
  79. if next(__hash) ~= nil then
  80. t_insert(buffer, "\"__hash\":[");
  81. for k,v in pairs(__hash) do
  82. simplesave(k, buffer);
  83. t_insert(buffer, ",");
  84. simplesave(v, buffer);
  85. t_insert(buffer, ",");
  86. end
  87. t_remove(buffer);
  88. t_insert(buffer, "]");
  89. t_insert(buffer, ",");
  90. end
  91. if next(__array) then
  92. t_insert(buffer, "\"__array\":");
  93. arraysave(__array, buffer);
  94. t_insert(buffer, ",");
  95. end
  96. if mark ~= #buffer then t_remove(buffer); end
  97. t_insert(buffer, "}");
  98. else
  99. arraysave(__array, buffer);
  100. end
  101. end
  102. function simplesave(o, buffer)
  103. local t = type(o);
  104. if t == "number" then
  105. t_insert(buffer, tostring(o));
  106. elseif t == "string" then
  107. stringsave(o, buffer);
  108. elseif t == "table" then
  109. tablesave(o, buffer);
  110. elseif t == "boolean" then
  111. t_insert(buffer, (o and "true" or "false"));
  112. else
  113. t_insert(buffer, "null");
  114. end
  115. end
  116. function _M.encode(obj)
  117. local t = {};
  118. simplesave(obj, t);
  119. return t_concat(t);
  120. end
  121. -----------------------------------
  122. function _M.decode(json)
  123. local pos = 1;
  124. local current = {};
  125. local stack = {};
  126. local ch, peek;
  127. local function next()
  128. ch = json:sub(pos, pos);
  129. pos = pos+1;
  130. peek = json:sub(pos, pos);
  131. return ch;
  132. end
  133. local function skipwhitespace()
  134. while ch and (ch == "\r" or ch == "\n" or ch == "\t" or ch == " ") do
  135. next();
  136. end
  137. end
  138. local function skiplinecomment()
  139. repeat next(); until not(ch) or ch == "\r" or ch == "\n";
  140. skipwhitespace();
  141. end
  142. local function skipstarcomment()
  143. next(); next(); -- skip '/', '*'
  144. while peek and ch ~= "*" and peek ~= "/" do next(); end
  145. if not peek then error("eof in star comment") end
  146. next(); next(); -- skip '*', '/'
  147. skipwhitespace();
  148. end
  149. local function skipstuff()
  150. while true do
  151. skipwhitespace();
  152. if ch == "/" and peek == "*" then
  153. skipstarcomment();
  154. elseif ch == "/" and peek == "*" then
  155. skiplinecomment();
  156. else
  157. return;
  158. end
  159. end
  160. end
  161. local readvalue;
  162. local function readarray()
  163. local t = {};
  164. next(); -- skip '['
  165. skipstuff();
  166. if ch == "]" then next(); return t; end
  167. t_insert(t, readvalue());
  168. while true do
  169. skipstuff();
  170. if ch == "]" then next(); return t; end
  171. if not ch then error("eof while reading array");
  172. elseif ch == "," then next();
  173. elseif ch then error("unexpected character in array, comma expected"); end
  174. if not ch then error("eof while reading array"); end
  175. t_insert(t, readvalue());
  176. end
  177. end
  178. local function checkandskip(c)
  179. local x = ch or "eof";
  180. if x ~= c then error("unexpected "..x..", '"..c.."' expected"); end
  181. next();
  182. end
  183. local function readliteral(lit, val)
  184. for c in lit:gmatch(".") do
  185. checkandskip(c);
  186. end
  187. return val;
  188. end
  189. local function readstring()
  190. local s = "";
  191. checkandskip("\"");
  192. while ch do
  193. while ch and ch ~= "\\" and ch ~= "\"" do
  194. s = s..ch; next();
  195. end
  196. if ch == "\\" then
  197. next();
  198. if unescapes[ch] then
  199. s = s..unescapes[ch];
  200. next();
  201. elseif ch == "u" then
  202. local seq = "";
  203. for i=1,4 do
  204. next();
  205. if not ch then error("unexpected eof in string"); end
  206. if not ch:match("[0-9a-fA-F]") then error("invalid unicode escape sequence in string"); end
  207. seq = seq..ch;
  208. end
  209. s = s..s.char(tonumber(seq, 16)); -- FIXME do proper utf-8
  210. next();
  211. else error("invalid escape sequence in string"); end
  212. end
  213. if ch == "\"" then
  214. next();
  215. return s;
  216. end
  217. end
  218. error("eof while reading string");
  219. end
  220. local function readnumber()
  221. local s = "";
  222. if ch == "-" then
  223. s = s..ch; next();
  224. if not ch:match("[0-9]") then error("number format error"); end
  225. end
  226. if ch == "0" then
  227. s = s..ch; next();
  228. if ch:match("[0-9]") then error("number format error"); end
  229. else
  230. while ch and ch:match("[0-9]") do
  231. s = s..ch; next();
  232. end
  233. end
  234. if ch == "." then
  235. s = s..ch; next();
  236. if not ch:match("[0-9]") then error("number format error"); end
  237. while ch and ch:match("[0-9]") do
  238. s = s..ch; next();
  239. end
  240. if ch == "e" or ch == "E" then
  241. s = s..ch; next();
  242. if ch == "+" or ch == "-" then
  243. s = s..ch; next();
  244. if not ch:match("[0-9]") then error("number format error"); end
  245. while ch and ch:match("[0-9]") do
  246. s = s..ch; next();
  247. end
  248. end
  249. end
  250. end
  251. return tonumber(s);
  252. end
  253. local function readmember(t)
  254. local k = readstring();
  255. checkandskip(":");
  256. t[k] = readvalue();
  257. end
  258. local function fixobject(obj)
  259. local __array = obj.__array;
  260. if __array then
  261. obj.__array = nil;
  262. for i,v in ipairs(__array) do
  263. t_insert(obj, v);
  264. end
  265. end
  266. local __hash = obj.__hash;
  267. if __hash then
  268. obj.__hash = nil;
  269. local k;
  270. for i,v in ipairs(__hash) do
  271. if k ~= nil then
  272. obj[k] = v; k = nil;
  273. else
  274. k = v;
  275. end
  276. end
  277. end
  278. return obj;
  279. end
  280. local function readobject()
  281. local t = {};
  282. next(); -- skip '{'
  283. skipstuff();
  284. if ch == "}" then next(); return t; end
  285. if not ch then error("eof while reading object"); end
  286. readmember(t);
  287. while true do
  288. skipstuff();
  289. if ch == "}" then next(); return fixobject(t); end
  290. if not ch then error("eof while reading object");
  291. elseif ch == "," then next();
  292. elseif ch then error("unexpected character in object, comma expected"); end
  293. if not ch then error("eof while reading object"); end
  294. readmember(t);
  295. end
  296. end
  297. function readvalue()
  298. skipstuff();
  299. while ch do
  300. if ch == "{" then
  301. return readobject();
  302. elseif ch == "[" then
  303. return readarray();
  304. elseif ch == "\"" then
  305. return readstring();
  306. elseif ch:match("[%-0-9%.]") then
  307. return readnumber();
  308. elseif ch == "n" then
  309. return readliteral("null", null);
  310. elseif ch == "t" then
  311. return readliteral("true", true);
  312. elseif ch == "f" then
  313. return readliteral("false", false);
  314. end
  315. end
  316. error("eof while reading value");
  317. end
  318. next();
  319. return readvalue();
  320. end
  321. function _M.test(object)
  322. local encoded = encode(object);
  323. local decoded = decode(encoded);
  324. local recoded = encode(decoded);
  325. if encoded ~= recoded then
  326. print("FAILED");
  327. print("encoded:", encoded);
  328. print("recoded:", recoded);
  329. else
  330. print(encoded);
  331. end
  332. return encoded ~= recoded;
  333. end
  334. return _M;