From 748be39b9f88d15159ab8879ff8e9b88e4b8718a Mon Sep 17 00:00:00 2001
From: Hans Hagen <pragma@wxs.nl>
Date: Sun, 10 Mar 2013 14:36:00 +0100
Subject: beta 2013.03.10 14:36

---
 tex/context/base/cldf-ini.lua                      |  55 ++--
 tex/context/base/colo-ini.lua                      |  35 +--
 tex/context/base/context-version.pdf               | Bin 4131 -> 4139 bytes
 tex/context/base/context-version.png               | Bin 39754 -> 40397 bytes
 tex/context/base/file-job.lua                      |  13 +-
 tex/context/base/font-syn.lua                      |   3 +-
 tex/context/base/grph-inc.mkiv                     |   4 +
 tex/context/base/l-lpeg.lua                        |  21 ++
 tex/context/base/l-lua.lua                         |   1 +
 tex/context/base/l-table.lua                       | 325 ++++++++++++++++++++
 tex/context/base/luat-lib.mkiv                     |   2 +-
 tex/context/base/lxml-lpt.lua                      |   7 +-
 tex/context/base/meta-ini.lua                      |   6 +-
 tex/context/base/mlib-pdf.lua                      |  41 +--
 tex/context/base/mlib-pps.lua                      | 108 +------
 tex/context/base/mlib-run.lua                      |   4 +-
 tex/context/base/status-files.pdf                  | Bin 24806 -> 24788 bytes
 tex/context/base/status-lua.pdf                    | Bin 209211 -> 209468 bytes
 tex/context/base/typo-del.mkiv                     |   1 +
 tex/context/base/typo-prc.lua                      |   4 +-
 tex/context/base/util-fmt.lua                      |  22 +-
 tex/context/base/util-lua.lua                      |  11 +
 tex/context/base/util-seq.lua                      |   2 +-
 tex/context/base/util-str.lua                      | 332 ++++++++++++++++-----
 tex/context/base/util-tab.lua                      | 155 ++++++++--
 tex/generic/context/luatex/luatex-fonts-merged.lua |  11 +-
 26 files changed, 854 insertions(+), 309 deletions(-)

(limited to 'tex')

diff --git a/tex/context/base/cldf-ini.lua b/tex/context/base/cldf-ini.lua
index e3b7ed82a..f4b8ece4c 100644
--- a/tex/context/base/cldf-ini.lua
+++ b/tex/context/base/cldf-ini.lua
@@ -32,6 +32,7 @@ local format, gsub, validstring = string.format, string.gsub, string.valid
 local next, type, tostring, tonumber, setmetatable = next, type, tostring, tonumber, setmetatable
 local insert, remove, concat = table.insert, table.remove, table.concat
 local lpegmatch, lpegC, lpegS, lpegP, lpegCc, patterns = lpeg.match, lpeg.C, lpeg.S, lpeg.P, lpeg.Cc, lpeg.patterns
+local formatters = string.formatters -- using formatteds is slower in this case
 
 local texsprint         = tex.sprint
 local textprint         = tex.tprint
@@ -533,7 +534,7 @@ local function caller(parent,f,a,...)
         local typ = type(f)
         if typ == "string" then
             if a then
-                flush(contentcatcodes,format(f,a,...)) -- was currentcatcodes
+                flush(contentcatcodes,formatters[f](a,...)) -- was currentcatcodes
             elseif processlines and lpegmatch(containseol,f) then
                 local flushlines = parent.__flushlines or flushlines
                 flushlines(f)
@@ -606,13 +607,13 @@ end
 function context.fprint(catcodes,fmt,first,...)
     if type(catcodes) == "number" then
         if first then
-            flush(catcodes,format(fmt,first,...))
+            flush(catcodes,formatters[fmt](first,...))
         else
             flush(catcodes,fmt)
         end
     else
         if fmt then
-            flush(format(catcodes,fmt,first,...))
+            flush(formatters[catcodes](fmt,first,...))
         else
             flush(catcodes)
         end
@@ -621,29 +622,29 @@ end
 
 function tex.fprint(fmt,first,...) -- goodie
     if first then
-        flush(currentcatcodes,format(fmt,first,...))
+        flush(currentcatcodes,formatters[fmt](first,...))
     else
         flush(currentcatcodes,fmt)
     end
 end
 
-local formatters = string.formatters
+-- function context.formatted(catcodes,fmt,first,...) -- no longer to be used ... context(...) now uses formatted
+--     if type(catcodes) == "number" then             -- and this was just a temporary helper that will go away
+--         if first then
+--             flush(catcodes,formatters[fmt](first,...))
+--         else
+--             flush(catcodes,fmt)
+--         end
+--     else
+--         if fmt then
+--             flush(formatters[catcodes](fmt,first,...))
+--         else
+--             flush(catcodes)
+--         end
+--     end
+-- end
 
-function context.formatted(catcodes,fmt,first,...)
-    if type(catcodes) == "number" then
-        if first then
-            flush(catcodes,formatters[fmt](first,...))
-        else
-            flush(catcodes,fmt)
-        end
-    else
-        if fmt then
-            flush(formatters[catcodes](fmt,first,...))
-        else
-            flush(catcodes)
-        end
-    end
-end
+context.formatted = context.fprint
 
 -- logging
 
@@ -697,7 +698,7 @@ local traced = function(normal,one,two,...)
         normal(one,two,...)
         local catcodes = type(one) == "number" and one
         local arguments = catcodes and { two, ... } or { one, two, ... }
-        local collapsed, c = { format("f : %s : ", catcodes or '-') }, 1
+        local collapsed, c = { formatters["f : %s : "](catcodes or '-') }, 1
         for i=1,#arguments do
             local argument = arguments[i]
             local argtype = type(argument)
@@ -707,7 +708,7 @@ local traced = function(normal,one,two,...)
             elseif argtype == "number" then
                 collapsed[c] = argument
             else
-                collapsed[c] = format("<<%s>>",tostring(argument))
+                collapsed[c] = formatters["<<%S>>"](argument)
             end
         end
         currenttrace(concat(collapsed))
@@ -716,11 +717,11 @@ local traced = function(normal,one,two,...)
         normal(one)
         local argtype = type(one)
         if argtype == "string" then
-            currenttrace(format("f : - : %s",lpegmatch(visualizer,one)))
+            currenttrace(formatters["f : - : %s"](lpegmatch(visualizer,one)))
         elseif argtype == "number" then
-            currenttrace(format("f : - : %s",one))
+            currenttrace(formatters["f : - : %s"](one))
         else
-            currenttrace(format("f : - : <<%s>>",tostring(one)))
+            currenttrace(formatters["f : - : <<%S>>"](one))
         end
     end
 end
@@ -924,7 +925,7 @@ local function caller(parent,f,a,...)
         local typ = type(f)
         if typ == "string" then
             if a then
-                flush(currentcatcodes,mpdrawing,"{",format(f,a,...),"}")
+                flush(currentcatcodes,mpdrawing,"{",formatters[f](a,...),"}")
             else
                 flush(currentcatcodes,mpdrawing,"{",f,"}")
             end
@@ -962,7 +963,7 @@ function metafun.stop()
 end
 
 function metafun.color(name)
-    return format([[\MPcolor{%s}]],name)
+    return formatters[ [[\MPcolor{%s}]] ](name)
 end
 
 -- metafun.delayed
diff --git a/tex/context/base/colo-ini.lua b/tex/context/base/colo-ini.lua
index 1a994b567..29cce70f0 100644
--- a/tex/context/base/colo-ini.lua
+++ b/tex/context/base/colo-ini.lua
@@ -11,6 +11,7 @@ local concat, insert, remove = table.concat, table.insert, table.remove
 local format, gmatch, gsub, lower, match, find = string.format, string.gmatch, string.gsub, string.lower, string.match, string.find
 local P, R, C, Cc = lpeg.P, lpeg.R, lpeg.C, lpeg.Cc
 local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
+local formatters = string.formatters
 
 local trace_define = false  trackers.register("colors.define",function(v) trace_define = v end)
 
@@ -470,38 +471,32 @@ local function mpcolor(model,ca,ta,default)
         model = forcedmodel(model)
         if tv then
             if model == 2 then
-                return format("transparent(%s,%s,(%s,%s,%s))",tv[1],tv[2],cv[3],cv[4],cv[5])
+                return formatters["transparent(%s,%s,(%s,%s,%s))"](tv[1],tv[2],cv[3],cv[4],cv[5])
             elseif model == 3 then
-                return format("transparent(%s,%s,(%s,%s,%s))",tv[1],tv[2],cv[3],cv[4],cv[5])
+                return formatters["transparent(%s,%s,(%s,%s,%s))"](tv[1],tv[2],cv[3],cv[4],cv[5])
             elseif model == 4 then
-                return format("transparent(%s,%s,cmyk(%s,%s,%s,%s))",tv[1],tv[2],cv[6],cv[7],cv[8],cv[9])
+                return formatters["transparent(%s,%s,cmyk(%s,%s,%s,%s))"](tv[1],tv[2],cv[6],cv[7],cv[8],cv[9])
             elseif model == 5 then
-                return format('transparent(%s,%s,multitonecolor("%s",%s,"%s","%s"))',tv[1],tv[2],cv[10],cv[11],cv[12],cv[13])
-            else
-                return format("transparent(%s,%s,(%s,%s,%s))",tv[1],tv[2],cv[3],cv[4],cv[5])
--- this will become (see ** in meta-ini.mkiv)
---
--- return format("transparent(%s,%s,(%s))",tv[1],tv[2],cv[2])
+                return formatters['transparent(%s,%s,multitonecolor("%s",%s,"%s","%s"))'](tv[1],tv[2],cv[10],cv[11],cv[12],cv[13])
+            else -- see ** in meta-ini.mkiv: return formatters["transparent(%s,%s,(%s))"](tv[1],tv[2],cv[2])
+                return formatters["transparent(%s,%s,(%s,%s,%s))"](tv[1],tv[2],cv[3],cv[4],cv[5])
             end
         else
             if model == 2 then
-                return format("(%s,%s,%s)",cv[3],cv[4],cv[5])
+                return formatters["(%s,%s,%s)"](cv[3],cv[4],cv[5])
             elseif model == 3 then
-                return format("(%s,%s,%s)",cv[3],cv[4],cv[5])
+                return formatters["(%s,%s,%s)"](cv[3],cv[4],cv[5])
             elseif model == 4 then
