From 1762bae45c50bf172313c08c004b8b22f1c48724 Mon Sep 17 00:00:00 2001
From: Philipp Gesang <philipp.gesang@alumni.uni-heidelberg.de>
Date: Fri, 19 Oct 2012 22:30:20 +0200
Subject: add util-jsn

---
 README               |   1 +
 lualibs-util-jsn.lua | 145 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lualibs.dtx          |   5 +-
 3 files changed, 149 insertions(+), 2 deletions(-)
 create mode 100644 lualibs-util-jsn.lua

diff --git a/README b/README
index b23d37f..57bd351 100644
--- a/README
+++ b/README
@@ -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
-- 
cgit v1.2.3