diff options
| -rw-r--r-- | README | 1 | ||||
| -rw-r--r-- | lualibs-util-jsn.lua | 145 | ||||
| -rw-r--r-- | lualibs.dtx | 5 | 
3 files changed, 149 insertions, 2 deletions
@@ -47,6 +47,7 @@ Source files:      lualibs-unicode.lua      lualibs-url.lua      lualibs-util-dim.lua +    lualibs-util-jsn.lua      lualibs-util-lua.lua      lualibs-util-mrg.lua      lualibs-util-sto.lua diff --git a/lualibs-util-jsn.lua b/lualibs-util-jsn.lua new file mode 100644 index 0000000..7493f10 --- /dev/null +++ b/lualibs-util-jsn.lua @@ -0,0 +1,145 @@ +if not modules then modules = { } end modules ['util-jsn'] = { +    version   = 1.001, +    comment   = "companion to m-json.mkiv", +    author    = "Hans Hagen, PRAGMA-ADE, Hasselt NL", +    copyright = "PRAGMA ADE / ConTeXt Development Team", +    license   = "see context related readme files" +} + +-- Of course we could make a nice complete parser with proper error messages but +-- as json is generated programmatically errors are systematic and we can assume +-- a correct stream. If not, we have some fatal error anyway. So, we can just rely +-- on strings being strings (apart from the unicode escape which is not in 5.1) and +-- as we first catch known types we just assume that anything else is a number. + +local P, V, R, S, C, Cc, Cs, Ct, Cf, Cg = lpeg.P, lpeg.V, lpeg.R, lpeg.S, lpeg.C, lpeg.Cc, lpeg.Cs, lpeg.Ct, lpeg.Cf, lpeg.Cg +local lpegmatch = lpeg.match +local format = string.format +local utfchar = utf.char +local concat = table.concat + +local tonumber, tostring, rawset, type = tonumber, tostring, rawset, type + +local json      = utilities.json or { } +utilities.json  = json + +-- moduledata      = moduledata or { } +-- moduledata.json = json + +-- \\ \/ \b \f \n \r \t \uHHHH + +local lbrace     = P("{") +local rbrace     = P("}") +local lparent    = P("[") +local rparent    = P("]") +local comma      = P(",") +local colon      = P(":") +local dquote     = P('"') + +local whitespace = lpeg.patterns.whitespace +local optionalws = whitespace^0 + +local escape     = C(P("\\u") / "0x" * S("09","AF","af")) / function(s) return utfchar(tonumber(s)) end +local jstring    = dquote * Cs((escape + (1-dquote))^0) * dquote +local jtrue      = P("true")  * Cc(true) +local jfalse     = P("false") * Cc(false) +local jnull      = P("null")  * Cc(nil) +local jnumber    = (1-whitespace-rparent-rbrace-comma)^1 / tonumber + +local key        = jstring + +local jsonconverter = { "value", +    object   = lbrace * Cf(Ct("") * V("pair") * (comma * V("pair"))^0,rawset) * rbrace, +    pair     = Cg(optionalws * key * optionalws * colon * V("value")), +    array    = Ct(lparent * V("value") * (comma * V("value"))^0 * rparent), +    value    = optionalws * (jstring + V("object") + V("array") + jtrue + jfalse + jnull + jnumber + #rparent) * optionalws, +} + +-- local jsonconverter = { "value", +--     object   = lbrace * Cf(Ct("") * V("pair") * (comma * V("pair"))^0,rawset) * rbrace, +--     pair     = Cg(optionalws * V("string") * optionalws * colon * V("value")), +--     array    = Ct(lparent * V("value") * (comma * V("value"))^0 * rparent), +--     string   = jstring, +--     value    = optionalws * (V("string") + V("object") + V("array") + jtrue + jfalse + jnull + jnumber) * optionalws, +-- } + +-- lpeg.print(jsonconverter) -- size 181 + +function json.tolua(str) +    return lpegmatch(jsonconverter,str) +end + +local function tojson(value,t) -- we could optimize #t +    local kind = type(value) +    if kind == "table" then +        local done = false +        local size = #value +        if size == 0 then +            for k, v in next, value do +                if done then +                    t[#t+1] = "," +                else +                    t[#t+1] = "{" +                    done = true +                end +                t[#t+1] = format("%q:",k) +                tojson(v,t) +            end +            if done then +                t[#t+1] = "}" +            else +                t[#t+1] = "{}" +            end +        elseif size == 1 then +            -- we can optimize for non tables +            t[#t+1] = "[" +            tojson(value[1],t) +            t[#t+1] = "]" +        else +            for i=1,size do +                if done then +                    t[#t+1] = "," +                else +                    t[#t+1] = "[" +                    done = true +                end +                tojson(value[i],t) +            end +            t[#t+1] = "]" +        end +    elseif kind == "string"  then +        t[#t+1] = format("%q",value) +    elseif kind == "number" then +        t[#t+1] = value +    elseif kind == "boolean" then +        t[#t+1] = tostring(value) +    end +    return t +end + +function json.tostring(value) +    -- todo optimize for non table +    local kind = type(value) +    if kind == "table" then +        return concat(tojson(value,{}),"") +    elseif kind == "string" or kind == "number" then +        return value +    else +        return tostring(value) +    end +end + +-- local tmp = [[ { "a" : true, "b" : [ 123 , 456E-10, { "a" : true, "b" : [ 123 , 456 ] } ] } ]] + +-- tmp = json.tolua(tmp) +-- inspect(tmp) +-- tmp = json.tostring(tmp) +-- inspect(tmp) +-- tmp = json.tolua(tmp) +-- inspect(tmp) +-- tmp = json.tostring(tmp) +-- inspect(tmp) + +-- inspect(json.tostring(true)) + +return json diff --git a/lualibs.dtx b/lualibs.dtx index 8f0d19c..cc3142a 100644 --- a/lualibs.dtx +++ b/lualibs.dtx @@ -193,12 +193,13 @@ require("lualibs-dir")  require("lualibs-unicode")  require("lualibs-url")  require("lualibs-set") -require("lualibs-util-dim")  require("lualibs-util-lua") -require("lualibs-util-mrg")  require("lualibs-util-sto") +require("lualibs-util-mrg") +require("lualibs-util-dim")  require("lualibs-util-str")  require("lualibs-util-tab") +require("lualibs-util-jsn")  %    \end{macrocode}  %  % \iffalse  | 