-                return format("cmyk(%s,%s,%s,%s)",cv[6],cv[7],cv[8],cv[9])
+                return formatters["cmyk(%s,%s,%s,%s)"](cv[6],cv[7],cv[8],cv[9])
             elseif model == 5 then
-                return format('multitonecolor("%s",%s,"%s","%s")',cv[10],cv[11],cv[12],cv[13])
-            else
-                return format("(%s,%s,%s)",cv[3],cv[4],cv[5])
--- this will become (see ** in meta-ini.mkiv)
---
--- return format("%s",(cv[2]))
+                return formatters['multitonecolor("%s",%s,"%s","%s")'](cv[10],cv[11],cv[12],cv[13])
+            else -- see ** in meta-ini.mkiv: return formatters["%s"]((cv[2]))
+                return formatters["(%s,%s,%s)"](cv[3],cv[4],cv[5])
             end
         end
     else
         default = default or 0 -- rgb !
-        return format("(%s,%s,%s)",default,default,default)
+        return formatters["(%s,%s,%s)"](default,default,default)
     end
 end
 
@@ -510,7 +505,7 @@ local function mpnamedcolor(name)
 end
 
 local function mpoptions(model,ca,ta,default) -- will move to mlib-col
-    return format("withcolor %s",mpcolor(model,ca,ta,default))
+    return formatters["withcolor %s"](mpcolor(model,ca,ta,default))
 end
 
 colors.mpcolor      = mpcolor
diff --git a/tex/context/base/context-version.pdf b/tex/context/base/context-version.pdf
index 398846f55..bbc4ff28d 100644
Binary files a/tex/context/base/context-version.pdf and b/tex/context/base/context-version.pdf differ
diff --git a/tex/context/base/context-version.png b/tex/context/base/context-version.png
index 3be5fcc66..43823ceb6 100644
Binary files a/tex/context/base/context-version.png and b/tex/context/base/context-version.png differ
diff --git a/tex/context/base/file-job.lua b/tex/context/base/file-job.lua
index 533103ec6..b5c4b2b05 100644
--- a/tex/context/base/file-job.lua
+++ b/tex/context/base/file-job.lua
@@ -823,15 +823,24 @@ function commands.getcommandline() -- has to happen at the tex end in order to e
         inputfile = basename(inputfile)
     end
 
+    local kindofrun  = arguments.kindofrun
+    local currentrun = arguments.maxnofruns
+    local maxnofruns = arguments.currentrun
+
     context.setupsystem {
         [constants.directory] = validstring(arguments.setuppath),
         [constants.inputfile] = inputfile,
         [constants.file]      = validstring(arguments.result),
         [constants.random]    = validstring(arguments.randomseed),
-        [constants.n]         = validstring(arguments.kindofrun),
-        [constants.m]         = validstring(arguments.currentrun),
+        -- old:
+        [constants.n]         = validstring(kindofrun),
+        [constants.m]         = validstring(currentrun),
     }
 
+    environment.kindofrun  = tonumber(kindofrun)  or 0
+    environment.maxnofruns = tonumber(maxnofruns) or 0
+    environment.currentrun = tonumber(currentrun) or 0
+
     if validstring(arguments.arguments) then
         context.setupenv { arguments.arguments }
     end
diff --git a/tex/context/base/font-syn.lua b/tex/context/base/font-syn.lua
index 3f90da91b..63564f4f9 100644
--- a/tex/context/base/font-syn.lua
+++ b/tex/context/base/font-syn.lua
@@ -15,6 +15,7 @@ local concat, sort, format = table.concat, table.sort, string.format
 local serialize = table.serialize
 local lpegmatch = lpeg.match
 local unpack = unpack or table.unpack
+local formatters = string.formatters
 
 local allocate = utilities.storage.allocate
 local sparse   = utilities.storage.sparse
@@ -581,7 +582,7 @@ local function checkduplicate(where) -- fails on "Romantik" but that's a border
         for _, m in next, mapping do
             for k, v in next, m do
                 local s = specifications[v]
-                local hash = format("%s-%s-%s-%s-%s",s.familyname,s.weight or "*",s.style or "*",s.width or "*",s.variant or "*")
+                local hash = formatters["%s-%s-%s-%s-%s"](s.familyname,s.weight or "*",s.style or "*",s.width or "*",s.variant or "*")
                 local h = loaded[hash]
                 if h then
                     local ok = true
diff --git a/tex/context/base/grph-inc.mkiv b/tex/context/base/grph-inc.mkiv
index 54d024b85..8557bbb0b 100644
--- a/tex/context/base/grph-inc.mkiv
+++ b/tex/context/base/grph-inc.mkiv
@@ -567,6 +567,10 @@
   {\startfoundexternalfigure\defaultfigurewidth\defaultfigureheight
    \stopfoundexternalfigure}
 
+% \doifmodeelse{*\v!last}
+%   {\settrue \c_grph_include_flush}
+%   {\setfalse\c_grph_include_flush}%
+
 \def\grph_include_finalize
   {\global\setbox\foundexternalfigure\vbox
      {\ifcase\figurestatus
diff --git a/tex/context/base/l-lpeg.lua b/tex/context/base/l-lpeg.lua
index 9bd9706bf..cfc26a5a5 100644
--- a/tex/context/base/l-lpeg.lua
+++ b/tex/context/base/l-lpeg.lua
@@ -829,3 +829,24 @@ end
 -- local s = "12" .. string.rep("AB",20) .. "34" .. string.rep("AB",30) .. "56"
 -- inspect(p)
 -- print(lpeg.match(p,s))
+
+-- moved here (before util-str)
+
+local digit         = R("09")
+local period        = P(".")
+local zero          = P("0")
+local trailingzeros = zero^0 * -digit -- suggested by Roberto R
+local case_1        = period * trailingzeros / ""
+local case_2        = period * (digit - trailingzeros)^1 * (trailingzeros / "")
+local number        = digit^1 * (case_1 + case_2)
+local stripper      = Cs((number + 1)^0)
+
+lpeg.patterns.stripzeros = stripper
+
+-- local sample = "bla 11.00 bla 11 bla 0.1100 bla 1.00100 bla 0.00 bla 0.001 bla 1.1100 bla 0.100100100 bla 0.00100100100"
+-- collectgarbage("collect")
+-- str = string.rep(sample,10000)
+-- local ts = os.clock()
+-- lpegmatch(stripper,str)
+-- print(#str, os.clock()-ts, lpegmatch(stripper,sample))
+
diff --git a/tex/context/base/l-lua.lua b/tex/context/base/l-lua.lua
index 5863960b3..9ac8192b0 100644
--- a/tex/context/base/l-lua.lua
+++ b/tex/context/base/l-lua.lua
@@ -12,6 +12,7 @@ local major, minor = string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$")
 
 _MAJORVERSION = tonumber(major) or 5
 _MINORVERSION = tonumber(minor) or 1
+_LUAVERSION   = _MAJORVERSION + _MINORVERSION/10
 
 -- lpeg
 
diff --git a/tex/context/base/l-table.lua b/tex/context/base/l-table.lua
index 92ef02503..c33cd195b 100644
--- a/tex/context/base/l-table.lua
+++ b/tex/context/base/l-table.lua
@@ -394,6 +394,8 @@ end
 
 -- todo: %g faster on numbers than %s
 
+-- we can speed this up with repeaters and formatters (is indeed faster)
+
 local propername = patterns.propername -- was find(name,"^%a[%w%_]*$")
 
 local function dummy() end
@@ -711,6 +713,322 @@ local function serialize(_handle,root,name,specification) -- handle wins
     handle("}")
 end
 
+--    -- This is some 20% faster than using format (because formatters are much faster) but
+--    -- of course, inlining the format using .. is then again faster .. anyway, as we do
+--    -- some pretty printing as well there is not that much to gain unless we make a 'fast'
+--    -- ugly variant as well. But, we would have to move the formatter to l-string then.
+
+--    local formatters = string.formatters
+
+--    local function do_serialize(root,name,level,indexed)
+--        if level > 0 then
+--            if indexed then
+--                handle(formatters["%w{"](level))
+--            else
+--                local tn = type(name)
+--                if tn == "number" then
+--                    if hexify then
+--                        handle(formatters["%w[%04H]={"](level,name))
+--                    else
+--                        handle(formatters["%w[%s]={"](level,name))
+--                    end
+--                elseif tn == "string" then
+--                    if noquotes and not reserved[name] and lpegmatch(propername,name) then
+--                        handle(formatters["%w%s={"](level,name))
+--                    else
+--                        handle(formatters["%w[%q]={"](level,name))
+--                    end
+--                elseif tn == "boolean" then
+--                    handle(formatters["%w[%S]={"](level,name))
+--                else
+--                    handle(formatters["%w{"](level))
+--                end
+--            end
+--        end
+--        -- we could check for k (index) being number (cardinal)
+--        if root and next(root) then
+--         -- local first, last = nil, 0 -- #root cannot be trusted here (will be ok in 5.2 when ipairs is gone)
+--         -- if compact then
+--         --     -- NOT: for k=1,#root do (we need to quit at nil)
+--         --     for k,v in ipairs(root) do -- can we use next?
+--         --         if not first then first = k end
+--         --         last = last + 1
+--         --     end
+--         -- end
+--            local first, last = nil, 0
+--            if compact then
+--                last = #root
+--                for k=1,last do
+--                    if root[k] == nil then
+--                        last = k - 1
+--                        break
+--                    end
+--                end
+--                if last > 0 then
+--                    first = 1
+--                end
+--            end
+--            local sk = sortedkeys(root)
+--            for i=1,#sk do
+--                local k = sk[i]
+--                local v = root[k]
+--                --~ if v == root then
+--                    -- circular
+--                --~ else
+--                local t, tk = type(v), type(k)
+--                if compact and first and tk == "number" and k >= first and k <= last then
+--                    if t == "number" then
+--                        if hexify then
+--                            handle(formatters["%w %04H,"](level,v))
+--                        else
+--                            handle(formatters["%w %s,"](level,v)) -- %.99g
+--                        end
+--                    elseif t == "string" then
+--                        if reduce and tonumber(v) then
+--                            handle(formatters["%w %s,"](level,v))
+--                        else
+--                            handle(formatters["%w %q,"](level,v))
+--                        end
+--                    elseif t == "table" then
+--                        if not next(v) then
+--                            handle(formatters["%w {},"](level))
+--                        elseif inline then -- and #t > 0
+--                            local st = simple_table(v)
+--                            if st then
+--                                handle(formatters["%w { %, t },"](level,st))
+--                            else
+--                                do_serialize(v,k,level+1,true)
+--                            end
+--                        else
+--                            do_serialize(v,k,level+1,true)
+--                        end
+--                    elseif t == "boolean" then
+--                        handle(formatters["%w %S,"](level,v))
+--                    elseif t == "function" then
+--                        if functions then
+--                            handle(formatters['%w load(%q),'](level,dump(v)))
+--                        else
+--                            handle(formatters['%w "function",'](level))
+--                        end
+--                    else
+--                        handle(formatters["%w %Q,"](level,v))
+--                    end
+--                elseif k == "__p__" then -- parent
+--                    if false then
+--                        handle(formatters["%w __p__=nil,"](level))
+--                    end
+--                elseif t == "number" then
+--                    if tk == "number" then
+--                        if hexify then
+--                            handle(formatters["%w [%04H]=%04H,"](level,k,v))
+--                        else
+--                            handle(formatters["%w [%s]=%s,"](level,k,v)) -- %.99g
+--                        end
+--                    elseif tk == "boolean" then
+--                        if hexify then
+--                            handle(formatters["%w [%S]=%04H,"](level,k,v))
+--                        else
+--                            handle(formatters["%w [%S]=%s,"](level,k,v)) -- %.99g
+--                        end
+--                    elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+--                        if hexify then
+--                            handle(formatters["%w %s=%04H,"](level,k,v))
+--                        else
+--                            handle(formatters["%w %s=%s,"](level,k,v)) -- %.99g
+--                        end
+--                    else
+--                        if hexify then
+--                            handle(formatters["%w [%q]=%04H,"](level,k,v))
+--                        else
+--                            handle(formatters["%w [%q]=%s,"](level,k,v)) -- %.99g
+--                        end
+--                    end
+--                elseif t == "string" then
+--                    if reduce and tonumber(v) then
+--                        if tk == "number" then
+--                            if hexify then
+--                                handle(formatters["%w [%04H]=%s,"](level,k,v))
+--                            else
+--                                handle(formatters["%w [%s]=%s,"](level,k,v))
+--                            end
+--                        elseif tk == "boolean" then
+--                            handle(formatters["%w [%S]=%s,"](level,k,v))
+--                        elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+--                            handle(formatters["%w %s=%s,"](level,k,v))
+--                        else
+--                            handle(formatters["%w [%q]=%s,"](level,k,v))
+--                        end
+--                    else
+--                        if tk == "number" then
+--                            if hexify then
+--                                handle(formatters["%w [%04H]=%q,"](level,k,v))
+--                            else
+--                                handle(formatters["%w [%s]=%q,"](level,k,v))
+--                            end
+--                        elseif tk == "boolean" then
+--                            handle(formatters["%w [%S]=%q,"](level,k,v))
+--                        elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+--                            handle(formatters["%w %s=%q,"](level,k,v))
+--                        else
+--                            handle(formatters["%w [%q]=%q,"](level,k,v))
+--                        end
+--                    end
+--                elseif t == "table" then
+--                    if not next(v) then
+--                        if tk == "number" then
+--                            if hexify then
+--                                handle(formatters["%w [%04H]={},"](level,k))
+--                            else
+--                                handle(formatters["%w [%s]={},"](level,k))
+--                            end
+--                        elseif tk == "boolean" then
+--                            handle(formatters["%w [%S]={},"](level,k))
+--                        elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+--                            handle(formatters["%w %s={},"](level,k))
+--                        else
+--                            handle(formatters["%w [%q]={},"](level,k))
+--                        end
+--                    elseif inline then
+--                        local st = simple_table(v)
+--                        if st then
+--                            if tk == "number" then
+--                                if hexify then
+--                                    handle(formatters["%w [%04H]={ %, t },"](level,k,st))
+--                                else
+--                                    handle(formatters["%w [%s]={ %, t },"](level,k,st))
+--                                end
+--                            elseif tk == "boolean" then
+--                                handle(formatters["%w [%S]={ %, t },"](level,k,st))
+--                            elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+--                                handle(formatters["%w %s={ %, t },"](level,k,st))
+--                            else
+--                                handle(formatters["%w [%q]={ %, t },"](level,k,st))
+--                            end
+--                        else
+--                            do_serialize(v,k,level+1)
+--                        end
+--                    else
+--                        do_serialize(v,k,level+1)
+--                    end
+--                elseif t == "boolean" then
+--                    if tk == "number" then
+--                        if hexify then
+--                            handle(formatters["%w [%04H]=%S,"](level,k,v))
+--                        else
+--                            handle(formatters["%w [%s]=%S,"](level,k,v))
+--                        end
+--                    elseif tk == "boolean" then
+--                        handle(formatters["%w [%S]=%S,"](level,k,v))
+--                    elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+--                        handle(formatters["%w %s=%S,"](level,k,v))
+--                    else
+--                        handle(formatters["%w [%q]=%S,"](level,k,v))
+--                    end
+--                elseif t == "function" then
+--                    if functions then
+--                        local f = getinfo(v).what == "C" and dump(dummy) or dump(v)
+--                     -- local f = getinfo(v).what == "C" and dump(function(...) return v(...) end) or dump(v)
+--                        if tk == "number" then
+--                            if hexify then
+--                                handle(formatters["%w [%04H]=load(%q),"](level,k,f))
+--                            else
+--                                handle(formatters["%w [%s]=load(%q),"](level,k,f))
+--                            end
+--                        elseif tk == "boolean" then
+--                            handle(formatters["%w [%S]=load(%q),"](level,k,f))
+--                        elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+--                            handle(formatters["%w %s=load(%q),"](level,k,f))
+--                        else
+--                            handle(formatters["%w [%q]=load(%q),"](level,k,f))
+--                        end
+--                    end
+--                else
+--                    if tk == "number" then
+--                        if hexify then
+--                            handle(formatters["%w [%04H]=%Q,"](level,k,v))
+--                        else
+--                            handle(formatters["%w [%s]=%Q,"](level,k,v))
+--                        end
+--                    elseif tk == "boolean" then
+--                        handle(formatters["%w [%S]=%Q,"](level,k,v))
+--                    elseif noquotes and not reserved[k] and lpegmatch(propername,k) then
+--                        handle(formatters["%w %s=%Q,"](level,k,v))
+--                    else
+--                        handle(formatters["%w [%q]=%Q,"](level,k,v))
+--                    end
+--                end
+--                --~ end
+--            end
+--        end
+--        if level > 0 then
+--            handle(formatters["%w}"](level))
+--        end
+--    end
+
+--    local function serialize(_handle,root,name,specification) -- handle wins
+--        local tname = type(name)
+--        if type(specification) == "table" then
+--            noquotes  = specification.noquotes
+--            hexify    = specification.hexify
+--            handle    = _handle or specification.handle or print
+--            reduce    = specification.reduce or false
+--            functions = specification.functions
+--            compact   = specification.compact
+--            inline    = specification.inline and compact
+--            if functions == nil then
+--                functions = true
+--            end
+--            if compact == nil then
+--                compact = true
+--            end
+--            if inline == nil then
+--                inline = compact
+--            end
+--        else
+--            noquotes  = false
+--            hexify    = false
+--            handle    = _handle or print
+--            reduce    = false
+--            compact   = true
+--            inline    = true
+--            functions = true
+--        end
+--        if tname == "string" then
+--            if name == "return" then
+--                handle("return {")
+--            else
+--                handle(name .. "={")
+--            end
+--        elseif tname == "number" then
+--            if hexify then
+--                handle(format("[0x%04X]={",name))
+--            else
+--                handle("[" .. name .. "]={")
+--            end
+--        elseif tname == "boolean" then
+--            if name then
+--                handle("return {")
+--            else
+--                handle("{")
+--            end
+--        else
+--            handle("t={")
+--        end
+--        if root then
+--            -- The dummy access will initialize a table that has a delayed initialization
+--            -- using a metatable. (maybe explicitly test for metatable)
+--            if getmetatable(root) then -- todo: make this an option, maybe even per subtable
+--                local dummy = root._w_h_a_t_e_v_e_r_
+--                root._w_h_a_t_e_v_e_r_ = nil
+--            end
+--            -- Let's forget about empty tables.
+--            if next(root) then
+--                do_serialize(root,name,0)
+--            end
+--        end
+--        handle("}")
+--    end
+
 -- name:
 --
 -- true     : return     { }
@@ -730,6 +1048,13 @@ function table.serialize(root,name,specification)
     return concat(t,"\n")
 end
 
+--   local a = { e = { 1,2,3,4,5,6}, a = 1, b = 2, c = "ccc", d = { a = 1, b = 2, c = "ccc", d = { a = 1, b = 2, c = "ccc" } } }
+--   local t = os.clock()
+--   for i=1,10000 do
+--       table.serialize(a)
+--   end
+--   print(os.clock()-t,table.serialize(a))
+
 table.tohandle = serialize
 
 -- sometimes tables are real use (zapfino extra pro is some 85M) in which
diff --git a/tex/context/base/luat-lib.mkiv b/tex/context/base/luat-lib.mkiv
index 4ece2a29a..b9ccd8b11 100644
--- a/tex/context/base/luat-lib.mkiv
+++ b/tex/context/base/luat-lib.mkiv
@@ -13,9 +13,9 @@
 
 \writestatus{loading}{ConTeXt Lua Macros / Libraries}
 
+\registerctxluafile{util-str}{1.001}
 \registerctxluafile{util-tab}{1.001}
 \registerctxluafile{util-sto}{1.001} % could also be done in trac-deb.mkiv
-\registerctxluafile{util-str}{1.001} % uses util-sto
 \registerctxluafile{util-pck}{1.001}
 \registerctxluafile{util-seq}{1.001}
 %registerctxluafile{util-mrg}{1.001} % not needed in context itself, only mtxrun
diff --git a/tex/context/base/lxml-lpt.lua b/tex/context/base/lxml-lpt.lua
index d73b87287..2be648075 100644
--- a/tex/context/base/lxml-lpt.lua
+++ b/tex/context/base/lxml-lpt.lua
@@ -15,6 +15,7 @@ local format, upper, lower, gmatch, gsub, find, rep = string.format, string.uppe
 local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns
 
 local setmetatableindex = table.setmetatableindex
+local formatters = string.formatters -- no need (yet) as paths are cached anyway
 
 -- beware, this is not xpath ... e.g. position is different (currently) and
 -- we have reverse-sibling as reversed preceding sibling
@@ -608,15 +609,12 @@ local converter = Cs (
 )
 
 cleaner = Cs ( (
---~     lp_fastpos +
+ -- lp_fastpos +
     lp_reserved +
     lp_number +
     lp_string +
 1 )^1 )
 
-
---~ expr
-
 local template_e = [[
     local expr = xml.expressions
     return function(list,ll,l,order)
@@ -671,6 +669,7 @@ local function errorrunner_e(str,cnv)
     end
     return false
 end
+
 local function errorrunner_f(str,arg)
     report_lpath("error in finalizer: %s(%s)",str,arg or "")
     return false
diff --git a/tex/context/base/meta-ini.lua b/tex/context/base/meta-ini.lua
index 928048776..05433121e 100644
--- a/tex/context/base/meta-ini.lua
+++ b/tex/context/base/meta-ini.lua
@@ -153,8 +153,12 @@ end
 --     context.mathematics(f)
 -- end
 
+-- formatters["\\times10^{%N}"](s) -- strips leading zeros too
+
 local one = Cs((P("@")/"%%." * (R("09")^1) + P("@")/"%%" + 1)^0)
-local two = Cs((P("e")/"" * ((S("+-")^0 * R("09")^1)/function(s) return format("\\times10^{%s}",tonumber(s) or s) end) + 1)^1)
+local two = Cs((P("e")/"" * ((S("+-")^0 * R("09")^1) / function(s) return format("\\times10^{%s}",tonumber(s) or s) end) + 1)^1)
+
+-- local two = Cs((P("e")/"" * ((S("+-")^0 * R("09")^1) / formatters["\\times10^{%N}"]) + 1)^1)
 
 function metapost.formatnumber(fmt,n) -- just lua format
     context.mathematics(lpegmatch(two,format(lpegmatch(one,fmt),n)))
diff --git a/tex/context/base/mlib-pdf.lua b/tex/context/base/mlib-pdf.lua
index e22e3af30..205dbd35a 100644
--- a/tex/context/base/mlib-pdf.lua
+++ b/tex/context/base/mlib-pdf.lua
@@ -12,6 +12,7 @@ local format, concat, gsub = string.format, table.concat, string.gsub
 local abs, sqrt, round = math.abs, math.sqrt, math.round
 local setmetatable = setmetatable
 local Cf, C, Cg, Ct, P, S, lpegmatch = lpeg.Cf, lpeg.C, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.S, lpeg.match
+local formatters = string.formatters
 
 local report_metapost = logs.reporter("metapost")
 
@@ -97,7 +98,7 @@ end
 
 function pdfflusher.comment(message)
     if message then
-        message = format("%% mps graphic %s: %s", metapost.n, message)
+        message = formatters["%% mps graphic %s: %s"](metapost.n,message)
         if experiment then
             context(pdfliteral(message))
         else
@@ -189,11 +190,11 @@ local function flushnormalpath(path, t, open)
         nt = nt + 1
         pth = path[i]
         if not ith then
-            t[nt] = format("%f %f m",pth.x_coord,pth.y_coord)
+            t[nt] = formatters["%f %f m"](pth.x_coord,pth.y_coord)
         elseif curved(ith,pth) then
-            t[nt] = format("%f %f %f %f %f %f c",ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord)
+            t[nt] = formatters["%f %f %f %f %f %f c"](ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord)
         else
-            t[nt] = format("%f %f l",pth.x_coord,pth.y_coord)
+            t[nt] = formatters["%f %f l"](pth.x_coord,pth.y_coord)
         end
         ith = pth
     end
@@ -201,15 +202,15 @@ local function flushnormalpath(path, t, open)
         nt = nt + 1
         local one = path[1]
         if curved(pth,one) then
-            t[nt] = format("%f %f %f %f %f %f c",pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord )
+            t[nt] = formatters["%f %f %f %f %f %f c"](pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord )
         else
-            t[nt] = format("%f %f l",one.x_coord,one.y_coord)
+            t[nt] = formatters["%f %f l"](one.x_coord,one.y_coord)
         end
     elseif #path == 1 then
         -- special case .. draw point
         local one = path[1]
         nt = nt + 1
-        t[nt] = format("%f %f l",one.x_coord,one.y_coord)
+        t[nt] = formatters["%f %f l"](one.x_coord,one.y_coord)
     end
     return t
 end
@@ -223,18 +224,18 @@ local function flushconcatpath(path, t, open)
         nt = 0
     end
     nt = nt + 1
-    t[nt] = format("%f %f %f %f %f %f cm", sx, rx, ry, sy, tx ,ty)
+    t[nt] = formatters["%f %f %f %f %f %f cm"](sx,rx,ry,sy,tx,ty)
     for i=1,#path do
         nt = nt + 1
         pth = path[i]
         if not ith then
-            t[nt] = format("%f %f m",mpconcat(pth.x_coord,pth.y_coord))
+            t[nt] = formatters["%f %f m"](mpconcat(pth.x_coord,pth.y_coord))
         elseif curved(ith,pth) then
             local a, b = mpconcat(ith.right_x,ith.right_y)
             local c, d = mpconcat(pth.left_x,pth.left_y)
-            t[nt] = format("%f %f %f %f %f %f c",a,b,c,d,mpconcat(pth.x_coord,pth.y_coord))
+            t[nt] = formatters["%f %f %f %f %f %f c"](a,b,c,d,mpconcat(pth.x_coord,pth.y_coord))
         else
-           t[nt] = format("%f %f l",mpconcat(pth.x_coord, pth.y_coord))
+           t[nt] = formatters["%f %f l"](mpconcat(pth.x_coord, pth.y_coord))
         end
         ith = pth
     end
@@ -244,15 +245,15 @@ local function flushconcatpath(path, t, open)
         if curved(pth,one) then
             local a, b = mpconcat(pth.right_x,pth.right_y)
             local c, d = mpconcat(one.left_x,one.left_y)
-            t[nt] = format("%f %f %f %f %f %f c",a,b,c,d,mpconcat(one.x_coord, one.y_coord))
+            t[nt] = formatters["%f %f %f %f %f %f c"](a,b,c,d,mpconcat(one.x_coord, one.y_coord))
         else
-            t[nt] = format("%f %f l",mpconcat(one.x_coord,one.y_coord))
+            t[nt] = formatters["%f %f l"](mpconcat(one.x_coord,one.y_coord))
         end
     elseif #path == 1 then
         -- special case .. draw point
         nt = nt + 1
         local one = path[1]
-        t[nt] = format("%f %f l",mpconcat(one.x_coord,one.y_coord))
+        t[nt] = formatters["%f %f l"](mpconcat(one.x_coord,one.y_coord))
     end
     return t
 end
@@ -319,7 +320,7 @@ function metapost.flush(result,flusher,askedfig)
                                 elseif objecttype == "text" then
                                     t[#t+1] = "q"
                                     local ot = object.transform -- 3,4,5,6,1,2
-                                    t[#t+1] = format("%f %f %f %f %f %f cm",ot[3],ot[4],ot[5],ot[6],ot[1],ot[2]) -- TH: format("%f %f m %f %f %f %f 0 0 cm",unpack(ot))
+                                    t[#t+1] = formatters["%f %f %f %f %f %f cm"](ot[3],ot[4],ot[5],ot[6],ot[1],ot[2]) -- TH: formatters["%f %f m %f %f %f %f 0 0 cm"](unpack(ot))
                                     flushfigure(t) -- flush accumulated literals
                                     t = { }
                                     textfigure(object.font,object.dsize,object.text,object.width,object.height,object.depth)
@@ -344,21 +345,21 @@ function metapost.flush(result,flusher,askedfig)
                                     local ml = object.miterlimit
                                     if ml and ml ~= miterlimit then
                                         miterlimit = ml
-                                        t[#t+1] = format("%f M",ml)
+                                        t[#t+1] = formatters["%f M"](ml)
                                     end
                                     local lj = object.linejoin
                                     if lj and lj ~= linejoin then
                                         linejoin = lj
-                                        t[#t+1] = format("%i j",lj)
+                                        t[#t+1] = formatters["%i j"](lj)
                                     end
                                     local lc = object.linecap
                                     if lc and lc ~= linecap then
                                         linecap = lc
-                                        t[#t+1] = format("%i J",lc)
+                                        t[#t+1] = formatters["%i J"](lc)
                                     end
                                     local dl = object.dash
                                     if dl then
-                                        local d = format("[%s] %f d",concat(dl.dashes or {}," "),dl.offset)
+                                        local d = formatters["[%s] %f d"](concat(dl.dashes or {}," "),dl.offset)
                                         if d ~= dashed then
                                             dashed = d
                                             t[#t+1] = dashed
@@ -374,7 +375,7 @@ function metapost.flush(result,flusher,askedfig)
                                     if pen then
                                        if pen.type == 'elliptical' then
                                             transformed, penwidth = pen_characteristics(original) -- boolean, value
-                                            t[#t+1] = format("%f w",penwidth) -- todo: only if changed
+                                            t[#t+1] = formatters["%f w"](penwidth) -- todo: only if changed
                                             if objecttype == 'fill' then
                                                 objecttype = 'both'
                                             end
diff --git a/tex/context/base/mlib-pps.lua b/tex/context/base/mlib-pps.lua
index 78e35d8b0..3a6f0dc77 100644
--- a/tex/context/base/mlib-pps.lua
+++ b/tex/context/base/mlib-pps.lua
@@ -15,7 +15,6 @@ local round = math.round
 local insert, concat = table.insert, table.concat
 local Cs, Cf, C, Cg, Ct, P, S, V, Carg = lpeg.Cs, lpeg.Cf, lpeg.C, lpeg.Cg, lpeg.Ct, lpeg.P, lpeg.S, lpeg.V, lpeg.Carg
 local lpegmatch = lpeg.match
-
 local formatters = string.formatters
 
 local mplib, metapost, lpdf, context = mplib, metapost, lpdf, context
@@ -665,7 +664,7 @@ function makempy.processgraphics(graphics)
                 local data = io.loaddata(mpyfile)
                 for figure in gmatch(data,"beginfig(.-)endfig") do
                     r = r + 1
-                    result[r] = format("begingraphictextfig%sendgraphictextfig ;\n", figure)
+                    result[r] = formatters["begingraphictextfig%sendgraphictextfig ;\n"](figure)
                 end
                 io.savedata(mpyfile,concat(result,""))
             end
@@ -800,91 +799,6 @@ local function cl_reset(t)
     t[#t+1] = metapost.colorinitializer() -- only color
 end
 
--- text
-
--- local tx_done = { }
---
--- local function tx_reset()
---     tx_done = { }
--- end
---
--- local function tx_analyze(object,prescript) -- todo: hash content and reuse them
---     local tx_stage = prescript.tx_stage
---     if tx_stage then
---         local tx_number = tonumber(prescript.tx_number)
---         if not tx_done[tx_number] then
---             tx_done[tx_number] = true
---             if trace_textexts then
---                 report_textexts("setting %s %s (first pass)",tx_stage,tx_number)
---             end
---             local s = object.postscript or ""
---             local c = object.color -- only simple ones, no transparency
---             local a = prescript.tr_alternative
---             local t = prescript.tr_transparency
---             if not c then
---                 -- no color
---             elseif #c == 1 then
---                 if a and t then
---                     s = format("\\directcolored[s=%f,a=%f,t=%f]%s",c[1],a,t,s)
---                 else
---                     s = format("\\directcolored[s=%f]%s",c[1],s)
---                 end
---             elseif #c == 3 then
---                 if a and t then
---                     s = format("\\directcolored[r=%f,g=%f,b=%f,a=%f,t=%f]%s",c[1],c[2],c[3],a,t,s)
---                 else
---                     s = format("\\directcolored[r=%f,g=%f,b=%f]%s",c[1],c[2],c[3],s)
---                 end
---             elseif #c == 4 then
---                 if a and t then
---                     s = format("\\directcolored[c=%f,m=%f,y=%f,k=%f,a=%f,t=%f]%s",c[1],c[2],c[3],c[4],a,t,s)
---                 else
---                     s = format("\\directcolored[c=%f,m=%f,y=%f,k=%f]%s",c[1],c[2],c[3],c[4],s)
---                 end
---             end
---             context.MPLIBsettext(tx_number,s) -- combine colored in here, saves call
---             metapost.multipass = true
---         end
---     end
--- end
---
--- local function tx_process(object,prescript,before,after)
---     local tx_number = prescript.tx_number
---     if tx_number then
---         tx_number = tonumber(tx_number)
---         local tx_stage = prescript.tx_stage
---         if tx_stage == "final" then -- redundant test
---             if trace_textexts then
---                 report_textexts("processing %s (second pass)",tx_number)
---             end
---             local sx,rx,ry,sy,tx,ty = cm(object) -- outside function !
---             before[#before+1] = function()
---                 -- flush always happens, we can have a special flush function injected before
---                 local box = textexts[tx_number]
---                 if box then
---                     context.MPLIBgettextscaledcm(tx_number,
---                         format("%f",sx), -- bah ... %s no longer checks
---                         format("%f",rx), -- bah ... %s no longer checks
---                         format("%f",ry), -- bah ... %s no longer checks
---                         format("%f",sy), -- bah ... %s no longer checks
---                         format("%f",tx), -- bah ... %s no longer checks
---                         format("%f",ty), -- bah ... %s no longer checks
---                         sxsy(box.width,box.height,box.depth))
---                 else
---                     report_textexts("unknown %s",tx_number)
---                 end
---             end
---             if not trace_textexts then
---                 object.path = false -- else: keep it
---             end
---             object.color = false
---             object.grouped = true
---         end
---     end
--- end
-
--- experiment
-
 local tx_hash = { }
 local tx_last = 0
 
@@ -915,21 +829,21 @@ local function tx_analyze(object,prescript) -- todo: hash content and reuse them
                 -- no color
             elseif #c == 1 then
                 if a and t then
-                    s = format("\\directcolored[s=%f,a=%f,t=%f]%s",c[1],a,t,s)
+                    s = formatters["\\directcolored[s=%f,a=%f,t=%f]%s"](c[1],a,t,s)
                 else
-                    s = format("\\directcolored[s=%f]%s",c[1],s)
+                    s = formatters["\\directcolored[s=%f]%s"](c[1],s)
                 end
             elseif #c == 3 then
                 if a and t then
-                    s = format("\\directcolored[r=%f,g=%f,b=%f,a=%f,t=%f]%s",c[1],c[2],c[3],a,t,s)
+                    s = formatters["\\directcolored[r=%f,g=%f,b=%f,a=%f,t=%f]%s"](c[1],c[2],c[3],a,t,s)
                 else
-                    s = format("\\directcolored[r=%f,g=%f,b=%f]%s",c[1],c[2],c[3],s)
+                    s = formatters["\\directcolored[r=%f,g=%f,b=%f]%s"](c[1],c[2],c[3],s)
                 end
             elseif #c == 4 then
                 if a and t then
-                    s = format("\\directcolored[c=%f,m=%f,y=%f,k=%f,a=%f,t=%f]%s",c[1],c[2],c[3],c[4],a,t,s)
+                    s = formatters["\\directcolored[c=%f,m=%f,y=%f,k=%f,a=%f,t=%f]%s"](c[1],c[2],c[3],c[4],a,t,s)
                 else
-                    s = format("\\directcolored[c=%f,m=%f,y=%f,k=%f]%s",c[1],c[2],c[3],c[4],s)
+                    s = formatters["\\directcolored[c=%f,m=%f,y=%f,k=%f]%s"](c[1],c[2],c[3],c[4],s)
                 end
             end
             context.MPLIBsettext(tx_last,s)
@@ -1022,7 +936,7 @@ end
 local function gt_analyze(object,prescript)
     local gt_stage = prescript.gt_stage
     if gt_stage == "trial" then
-        graphics[#graphics+1] = format("\\MPLIBgraphictext{%s}",object.postscript or "")
+        graphics[#graphics+1] = formatters["\\MPLIBgraphictext{%s}"](object.postscript or "")
         metapost.intermediate.needed = true
         metapost.multipass = true
     end
@@ -1098,7 +1012,7 @@ local function sh_process(object,prescript,before,after)
         else
             -- fatal error
         end
-        before[#before+1], after[#after+1] = "q /Pattern cs", format("W n /%s sh Q",name)
+        before[#before+1], after[#after+1] = "q /Pattern cs", formatters["W n /%s sh Q"](name)
         -- false, not nil, else mt triggered
         object.colored = false -- hm, not object.color ?
         object.type = false
@@ -1179,7 +1093,7 @@ local function tr_process(object,prescript,before,after)
     if tr_alternative then
         tr_alternative = tonumber(tr_alternative)
         local tr_transparency = tonumber(prescript.tr_transparency)
-        before[#before+1] = format("/Tr%s gs",registertransparency(nil,tr_alternative,tr_transparency,true))
+        before[#before+1] = formatters["/Tr%s gs"](registertransparency(nil,tr_alternative,tr_transparency,true))
         after[#after+1] = "/Tr0 gs" -- outertransparency
     end
     local cs = object.color
@@ -1209,7 +1123,7 @@ local function tr_process(object,prescript,before,after)
                 local t = t_list[sp_name] -- string or attribute
                 local v = t and attributes.transparencies.value(t)
                 if v then
-                    before[#before+1] = format("/Tr%s gs",registertransparency(nil,v[1],v[2],true))
+                    before[#before+1] = formatters["/Tr%s gs"](registertransparency(nil,v[1],v[2],true))
                     after[#after+1] = "/Tr0 gs" -- outertransparency
                 end
             end
diff --git a/tex/context/base/mlib-run.lua b/tex/context/base/mlib-run.lua
index 35cbd40f5..5adf43b46 100644
--- a/tex/context/base/mlib-run.lua
+++ b/tex/context/base/mlib-run.lua
@@ -444,10 +444,10 @@ function metapost.process(mpx, data, trialrun, flusher, multipass, isextrapass,
             if trace_tracingall then
                 mpx:execute("tracingall;")
             end
--- table.insert(data,2,"")
+         -- table.insert(data,2,"")
             for i=1,#data do
                 local d = data[i]
--- d = string.gsub(d,"\r","")
+             -- d = string.gsub(d,"\r","")
                 if d then
                     if trace_graphics then
                         mp_inp[mpx]:write(format("\n%% begin snippet %s\n",i))
diff --git a/tex/context/base/status-files.pdf b/tex/context/base/status-files.pdf
index 6fb7f16c3..4803e083f 100644
Binary files a/tex/context/base/status-files.pdf and b/tex/context/base/status-files.pdf differ
diff --git a/tex/context/base/status-lua.pdf b/tex/context/base/status-lua.pdf
index 19170f8b0..a0bbca101 100644
Binary files a/tex/context/base/status-lua.pdf and b/tex/context/base/status-lua.pdf differ
diff --git a/tex/context/base/typo-del.mkiv b/tex/context/base/typo-del.mkiv
index 8dfb2d461..b48c71e58 100644
--- a/tex/context/base/typo-del.mkiv
+++ b/tex/context/base/typo-del.mkiv
@@ -476,6 +476,7 @@
      \fi
      \strut % new, needed below
      \delimitedtextparameter#1% unhbox\scratchbox
+     \penalty\plustenthousand % new per 2013-03-09 WS mailing list
      \hskip\d_typo_delimited_signal % +- \prewordbreak
    \fi
    \endgroup}
diff --git a/tex/context/base/typo-prc.lua b/tex/context/base/typo-prc.lua
index bb965ff66..ae9b541bf 100644
--- a/tex/context/base/typo-prc.lua
+++ b/tex/context/base/typo-prc.lua
@@ -9,7 +9,7 @@ if not modules then modules = { } end modules ['typo-prc'] = {
 -- moved from strc-ini.lua
 
 
-local format = string.format
+local formatters = string.formatters
 local lpegmatch, patterns, P, C, Cs = lpeg.match, lpeg.patterns, lpeg.P, lpeg.C, lpeg.Cs
 
 -- processors: syntax: processor->data ... not ok yet
@@ -108,7 +108,7 @@ end
 function processors.tostring(str)
     local p, s = lpegmatch(splitter,str)
     if registered[p] then
-        return format("\\applyprocessor{%s}{%s}",p,s)
+        return formatters["\\applyprocessor{%s}{%s}"](p,s)
     else
         return str
     end
diff --git a/tex/context/base/util-fmt.lua b/tex/context/base/util-fmt.lua
index e049d0b94..371a5dfce 100644
--- a/tex/context/base/util-fmt.lua
+++ b/tex/context/base/util-fmt.lua
@@ -14,28 +14,8 @@ local concat, format = table.concat, string.format
 local tostring, type = tostring, type
 local strip = string.strip
 
-local P, R, Cs = lpeg.P, lpeg.R, lpeg.Cs
 local lpegmatch = lpeg.match
-
--- temporary here
-
-local digit         = R("09")
-local period        = P(".")
-local zero          = P("0")
-local trailingzeros = zero^0 * -digit -- suggested by Roberto R
-local case_1        = period * trailingzeros / ""
-local case_2        = period * (digit - trailingzeros)^1 * (trailingzeros / "")
-local number        = digit^1 * (case_1 + case_2)
-local stripper      = Cs((number + 1)^0)
-
---~ local sample = "bla 11.00 bla 11 bla 0.1100 bla 1.00100 bla 0.00 bla 0.001 bla 1.1100 bla 0.100100100 bla 0.00100100100"
---~ collectgarbage("collect")
---~ str = string.rep(sample,10000)
---~ local ts = os.clock()
---~ lpegmatch(stripper,str)
---~ print(#str, os.clock()-ts, lpegmatch(stripper,sample))
-
-lpeg.patterns.stripzeros = stripper
+local stripper  = lpeg.patterns.stripzeros
 
 function formatters.stripzeros(str)
     return lpegmatch(stripper,str)
diff --git a/tex/context/base/util-lua.lua b/tex/context/base/util-lua.lua
index 36daaff55..96101f8ec 100644
--- a/tex/context/base/util-lua.lua
+++ b/tex/context/base/util-lua.lua
@@ -7,6 +7,8 @@ if not modules then modules = { } end modules ['util-lua'] = {
     license   = "see context related readme files"
 }
 
+-- we will remove the 5.1 code some day soon
+
 local rep, sub, byte, dump, format = string.rep, string.sub, string.byte, string.dump, string.format
 local load, loadfile, type = load, loadfile, type
 
@@ -126,6 +128,13 @@ if jit or status.luatex_version >= 74 then
         return done
     end
 
+    function luautilities.loadstripped(...)
+        local l = load(...)
+        if l then
+            return load(dump(l,true))
+        end
+    end
+
 else
 
     -- The next function was posted by Peter Cawley on the lua list and strips line
@@ -319,6 +328,8 @@ else
         return done
     end
 
+    luautilities.loadstripped = loadstring
+
 end
 
 -- local getmetatable, type = getmetatable, type
diff --git a/tex/context/base/util-seq.lua b/tex/context/base/util-seq.lua
index 0bf056365..27f95f0ee 100644
--- a/tex/context/base/util-seq.lua
+++ b/tex/context/base/util-seq.lua
@@ -258,7 +258,7 @@ compile = function(t,compiler,n) -- already referred to in sequencers.new
     if compiled == "" then
         runner = false
     else
-        runner = compiled and load(compiled)()
+        runner = compiled and load(compiled)() -- we can use loadstripped here
     end
     t.runner = runner
     return runner
diff --git a/tex/context/base/util-str.lua b/tex/context/base/util-str.lua
index a4889d252..959955867 100644
--- a/tex/context/base/util-str.lua
+++ b/tex/context/base/util-str.lua
@@ -10,14 +10,18 @@ utilities         = utilities or {}
 utilities.strings = utilities.strings or { }
 local strings     = utilities.strings
 
-local load = load
 local format, gsub, rep, sub = string.format, string.gsub, string.rep, string.sub
+local load, dump = load, string.dump
 local concat = table.concat
-local P, V, C, S, R, Ct, Cs, Cp, Carg = lpeg.P, lpeg.V, lpeg.C, lpeg.S, lpeg.R, lpeg.Ct, lpeg.Cs, lpeg.Cp, lpeg.Carg
+local P, V, C, S, R, Ct, Cs, Cp, Carg, Cc = lpeg.P, lpeg.V, lpeg.C, lpeg.S, lpeg.R, lpeg.Ct, lpeg.Cs, lpeg.Cp, lpeg.Carg, lpeg.Cc
 local patterns, lpegmatch = lpeg.patterns, lpeg.match
 local utfchar, utfbyte = utf.char, utf.byte
-local setmetatableindex = table.setmetatableindex
---
+----- loadstripped = utilities.lua.loadstripped
+----- setmetatableindex = table.setmetatableindex
+
+local loadstripped = _LUAVERSION < 5.2 and load or function(str)
+    return load(dump(load(str),true)) -- it only makes sense in luajit and luatex where we have a stipped load
+end
 
 local stripper = patterns.stripzeros
 
@@ -65,7 +69,7 @@ function strings.newrepeater(str,offset)
         return t
     end
     t = { }
-    setmetatableindex(t, function(t,k)
+    setmetatable(t, { __index = function(t,k)
         if not k then
             return ""
         end
@@ -73,7 +77,7 @@ function strings.newrepeater(str,offset)
         local s = n > 0 and rep(str,n) or ""
         t[k] = s
         return s
-    end)
+    end })
     s[offset] = t
     return t
 end
@@ -85,6 +89,8 @@ local extra, tab, start = 0, 0, 4, 0
 
 local nspaces = strings.newrepeater(" ")
 
+string.nspaces = nspaces
+
 local pattern =
     Carg(1) / function(t)
         extra, tab, start = 0, t or 7, 1
@@ -144,6 +150,13 @@ end
 
 -- Work in progress. Interesting is that compared to the built-in this
 -- is faster in luatex than in luajittex where we have a comparable speed.
+-- It only makes sense to use the formatter when a (somewhat) complex format
+-- is used a lot. Each formatter is a function so there is some overhead
+-- and not all formatted output is worth that overhead. Keep in mind that
+-- there is an extra function call involved. In principle we end up with a
+-- string concatination so one could inline such a sequence but often at the
+-- cost of less readabinity. So, it's a sort of (visual) compromise. Of course
+-- there is the benefit of more variants.
 
 local n = 0
 
@@ -155,6 +168,20 @@ local n = 0
 --     print(...,...,...) -- 1,1,1,2,3
 -- end
 
+local template_shortcuts = [[
+local tostring = tostring
+local format = string.format
+local concat = table.concat
+local signed = number.signed
+local points = number.points
+local basepoints = number.basepoints
+local utfchar = utf.char
+local utfbyte = utf.byte
+local lpegmatch = lpeg.match
+local xmlescape = lpeg.patterns.xmlescape
+local spaces = string.nspaces
+]]
+
 local prefix_any = C((S("+- .") + R("09"))^0)
 local prefix_tab = C((1-R("az","AZ","09","%%"))^0)
 
@@ -163,23 +190,37 @@ local prefix_tab = C((1-R("az","AZ","09","%%"))^0)
 local format_s = function(f)
     n = n + 1
     if f and f ~= "" then
-        return format("format('%%%ss',(select(%s,...)))",f,n)
+        return format("format('%%%ss',a%s)",f,n)
+    else
+        return format("a%s",n)
+    end
+end
+
+local format_S = function(f) -- can be optimized
+    n = n + 1
+    if f and f ~= "" then
+        return format("format('%%%ss',tostring(a%s))",f,n)
     else
-        return format("(select(%s,...))",n)
+        return format("tostring(a%s)",n)
     end
 end
 
 local format_q = function()
     n = n + 1
-    return format("format('%%q',(select(%s,...)))",n) -- maybe an own lpeg
+    return format("format('%%q',a%s)",n) -- maybe an own lpeg
+end
+
+local format_Q = function() -- can be optimized
+    n = n + 1
+    return format("format('%%q',tostring(a%s))",n)
 end
 
 local format_i = function(f)
     n = n + 1
     if f and f ~= "" then
-        return format("format('%%%si',(select(%s,...)))",f,n)
+        return format("format('%%%si',a%s)",f,n)
     else
-        return format("(select(%s,...))",n)
+        return format("a%s",n)
     end
 end
 
@@ -196,79 +237,79 @@ end
 local format_I = function(f)
     n = n + 1
     if f and f ~= "" then
-        return format("format('%%s%%%si',signed((select(%s,...))))",f,n)
+        return format("format('%%s%%%si',signed(a%s))",f,n)
     else
-        return format("format('%%s%%i',signed((select(%s,...))))",n)
+        return format("format('%%s%%i',signed(a%s))",n)
     end
 end
 
 local format_f = function(f)
     n = n + 1
-    return format("format('%%%sf',(select(%s,...)))",f,n)
+    return format("format('%%%sf',a%s)",f,n)
 end
 
 local format_g = function(f)
     n = n + 1
-    return format("format('%%%sg',(select(%s,...)))",f,n)
+    return format("format('%%%sg',a%s)",f,n)
 end
 
 local format_G = function(f)
     n = n + 1
-    return format("format('%%%sG',(select(%s,...)))",f,n)
+    return format("format('%%%sG',a%s)",f,n)
 end
 
 local format_e = function(f)
     n = n + 1
-    return format("format('%%%se',(select(%s,...)))",f,n)
+    return format("format('%%%se',a%s)",f,n)
 end
 
 local format_E = function(f)
     n = n + 1
-    return format("format('%%%sE',(select(%s,...)))",f,n)
+    return format("format('%%%sE',a%s)",f,n)
 end
 
 local format_x = function(f)
     n = n + 1
-    return format("format('%%%sx',(select(%s,...)))",f,n)
+    return format("format('%%%sx',a%s)",f,n)
 end
 
 local format_X = function(f)
     n = n + 1
-    return format("format('%%%sX',(select(%s,...)))",f,n)
+    return format("format('%%%sX',a%s)",f,n)
 end
 
 local format_o = function(f)
     n = n + 1
-    return format("format('%%%so',(select(%s,...)))",f,n)
+    return format("format('%%%so',a%s)",f,n)
 end
 
 local format_c = function()
     n = n + 1
-    return format("utfchar((select(%s,...)))",n)
+    return format("utfchar(a%s)",n)
 end
 
 local format_r = function(f)
     n = n + 1
-    return format("format('%%%s.0f',(select(%s,...)))",f,n)
+    return format("format('%%%s.0f',a%s)",f,n)
 end
 
-local format_v = function(f)
+local format_h = function(f)
     n = n + 1
     if f == "-" then
         f = sub(f,2)
-        return format("format('%%%sx',utfbyte((select(%s,...))))",f == "" and "05" or f,n)
+        return format("format('%%%sx',utfbyte(a%s))",f == "" and "05" or f,n)
     else
-        return format("format('0x%%%sx',utfbyte((select(%s,...))))",f == "" and "05" or f,n)
+        return format("format('0x%%%sx',utfbyte(a%s))",f == "" and "05" or f,n)
     end
 end
 
-local format_V = function(f)
+local format_H = function(f)
     n = n + 1
     if f == "-" then
         f = sub(f,2)
-        return format("format('%%%sX',utfbyte((select(%s,...))))",f == "" and "05" or f,n)
+        return format("format('%%%sX',utfbyte(a%s))",f == "" and "05" or f,n)
     else
-        return format("format('0x%%%sX',utfbyte((select(%s,...))))",f == "" and "05" or f,n)
+        return format("format('0x%%%sX',utfbyte(a%s))",f == "" and "05" or f,n)
     end
 end
 
@@ -276,9 +317,9 @@ local format_u = function(f)
     n = n + 1
     if f == "-" then
         f = sub(f,2)
-        return format("format('%%%sx',utfbyte((select(%s,...))))",f == "" and "05" or f,n)
+        return format("format('%%%sx',utfbyte(a%s))",f == "" and "05" or f,n)
     else
-        return format("format('u+%%%sx',utfbyte((select(%s,...))))",f == "" and "05" or f,n)
+        return format("format('u+%%%sx',utfbyte(a%s))",f == "" and "05" or f,n)
     end
 end
 
@@ -286,57 +327,117 @@ local format_U = function(f)
     n = n + 1
     if f == "-" then
         f = sub(f,2)
-        return format("format('%%%sX',utfbyte((select(%s,...))))",f == "" and "05" or f,n)
+        return format("format('%%%sX',utfbyte(a%s))",f == "" and "05" or f,n)
     else
-        return format("format('U+%%%sX',utfbyte((select(%s,...))))",f == "" and "05" or f,n)
+        return format("format('U+%%%sX',utfbyte(a%s))",f == "" and "05" or f,n)
     end
 end
 
 local format_p = function()
     n = n + 1
-    return format("points((select(%s,...)))",n)
+    return format("points(a%s)",n)
 end
 
 local format_b = function()
     n = n + 1
-    return format("basepoints((select(%s,...)))",n)
+    return format("basepoints(a%s)",n)
 end
 
 local format_t = function(f)
     n = n + 1
     if f and f ~= "" then
-        return format("concat((select(%s,...)),%q)",n,f)
+        return format("concat(a%s,%q)",n,f)
     else
-        return format("concat((select(%s,...)))",n)
+        return format("concat(a%s)",n)
     end
 end
 
 local format_l = function()
     n = n + 1
-    return format("(select(%s,...) and 'true' or 'false')",n)
+    return format("(a%s and 'true' or 'false')",n)
+end
+
+local format_L = function()
+    n = n + 1
+    return format("(a%s and 'TRUE' or 'FALSE')",n)
+end
+
+local format_N = function() -- strips leading zeros
+    n = n + 1
+    return format("tostring(tonumber(a%s) or a%s)",n,n)
 end
 
 local format_a = function(s)
     return format("%q",s)
 end
 
-local builder = Ct { "start",
-    start = (P("%") * (
-        V("s") + V("q")
-      + V("i") + V("d")
-      + V("f") + V("g") + V("G") + V("e") + V("E")
-      + V("x") + V("X") + V("o")
-      --
-      + V("c")
-      --
-      + V("r")
-      + V("v") + V("V") + V("u") + V("U")
-      + V("p") + V("b")
-      + V("t")
-      + V("l")
-      + V("I")
-    )
-      + V("a")
+local format_w = function(f) -- handy when doing depth related indent
+    n = n + 1
+    f = tonumber(f)
+    if f then
+        return format("spaces[%s+tonumber(a%s)]",f,n)
+    else
+        return format("spaces[tonumber(a%s)]",n)
+    end
+end
+
+local format_W = function(f) -- handy when doing depth related indent
+    return format("spaces[%s]",tonumber(f) or 0)
+end
+
+local extensions = { }
+
+local format_extension = function(name)
+    n = n + 1
+    local extension = extensions[name] or "tostring(%s)"
+    return format(extension,format("a%s",n))
+end
+
+function addextension(name,template,shortcuts)
+    extensions[name] = template
+    if shortcuts then
+        template_shortcuts = shortcuts .. "\n" .. template_shortcuts -- so we can't overload
+    end
+end
+
+lpeg.patterns.xmlescape = Cs((P("<")/"&lt;" + P(">")/"&gt;" + P("&")/"&amp;" + P('"')/"&quot;" + P(1))^0)
+lpeg.patterns.texescape = Cs((C(S("#$%\\{}"))/"\\%1" + P(1))^0)
+
+addextension("xml",[[lpegmatch(xmlescape,%s)]],[[local xmlescape = lpeg.patterns.xmlescape]])
+addextension("tex",[[lpegmatch(texescape,%s)]],[[local texescape = lpeg.patterns.texescape]])
+
+local builder = Cs { "start",
+    start = (
+        (
+            P("%") / ""
+          * (
+                V("!") -- new
+              + V("s") + V("q")
+              + V("i") + V("d")
+              + V("f") + V("g") + V("G") + V("e") + V("E")
+              + V("x") + V("X") + V("o")
+              --
+              + V("c")
+              + V("S") -- new
+              + V("Q") -- new
+              + V("N") -- new
+              --
+              + V("r")
+              + V("h") + V("H") + V("u") + V("U")
+              + V("p") + V("b")
+              + V("t")
+              + V("l") + V("L")
+              + V("I")
+              + V("h") -- new
+              + V("w") -- new
+              + V("W") -- new
+              --
+              + V("a") -- ignores probably messed up %
+            )
+          + V("a")
+        )
+--      * (P(-1) + Cc(".."))
+     * (P(-1) + Carg(1))
     )^0,
     --
     ["s"] = (prefix_any * P("s")) / format_s, -- %s => regular %s (string)
@@ -352,61 +453,132 @@ local builder = Ct { "start",
     ["X"] = (prefix_any * P("X")) / format_X, -- %X => regular %X (HEXADECIMAL)
     ["o"] = (prefix_any * P("o")) / format_o, -- %o => regular %o (octal)
     --
+    ["S"] = (prefix_any * P("S")) / format_S, -- %S => %s (tostring)
+    ["Q"] = (prefix_any * P("Q")) / format_S, -- %Q => %q (tostring)
+    ["N"] = (prefix_any * P("N")) / format_N, -- %N => tonumber (strips leading zeros)
     ["c"] = (prefix_any * P("c")) / format_c, -- %c => utf character (extension to regular)
     --
     ["r"] = (prefix_any * P("r")) / format_r, -- %r => round
-    ["v"] = (prefix_any * P("v")) / format_v, -- %v => 0x0a1b2 (when - no 0x)
-    ["V"] = (prefix_any * P("V")) / format_V, -- %V => 0x0A1B2 (when - no 0x)
+    ["h"] = (prefix_any * P("h")) / format_h, -- %h => 0x0a1b2 (when - no 0x) was v
+    ["H"] = (prefix_any * P("H")) / format_H, -- %H => 0x0A1B2 (when - no 0x) was V
     ["u"] = (prefix_any * P("u")) / format_u, -- %u => u+0a1b2 (when - no u+)
     ["U"] = (prefix_any * P("U")) / format_U, -- %U => U+0A1B2 (when - no U+)
     ["p"] = (prefix_any * P("p")) / format_p, -- %p => 12.345pt / maybe: P (and more units)
     ["b"] = (prefix_any * P("b")) / format_b, -- %b => 12.342bp / maybe: B (and more units)
     ["t"] = (prefix_tab * P("t")) / format_t, -- %t => concat
     ["l"] = (prefix_tab * P("l")) / format_l, -- %l => boolean
+    ["L"] = (prefix_tab * P("L")) / format_L, -- %L => BOOLEAN
     ["I"] = (prefix_any * P("I")) / format_I, -- %I => signed integer
     --
-    ["a"] = Cs(((1-P("%"))^1 + P("%%")/"%%")^1) / format_a, -- %a => text (including %%)
+    ["w"] = (prefix_any * P("w")) / format_w, -- %w => n spaces (optional prefix is added)
+    ["W"] = (prefix_any * P("W")) / format_W, -- %w => mandate prefix, no specifier
+    --
+    ["a"] = Cs(((1-P("%"))^1 + P("%%")/"%%%%")^1) / format_a, -- rest (including %%)
+    --
+ -- ["!"] = P("!xml!") / format_xml, -- %!xml! => hypertext escaped " < > &
+    ["!"] = P("!") * C((1-P("!"))^1) * P("!") / format_extension,
 }
 
 -- we can be clever and only alias what is needed
 
-local template = [[
-local format = string.format
-local concat = table.concat
-local signed = number.signed
-local points = number.points
-local basepoints = number.basepoints
-local utfchar = utf.char
-local utfbyte = utf.byte
-return function(...)
-    return %s
-end
+local direct = Cs (
+        P("%")/""
+      * Cc([[local format = string.format return function(str) return format("%]])
+      * C(S("+- .") + R("09"))^0 * S("sqidfgGeExXo")
+      * Cc([[",str) end]])
+      * P(-1)
+    )
+
+local template  = [[
+%s
+return function(%s) return %s end
 ]]
 
+local arguments = { "a1" } -- faster than previously used (select(n,...))
+
+setmetatable(arguments, { __index =
+     function(t,k)
+        local v = t[k-1] .. ",a" .. k
+        t[k] = v
+        return v
+    end
+})
+
 local function make(t,str)
-    n = 0
-    local p = lpegmatch(builder,str)
--- inspect(p)
-    local c = format(template,concat(p,".."))
--- inspect(c)
-    formatter = load(c)()
-    t[str] = formatter
-    return formatter
+    local f
+    local p = lpegmatch(direct,str)
+    if p then
+        f = loadstripped(p)()
+    else
+        n = 0
+        p = lpegmatch(builder,str,1,"..") -- after this we know n
+        if n > 0 then
+            p = format(template,template_shortcuts,arguments[n],p)
+         -- print("builder>",p)
+            f = loadstripped(p)()
+        else
+            f = function() return str end
+        end
+    end
+    t[str] = f
+    return f
+end
+
+local function use(t,fmt,...)
+    return t[fmt](...)
 end
 
 local formatters  = string.formatters or { }
 string.formatters = formatters
 
-setmetatableindex(formatters,make)
+setmetatable(formatters, { __index = make, __call = use })
 
-function string.makeformatter(str)
+-- -- yes or no:
+--
+-- local function make(t,str)
+--     local f
+--     local p = lpegmatch(direct,str)
+--     if p then
+--         f = loadstripped(p)()
+--     else
+--         n = 0
+--         p = lpegmatch(builder,str,1,",") -- after this we know n
+--         if n > 0 then
+--             p = format(template,template_shortcuts,arguments[n],p)
+--             f = loadstripped(p)()
+--         else
+--             f = function() return str end
+--         end
+--     end
+--     t[str] = f
+--     return f
+-- end
+--
+-- local formatteds  = string.formatteds or { }
+-- string.formatteds = formatteds
+--
+-- setmetatable(formatteds, { __index = make, __call = use })
+
+--
+
+-- print(formatters["hans %N and %N done"](123,"0123"))
+-- local test = formatters["1%%23%4w56%s78 %p %!xml! test and %!tex! more %s"]
+-- print(#string.dump(test))
+-- print(test(2,123,99999,"abc&def","# and $","okay","!!!"))
+-- local test = formatters["%s"]
+-- print(#string.dump(test))
+-- print(test("okay"))
+
+function string.makeformatter(str) -- redundant
     return formatters[str]
 end
 
-function string.formatter(str,...)
+function string.formatter(str,...) -- redundant
     return formatters[str](...)
 end
 
+string.addformatter = addextension
+
 -- local p1 = "%s test %f done %p and %c and %V or %+t or %%"
 -- local p2 = "%s test %f done %s and %s and 0x%05X or %s or %%"
 --
diff --git a/tex/context/base/util-tab.lua b/tex/context/base/util-tab.lua
index 539d70d1b..9efdae4d6 100644
--- a/tex/context/base/util-tab.lua
+++ b/tex/context/base/util-tab.lua
@@ -10,12 +10,13 @@ utilities        = utilities or {}
 utilities.tables = utilities.tables or { }
 local tables     = utilities.tables
 
-local format, gmatch, rep, gsub = string.format, string.gmatch, string.rep, string.gsub
+local format, gmatch, gsub = string.format, string.gmatch, string.gsub
 local concat, insert, remove = table.concat, table.insert, table.remove
 local setmetatable, getmetatable, tonumber, tostring = setmetatable, getmetatable, tonumber, tostring
-local type, next, rawset, tonumber, load, select = type, next, rawset, tonumber, load, select
-local lpegmatch, P, Cs = lpeg.match, lpeg.P, lpeg.Cs
-local serialize = table.serialize
+local type, next, rawset, tonumber, tostring, load, select = type, next, rawset, tonumber, tostring, load, select
+local lpegmatch, P, Cs, Cc = lpeg.match, lpeg.P, lpeg.Cs, lpeg.Cc
+local serialize, sortedkeys, sortedpairs = table.serialize, table.sortedkeys, table.sortedpairs
+local formatters = string.formatters
 
 local splitter = lpeg.tsplitat(".")
 
@@ -126,35 +127,131 @@ end
 
 -- experimental
 
+local escape = Cs(Cc('"') * ((P('"')/'""' + P(1))^0) * Cc('"'))
+
+function table.tocsv(t,specification)
+    if t and #t > 0 then
+        local result = { }
+        local r = { }
+        specification = specification or { }
+        local fields = specification.fields
+        if type(fields) ~= "string" then
+            fields = sortedkeys(t[1])
+        end
+        local separator = specification.separator or ","
+        if specification.preamble == true then
+            for f=1,#fields do
+                r[f] = lpegmatch(escape,tostring(fields[f]))
+            end
+            result[1] = concat(r,separator)
+        end
+        for i=1,#t do
+            local ti = t[i]
+            for f=1,#fields do
+                local field = ti[fields[f]]
+                if type(field) == "string" then
+                    r[f] = lpegmatch(escape,field)
+                else
+                    r[f] = tostring(field)
+                end
+            end
+            result[#result+1] = concat(r,separator)
+        end
+        return concat(result,"\n")
+    else
+        return ""
+    end
+end
+
+-- local nspaces = utilities.strings.newrepeater(" ")
+-- local escape  = Cs((P("<")/"&lt;" + P(">")/"&gt;" + P("&")/"&amp;" + P(1))^0)
+--
+-- local function toxml(t,d,result,step)
+--     for k, v in sortedpairs(t) do
+--         local s = nspaces[d]
+--         local tk = type(k)
+--         local tv = type(v)
+--         if tv == "table" then
+--             if tk == "number" then
+--                 result[#result+1] = format("%s<entry n='%s'>",s,k)
+--                 toxml(v,d+step,result,step)
+--                 result[#result+1] = format("%s</entry>",s,k)
+--             else
+--                 result[#result+1] = format("%s<%s>",s,k)
+--                 toxml(v,d+step,result,step)
+--                 result[#result+1] = format("%s</%s>",s,k)
+--             end
+--         elseif tv == "string" then
+--             if tk == "number" then
+--                 result[#result+1] = format("%s<entry n='%s'>%s</entry>",s,k,lpegmatch(escape,v),k)
+--             else
+--                 result[#result+1] = format("%s<%s>%s</%s>",s,k,lpegmatch(escape,v),k)
+--             end
+--         elseif tk == "number" then
+--             result[#result+1] = format("%s<entry n='%s'>%s</entry>",s,k,tostring(v),k)
+--         else
+--             result[#result+1] = format("%s<%s>%s</%s>",s,k,tostring(v),k)
+--         end
+--     end
+-- end
+--
+-- much faster
+
+local nspaces = utilities.strings.newrepeater(" ")
+
 local function toxml(t,d,result,step)
-    for k, v in table.sortedpairs(t) do
-        if type(v) == "table" then
-            if type(k) == "number" then
-                result[#result+1] = format("%s<entry n='%s'>",d,k)
-                toxml(v,d..step,result,step)
-                result[#result+1] = format("%s</entry>",d,k)
+    for k, v in sortedpairs(t) do
+        local s = nspaces[d] -- inlining this is somewhat faster but gives more formatters
+        local tk = type(k)
+        local tv = type(v)
+        if tv == "table" then
+            if tk == "number" then
+                result[#result+1] = formatters["%s<entry n='%s'>"](s,k)
+                toxml(v,d+step,result,step)
+                result[#result+1] = formatters["%s</entry>"](s,k)
+            else
+                result[#result+1] = formatters["%s<%s>"](s,k)
+                toxml(v,d+step,result,step)
+                result[#result+1] = formatters["%s</%s>"](s,k)
+            end
+        elseif tv == "string" then
+            if tk == "number" then
+                result[#result+1] = formatters["%s<entry n='%s'>%!xml!</entry>"](s,k,v,k)
             else
-                result[#result+1] = format("%s<%s>",d,k)
-                toxml(v,d..step,result,step)
-                result[#result+1] = format("%s</%s>",d,k)
+                result[#result+1] = formatters["%s<%s>%!xml!</%s>"](s,k,v,k)
             end
-        elseif type(k) == "number" then
-            result[#result+1] = format("%s<entry n='%s'>%s</entry>",d,k,v,k)
+        elseif tk == "number" then
+            result[#result+1] = formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k)
         else
-            result[#result+1] = format("%s<%s>%s</%s>",d,k,tostring(v),k)
+            result[#result+1] = formatters["%s<%s>%S</%s>"](s,k,v,k)
         end
     end
 end
 
-function table.toxml(t,name,nobanner,indent,spaces)
+-- function table.toxml(t,name,nobanner,indent,spaces)
+--     local noroot = name == false
+--     local result = (nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
+--     local indent = rep(" ",indent or 0)
+--     local spaces = rep(" ",spaces or 1)
+--     if noroot then
+--         toxml( t, inndent, result, spaces)
+--     else
+--         toxml( { [name or "root"] = t }, indent, result, spaces)
+--     end
+--     return concat(result,"\n")
+-- end
+
+function table.toxml(t,specification)
+    specification = specification or { }
+    local name   = specification.name
     local noroot = name == false
-    local result = (nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
-    local indent = rep(" ",indent or 0)
-    local spaces = rep(" ",spaces or 1)
+    local result = (specification.nobanner or noroot) and { } or { "<?xml version='1.0' standalone='yes' ?>" }
+    local indent = specification.indent or 0
+    local spaces = specification.spaces or 1
     if noroot then
-        toxml( t, inndent, result, spaces)
+        toxml( t, indent, result, spaces)
     else
-        toxml( { [name or "root"] = t }, indent, result, spaces)
+        toxml( { [name or "data"] = t }, indent, result, spaces)
     end
     return concat(result,"\n")
 end
@@ -204,27 +301,27 @@ local function fastserialize(t,r,outer) -- no mixes
             local v = t[i]
             local tv = type(v)
             if tv == "string" then
-                r[#r+1] = format("%q,",v)
+                r[#r+1] = formatters["%q,"](v)
             elseif tv == "number" then
-                r[#r+1] = format("%s,",v)
+                r[#r+1] = formatters["%s,"](v)
             elseif tv == "table" then
                 fastserialize(v,r)
             elseif tv == "boolean" then
-                r[#r+1] = format("%s,",tostring(v))
+                r[#r+1] = formatters["%S,"](v)
             end
         end
     else
         for k, v in next, t do
             local tv = type(v)
             if tv == "string" then
-                r[#r+1] = format("[%q]=%q,",k,v)
+                r[#r+1] = formatters["[%q]=%q,"](k,v)
             elseif tv == "number" then
-                r[#r+1] = format("[%q]=%s,",k,v)
+                r[#r+1] = formatters["[%q]=%s,"](k,v)
             elseif tv == "table" then
-                r[#r+1] = format("[%q]=",k)
+                r[#r+1] = formatters["[%q]="](k)
                 fastserialize(v,r)
             elseif tv == "boolean" then
-                r[#r+1] = format("[%q]=%s,",k,tostring(v))
+                r[#r+1] = formatters["[%q]=%S,"](k,v)
             end
         end
     end
diff --git a/tex/generic/context/luatex/luatex-fonts-merged.lua b/tex/generic/context/luatex/luatex-fonts-merged.lua
index 28859a963..1013f5467 100644
--- a/tex/generic/context/luatex/luatex-fonts-merged.lua
+++ b/tex/generic/context/luatex/luatex-fonts-merged.lua
@@ -1,6 +1,6 @@
 -- merged file : luatex-fonts-merged.lua
 -- parent file : luatex-fonts.lua
--- merge date  : 03/08/13 01:14:04
+-- merge date  : 03/10/13 14:36:13
 
 do -- begin closure to overcome local limits and interference
 
@@ -564,6 +564,15 @@ end
 function lpeg.times(pattern,n)
   return P(nextstep(n,2^16,{ "start",["1"]=pattern }))
 end
+local digit=R("09")
+local period=P(".")
+local zero=P("0")
+local trailingzeros=zero^0*-digit 
+local case_1=period*trailingzeros/""
+local case_2=period*(digit-trailingzeros)^1*(trailingzeros/"")
+local number=digit^1*(case_1+case_2)
+local stripper=Cs((number+1)^0)
+lpeg.patterns.stripzeros=stripper
 
 end -- closure
 
-- 
cgit v1.2.3