-- merged file : lualibs-extended-merged.lua -- parent file : lualibs-extended.lua -- merge date : Sat Jul 26 13:09:43 2014 do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['util-str']={ version=1.001, comment="companion to luat-lib.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } utilities=utilities or {} utilities.strings=utilities.strings or {} local strings=utilities.strings local format,gsub,rep,sub=string.format,string.gsub,string.rep,string.sub local load,dump=load,string.dump local tonumber,type,tostring=tonumber,type,tostring local unpack,concat=table.unpack,table.concat 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 loadstripped=nil if _LUAVERSION<5.2 then loadstripped=function(str,shortcuts) return load(str) end else loadstripped=function(str,shortcuts) if shortcuts then return load(dump(load(str),true),nil,nil,shortcuts) else return load(dump(load(str),true)) end end end if not number then number={} end local stripper=patterns.stripzeros local function points(n) n=tonumber(n) return (not n or n==0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536)) end local function basepoints(n) n=tonumber(n) return (not n or n==0) and "0bp" or lpegmatch(stripper,format("%.5fbp",n*(7200/7227)/65536)) end number.points=points number.basepoints=basepoints local rubish=patterns.spaceortab^0*patterns.newline local anyrubish=patterns.spaceortab+patterns.newline local anything=patterns.anything local stripped=(patterns.spaceortab^1/"")*patterns.newline local leading=rubish^0/"" local trailing=(anyrubish^1*patterns.endofstring)/"" local redundant=rubish^3/"\n" local pattern=Cs(leading*(trailing+redundant+stripped+anything)^0) function strings.collapsecrlf(str) return lpegmatch(pattern,str) end local repeaters={} function strings.newrepeater(str,offset) offset=offset or 0 local s=repeaters[str] if not s then s={} repeaters[str]=s end local t=s[offset] if t then return t end t={} setmetatable(t,{ __index=function(t,k) if not k then return "" end local n=k+offset local s=n>0 and rep(str,n) or "" t[k]=s return s end }) s[offset]=t return t end 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 end*Cs(( Cp()*patterns.tab/function(position) local current=(position-start+1)+extra local spaces=tab-(current-1)%tab if spaces>0 then extra=extra+spaces-1 return nspaces[spaces] else return "" end end+patterns.newline*Cp()/function(position) extra,start=0,position end+patterns.anything )^1) function strings.tabtospace(str,tab) return lpegmatch(pattern,str,1,tab or 7) end local newline=patterns.newline local endofstring=patterns.endofstring local whitespace=patterns.whitespace local spacer=patterns.spacer local space=spacer^0 local nospace=space/"" local endofline=nospace*newline local stripend=(whitespace^1*endofstring)/"" local normalline=(nospace*((1-space*(newline+endofstring))^1)*nospace) local stripempty=endofline^1/"" local normalempty=endofline^1 local singleempty=endofline*(endofline^0/"") local doubleempty=endofline*endofline^-1*(endofline^0/"") local stripstart=stripempty^0 local p_prune_normal=Cs (stripstart*(stripend+normalline+normalempty )^0 ) local p_prune_collapse=Cs (stripstart*(stripend+normalline+doubleempty )^0 ) local p_prune_noempty=Cs (stripstart*(stripend+normalline+singleempty )^0 ) local p_retain_normal=Cs ((normalline+normalempty )^0 ) local p_retain_collapse=Cs ((normalline+doubleempty )^0 ) local p_retain_noempty=Cs ((normalline+singleempty )^0 ) local striplinepatterns={ ["prune"]=p_prune_normal, ["prune and collapse"]=p_prune_collapse, ["prune and no empty"]=p_prune_noempty, ["retain"]=p_retain_normal, ["retain and collapse"]=p_retain_collapse, ["retain and no empty"]=p_retain_noempty, ["collapse"]=patterns.collapser, } strings.striplinepatterns=striplinepatterns function strings.striplines(str,how) return str and lpegmatch(how and striplinepatterns[how] or p_prune_collapse,str) or str end strings.striplong=strings.striplines function strings.nice(str) str=gsub(str,"[:%-+_]+"," ") return str end local n=0 local sequenced=table.sequenced function string.autodouble(s,sep) if s==nil then return '""' end local t=type(s) if t=="number" then return tostring(s) end if t=="table" then return ('"'..sequenced(s,sep or ",")..'"') end return ('"'..tostring(s)..'"') end function string.autosingle(s,sep) if s==nil then return "''" end local t=type(s) if t=="number" then return tostring(s) end if t=="table" then return ("'"..sequenced(s,sep or ",").."'") end return ("'"..tostring(s).."'") end local tracedchars={} string.tracedchars=tracedchars strings.tracers=tracedchars function string.tracedchar(b) if type(b)=="number" then return tracedchars[b] or (utfchar(b).." (U+"..format('%05X',b)..")") else local c=utfbyte(b) return tracedchars[c] or (b.." (U+"..format('%05X',c)..")") end end function number.signed(i) if i>0 then return "+",i else return "-",-i end end local zero=P("0")^1/"" local plus=P("+")/"" local minus=P("-") local separator=S(".") local digit=R("09") local trailing=zero^1*#S("eE") local exponent=(S("eE")*(plus+Cs((minus*zero^0*P(-1))/"")+minus)*zero^0*(P(-1)*Cc("0")+P(1)^1)) local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent) local pattern_b=Cs((exponent+P(1))^0) function number.sparseexponent(f,n) if not n then n=f f="%e" end local tn=type(n) if tn=="string" then local m=tonumber(n) if m then return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,m)) end elseif tn=="number" then return lpegmatch((f=="%e" or f=="%E") and pattern_a or pattern_b,format(f,n)) end return tostring(n) end local template=[[ %s %s return function(%s) return %s end ]] local preamble,environment="",{} if _LUAVERSION<5.2 then preamble=[[ local lpeg=lpeg local type=type local tostring=tostring local tonumber=tonumber 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 nspaces=string.nspaces local tracedchar=string.tracedchar local autosingle=string.autosingle local autodouble=string.autodouble local sequenced=table.sequenced local formattednumber=number.formatted local sparseexponent=number.sparseexponent ]] else environment={ global=global or _G, lpeg=lpeg, type=type, tostring=tostring, tonumber=tonumber, format=string.format, concat=table.concat, signed=number.signed, points=number.points, basepoints=number.basepoints, utfchar=utf.char, utfbyte=utf.byte, lpegmatch=lpeg.match, nspaces=string.nspaces, tracedchar=string.tracedchar, autosingle=string.autosingle, autodouble=string.autodouble, sequenced=table.sequenced, formattednumber=number.formatted, sparseexponent=number.sparseexponent, } end local arguments={ "a1" } setmetatable(arguments,{ __index=function(t,k) local v=t[k-1]..",a"..k t[k]=v return v end }) local prefix_any=C((S("+- .")+R("09"))^0) local prefix_tab=P("{")*C((1-P("}"))^0)*P("}")+C((1-R("az","AZ","09","%%"))^0) local format_s=function(f) n=n+1 if f and f~="" then return format("format('%%%ss',a%s)",f,n) else return format("(a%s or '')",n) end end local format_S=function(f) n=n+1 if f and f~="" then return format("format('%%%ss',tostring(a%s))",f,n) else return format("tostring(a%s)",n) end end local format_q=function() n=n+1 return format("(a%s and format('%%q',a%s) or '')",n,n) end local format_Q=function() 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',a%s)",f,n) else return format("format('%%i',a%s)",n) end end local format_d=format_i local format_I=function(f) n=n+1 return format("format('%%s%%%si',signed(a%s))",f,n) end local format_f=function(f) n=n+1 return format("format('%%%sf',a%s)",f,n) end local format_F=function() n=n+1 if not f or f=="" then return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n) else return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n) end end local format_g=function(f) n=n+1 return format("format('%%%sg',a%s)",f,n) end local format_G=function(f) n=n+1 return format("format('%%%sG',a%s)",f,n) end local format_e=function(f) n=n+1 return format("format('%%%se',a%s)",f,n) end local format_E=function(f) n=n+1 return format("format('%%%sE',a%s)",f,n) end local format_j=function(f) n=n+1 return format("sparseexponent('%%%se',a%s)",f,n) end local format_J=function(f) n=n+1 return format("sparseexponent('%%%sE',a%s)",f,n) end local format_x=function(f) n=n+1 return format("format('%%%sx',a%s)",f,n) end local format_X=function(f) n=n+1 return format("format('%%%sX',a%s)",f,n) end local format_o=function(f) n=n+1 return format("format('%%%so',a%s)",f,n) end local format_c=function() n=n+1 return format("utfchar(a%s)",n) end local format_C=function() n=n+1 return format("tracedchar(a%s)",n) end local format_r=function(f) n=n+1 return format("format('%%%s.0f',a%s)",f,n) end local format_h=function(f) n=n+1 if f=="-" then f=sub(f,2) return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) else return format("format('0x%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) end end local format_H=function(f) n=n+1 if f=="-" then f=sub(f,2) return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) else return format("format('0x%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) end end local format_u=function(f) n=n+1 if f=="-" then f=sub(f,2) return format("format('%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) else return format("format('u+%%%sx',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) end end local format_U=function(f) n=n+1 if f=="-" then f=sub(f,2) return format("format('%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) else return format("format('U+%%%sX',type(a%s) == 'number' and a%s or utfbyte(a%s))",f=="" and "05" or f,n,n,n) end end local format_p=function() n=n+1 return format("points(a%s)",n) end local format_b=function() n=n+1 return format("basepoints(a%s)",n) end local format_t=function(f) n=n+1 if f and f~="" then return format("concat(a%s,%q)",n,f) else return format("concat(a%s)",n) end end local format_T=function(f) n=n+1 if f and f~="" then return format("sequenced(a%s,%q)",n,f) else return format("sequenced(a%s)",n) end end local format_l=function() n=n+1 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() n=n+1 return format("tostring(tonumber(a%s) or a%s)",n,n) end local format_a=function(f) n=n+1 if f and f~="" then return format("autosingle(a%s,%q)",n,f) else return format("autosingle(a%s)",n) end end local format_A=function(f) n=n+1 if f and f~="" then return format("autodouble(a%s,%q)",n,f) else return format("autodouble(a%s)",n) end end local format_w=function(f) n=n+1 f=tonumber(f) if f then return format("nspaces[%s+a%s]",f,n) else return format("nspaces[a%s]",n) end end local format_W=function(f) return format("nspaces[%s]",tonumber(f) or 0) end local digit=patterns.digit local period=patterns.period local three=digit*digit*digit local splitter=Cs ( (((1-(three^1*period))^1+C(three))*(Carg(1)*three)^1+C((1-period)^1))*(P(1)/""*Carg(2))*C(2) ) patterns.formattednumber=splitter function number.formatted(n,sep1,sep2) local s=type(s)=="string" and n or format("%0.2f",n) if sep1==true then return lpegmatch(splitter,s,1,".",",") elseif sep1=="." then return lpegmatch(splitter,s,1,sep1,sep2 or ",") elseif sep1=="," then return lpegmatch(splitter,s,1,sep1,sep2 or ".") else return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") end end local format_m=function(f) n=n+1 if not f or f=="" then f="," end return format([[formattednumber(a%s,%q,".")]],n,f) end local format_M=function(f) n=n+1 if not f or f=="" then f="." end return format([[formattednumber(a%s,%q,",")]],n,f) end local format_z=function(f) n=n+(tonumber(f) or 1) return "''" end local format_rest=function(s) return format("%q",s) end local format_extension=function(extensions,f,name) local extension=extensions[name] or "tostring(%s)" local f=tonumber(f) or 1 if f==0 then return extension elseif f==1 then n=n+1 local a="a"..n return format(extension,a,a) elseif f<0 then local a="a"..(n+f+1) return format(extension,a,a) else local t={} for i=1,f do n=n+1 t[#t+1]="a"..n end return format(extension,unpack(t)) end end local builder=Cs { "start", start=( ( P("%")/""*( V("!") +V("s")+V("q")+V("i")+V("d")+V("f")+V("F")+V("g")+V("G")+V("e")+V("E")+V("x")+V("X")+V("o") +V("c")+V("C")+V("S") +V("Q") +V("N") +V("r")+V("h")+V("H")+V("u")+V("U")+V("p")+V("b")+V("t")+V("T")+V("l")+V("L")+V("I")+V("w") +V("W") +V("a") +V("A") +V("j")+V("J") +V("m")+V("M") +V("z") +V("*") )+V("*") )*(P(-1)+Carg(1)) )^0, ["s"]=(prefix_any*P("s"))/format_s, ["q"]=(prefix_any*P("q"))/format_q, ["i"]=(prefix_any*P("i"))/format_i, ["d"]=(prefix_any*P("d"))/format_d, ["f"]=(prefix_any*P("f"))/format_f, ["F"]=(prefix_any*P("F"))/format_F, ["g"]=(prefix_any*P("g"))/format_g, ["G"]=(prefix_any*P("G"))/format_G, ["e"]=(prefix_any*P("e"))/format_e, ["E"]=(prefix_any*P("E"))/format_E, ["x"]=(prefix_any*P("x"))/format_x, ["X"]=(prefix_any*P("X"))/format_X, ["o"]=(prefix_any*P("o"))/format_o, ["S"]=(prefix_any*P("S"))/format_S, ["Q"]=(prefix_any*P("Q"))/format_S, ["N"]=(prefix_any*P("N"))/format_N, ["c"]=(prefix_any*P("c"))/format_c, ["C"]=(prefix_any*P("C"))/format_C, ["r"]=(prefix_any*P("r"))/format_r, ["h"]=(prefix_any*P("h"))/format_h, ["H"]=(prefix_any*P("H"))/format_H, ["u"]=(prefix_any*P("u"))/format_u, ["U"]=(prefix_any*P("U"))/format_U, ["p"]=(prefix_any*P("p"))/format_p, ["b"]=(prefix_any*P("b"))/format_b, ["t"]=(prefix_tab*P("t"))/format_t, ["T"]=(prefix_tab*P("T"))/format_T, ["l"]=(prefix_any*P("l"))/format_l, ["L"]=(prefix_any*P("L"))/format_L, ["I"]=(prefix_any*P("I"))/format_I, ["w"]=(prefix_any*P("w"))/format_w, ["W"]=(prefix_any*P("W"))/format_W, ["j"]=(prefix_any*P("j"))/format_j, ["J"]=(prefix_any*P("J"))/format_J, ["m"]=(prefix_tab*P("m"))/format_m, ["M"]=(prefix_tab*P("M"))/format_M, ["z"]=(prefix_any*P("z"))/format_z, ["a"]=(prefix_any*P("a"))/format_a, ["A"]=(prefix_any*P("A"))/format_A, ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest, ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension, } local direct=Cs ( P("%")*(S("+- .")+R("09"))^0*S("sqidfgGeExXo")*P(-1)/[[local format = string.format return function(str) return format("%0",str) end]] ) local function make(t,str) local f local p local p=lpegmatch(direct,str) if p then f=loadstripped(p)() else n=0 p=lpegmatch(builder,str,1,t._connector_,t._extensions_) if n>0 then p=format(template,preamble,t._preamble_,arguments[n],p) f=loadstripped(p,t._environment_)() else f=function() return str end end end t[str]=f return f end local function use(t,fmt,...) return t[fmt](...) end strings.formatters={} if _LUAVERSION<5.2 then function strings.formatters.new(noconcat) local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_=preamble,_environment_={} } setmetatable(t,{ __index=make,__call=use }) return t end else function strings.formatters.new(noconcat) local e={} for k,v in next,environment do e[k]=v end local t={ _type_="formatter",_connector_=noconcat and "," or "..",_extensions_={},_preamble_="",_environment_=e } setmetatable(t,{ __index=make,__call=use }) return t end end local formatters=strings.formatters.new() string.formatters=formatters string.formatter=function(str,...) return formatters[str](...) end local function add(t,name,template,preamble) if type(t)=="table" and t._type_=="formatter" then t._extensions_[name]=template or "%s" if type(preamble)=="string" then t._preamble_=preamble.."\n"..t._preamble_ elseif type(preamble)=="table" then for k,v in next,preamble do t._environment_[k]=v end end end end strings.formatters.add=add patterns.xmlescape=Cs((P("<")/"<"+P(">")/">"+P("&")/"&"+P('"')/"""+P(1))^0) patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+P(1))^0) patterns.luaescape=Cs(((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0) patterns.luaquoted=Cs(Cc('"')*((1-S('"\n'))^1+P('"')/'\\"'+P('\n')/'\\n"')^0*Cc('"')) if _LUAVERSION<5.2 then add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape") add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape") add(formatters,"lua",[[lpegmatch(luaescape,%s)]],"local luaescape = lpeg.patterns.luaescape") else add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],{ xmlescape=lpeg.patterns.xmlescape }) add(formatters,"tex",[[lpegmatch(texescape,%s)]],{ texescape=lpeg.patterns.texescape }) add(formatters,"lua",[[lpegmatch(luaescape,%s)]],{ luaescape=lpeg.patterns.luaescape }) end local dquote=patterns.dquote local equote=patterns.escaped+dquote/'\\"'+1 local space=patterns.space local cquote=Cc('"') local pattern=Cs(dquote*(equote-P(-2))^0*dquote) +Cs(cquote*(equote-space)^0*space*equote^0*cquote) function string.optionalquoted(str) return lpegmatch(pattern,str) or str end end -- closure do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['util-tab']={ version=1.001, comment="companion to luat-lib.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } utilities=utilities or {} utilities.tables=utilities.tables or {} local tables=utilities.tables local format,gmatch,gsub,sub=string.format,string.gmatch,string.gsub,string.sub local concat,insert,remove=table.concat,table.insert,table.remove local setmetatable,getmetatable,tonumber,tostring=setmetatable,getmetatable,tonumber,tostring 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 sortedkeys,sortedpairs=table.sortedkeys,table.sortedpairs local formatters=string.formatters local utftoeight=utf.toeight local splitter=lpeg.tsplitat(".") function utilities.tables.definetable(target,nofirst,nolast) local composed,t=nil,{} local snippets=lpegmatch(splitter,target) for i=1,#snippets-(nolast and 1 or 0) do local name=snippets[i] if composed then composed=composed.."."..name t[#t+1]=formatters["if not %s then %s = { } end"](composed,composed) else composed=name if not nofirst then t[#t+1]=formatters["%s = %s or { }"](composed,composed) end end end if composed then if nolast then composed=composed.."."..snippets[#snippets] end return concat(t,"\n"),composed else return "",target end end function tables.definedtable(...) local t=_G for i=1,select("#",...) do local li=select(i,...) local tl=t[li] if not tl then tl={} t[li]=tl end t=tl end return t end function tables.accesstable(target,root) local t=root or _G for name in gmatch(target,"([^%.]+)") do t=t[name] if not t then return end end return t end function tables.migratetable(target,v,root) local t=root or _G local names=lpegmatch(splitter,target) for i=1,#names-1 do local name=names[i] t[name]=t[name] or {} t=t[name] if not t then return end end t[names[#names]]=v end function tables.removevalue(t,value) if value then for i=1,#t do if t[i]==value then remove(t,i) end end end end function tables.insertbeforevalue(t,value,extra) for i=1,#t do if t[i]==extra then remove(t,i) end end for i=1,#t do if t[i]==value then insert(t,i,extra) return end end insert(t,1,extra) end function tables.insertaftervalue(t,value,extra) for i=1,#t do if t[i]==extra then remove(t,i) end end for i=1,#t do if t[i]==value then insert(t,i+1,extra) return end end insert(t,#t+1,extra) end 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 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]=formatters["%s"](s,k) toxml(v,d+step,result,step) result[#result+1]=formatters["%s"](s,k) else result[#result+1]=formatters["%s<%s>"](s,k) toxml(v,d+step,result,step) result[#result+1]=formatters["%s"](s,k) end elseif tv=="string" then if tk=="number" then result[#result+1]=formatters["%s%!xml!"](s,k,v,k) else result[#result+1]=formatters["%s<%s>%!xml!"](s,k,v,k) end elseif tk=="number" then result[#result+1]=formatters["%s%S"](s,k,v,k) else result[#result+1]=formatters["%s<%s>%S"](s,k,v,k) end end end function table.toxml(t,specification) specification=specification or {} local name=specification.name local noroot=name==false local result=(specification.nobanner or noroot) and {} or { "" } local indent=specification.indent or 0 local spaces=specification.spaces or 1 if noroot then toxml(t,indent,result,spaces) else toxml({ [name or "data"]=t },indent,result,spaces) end return concat(result,"\n") end function tables.encapsulate(core,capsule,protect) if type(capsule)~="table" then protect=true capsule={} end for key,value in next,core do if capsule[key] then print(formatters["\ninvalid %s %a in %a"]("inheritance",key,core)) os.exit() else capsule[key]=value end end if protect then for key,value in next,core do core[key]=nil end setmetatable(core,{ __index=capsule, __newindex=function(t,key,value) if capsule[key] then print(formatters["\ninvalid %s %a' in %a"]("overload",key,core)) os.exit() else rawset(t,key,value) end end } ) end end local f_hashed_string=formatters["[%q]=%q,"] local f_hashed_number=formatters["[%q]=%s,"] local f_hashed_boolean=formatters["[%q]=%l,"] local f_hashed_table=formatters["[%q]="] local f_indexed_string=formatters["[%s]=%q,"] local f_indexed_number=formatters["[%s]=%s,"] local f_indexed_boolean=formatters["[%s]=%l,"] local f_indexed_table=formatters["[%s]="] local f_ordered_string=formatters["%q,"] local f_ordered_number=formatters["%s,"] local f_ordered_boolean=formatters["%l,"] function table.fastserialize(t,prefix) local r={ type(prefix)=="string" and prefix or "return" } local m=1 local function fastserialize(t,outer) local n=#t m=m+1 r[m]="{" if n>0 then for i=0,n do local v=t[i] local tv=type(v) if tv=="string" then m=m+1 r[m]=f_ordered_string(v) elseif tv=="number" then m=m+1 r[m]=f_ordered_number(v) elseif tv=="table" then fastserialize(v) elseif tv=="boolean" then m=m+1 r[m]=f_ordered_boolean(v) end end end for k,v in next,t do local tk=type(k) if tk=="number" then if k>n or k<0 then local tv=type(v) if tv=="string" then m=m+1 r[m]=f_indexed_string(k,v) elseif tv=="number" then m=m+1 r[m]=f_indexed_number(k,v) elseif tv=="table" then m=m+1 r[m]=f_indexed_table(k) fastserialize(v) elseif tv=="boolean" then m=m+1 r[m]=f_indexed_boolean(k,v) end end else local tv=type(v) if tv=="string" then m=m+1 r[m]=f_hashed_string(k,v) elseif tv=="number" then m=m+1 r[m]=f_hashed_number(k,v) elseif tv=="table" then m=m+1 r[m]=f_hashed_table(k) fastserialize(v) elseif tv=="boolean" then m=m+1 r[m]=f_hashed_boolean(k,v) end end end m=m+1 if outer then r[m]="}" else r[m]="}," end return r end return concat(fastserialize(t,true)) end function table.deserialize(str) if not str or str=="" then return end local code=load(str) if not code then return end code=code() if not code then return end return code end function table.load(filename,loader) if filename then local t=(loader or io.loaddata)(filename) if t and t~="" then local t=utftoeight(t) t=load(t) if type(t)=="function" then t=t() if type(t)=="table" then return t end end end end end function table.save(filename,t,n,...) io.savedata(filename,table.serialize(t,n==nil and true or n,...)) end local f_key_value=formatters["%s=%q"] local f_add_table=formatters[" {%t},\n"] local f_return_table=formatters["return {\n%t}"] local function slowdrop(t) local r={} local l={} for i=1,#t do local ti=t[i] local j=0 for k,v in next,ti do j=j+1 l[j]=f_key_value(k,v) end r[i]=f_add_table(l) end return f_return_table(r) end local function fastdrop(t) local r={ "return {\n" } local m=1 for i=1,#t do local ti=t[i] m=m+1 r[m]=" {" for k,v in next,ti do m=m+1 r[m]=f_key_value(k,v) end m=m+1 r[m]="},\n" end m=m+1 r[m]="}" return concat(r) end function table.drop(t,slow) if #t==0 then return "return { }" elseif slow==true then return slowdrop(t) else return fastdrop(t) end end function table.autokey(t,k) local v={} t[k]=v return v end local selfmapper={ __index=function(t,k) t[k]=k return k end } function table.twowaymapper(t) if not t then t={} else for i=0,#t do local ti=t[i] if ti then local i=tostring(i) t[i]=ti t[ti]=i end end t[""]=t[0] or "" end setmetatable(t,selfmapper) return t end local f_start_key_idx=formatters["%w{"] local f_start_key_num=formatters["%w[%s]={"] local f_start_key_str=formatters["%w[%q]={"] local f_start_key_boo=formatters["%w[%l]={"] local f_start_key_nop=formatters["%w{"] local f_stop=formatters["%w},"] local f_key_num_value_num=formatters["%w[%s]=%s,"] local f_key_str_value_num=formatters["%w[%q]=%s,"] local f_key_boo_value_num=formatters["%w[%l]=%s,"] local f_key_num_value_str=formatters["%w[%s]=%q,"] local f_key_str_value_str=formatters["%w[%q]=%q,"] local f_key_boo_value_str=formatters["%w[%l]=%q,"] local f_key_num_value_boo=formatters["%w[%s]=%l,"] local f_key_str_value_boo=formatters["%w[%q]=%l,"] local f_key_boo_value_boo=formatters["%w[%l]=%l,"] local f_key_num_value_not=formatters["%w[%s]={},"] local f_key_str_value_not=formatters["%w[%q]={},"] local f_key_boo_value_not=formatters["%w[%l]={},"] local f_key_num_value_seq=formatters["%w[%s]={ %, t },"] local f_key_str_value_seq=formatters["%w[%q]={ %, t },"] local f_key_boo_value_seq=formatters["%w[%l]={ %, t },"] local f_val_num=formatters["%w%s,"] local f_val_str=formatters["%w%q,"] local f_val_boo=formatters["%w%l,"] local f_val_not=formatters["%w{},"] local f_val_seq=formatters["%w{ %, t },"] local f_table_return=formatters["return {"] local f_table_name=formatters["%s={"] local f_table_direct=formatters["{"] local f_table_entry=formatters["[%q]={"] local f_table_finish=formatters["}"] local spaces=utilities.strings.newrepeater(" ") local serialize=table.serialize function table.serialize(root,name,specification) if type(specification)=="table" then return serialize(root,name,specification) end local t local n=1 local function simple_table(t) if #t>0 then local n=0 for _,v in next,t do n=n+1 if type(v)=="table" then return nil end end if n==#t then local tt={} local nt=0 for i=1,#t do local v=t[i] local tv=type(v) nt=nt+1 if tv=="number" then tt[nt]=v elseif tv=="string" then tt[nt]=format("%q",v) elseif tv=="boolean" then tt[nt]=v and "true" or "false" else return nil end end return tt end end return nil end local function do_serialize(root,name,depth,level,indexed) if level>0 then n=n+1 if indexed then t[n]=f_start_key_idx(depth) else local tn=type(name) if tn=="number" then t[n]=f_start_key_num(depth,name) elseif tn=="string" then t[n]=f_start_key_str(depth,name) elseif tn=="boolean" then t[n]=f_start_key_boo(depth,name) else t[n]=f_start_key_nop(depth) end end depth=depth+1 end if root and next(root) then local first=nil local last=0 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 local sk=sortedkeys(root) for i=1,#sk do local k=sk[i] local v=root[k] local tv=type(v) local tk=type(k) if first and tk=="number" and k>=first and k<=last then if tv=="number" then n=n+1 t[n]=f_val_num(depth,v) elseif tv=="string" then n=n+1 t[n]=f_val_str(depth,v) elseif tv=="table" then if not next(v) then n=n+1 t[n]=f_val_not(depth) else local st=simple_table(v) if st then n=n+1 t[n]=f_val_seq(depth,st) else do_serialize(v,k,depth,level+1,true) end end elseif tv=="boolean" then n=n+1 t[n]=f_val_boo(depth,v) end elseif tv=="number" then if tk=="number" then n=n+1 t[n]=f_key_num_value_num(depth,k,v) elseif tk=="string" then n=n+1 t[n]=f_key_str_value_num(depth,k,v) elseif tk=="boolean" then n=n+1 t[n]=f_key_boo_value_num(depth,k,v) end elseif tv=="string" then if tk=="number" then n=n+1 t[n]=f_key_num_value_str(depth,k,v) elseif tk=="string" then n=n+1 t[n]=f_key_str_value_str(depth,k,v) elseif tk=="boolean" then n=n+1 t[n]=f_key_boo_value_str(depth,k,v) end elseif tv=="table" then if not next(v) then if tk=="number" then n=n+1 t[n]=f_key_num_value_not(depth,k,v) elseif tk=="string" then n=n+1 t[n]=f_key_str_value_not(depth,k,v) elseif tk=="boolean" then n=n+1 t[n]=f_key_boo_value_not(depth,k,v) end else local st=simple_table(v) if not st then do_serialize(v,k,depth,level+1) elseif tk=="number" then n=n+1 t[n]=f_key_num_value_seq(depth,k,st) elseif tk=="string" then n=n+1 t[n]=f_key_str_value_seq(depth,k,st) elseif tk=="boolean" then n=n+1 t[n]=f_key_boo_value_seq(depth,k,st) end end elseif tv=="boolean" then if tk=="number" then n=n+1 t[n]=f_key_num_value_boo(depth,k,v) elseif tk=="string" then n=n+1 t[n]=f_key_str_value_boo(depth,k,v) elseif tk=="boolean" then n=n+1 t[n]=f_key_boo_value_boo(depth,k,v) end end end end if level>0 then n=n+1 t[n]=f_stop(depth-1) end end local tname=type(name) if tname=="string" then if name=="return" then t={ f_table_return() } else t={ f_table_name(name) } end elseif tname=="number" then t={ f_table_entry(name) } elseif tname=="boolean" then if name then t={ f_table_return() } else t={ f_table_direct() } end else t={ f_table_name("t") } end if root then if getmetatable(root) then local dummy=root._w_h_a_t_e_v_e_r_ root._w_h_a_t_e_v_e_r_=nil end if next(root) then do_serialize(root,name,1,0) end end n=n+1 t[n]=f_table_finish() return concat(t,"\n") end end -- closure do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['util-sto']={ version=1.001, comment="companion to luat-lib.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } local setmetatable,getmetatable,type=setmetatable,getmetatable,type utilities=utilities or {} utilities.storage=utilities.storage or {} local storage=utilities.storage function storage.mark(t) if not t then print("\nfatal error: storage cannot be marked\n") os.exit() return end local m=getmetatable(t) if not m then m={} setmetatable(t,m) end m.__storage__=true return t end function storage.allocate(t) t=t or {} local m=getmetatable(t) if not m then m={} setmetatable(t,m) end m.__storage__=true return t end function storage.marked(t) local m=getmetatable(t) return m and m.__storage__ end function storage.checked(t) if not t then report("\nfatal error: storage has not been allocated\n") os.exit() return end return t end function storage.setinitializer(data,initialize) local m=getmetatable(data) or {} m.__index=function(data,k) m.__index=nil initialize() return data[k] end setmetatable(data,m) end local keyisvalue={ __index=function(t,k) t[k]=k return k end } function storage.sparse(t) t=t or {} setmetatable(t,keyisvalue) return t end local function f_empty () return "" end local function f_self (t,k) t[k]=k return k end local function f_table (t,k) local v={} t[k]=v return v end local function f_number(t,k) t[k]=0 return 0 end local function f_ignore() end local f_index={ ["empty"]=f_empty, ["self"]=f_self, ["table"]=f_table, ["number"]=f_number, } local t_index={ ["empty"]={ __index=f_empty }, ["self"]={ __index=f_self }, ["table"]={ __index=f_table }, ["number"]={ __index=f_number }, } function table.setmetatableindex(t,f) if type(t)~="table" then f,t=t,{} end local m=getmetatable(t) if m then m.__index=f_index[f] or f else setmetatable(t,t_index[f] or { __index=f }) end return t end local f_index={ ["ignore"]=f_ignore, } local t_index={ ["ignore"]={ __newindex=f_ignore }, } function table.setmetatablenewindex(t,f) if type(t)~="table" then f,t=t,{} end local m=getmetatable(t) if m then m.__newindex=f_index[f] or f else setmetatable(t,t_index[f] or { __newindex=f }) end return t end function table.setmetatablecall(t,f) if type(t)~="table" then f,t=t,{} end local m=getmetatable(t) if m then m.__call=f else setmetatable(t,{ __call=f }) end return t end function table.setmetatablekey(t,key,value) local m=getmetatable(t) if not m then m={} setmetatable(t,m) end m[key]=value return t end function table.getmetatablekey(t,key,value) local m=getmetatable(t) return m and m[key] end end -- closure do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['util-prs']={ version=1.001, comment="companion to luat-lib.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } local lpeg,table,string=lpeg,table,string local P,R,V,S,C,Ct,Cs,Carg,Cc,Cg,Cf,Cp=lpeg.P,lpeg.R,lpeg.V,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cs,lpeg.Carg,lpeg.Cc,lpeg.Cg,lpeg.Cf,lpeg.Cp local lpegmatch,lpegpatterns=lpeg.match,lpeg.patterns local concat,gmatch,find=table.concat,string.gmatch,string.find local tostring,type,next,rawset=tostring,type,next,rawset local mod,div=math.mod,math.div utilities=utilities or {} local parsers=utilities.parsers or {} utilities.parsers=parsers local patterns=parsers.patterns or {} parsers.patterns=patterns local setmetatableindex=table.setmetatableindex local sortedhash=table.sortedhash local digit=R("09") local space=P(' ') local equal=P("=") local comma=P(",") local lbrace=P("{") local rbrace=P("}") local lparent=P("(") local rparent=P(")") local period=S(".") local punctuation=S(".,:;") local spacer=lpegpatterns.spacer local whitespace=lpegpatterns.whitespace local newline=lpegpatterns.newline local anything=lpegpatterns.anything local endofstring=lpegpatterns.endofstring local nobrace=1-(lbrace+rbrace ) local noparent=1-(lparent+rparent) local escape,left,right=P("\\"),P('{'),P('}') lpegpatterns.balanced=P { [1]=((escape*(left+right))+(1-(left+right))+V(2))^0, [2]=left*V(1)*right } local nestedbraces=P { lbrace*(nobrace+V(1))^0*rbrace } local nestedparents=P { lparent*(noparent+V(1))^0*rparent } local spaces=space^0 local argument=Cs((lbrace/"")*((nobrace+nestedbraces)^0)*(rbrace/"")) local content=(1-endofstring)^0 lpegpatterns.nestedbraces=nestedbraces lpegpatterns.nestedparents=nestedparents lpegpatterns.nested=nestedbraces lpegpatterns.argument=argument lpegpatterns.content=content local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-comma))^0) local key=C((1-equal-comma)^1) local pattern_a=(space+comma)^0*(key*equal*value+key*C("")) local pattern_c=(space+comma)^0*(key*equal*value) local key=C((1-space-equal-comma)^1) local pattern_b=spaces*comma^0*spaces*(key*((spaces*equal*spaces*value)+C(""))) local hash={} local function set(key,value) hash[key]=value end local pattern_a_s=(pattern_a/set)^1 local pattern_b_s=(pattern_b/set)^1 local pattern_c_s=(pattern_c/set)^1 patterns.settings_to_hash_a=pattern_a_s patterns.settings_to_hash_b=pattern_b_s patterns.settings_to_hash_c=pattern_c_s function parsers.make_settings_to_hash_pattern(set,how) if type(str)=="table" then return set elseif how=="strict" then return (pattern_c/set)^1 elseif how=="tolerant" then return (pattern_b/set)^1 else return (pattern_a/set)^1 end end function parsers.settings_to_hash(str,existing) if type(str)=="table" then if existing then for k,v in next,str do existing[k]=v end return exiting else return str end elseif str and str~="" then hash=existing or {} lpegmatch(pattern_a_s,str) return hash else return {} end end function parsers.settings_to_hash_tolerant(str,existing) if type(str)=="table" then if existing then for k,v in next,str do existing[k]=v end return exiting else return str end elseif str and str~="" then hash=existing or {} lpegmatch(pattern_b_s,str) return hash else return {} end end function parsers.settings_to_hash_strict(str,existing) if type(str)=="table" then if existing then for k,v in next,str do existing[k]=v end return exiting else return str end elseif str and str~="" then hash=existing or {} lpegmatch(pattern_c_s,str) return next(hash) and hash else return nil end end local separator=comma*space^0 local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-comma))^0) local pattern=spaces*Ct(value*(separator*value)^0) patterns.settings_to_array=pattern function parsers.settings_to_array(str,strict) if type(str)=="table" then return str elseif not str or str=="" then return {} elseif strict then if find(str,"{",1,true) then return lpegmatch(pattern,str) else return { str } end elseif find(str,",",1,true) then return lpegmatch(pattern,str) else return { str } end end local separator=space^0*comma*space^0 local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C((nestedbraces+(1-(space^0*(comma+P(-1)))))^0) local withvalue=Carg(1)*value/function(f,s) return f(s) end local pattern_a=spaces*Ct(value*(separator*value)^0) local pattern_b=spaces*withvalue*(separator*withvalue)^0 function parsers.stripped_settings_to_array(str) if not str or str=="" then return {} else return lpegmatch(pattern_a,str) end end function parsers.process_stripped_settings(str,action) if not str or str=="" then return {} else return lpegmatch(pattern_b,str,1,action) end end local function set(t,v) t[#t+1]=v end local value=P(Carg(1)*value)/set local pattern=value*(separator*value)^0*Carg(1) function parsers.add_settings_to_array(t,str) return lpegmatch(pattern,str,nil,t) end function parsers.hash_to_string(h,separator,yes,no,strict,omit) if h then local t,tn,s={},0,table.sortedkeys(h) omit=omit and table.tohash(omit) for i=1,#s do local key=s[i] if not omit or not omit[key] then local value=h[key] if type(value)=="boolean" then if yes and no then if value then tn=tn+1 t[tn]=key..'='..yes elseif not strict then tn=tn+1 t[tn]=key..'='..no end elseif value or not strict then tn=tn+1 t[tn]=key..'='..tostring(value) end else tn=tn+1 t[tn]=key..'='..value end end end return concat(t,separator or ",") else return "" end end function parsers.array_to_string(a,separator) if a then return concat(a,separator or ",") else return "" end end function parsers.settings_to_set(str,t) t=t or {} for s in gmatch(str,"[^, ]+") do t[s]=true end return t end function parsers.simple_hash_to_string(h,separator) local t,tn={},0 for k,v in sortedhash(h) do if v then tn=tn+1 t[tn]=k end end return concat(t,separator or ",") end local str=C((1-whitespace-equal)^1) local setting=Cf(Carg(1)*(whitespace^0*Cg(str*whitespace^0*(equal*whitespace^0*str+Cc(""))))^1,rawset) local splitter=setting^1 function utilities.parsers.options_to_hash(str,target) return str and lpegmatch(splitter,str,1,target or {}) or {} end local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+C(digit^1*lparent*(noparent+nestedparents)^1*rparent)+C((nestedbraces+(1-comma))^1) local pattern_a=spaces*Ct(value*(separator*value)^0) local function repeater(n,str) if not n then return str else local s=lpegmatch(pattern_a,str) if n==1 then return unpack(s) else local t,tn={},0 for i=1,n do for j=1,#s do tn=tn+1 t[tn]=s[j] end end return unpack(t) end end end local value=P(lbrace*C((nobrace+nestedbraces)^0)*rbrace)+(C(digit^1)/tonumber*lparent*Cs((noparent+nestedparents)^1)*rparent)/repeater+C((nestedbraces+(1-comma))^1) local pattern_b=spaces*Ct(value*(separator*value)^0) function parsers.settings_to_array_with_repeat(str,expand) if expand then return lpegmatch(pattern_b,str) or {} else return lpegmatch(pattern_a,str) or {} end end local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace local pattern=Ct((space+value)^0) function parsers.arguments_to_table(str) return lpegmatch(pattern,str) end function parsers.getparameters(self,class,parentclass,settings) local sc=self[class] if not sc then sc={} self[class]=sc if parentclass then local sp=self[parentclass] if not sp then sp={} self[parentclass]=sp end setmetatableindex(sc,sp) end end parsers.settings_to_hash(settings,sc) end function parsers.listitem(str) return gmatch(str,"[^, ]+") end local pattern=Cs { "start", start=V("one")+V("two")+V("three"), rest=(Cc(",")*V("thousand"))^0*(P(".")+endofstring)*anything^0, thousand=digit*digit*digit, one=digit*V("rest"), two=digit*digit*V("rest"), three=V("thousand")*V("rest"), } lpegpatterns.splitthousands=pattern function parsers.splitthousands(str) return lpegmatch(pattern,str) or str end local optionalwhitespace=whitespace^0 lpegpatterns.words=Ct((Cs((1-punctuation-whitespace)^1)+anything)^1) lpegpatterns.sentences=Ct((optionalwhitespace*Cs((1-period)^0*period))^1) lpegpatterns.paragraphs=Ct((optionalwhitespace*Cs((whitespace^1*endofstring/""+1-(spacer^0*newline*newline))^1))^1) local dquote=P('"') local equal=P('=') local escape=P('\\') local separator=S(' ,') local key=C((1-equal)^1) local value=dquote*C((1-dquote-escape*dquote)^0)*dquote local pattern=Cf(Ct("")*(Cg(key*equal*value)*separator^0)^1,rawset)^0*P(-1) function parsers.keq_to_hash(str) if str and str~="" then return lpegmatch(pattern,str) else return {} end end local defaultspecification={ separator=",",quote='"' } function parsers.csvsplitter(specification) specification=specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification local separator=specification.separator local quotechar=specification.quote local separator=S(separator~="" and separator or ",") local whatever=C((1-separator-newline)^0) if quotechar and quotechar~="" then local quotedata=nil for chr in gmatch(quotechar,".") do local quotechar=P(chr) local quoteword=quotechar*C((1-quotechar)^0)*quotechar if quotedata then quotedata=quotedata+quoteword else quotedata=quoteword end end whatever=quotedata+whatever end local parser=Ct((Ct(whatever*(separator*whatever)^0)*S("\n\r")^1)^0 ) return function(data) return lpegmatch(parser,data) end end function parsers.rfc4180splitter(specification) specification=specification and table.setmetatableindex(specification,defaultspecification) or defaultspecification local separator=specification.separator local quotechar=P(specification.quote) local dquotechar=quotechar*quotechar /specification.quote local separator=S(separator~="" and separator or ",") local escaped=quotechar*Cs((dquotechar+(1-quotechar))^0)*quotechar local non_escaped=C((1-quotechar-newline-separator)^1) local field=escaped+non_escaped+Cc("") local record=Ct(field*(separator*field)^1) local headerline=record*Cp() local wholeblob=Ct((newline^-1*record)^0) return function(data,getheader) if getheader then local header,position=lpegmatch(headerline,data) local data=lpegmatch(wholeblob,data,position) return data,header else return lpegmatch(wholeblob,data) end end end local function ranger(first,last,n,action) if not first then elseif last==true then for i=first,n or first do action(i) end elseif last then for i=first,last do action(i) end else action(first) end end local cardinal=lpegpatterns.cardinal/tonumber local spacers=lpegpatterns.spacer^0 local endofstring=lpegpatterns.endofstring local stepper=spacers*(C(cardinal)*(spacers*S(":-")*spacers*(C(cardinal)+Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1 local stepper=spacers*(C(cardinal)*(spacers*S(":-")*spacers*(C(cardinal)+(P("*")+endofstring)*Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1*endofstring function parsers.stepper(str,n,action) if type(n)=="function" then lpegmatch(stepper,str,1,false,n or print) else lpegmatch(stepper,str,1,n,action or print) end end local pattern_math=Cs((P("%")/"\\percent "+P("^")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0) local pattern_text=Cs((P("%")/"\\percent "+(P("^")/"\\high")*Cc("{")*lpegpatterns.integer*Cc("}")+anything)^0) patterns.unittotex=pattern function parsers.unittotex(str,textmode) return lpegmatch(textmode and pattern_text or pattern_math,str) end local pattern=Cs((P("^")/""*lpegpatterns.integer*Cc("")+anything)^0) function parsers.unittoxml(str) return lpegmatch(pattern,str) end local cache={} local spaces=lpeg.patterns.space^0 local dummy=function() end table.setmetatableindex(cache,function(t,k) local separator=P(k) local value=(1-separator)^0 local pattern=spaces*C(value)*separator^0*Cp() t[k]=pattern return pattern end) local commalistiterator=cache[","] function utilities.parsers.iterator(str,separator) local n=#str if n==0 then return dummy else local pattern=separator and cache[separator] or commalistiterator local p=1 return function() if p<=n then local s,e=lpegmatch(pattern,str,p) if e then p=e return s end end end end end local function initialize(t,name) local source=t[name] if source then local result={} for k,v in next,t[name] do result[k]=v end return result else return {} end end local function fetch(t,name) return t[name] or {} end local function process(result,more) for k,v in next,more do result[k]=v end return result end local name=C((1-S(", "))^1) local parser=(Carg(1)*name/initialize)*(S(", ")^1*(Carg(1)*name/fetch))^0 local merge=Cf(parser,process) function utilities.parsers.mergehashes(hash,list) return lpegmatch(merge,list,1,hash) end function utilities.parsers.runtime(time) if not time then time=os.runtime() end local days=div(time,24*60*60) time=mod(time,24*60*60) local hours=div(time,60*60) time=mod(time,60*60) local minutes=div(time,60) local seconds=mod(time,60) return days,hours,minutes,seconds end local spacing=whitespace^0 local apply=P("->") local method=C((1-apply)^1) local token=lbrace*C((1-rbrace)^1)*rbrace+C(anything^1) local pattern=spacing*(method*spacing*apply+Carg(1))*spacing*token function utilities.parsers.splitmethod(str,default) if str then return lpegmatch(pattern,str,1,default or false) else return default or false,"" end end end -- closure do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['util-dim']={ version=1.001, comment="support for dimensions", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } local format,match,gsub,type,setmetatable=string.format,string.match,string.gsub,type,setmetatable local P,S,R,Cc,C,lpegmatch=lpeg.P,lpeg.S,lpeg.R,lpeg.Cc,lpeg.C,lpeg.match local allocate=utilities.storage.allocate local setmetatableindex=table.setmetatableindex local formatters=string.formatters local texget=tex and tex.get or function() return 65536*10*100 end local p_stripzeros=lpeg.patterns.stripzeros number=number or {} local number=number number.tonumberf=function(n) return lpegmatch(p_stripzeros,format("%.20f",n)) end number.tonumberg=function(n) return format("%.20g",n) end local dimenfactors=allocate { ["pt"]=1/65536, ["in"]=(100/7227)/65536, ["cm"]=(254/7227)/65536, ["mm"]=(2540/7227)/65536, ["sp"]=1, ["bp"]=(7200/7227)/65536, ["pc"]=(1/12)/65536, ["dd"]=(1157/1238)/65536, ["cc"]=(1157/14856)/65536, ["nd"]=(20320/21681)/65536, ["nc"]=(5080/65043)/65536 } local f_none=formatters["%s%s"] local f_true=formatters["%0.5f%s"] local function numbertodimen(n,unit,fmt) if type(n)=='string' then return n else unit=unit or 'pt' n=n*dimenfactors[unit] if not fmt then fmt=f_none(n,unit) elseif fmt==true then fmt=f_true(n,unit) else return formatters[fmt](n,unit) end end end number.maxdimen=1073741823 number.todimen=numbertodimen number.dimenfactors=dimenfactors function number.topoints (n,fmt) return numbertodimen(n,"pt",fmt) end function number.toinches (n,fmt) return numbertodimen(n,"in",fmt) end function number.tocentimeters (n,fmt) return numbertodimen(n,"cm",fmt) end function number.tomillimeters (n,fmt) return numbertodimen(n,"mm",fmt) end function number.toscaledpoints(n,fmt) return numbertodimen(n,"sp",fmt) end function number.toscaledpoints(n) return n.."sp" end function number.tobasepoints (n,fmt) return numbertodimen(n,"bp",fmt) end function number.topicas (n,fmt) return numbertodimen(n "pc",fmt) end function number.todidots (n,fmt) return numbertodimen(n,"dd",fmt) end function number.tociceros (n,fmt) return numbertodimen(n,"cc",fmt) end function number.tonewdidots (n,fmt) return numbertodimen(n,"nd",fmt) end function number.tonewciceros (n,fmt) return numbertodimen(n,"nc",fmt) end local amount=(S("+-")^0*R("09")^0*P(".")^0*R("09")^0)+Cc("0") local unit=R("az")^1+P("%") local dimenpair=amount/tonumber*(unit^1/dimenfactors+Cc(1)) lpeg.patterns.dimenpair=dimenpair local splitter=amount/tonumber*C(unit^1) function number.splitdimen(str) return lpegmatch(splitter,str) end setmetatableindex(dimenfactors,function(t,s) return false end) local stringtodimen local amount=S("+-")^0*R("09")^0*S(".,")^0*R("09")^0 local unit=P("pt")+P("cm")+P("mm")+P("sp")+P("bp")+P("in")+P("pc")+P("dd")+P("cc")+P("nd")+P("nc") local validdimen=amount*unit lpeg.patterns.validdimen=validdimen local dimensions={} function dimensions.__add(a,b) local ta,tb=type(a),type(b) if ta=="string" then a=stringtodimen(a) elseif ta=="table" then a=a[1] end if tb=="string" then b=stringtodimen(b) elseif tb=="table" then b=b[1] end return setmetatable({ a+b },dimensions) end function dimensions.__sub(a,b) local ta,tb=type(a),type(b) if ta=="string" then a=stringtodimen(a) elseif ta=="table" then a=a[1] end if tb=="string" then b=stringtodimen(b) elseif tb=="table" then b=b[1] end return setmetatable({ a-b },dimensions) end function dimensions.__mul(a,b) local ta,tb=type(a),type(b) if ta=="string" then a=stringtodimen(a) elseif ta=="table" then a=a[1] end if tb=="string" then b=stringtodimen(b) elseif tb=="table" then b=b[1] end return setmetatable({ a*b },dimensions) end function dimensions.__div(a,b) local ta,tb=type(a),type(b) if ta=="string" then a=stringtodimen(a) elseif ta=="table" then a=a[1] end if tb=="string" then b=stringtodimen(b) elseif tb=="table" then b=b[1] end return setmetatable({ a/b },dimensions) end function dimensions.__unm(a) local ta=type(a) if ta=="string" then a=stringtodimen(a) elseif ta=="table" then a=a[1] end return setmetatable({-a },dimensions) end function dimensions.__lt(a,b) return a[1]1 then timer.timing=it-1 else local starttime=timer.starttime if starttime then local stoptime=clock() local loadtime=stoptime-starttime timer.stoptime=stoptime timer.loadtime=timer.loadtime+loadtime timer.timing=0 return loadtime end end return 0 end local function elapsed(instance) if type(instance)=="number" then return instance or 0 else local timer=timers[instance or "notimer"] return timer and timer.loadtime or 0 end end local function elapsedtime(instance) return format("%0.3f",elapsed(instance)) end local function elapsedindeed(instance) return elapsed(instance)>statistics.threshold end local function elapsedseconds(instance,rest) if elapsedindeed(instance) then return format("%0.3f seconds %s",elapsed(instance),rest or "") end end statistics.hastiming=hastiming statistics.resettiming=resettiming statistics.starttiming=starttiming statistics.stoptiming=stoptiming statistics.elapsed=elapsed statistics.elapsedtime=elapsedtime statistics.elapsedindeed=elapsedindeed statistics.elapsedseconds=elapsedseconds function statistics.register(tag,fnc) if statistics.enable and type(fnc)=="function" then local rt=registered[tag] or (#statusinfo+1) statusinfo[rt]={ tag,fnc } registered[tag]=rt if #tag>n then n=#tag end end end local report=logs.reporter("mkiv lua stats") function statistics.show() if statistics.enable then local register=statistics.register register("used platform",function() return format("%s, type: %s, binary subtree: %s", os.platform or "unknown",os.type or "unknown",environment.texos or "unknown") end) register("luatex banner",function() return lower(status.banner) end) register("control sequences",function() return format("%s of %s + %s",status.cs_count,status.hash_size,status.hash_extra) end) register("callbacks",function() local total,indirect=status.callbacks or 0,status.indirect_callbacks or 0 return format("%s direct, %s indirect, %s total",total-indirect,indirect,total) end) if jit then local jitstatus={ jit.status() } if jitstatus[1] then register("luajit options",concat(jitstatus," ",2)) end end register("lua properties",function() local list=status.list() local hashchar=tonumber(list.luatex_hashchars) local mask=lua.mask or "ascii" return format("engine: %s, used memory: %s, hash type: %s, hash chars: min(%s,40), symbol mask: %s (%s)", jit and "luajit" or "lua", statistics.memused(), list.luatex_hashtype or "default", hashchar and 2^hashchar or "unknown", mask, mask=="utf" and "τεχ" or "tex") end) register("runtime",statistics.runtime) logs.newline() for i=1,#statusinfo do local s=statusinfo[i] local r=s[2]() if r then report("%s: %s",s[1],r) end end statistics.enable=false end end function statistics.memused() local round=math.round or math.floor return format("%s MB (ctx: %s MB)",round(collectgarbage("count")/1000),round(status.luastate_bytes/1000000)) end starttiming(statistics) function statistics.formatruntime(runtime) return format("%s seconds",runtime) end function statistics.runtime() stoptiming(statistics) return statistics.formatruntime(elapsedtime(statistics)) end local report=logs.reporter("system") function statistics.timed(action) starttiming("run") action() stoptiming("run") report("total runtime: %s seconds",elapsedtime("run")) end function statistics.tracefunction(base,tag,...) for i=1,select("#",...) do local name=select(i,...) local stat={} local func=base[name] setmetatableindex(stat,function(t,k) t[k]=0 return 0 end) base[name]=function(n,k,v) stat[k]=stat[k]+1 return func(n,k,v) end statistics.register(formatters["%s.%s"](tag,name),function() return serialize(stat,"calls") end) end end commands=commands or {} function commands.resettimer(name) resettiming(name or "whatever") starttiming(name or "whatever") end function commands.elapsedtime(name) stoptiming(name or "whatever") context(elapsedtime(name or "whatever")) end end -- closure do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['util-lua']={ version=1.001, comment="companion to luat-lib.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", comment="the strip code is written by Peter Cawley", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } local rep,sub,byte,dump,format=string.rep,string.sub,string.byte,string.dump,string.format local load,loadfile,type=load,loadfile,type utilities=utilities or {} utilities.lua=utilities.lua or {} local luautilities=utilities.lua local report_lua=logs.reporter("system","lua") local tracestripping=false local forcestupidcompile=true luautilities.stripcode=true luautilities.alwaysstripcode=false luautilities.nofstrippedchunks=0 luautilities.nofstrippedbytes=0 local strippedchunks={} luautilities.strippedchunks=strippedchunks luautilities.suffixes={ tma="tma", tmc=jit and "tmb" or "tmc", lua="lua", luc=jit and "lub" or "luc", lui="lui", luv="luv", luj="luj", tua="tua", tuc="tuc", } local function register(name) if tracestripping then report_lua("stripped bytecode from %a",name or "unknown") end strippedchunks[#strippedchunks+1]=name luautilities.nofstrippedchunks=luautilities.nofstrippedchunks+1 end local function stupidcompile(luafile,lucfile,strip) local code=io.loaddata(luafile) if code and code~="" then code=load(code) if code then code=dump(code,strip and luautilities.stripcode or luautilities.alwaysstripcode) if code and code~="" then register(name) io.savedata(lucfile,code) return true,0 end else report_lua("fatal error %a in file %a",1,luafile) end else report_lua("fatal error %a in file %a",2,luafile) end return false,0 end function luautilities.loadedluacode(fullname,forcestrip,name) name=name or fullname local code,message if environment.loadpreprocessedfile then code,message=environment.loadpreprocessedfile(fullname) else code,message=loadfile(fullname) end if code then code() else report_lua("loading of file %a failed:\n\t%s",fullname,message or "no message") end if forcestrip and luautilities.stripcode then if type(forcestrip)=="function" then forcestrip=forcestrip(fullname) end if forcestrip or luautilities.alwaysstripcode then register(name) return load(dump(code,true)),0 else return code,0 end elseif luautilities.alwaysstripcode then register(name) return load(dump(code,true)),0 else return code,0 end end function luautilities.strippedloadstring(code,forcestrip,name) local code,message=load(code) if not code then report_lua("loading of file %a failed:\n\t%s",name,message or "no message") end if forcestrip and luautilities.stripcode or luautilities.alwaysstripcode then register(name) return load(dump(code,true)),0 else return code,0 end end function luautilities.compile(luafile,lucfile,cleanup,strip,fallback) report_lua("compiling %a into %a",luafile,lucfile) os.remove(lucfile) local done=stupidcompile(luafile,lucfile,strip~=false) if done then report_lua("dumping %a into %a stripped",luafile,lucfile) if cleanup==true and lfs.isfile(lucfile) and lfs.isfile(luafile) then report_lua("removing %a",luafile) os.remove(luafile) end end return done end function luautilities.loadstripped(...) local l=load(...) if l then return load(dump(l,true)) end end end -- closure do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['util-deb']={ version=1.001, comment="companion to luat-lib.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } local debug=require "debug" local getinfo=debug.getinfo local type,next,tostring=type,next,tostring local format,find=string.format,string.find local is_boolean=string.is_boolean utilities=utilities or {} local debugger=utilities.debugger or {} utilities.debugger=debugger local counters={} local names={} local report=logs.reporter("debugger") local function hook() local f=getinfo(2) if f then local n="unknown" if f.what=="C" then n=f.name or '' if not names[n] then names[n]=format("%42s",n) end else n=f.name or f.namewhat or f.what if not n or n=="" then n="?" end if not names[n] then names[n]=format("%42s : % 5i : %s",n,f.linedefined or 0,f.short_src or "unknown source") end end counters[n]=(counters[n] or 0)+1 end end function debugger.showstats(printer,threshold) printer=printer or report threshold=threshold or 0 local total,grandtotal,functions=0,0,0 local dataset={} for name,count in next,counters do dataset[#dataset+1]={ name,count } end table.sort(dataset,function(a,b) return a[2]==b[2] and b[1]>a[1] or a[2]>b[2] end) for i=1,#dataset do local d=dataset[i] local name=d[1] local count=d[2] if count>threshold and not find(name,"for generator") then printer(format("%8i %s\n",count,names[name])) total=total+count end grandtotal=grandtotal+count functions=functions+1 end printer("\n") printer(format("functions : % 10i\n",functions)) printer(format("total : % 10i\n",total)) printer(format("grand total: % 10i\n",grandtotal)) printer(format("threshold : % 10i\n",threshold)) end function debugger.savestats(filename,threshold) local f=io.open(filename,'w') if f then debugger.showstats(function(str) f:write(str) end,threshold) f:close() end end function debugger.enable() debug.sethook(hook,"c") end function debugger.disable() debug.sethook() end local function showtraceback(rep) local level=2 local reporter=rep or report while true do local info=getinfo(level,"Sl") if not info then break elseif info.what=="C" then reporter("%2i : %s",level-1,"C function") else reporter("%2i : %s : %s",level-1,info.short_src,info.currentline) end level=level+1 end end debugger.showtraceback=showtraceback end -- closure do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['util-tpl']={ version=1.001, comment="companion to luat-lib.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } utilities.templates=utilities.templates or {} local templates=utilities.templates local trace_template=false trackers.register("templates.trace",function(v) trace_template=v end) local report_template=logs.reporter("template") local tostring=tostring local format,sub,byte=string.format,string.sub,string.byte local P,C,R,Cs,Cc,Carg,lpegmatch,lpegpatterns=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Cc,lpeg.Carg,lpeg.match,lpeg.patterns local replacer local function replacekey(k,t,how,recursive) local v=t[k] if not v then if trace_template then report_template("unknown key %a",k) end return "" else v=tostring(v) if trace_template then report_template("setting key %a to value %a",k,v) end if recursive then return lpegmatch(replacer,v,1,t,how,recursive) else return v end end end local sqlescape=lpeg.replacer { { "'","''" }, { "\\","\\\\" }, { "\r\n","\\n" }, { "\r","\\n" }, } local sqlquoted=lpeg.Cs(lpeg.Cc("'")*sqlescape*lpeg.Cc("'")) lpegpatterns.sqlescape=sqlescape lpegpatterns.sqlquoted=sqlquoted local luaescape=lpegpatterns.luaescape local escapers={ lua=function(s) return lpegmatch(luaescape,s) end, sql=function(s) return lpegmatch(sqlescape,s) end, } local quotedescapers={ lua=function(s) return format("%q",s) end, sql=function(s) return lpegmatch(sqlquoted,s) end, } local luaescaper=escapers.lua local quotedluaescaper=quotedescapers.lua local function replacekeyunquoted(s,t,how,recurse) local escaper=how and escapers[how] or luaescaper return escaper(replacekey(s,t,how,recurse)) end local function replacekeyquoted(s,t,how,recurse) local escaper=how and quotedescapers[how] or quotedluaescaper return escaper(replacekey(s,t,how,recurse)) end local single=P("%") local double=P("%%") local lquoted=P("%[") local rquoted=P("]%") local lquotedq=P("%(") local rquotedq=P(")%") local escape=double/'%%' local nosingle=single/'' local nodouble=double/'' local nolquoted=lquoted/'' local norquoted=rquoted/'' local nolquotedq=lquotedq/'' local norquotedq=rquotedq/'' local key=nosingle*((C((1-nosingle )^1)*Carg(1)*Carg(2)*Carg(3))/replacekey )*nosingle local quoted=nolquotedq*((C((1-norquotedq)^1)*Carg(1)*Carg(2)*Carg(3))/replacekeyquoted )*norquotedq local unquoted=nolquoted*((C((1-norquoted )^1)*Carg(1)*Carg(2)*Carg(3))/replacekeyunquoted)*norquoted local any=P(1) replacer=Cs((unquoted+quoted+escape+key+any)^0) local function replace(str,mapping,how,recurse) if mapping and str then return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str else return str end end templates.replace=replace function templates.replacer(str,how,recurse) return function(mapping) return lpegmatch(replacer,str,1,mapping,how or "lua",recurse or false) or str end end function templates.load(filename,mapping,how,recurse) local data=io.loaddata(filename) or "" if mapping and next(mapping) then return replace(data,mapping,how,recurse) else return data end end function templates.resolve(t,mapping,how,recurse) if not mapping then mapping=t end for k,v in next,t do t[k]=replace(v,mapping,how,recurse) end return t end end -- closure do -- begin closure to overcome local limits and interference if not modules then modules={} end modules ['util-sta']={ version=1.001, comment="companion to util-ini.mkiv", author="Hans Hagen, PRAGMA-ADE, Hasselt NL", copyright="PRAGMA ADE / ConTeXt Development Team", license="see context related readme files" } local insert,remove,fastcopy,concat=table.insert,table.remove,table.fastcopy,table.concat local format=string.format local select,tostring=select,tostring local trace_stacker=false trackers.register("stacker.resolve",function(v) trace_stacker=v end) local stacker=stacker or {} utilities.stacker=stacker local function start(s,t,first,last) if s.mode=="switch" then local n=tostring(t[last]) if trace_stacker then s.report("start: %s",n) end return n else local r={} for i=first,last do r[#r+1]=tostring(t[i]) end local n=concat(r," ") if trace_stacker then s.report("start: %s",n) end return n end end local function stop(s,t,first,last) if s.mode=="switch" then local n=tostring(false) if trace_stacker then s.report("stop: %s",n) end return n else local r={} for i=last,first,-1 do r[#r+1]=tostring(false) end local n=concat(r," ") if trace_stacker then s.report("stop: %s",n) end return n end end local function change(s,t1,first1,last1,t2,first2,last2) if s.mode=="switch" then local n=tostring(t2[last2]) if trace_stacker then s.report("change: %s",n) end return n else local r={} for i=last1,first1,-1 do r[#r+1]=tostring(false) end local n=concat(r," ") for i=first2,last2 do r[#r+1]=tostring(t2[i]) end if trace_stacker then s.report("change: %s",n) end return n end end function stacker.new(name) local s local stack={} local list={} local ids={} local hash={} local hashing=true local function push(...) for i=1,select("#",...) do insert(stack,(select(i,...))) end if hashing then local c=concat(stack,"|") local n=hash[c] if not n then n=#list+1 hash[c]=n list[n]=fastcopy(stack) end insert(ids,n) return n else local n=#list+1 list[n]=fastcopy(stack) insert(ids,n) return n end end local function pop() remove(stack) remove(ids) return ids[#ids] or s.unset or -1 end local function clean() if #stack==0 then if trace_stacker then s.report("%s list entries, %s stack entries",#list,#stack) end end end local tops={} local top,switch local function resolve_begin(mode) if mode then switch=mode=="switch" else switch=s.mode=="switch" end top={ switch=switch } insert(tops,top) end local function resolve_step(ti) local result=nil local noftop=#top if ti>0 then local current=list[ti] if current then local noflist=#current local nofsame=0 if noflist>noftop then for i=1,noflist do if current[i]==top[i] then nofsame=i else break end end else for i=1,noflist do if current[i]==top[i] then nofsame=i else break end end end local plus=nofsame+1 if plus<=noftop then if plus<=noflist then if switch then result=s.change(s,top,plus,noftop,current,nofsame,noflist) else result=s.change(s,top,plus,noftop,current,plus,noflist) end else if switch then result=s.change(s,top,plus,noftop,current,nofsame,noflist) else result=s.stop(s,top,plus,noftop) end end elseif plus<=noflist then if switch then result=s.start(s,current,nofsame,noflist) else result=s.start(s,current,plus,noflist) end end top=current else if 1<=noftop then result=s.stop(s,top,1,noftop) end top={} end return result else if 1<=noftop then result=s.stop(s,top,1,noftop) end top={} return result end end local function resolve_end() local noftop=#top if noftop>0 then local result=s.stop(s,top,1,#top) remove(tops) top=tops[#tops] switch=top and top.switch return result end end local function resolve(t) resolve_begin() for i=1,#t do resolve_step(t[i]) end resolve_end() end local report=logs.reporter("stacker",name or nil) s={ name=name or "unknown", unset=-1, report=report, start=start, stop=stop, change=change, push=push, pop=pop, clean=clean, resolve=resolve, resolve_begin=resolve_begin, resolve_step=resolve_step, resolve_end=resolve_end, } return s end end -- closure