Advertisement
Guest User

Tiny JSON Parser for Lua, by Duncan Cross (MIT License) (v2)

a guest
Oct 27th, 2011
242
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. local json = {}
  2. do
  3.  
  4.   local valuePipes = {}
  5.  
  6.   local function pipeValue(jsonStr, copyPos, pos)
  7.     local cPos, c = jsonStr:match('()(%S)', pos)
  8.     if not c then
  9.       error('unexpected end of input')
  10.     end
  11.     local pipe = valuePipes[c]
  12.     if not pipe then
  13.       error('invalid JSON at: ' .. cPos)
  14.     end
  15.     return pipe(jsonStr, copyPos, cPos)
  16.   end
  17.  
  18.   function json.parse(jsonStr)
  19.     local chunk = assert(load(coroutine.wrap(function()
  20.       coroutine.yield('local json = ...; return ')
  21.       local copyPos, pos = pipeValue(jsonStr, 1, 1)
  22.       local invalidPos = jsonStr:match('()%S', pos)
  23.       if invalidPos then
  24.         error('invalid JSON at: ' .. invalidPos)
  25.       end
  26.       if (copyPos < pos) then
  27.         coroutine.yield(jsonStr:sub(copyPos))
  28.       end
  29.     end)))
  30.     return chunk(json)
  31.   end
  32.  
  33.   local function pipeStringContent(jsonStr, copyPos, pos)
  34.     while true do
  35.       local cPos, c = jsonStr:match('()(["\\])', pos)
  36.       if (c == '"') then
  37.         return copyPos, cPos+1
  38.       elseif (c == '\\') then
  39.         local escape = jsonStr:sub(cPos+1, cPos+1)
  40.         if (escape == 'u') then
  41.           local code = tonumber(jsonStr:sub(cPos+2, cPos+5), 16)
  42.           if not code then
  43.             error('invalid string escape')
  44.           end
  45.           if (code < 0x80) then
  46.             code = string.format('\\%03d', code)
  47.           elseif (code < 0x800) then
  48.             code = string.format('\\%03d\\%03d',
  49.               0xC0 + math.floor(code/0x40),
  50.               0x80 + code%0x40)
  51.           else
  52.             code = string.format('\\%03d\\%03d\\%03d',
  53.               0xE0 + math.floor(code/0x1000),
  54.               0x80 + math.floor(code/0x40)%0x40,
  55.               0x80 + code%0x40)
  56.           end
  57.           coroutine.yield(jsonStr:sub(copyPos, cPos-1) .. code)
  58.           copyPos = cPos+6
  59.           pos = copyPos
  60.         elseif escape == '/' then
  61.           coroutine.yield(jsonStr:sub(copyPos, cPos-1) .. '/')
  62.           pos = cPos+2
  63.           copyPos = pos
  64.         elseif escape:match('["\\bfnrt]') then
  65.           pos = cPos+2
  66.         else
  67.           error('invalid string escape')
  68.         end
  69.       else
  70.         error('unexpected end of input')
  71.       end
  72.     end
  73.   end
  74.  
  75.   valuePipes['['] = function(jsonStr, copyPos, pos)
  76.     coroutine.yield(jsonStr:sub(copyPos, pos-1) .. 'json.array{')
  77.     copyPos = pos+1
  78.     pos = copyPos
  79.     local emptyPos = jsonStr:match('^%s*%]()', copyPos)
  80.     if emptyPos then
  81.       coroutine.yield('}')
  82.       return emptyPos, emptyPos
  83.     end
  84.     while true do
  85.       copyPos, pos = pipeValue(jsonStr, copyPos, pos)
  86.       local cPos, c = jsonStr:match('()(%S)', pos)
  87.       if not c then
  88.         error('unexpected end of input')
  89.       end
  90.       if (c == ']') then
  91.         coroutine.yield(jsonStr:sub(copyPos, cPos-1) .. '}')
  92.         return cPos+1, cPos+1
  93.       elseif (c ~= ',') then
  94.         error('invalid JSON at: ' .. cPos)
  95.       end
  96.       pos = cPos+1
  97.     end
  98.   end
  99.   valuePipes['"'] = function(jsonStr, copyPos, pos)
  100.     return pipeStringContent(jsonStr, copyPos, pos+1)
  101.   end
  102.   valuePipes['{'] = function(jsonStr, copyPos, pos)
  103.     pos = pos + 1
  104.     local emptyPos = jsonStr:match('^%s*%}()', pos)
  105.     if emptyPos then
  106.       return copyPos, emptyPos
  107.     end
  108.     while true do
  109.       local keyPos, key = jsonStr:match('^%s*()(%S)', pos)
  110.       if (key ~= '"') then
  111.         if not keyPos then
  112.           error('unexpected end of input')
  113.         end
  114.         error('invalid JSON at: ' .. pos)
  115.       end
  116.       coroutine.yield(jsonStr:sub(copyPos, keyPos-1) .. '["')
  117.       copyPos, pos = pipeStringContent(jsonStr, keyPos+1, keyPos+1)
  118.       local valPos, v = jsonStr:match('^%s*()(%S)', pos)
  119.       if (v ~= ':') then
  120.         if not valPos then
  121.           error('unexpected end of input')
  122.         end
  123.         error('invalid JSON at: ' .. pos)
  124.       end
  125.       coroutine.yield(jsonStr:sub(copyPos, pos-1) .. ']=')
  126.       copyPos, pos = pipeValue(jsonStr, valPos+1, valPos+1)
  127.       local cPos, c = jsonStr:match('()(%S)', pos)
  128.       if not c then
  129.         error('unexpected end of input')
  130.       end
  131.       pos = cPos + 1
  132.       if (c == '}') then
  133.         return copyPos, pos
  134.       elseif (c ~= ',') then
  135.         error('invalid JSON at: ' .. cPos)
  136.       end
  137.     end
  138.   end
  139.   valuePipes['t'] = function(jsonStr, copyPos, pos)
  140.     assert(jsonStr:sub(pos, pos+3) == 'true', 'invalid JSON at: ' .. pos)
  141.     return copyPos, pos+4
  142.   end
  143.   valuePipes['f'] = function(jsonStr, copyPos, pos)
  144.     assert(jsonStr:sub(pos, pos+4) == 'false', 'invalid JSON at: ' .. pos)
  145.     return copyPos, pos+5
  146.   end
  147.   valuePipes['n'] = function(jsonStr, copyPos, pos)
  148.     assert(jsonStr:sub(pos, pos+3) == 'null', 'invalid JSON at: ' .. pos)
  149.     coroutine.yield(jsonStr:sub(copyPos, pos-1) .. 'json.')
  150.     return pos, pos+4
  151.   end
  152.   local function numParser(jsonStr, copyPos, pos)
  153.     local afterPos = jsonStr:match('^%-?[0-9]+()', pos)
  154.     if not afterPos then
  155.       error('invalid JSON at: ' .. pos)
  156.     end
  157.     afterPos = jsonStr:match('^%.[0-9]+()', afterPos) or afterPos
  158.     afterPos = jsonStr:match('^[eE][%+%-]?[0-9]+()', afterPos) or afterPos
  159.     return copyPos, afterPos
  160.   end
  161.   for i = 0, 9 do
  162.     valuePipes[tostring(i)] = numParser
  163.   end
  164.   valuePipes['-'] = numParser
  165.  
  166.   -- override to replace default functionality
  167.   json.null = nil
  168.   json.array = function(a)
  169.     return a
  170.   end
  171.  
  172. end
  173. return json
  174.  
  175.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement