Files
fusionpbx/resources/install/scripts/resources/functions/lunajson/sax.lua
Chris Black 7c4a726766 add built in ability for microsoft bing speech to text (#1960)
* add built in ability for microsoft bing speech to text

* move json.lua to lower case and more error checking in record_message

* Replaced Creative Commons json.lua with lunajson.lua which is MIT license

https://github.com/grafi-tt/lunajson/blob/master/LICENSE
2016-11-03 16:11:21 -06:00

526 lines
12 KiB
Lua

local error = error
local byte, char, find, gsub, match, sub = string.byte, string.char, string.find, string.gsub, string.match, string.sub
local tonumber = tonumber
local tostring, type, unpack = tostring, type, table.unpack or unpack
-- The function that interprets JSON strings is separated into another file so as to
-- use bitwise operation to speedup unicode codepoints processing on Lua 5.3.
local genstrlib
if _VERSION == "Lua 5.3" then
genstrlib = require 'resources.functions.lunajson._str_lib_lua53'
else
genstrlib = require 'resources.functions.lunajson._str_lib'
end
local _ENV = nil
local function nop() end
local function newparser(src, saxtbl)
local json, jsonnxt
local jsonlen, pos, acc = 0, 1, 0
-- `f` is the temporary for dispatcher[c] and
-- the dummy for the first return value of `find`
local dispatcher, f
-- initialize
if type(src) == 'string' then
json = src
jsonlen = #json
jsonnxt = function()
json = ''
jsonlen = 0
jsonnxt = nop
end
else
jsonnxt = function()
acc = acc + jsonlen
pos = 1
repeat
json = src()
if not json then
json = ''
jsonlen = 0
jsonnxt = nop
return
end
jsonlen = #json
until jsonlen > 0
end
jsonnxt()
end
local sax_startobject = saxtbl.startobject or nop
local sax_key = saxtbl.key or nop
local sax_endobject = saxtbl.endobject or nop
local sax_startarray = saxtbl.startarray or nop
local sax_endarray = saxtbl.endarray or nop
local sax_string = saxtbl.string or nop
local sax_number = saxtbl.number or nop
local sax_boolean = saxtbl.boolean or nop
local sax_null = saxtbl.null or nop
--[[
Helper
--]]
local function tryc()
local c = byte(json, pos)
if not c then
jsonnxt()
c = byte(json, pos)
end
return c
end
local function parseerror(errmsg)
error("parse error at " .. acc + pos .. ": " .. errmsg)
end
local function tellc()
return tryc() or parseerror("unexpected termination")
end
local function spaces() -- skip spaces and prepare the next char
while true do
f, pos = find(json, '^[ \n\r\t]*', pos)
if pos ~= jsonlen then
pos = pos+1
return
end
if jsonlen == 0 then
parseerror("unexpected termination")
end
jsonnxt()
end
end
--[[
Invalid
--]]
local function f_err()
parseerror('invalid value')
end
--[[
Constants
--]]
-- fallback slow constants parser
local function generic_constant(target, targetlen, ret, sax_f)
for i = 1, targetlen do
local c = tellc()
if byte(target, i) ~= c then
parseerror("invalid char")
end
pos = pos+1
end
return sax_f(ret)
end
-- null
local function f_nul()
if sub(json, pos, pos+2) == 'ull' then
pos = pos+3
return sax_null(nil)
end
return generic_constant('ull', 3, nil, sax_null)
end
-- false
local function f_fls()
if sub(json, pos, pos+3) == 'alse' then
pos = pos+4
return sax_boolean(false)
end
return generic_constant('alse', 4, false, sax_boolean)
end
-- true
local function f_tru()
if sub(json, pos, pos+2) == 'rue' then
pos = pos+3
return sax_boolean(true)
end
return generic_constant('rue', 3, true, sax_boolean)
end
--[[
Numbers
Conceptually, the longest prefix that matches to `(0|[1-9][0-9]*)(\.[0-9]*)?([eE][+-]?[0-9]*)?`
(in regexp) is captured as a number and its conformance to the JSON spec is checked.
--]]
-- deal with non-standard locales
local radixmark = match(tostring(0.5), '[^0-9]')
local fixedtonumber = tonumber
if radixmark ~= '.' then -- deals with non-standard locales
if find(radixmark, '%W') then
radixmark = '%' .. radixmark
end
fixedtonumber = function(s)
return tonumber(gsub(s, '.', radixmark))
end
end
-- fallback slow parser
local function generic_number(mns)
local buf = {}
local i = 1
local c = byte(json, pos)
pos = pos+1
local function nxt()
buf[i] = c
i = i+1
c = tryc()
pos = pos+1
end
if c == 0x30 then
nxt()
else
repeat nxt() until not (c and 0x30 <= c and c < 0x3A)
end
if c == 0x2E then
nxt()
if not (c and 0x30 <= c and c < 0x3A) then
parseerror('invalid number')
end
repeat nxt() until not (c and 0x30 <= c and c < 0x3A)
end
if c == 0x45 or c == 0x65 then
nxt()
if c == 0x2B or c == 0x2D then
nxt()
end
if not (c and 0x30 <= c and c < 0x3A) then
parseerror('invalid number')
end
repeat nxt() until not (c and 0x30 <= c and c < 0x3A)
end
pos = pos-1
local num = char(unpack(buf))
num = fixedtonumber(num)-0.0
if mns then
num = -num
end
return sax_number(num)
end
-- `0(\.[0-9]*)?([eE][+-]?[0-9]*)?`
local function f_zro(mns)
local postmp = pos
local num
local c = byte(json, postmp)
if c == 0x2E then -- is this `.`?
num = match(json, '^.[0-9]*', pos) -- skipping 0
local numlen = #num
if numlen == 1 then
pos = pos-1
return generic_number(mns)
end
postmp = pos + numlen
c = byte(json, postmp)
end
if c == 0x45 or c == 0x65 then -- is this e or E?
local numexp = match(json, '^[^eE]*[eE][-+]?[0-9]+', pos)
if not numexp then
pos = pos-1
return generic_number(mns)
end
if num then -- since `0e.*` is always 0.0, ignore those
num = numexp
end
postmp = pos + #numexp
end
if postmp > jsonlen then
pos = pos-1
return generic_number(mns)
end
pos = postmp
if num then
num = fixedtonumber(num)
else
num = 0.0
end
if mns then
num = -num
end
return sax_number(num)
end
-- `[1-9][0-9]*(\.[0-9]*)?([eE][+-]?[0-9]*)?`
local function f_num(mns)
pos = pos-1
local num = match(json, '^.[0-9]*%.?[0-9]*', pos)
if byte(num, -1) == 0x2E then
return generic_number(mns)
end
local postmp = pos + #num
local c = byte(json, postmp)
if c == 0x45 or c == 0x65 then -- e or E?
num = match(json, '^[^eE]*[eE][-+]?[0-9]+', pos)
if not num then
return generic_number(mns)
end
postmp = pos + #num
end
if postmp > jsonlen then
return generic_number(mns)
end
pos = postmp
num = fixedtonumber(num)-0.0
if mns then
num = -num
end
return sax_number(num)
end
-- skip minus sign
local function f_mns()
local c = byte(json, pos) or tellc()
if c then
pos = pos+1
if c > 0x30 then
if c < 0x3A then
return f_num(true)
end
else
if c > 0x2F then
return f_zro(true)
end
end
end
parseerror("invalid number")
end
--[[
Strings
--]]
local f_str_lib = genstrlib(parseerror)
local f_str_surrogateok = f_str_lib.surrogateok -- whether codepoints for surrogate pair are correctly paired
local f_str_subst = f_str_lib.subst -- the function passed to gsub that interprets escapes
local function f_str(iskey)
local pos2 = pos
local newpos
local str = ''
local bs
while true do
while true do -- search '\' or '"'
newpos = find(json, '[\\"]', pos2)
if newpos then
break
end
str = str .. sub(json, pos, jsonlen)
if pos2 == jsonlen+2 then
pos2 = 2
else
pos2 = 1
end
jsonnxt()
end
if byte(json, newpos) == 0x22 then -- break if '"'
break
end
pos2 = newpos+2 -- skip '\<char>'
bs = true -- remember that backslash occurs
end
str = str .. sub(json, pos, newpos-1)
pos = newpos+1
if bs then -- check if backslash occurs
str = gsub(str, '\\(.)([^\\]*)', f_str_subst) -- interpret escapes
if not f_str_surrogateok() then
parseerror("invalid surrogate pair")
end
end
if iskey then
return sax_key(str)
end
return sax_string(str)
end
--[[
Arrays, Objects
--]]
-- arrays
local function f_ary()
sax_startarray()
spaces()
if byte(json, pos) ~= 0x5D then -- check the closing bracket ']', that consists an empty array
local newpos
while true do
f = dispatcher[byte(json, pos)] -- parse value
pos = pos+1
f()
f, newpos = find(json, '^[ \n\r\t]*,[ \n\r\t]*', pos) -- check comma
if not newpos then
f, newpos = find(json, '^[ \n\r\t]*%]', pos) -- check closing bracket
if newpos then
pos = newpos
break
end
spaces() -- since the current chunk can be ended, skip spaces toward following chunks
local c = byte(json, pos)
if c == 0x2C then -- check comma again
pos = pos+1
spaces()
newpos = pos-1
elseif c == 0x5D then -- check closing bracket again
break
else
parseerror("no closing bracket of an array")
end
end
pos = newpos+1
if pos > jsonlen then
spaces()
end
end
end
pos = pos+1
return sax_endarray()
end
-- objects
local function f_obj()
sax_startobject()
spaces()
if byte(json, pos) ~= 0x7D then -- check the closing bracket `}`, that consists an empty object
local newpos
while true do
if byte(json, pos) ~= 0x22 then
parseerror("not key")
end
pos = pos+1
f_str(true)
f, newpos = find(json, '^[ \n\r\t]*:[ \n\r\t]*', pos) -- check colon
if not newpos then
spaces() -- since the current chunk can be ended, skip spaces toward following chunks
if byte(json, pos) ~= 0x3A then -- check colon again
parseerror("no colon after a key")
end
pos = pos+1
spaces()
newpos = pos-1
end
pos = newpos+1
if pos > jsonlen then
spaces()
end
f = dispatcher[byte(json, pos)] -- parse value
pos = pos+1
f()
f, newpos = find(json, '^[ \n\r\t]*,[ \n\r\t]*', pos) -- check comma
if not newpos then
f, newpos = find(json, '^[ \n\r\t]*}', pos) -- check closing bracket
if newpos then
pos = newpos
break
end
spaces() -- since the current chunk can be ended, skip spaces toward following chunks
local c = byte(json, pos)
if c == 0x2C then -- check comma again
pos = pos+1
spaces()
newpos = pos-1
elseif c == 0x7D then -- check closing bracket again
break
else
parseerror("no closing bracket of an object")
end
end
pos = newpos+1
if pos > jsonlen then
spaces()
end
end
end
pos = pos+1
return sax_endobject()
end
--[[
The jump table to dispatch a parser for a value, indexed by the code of the value's first char.
Key should be non-nil.
--]]
dispatcher = {
f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err,
f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err,
f_err, f_err, f_str, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_mns, f_err, f_err,
f_zro, f_num, f_num, f_num, f_num, f_num, f_num, f_num, f_num, f_num, f_err, f_err, f_err, f_err, f_err, f_err,
f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err,
f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_ary, f_err, f_err, f_err, f_err,
f_err, f_err, f_err, f_err, f_err, f_err, f_fls, f_err, f_err, f_err, f_err, f_err, f_err, f_err, f_nul, f_err,
f_err, f_err, f_err, f_err, f_tru, f_err, f_err, f_err, f_err, f_err, f_err, f_obj, f_err, f_err, f_err, f_err,
}
dispatcher[0] = f_err
--[[
public funcitons
--]]
local function run()
spaces()
f = dispatcher[byte(json, pos)]
pos = pos+1
f()
end
local function read(n)
if n < 0 then
error("the argument must be non-negative")
end
local pos2 = (pos-1) + n
local str = sub(json, pos, pos2)
while pos2 > jsonlen and jsonlen ~= 0 do
jsonnxt()
pos2 = pos2 - (jsonlen - (pos-1))
str = str .. sub(json, pos, pos2)
end
if jsonlen ~= 0 then
pos = pos2+1
end
return str
end
local function tellpos()
return acc + pos
end
return {
run = run,
tryc = tryc,
read = read,
tellpos = tellpos,
}
end
local function newfileparser(fn, saxtbl)
local fp = io.open(fn)
local function gen()
local s
if fp then
s = fp:read(8192)
if not s then
fp:close()
fp = nil
end
end
return s
end
return newparser(gen, saxtbl)
end
return {
newparser = newparser,
newfileparser = newfileparser
}