diff options
author | Karl Berry <karl@freefriends.org> | 2018-09-26 20:49:48 +0000 |
---|---|---|
committer | Karl Berry <karl@freefriends.org> | 2018-09-26 20:49:48 +0000 |
commit | 3a0140d05a7e481750ec23a30ed5cf3cdef61eb7 (patch) | |
tree | 8fac1dc16795d22db3b45205fa55db220a9beed4 /Master/texmf-dist/tex/luatex | |
parent | b573c63f79c24b5c8dff312e023c2745b49f6089 (diff) |
lualibs (26sep18)
git-svn-id: svn://tug.org/texlive/trunk@48770 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Master/texmf-dist/tex/luatex')
31 files changed, 11869 insertions, 993 deletions
diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic-merged.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic-merged.lua new file mode 100644 index 00000000000..8d3f87b9d66 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic-merged.lua @@ -0,0 +1,5552 @@ +-- merged file : lualibs-basic-merged.lua +-- parent file : lualibs-basic.lua +-- merge date : Fri Sep 21 15:12:31 2018 + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-lua']={ + 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 next,type,tonumber=next,type,tonumber +LUAMAJORVERSION,LUAMINORVERSION=string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") +LUAMAJORVERSION=tonumber(LUAMAJORVERSION) or 5 +LUAMINORVERSION=tonumber(LUAMINORVERSION) or 1 +LUAVERSION=LUAMAJORVERSION+LUAMINORVERSION/10 +if LUAVERSION<5.2 and jit then + MINORVERSION=2 + LUAVERSION=5.2 +end +_LUAVERSION=LUAVERSION +if not lpeg then + lpeg=require("lpeg") +end +if loadstring then + local loadnormal=load + function load(first,...) + if type(first)=="string" then + return loadstring(first,...) + else + return loadnormal(first,...) + end + end +else + loadstring=load +end +if not ipairs then + local function iterate(a,i) + i=i+1 + local v=a[i] + if v~=nil then + return i,v + end + end + function ipairs(a) + return iterate,a,0 + end +end +if not pairs then + function pairs(t) + return next,t + end +end +if not table.unpack then + table.unpack=_G.unpack +elseif not unpack then + _G.unpack=table.unpack +end +if not package.loaders then + package.loaders=package.searchers +end +local print,select,tostring=print,select,tostring +local inspectors={} +function setinspector(kind,inspector) + inspectors[kind]=inspector +end +function inspect(...) + for s=1,select("#",...) do + local value=select(s,...) + if value==nil then + print("nil") + else + local done=false + local kind=type(value) + local inspector=inspectors[kind] + if inspector then + done=inspector(value) + if done then + break + end + end + for kind,inspector in next,inspectors do + done=inspector(value) + if done then + break + end + end + if not done then + print(tostring(value)) + end + end + end +end +local dummy=function() end +function optionalrequire(...) + local ok,result=xpcall(require,dummy,...) + if ok then + return result + end +end +if lua then + lua.mask=load([[τεχ = 1]]) and "utf" or "ascii" +end +local flush=io.flush +if flush then + local execute=os.execute if execute then function os.execute(...) flush() return execute(...) end end + local exec=os.exec if exec then function os.exec (...) flush() return exec (...) end end + local spawn=os.spawn if spawn then function os.spawn (...) flush() return spawn (...) end end + local popen=io.popen if popen then function io.popen (...) flush() return popen (...) end end +end +FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load +if not FFISUPPORTED then + local okay;okay,ffi=pcall(require,"ffi") + FFISUPPORTED=type(ffi)=="table" and ffi.os~="" and ffi.arch~="" and ffi.load +end +if not FFISUPPORTED then + ffi=nil +elseif not ffi.number then + ffi.number=tonumber +end +if not bit32 then + bit32=require("l-bit32") +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-package']={ + 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 type=type +local gsub,format,find=string.gsub,string.format,string.find +local insert,remove=table.insert,table.remove +local P,S,Cs,lpegmatch=lpeg.P,lpeg.S,lpeg.Cs,lpeg.match +local package=package +local searchers=package.searchers or package.loaders +local filejoin=file and file.join or function(path,name) return path.."/"..name end +local isreadable=file and file.is_readable or function(name) local f=io.open(name) if f then f:close() return true end end +local addsuffix=file and file.addsuffix or function(name,suffix) return name.."."..suffix end +local function cleanpath(path) + return path +end +local pattern=Cs((((1-S("\\/"))^0*(S("\\/")^1/"/"))^0*(P(".")^1/"/"+P(1))^1)*-1) +local function lualibfile(name) + return lpegmatch(pattern,name) or name +end +local offset=luarocks and 1 or 0 +local helpers=package.helpers or { + cleanpath=cleanpath, + lualibfile=lualibfile, + trace=false, + report=function(...) print(format(...)) end, + builtin={ + ["preload table"]=searchers[1+offset], + ["path specification"]=searchers[2+offset], + ["cpath specification"]=searchers[3+offset], + ["all in one fallback"]=searchers[4+offset], + }, + methods={}, + sequence={ + "already loaded", + "preload table", + "qualified path", + "lua extra list", + "lib extra list", + "path specification", + "cpath specification", + "all in one fallback", + "not loaded", + } +} +package.helpers=helpers +local methods=helpers.methods +local builtin=helpers.builtin +local extraluapaths={} +local extralibpaths={} +local luapaths=nil +local libpaths=nil +local oldluapath=nil +local oldlibpath=nil +local nofextralua=-1 +local nofextralib=-1 +local nofpathlua=-1 +local nofpathlib=-1 +local function listpaths(what,paths) + local nofpaths=#paths + if nofpaths>0 then + for i=1,nofpaths do + helpers.report("using %s path %i: %s",what,i,paths[i]) + end + else + helpers.report("no %s paths defined",what) + end + return nofpaths +end +local function getextraluapaths() + if helpers.trace and #extraluapaths~=nofextralua then + nofextralua=listpaths("extra lua",extraluapaths) + end + return extraluapaths +end +local function getextralibpaths() + if helpers.trace and #extralibpaths~=nofextralib then + nofextralib=listpaths("extra lib",extralibpaths) + end + return extralibpaths +end +local function getluapaths() + local luapath=package.path or "" + if oldluapath~=luapath then + luapaths=file.splitpath(luapath,";") + oldluapath=luapath + nofpathlua=-1 + end + if helpers.trace and #luapaths~=nofpathlua then + nofpathlua=listpaths("builtin lua",luapaths) + end + return luapaths +end +local function getlibpaths() + local libpath=package.cpath or "" + if oldlibpath~=libpath then + libpaths=file.splitpath(libpath,";") + oldlibpath=libpath + nofpathlib=-1 + end + if helpers.trace and #libpaths~=nofpathlib then + nofpathlib=listpaths("builtin lib",libpaths) + end + return libpaths +end +package.luapaths=getluapaths +package.libpaths=getlibpaths +package.extraluapaths=getextraluapaths +package.extralibpaths=getextralibpaths +local hashes={ + lua={}, + lib={}, +} +local function registerpath(tag,what,target,...) + local pathlist={... } + local cleanpath=helpers.cleanpath + local trace=helpers.trace + local report=helpers.report + local hash=hashes[what] + local function add(path) + local path=cleanpath(path) + if not hash[path] then + target[#target+1]=path + hash[path]=true + if trace then + report("registered %s path %s: %s",tag,#target,path) + end + else + if trace then + report("duplicate %s path: %s",tag,path) + end + end + end + for p=1,#pathlist do + local path=pathlist[p] + if type(path)=="table" then + for i=1,#path do + add(path[i]) + end + else + add(path) + end + end +end +local function pushpath(tag,what,target,path) + local path=helpers.cleanpath(path) + insert(target,1,path) + if helpers.trace then + helpers.report("pushing %s path in front: %s",tag,path) + end +end +local function poppath(tag,what,target) + local path=remove(target,1) + if helpers.trace then + if path then + helpers.report("popping %s path from front: %s",tag,path) + else + helpers.report("no %s path to pop",tag) + end + end +end +helpers.registerpath=registerpath +function package.extraluapath(...) + registerpath("extra lua","lua",extraluapaths,...) +end +function package.pushluapath(path) + pushpath("extra lua","lua",extraluapaths,path) +end +function package.popluapath() + poppath("extra lua","lua",extraluapaths) +end +function package.extralibpath(...) + registerpath("extra lib","lib",extralibpaths,...) +end +function package.pushlibpath(path) + pushpath("extra lib","lib",extralibpaths,path) +end +function package.poplibpath() + poppath("extra lib","lua",extralibpaths) +end +local function loadedaslib(resolved,rawname) + local base=gsub(rawname,"%.","_") + local init="luaopen_"..gsub(base,"%.","_") + if helpers.trace then + helpers.report("calling loadlib with '%s' with init '%s'",resolved,init) + end + return package.loadlib(resolved,init) +end +helpers.loadedaslib=loadedaslib +local function loadedbypath(name,rawname,paths,islib,what) + local trace=helpers.trace + for p=1,#paths do + local path=paths[p] + local resolved=filejoin(path,name) + if trace then + helpers.report("%s path, identifying '%s' on '%s'",what,name,path) + end + if isreadable(resolved) then + if trace then + helpers.report("%s path, '%s' found on '%s'",what,name,resolved) + end + if islib then + return loadedaslib(resolved,rawname) + else + return loadfile(resolved) + end + end + end +end +helpers.loadedbypath=loadedbypath +local function loadedbyname(name,rawname) + if find(name,"^/") or find(name,"^[a-zA-Z]:/") then + local trace=helpers.trace + if trace then + helpers.report("qualified name, identifying '%s'",what,name) + end + if isreadable(name) then + if trace then + helpers.report("qualified name, '%s' found",what,name) + end + return loadfile(name) + end + end +end +helpers.loadedbyname=loadedbyname +methods["already loaded"]=function(name) + return package.loaded[name] +end +methods["preload table"]=function(name) + return builtin["preload table"](name) +end +methods["qualified path"]=function(name) + return loadedbyname(addsuffix(lualibfile(name),"lua"),name) +end +methods["lua extra list"]=function(name) + return loadedbypath(addsuffix(lualibfile(name),"lua"),name,getextraluapaths(),false,"lua") +end +methods["lib extra list"]=function(name) + return loadedbypath(addsuffix(lualibfile(name),os.libsuffix),name,getextralibpaths(),true,"lib") +end +methods["path specification"]=function(name) + getluapaths() + return builtin["path specification"](name) +end +methods["cpath specification"]=function(name) + getlibpaths() + return builtin["cpath specification"](name) +end +methods["all in one fallback"]=function(name) + return builtin["all in one fallback"](name) +end +methods["not loaded"]=function(name) + if helpers.trace then + helpers.report("unable to locate '%s'",name or "?") + end + return nil +end +local level=0 +local used={} +helpers.traceused=false +function helpers.loaded(name) + local sequence=helpers.sequence + level=level+1 + for i=1,#sequence do + local method=sequence[i] + if helpers.trace then + helpers.report("%s, level '%s', method '%s', name '%s'","locating",level,method,name) + end + local result,rest=methods[method](name) + if type(result)=="function" then + if helpers.trace then + helpers.report("%s, level '%s', method '%s', name '%s'","found",level,method,name) + end + if helpers.traceused then + used[#used+1]={ level=level,name=name } + end + level=level-1 + return result,rest + end + end + level=level-1 + return nil +end +function helpers.showused() + local n=#used + if n>0 then + helpers.report("%s libraries loaded:",n) + helpers.report() + for i=1,n do + local u=used[i] + helpers.report("%i %a",u.level,u.name) + end + helpers.report() + end +end +function helpers.unload(name) + if helpers.trace then + if package.loaded[name] then + helpers.report("unloading, name '%s', %s",name,"done") + else + helpers.report("unloading, name '%s', %s",name,"not loaded") + end + end + package.loaded[name]=nil +end +table.insert(searchers,1,helpers.loaded) +if context then + package.path="" +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-lpeg']={ + 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" +} +lpeg=require("lpeg") +local lpeg=lpeg +if not lpeg.print then function lpeg.print(...) print(lpeg.pcode(...)) end end +local type,next,tostring=type,next,tostring +local byte,char,gmatch,format=string.byte,string.char,string.gmatch,string.format +local floor=math.floor +local P,R,S,V,Ct,C,Cs,Cc,Cp,Cmt=lpeg.P,lpeg.R,lpeg.S,lpeg.V,lpeg.Ct,lpeg.C,lpeg.Cs,lpeg.Cc,lpeg.Cp,lpeg.Cmt +local lpegtype,lpegmatch,lpegprint=lpeg.type,lpeg.match,lpeg.print +if setinspector then + setinspector("lpeg",function(v) if lpegtype(v) then lpegprint(v) return true end end) +end +lpeg.patterns=lpeg.patterns or {} +local patterns=lpeg.patterns +local anything=P(1) +local endofstring=P(-1) +local alwaysmatched=P(true) +patterns.anything=anything +patterns.endofstring=endofstring +patterns.beginofstring=alwaysmatched +patterns.alwaysmatched=alwaysmatched +local sign=S('+-') +local zero=P('0') +local digit=R('09') +local digits=digit^1 +local octdigit=R("07") +local octdigits=octdigit^1 +local lowercase=R("az") +local uppercase=R("AZ") +local underscore=P("_") +local hexdigit=digit+lowercase+uppercase +local hexdigits=hexdigit^1 +local cr,lf,crlf=P("\r"),P("\n"),P("\r\n") +local newline=P("\r")*(P("\n")+P(true))+P("\n") +local escaped=P("\\")*anything +local squote=P("'") +local dquote=P('"') +local space=P(" ") +local period=P(".") +local comma=P(",") +local utfbom_32_be=P('\000\000\254\255') +local utfbom_32_le=P('\255\254\000\000') +local utfbom_16_be=P('\254\255') +local utfbom_16_le=P('\255\254') +local utfbom_8=P('\239\187\191') +local utfbom=utfbom_32_be+utfbom_32_le+utfbom_16_be+utfbom_16_le+utfbom_8 +local utftype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8")+alwaysmatched*Cc("utf-8") +local utfstricttype=utfbom_32_be*Cc("utf-32-be")+utfbom_32_le*Cc("utf-32-le")+utfbom_16_be*Cc("utf-16-be")+utfbom_16_le*Cc("utf-16-le")+utfbom_8*Cc("utf-8") +local utfoffset=utfbom_32_be*Cc(4)+utfbom_32_le*Cc(4)+utfbom_16_be*Cc(2)+utfbom_16_le*Cc(2)+utfbom_8*Cc(3)+Cc(0) +local utf8next=R("\128\191") +patterns.utfbom_32_be=utfbom_32_be +patterns.utfbom_32_le=utfbom_32_le +patterns.utfbom_16_be=utfbom_16_be +patterns.utfbom_16_le=utfbom_16_le +patterns.utfbom_8=utfbom_8 +patterns.utf_16_be_nl=P("\000\r\000\n")+P("\000\r")+P("\000\n") +patterns.utf_16_le_nl=P("\r\000\n\000")+P("\r\000")+P("\n\000") +patterns.utf_32_be_nl=P("\000\000\000\r\000\000\000\n")+P("\000\000\000\r")+P("\000\000\000\n") +patterns.utf_32_le_nl=P("\r\000\000\000\n\000\000\000")+P("\r\000\000\000")+P("\n\000\000\000") +patterns.utf8one=R("\000\127") +patterns.utf8two=R("\194\223")*utf8next +patterns.utf8three=R("\224\239")*utf8next*utf8next +patterns.utf8four=R("\240\244")*utf8next*utf8next*utf8next +patterns.utfbom=utfbom +patterns.utftype=utftype +patterns.utfstricttype=utfstricttype +patterns.utfoffset=utfoffset +local utf8char=patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four +local validutf8char=utf8char^0*endofstring*Cc(true)+Cc(false) +local utf8character=P(1)*R("\128\191")^0 +patterns.utf8=utf8char +patterns.utf8char=utf8char +patterns.utf8character=utf8character +patterns.validutf8=validutf8char +patterns.validutf8char=validutf8char +local eol=S("\n\r") +local spacer=S(" \t\f\v") +local whitespace=eol+spacer +local nonspacer=1-spacer +local nonwhitespace=1-whitespace +patterns.eol=eol +patterns.spacer=spacer +patterns.whitespace=whitespace +patterns.nonspacer=nonspacer +patterns.nonwhitespace=nonwhitespace +local stripper=spacer^0*C((spacer^0*nonspacer^1)^0) +local fullstripper=whitespace^0*C((whitespace^0*nonwhitespace^1)^0) +local collapser=Cs(spacer^0/""*nonspacer^0*((spacer^0/" "*nonspacer^1)^0)) +local nospacer=Cs((whitespace^1/""+nonwhitespace^1)^0) +local b_collapser=Cs(whitespace^0/""*(nonwhitespace^1+whitespace^1/" ")^0) +local e_collapser=Cs((whitespace^1*endofstring/""+nonwhitespace^1+whitespace^1/" ")^0) +local m_collapser=Cs((nonwhitespace^1+whitespace^1/" ")^0) +local b_stripper=Cs(spacer^0/""*(nonspacer^1+spacer^1/" ")^0) +local e_stripper=Cs((spacer^1*endofstring/""+nonspacer^1+spacer^1/" ")^0) +local m_stripper=Cs((nonspacer^1+spacer^1/" ")^0) +patterns.stripper=stripper +patterns.fullstripper=fullstripper +patterns.collapser=collapser +patterns.nospacer=nospacer +patterns.b_collapser=b_collapser +patterns.m_collapser=m_collapser +patterns.e_collapser=e_collapser +patterns.b_stripper=b_stripper +patterns.m_stripper=m_stripper +patterns.e_stripper=e_stripper +patterns.lowercase=lowercase +patterns.uppercase=uppercase +patterns.letter=patterns.lowercase+patterns.uppercase +patterns.space=space +patterns.tab=P("\t") +patterns.spaceortab=patterns.space+patterns.tab +patterns.newline=newline +patterns.emptyline=newline^1 +patterns.equal=P("=") +patterns.comma=comma +patterns.commaspacer=comma*spacer^0 +patterns.period=period +patterns.colon=P(":") +patterns.semicolon=P(";") +patterns.underscore=underscore +patterns.escaped=escaped +patterns.squote=squote +patterns.dquote=dquote +patterns.nosquote=(escaped+(1-squote))^0 +patterns.nodquote=(escaped+(1-dquote))^0 +patterns.unsingle=(squote/"")*patterns.nosquote*(squote/"") +patterns.undouble=(dquote/"")*patterns.nodquote*(dquote/"") +patterns.unquoted=patterns.undouble+patterns.unsingle +patterns.unspacer=((patterns.spacer^1)/"")^0 +patterns.singlequoted=squote*patterns.nosquote*squote +patterns.doublequoted=dquote*patterns.nodquote*dquote +patterns.quoted=patterns.doublequoted+patterns.singlequoted +patterns.digit=digit +patterns.digits=digits +patterns.octdigit=octdigit +patterns.octdigits=octdigits +patterns.hexdigit=hexdigit +patterns.hexdigits=hexdigits +patterns.sign=sign +patterns.cardinal=digits +patterns.integer=sign^-1*digits +patterns.unsigned=digit^0*period*digits +patterns.float=sign^-1*patterns.unsigned +patterns.cunsigned=digit^0*comma*digits +patterns.cpunsigned=digit^0*(period+comma)*digits +patterns.cfloat=sign^-1*patterns.cunsigned +patterns.cpfloat=sign^-1*patterns.cpunsigned +patterns.number=patterns.float+patterns.integer +patterns.cnumber=patterns.cfloat+patterns.integer +patterns.cpnumber=patterns.cpfloat+patterns.integer +patterns.oct=zero*octdigits +patterns.octal=patterns.oct +patterns.HEX=zero*P("X")*(digit+uppercase)^1 +patterns.hex=zero*P("x")*(digit+lowercase)^1 +patterns.hexadecimal=zero*S("xX")*hexdigits +patterns.hexafloat=sign^-1*zero*S("xX")*(hexdigit^0*period*hexdigits+hexdigits*period*hexdigit^0+hexdigits)*(S("pP")*sign^-1*hexdigits)^-1 +patterns.decafloat=sign^-1*(digit^0*period*digits+digits*period*digit^0+digits)*S("eE")*sign^-1*digits +patterns.propername=(uppercase+lowercase+underscore)*(uppercase+lowercase+underscore+digit)^0*endofstring +patterns.somecontent=(anything-newline-space)^1 +patterns.beginline=#(1-newline) +patterns.longtostring=Cs(whitespace^0/""*((patterns.quoted+nonwhitespace^1+whitespace^1/""*(endofstring+Cc(" ")))^0)) +function anywhere(pattern) + return (1-P(pattern))^0*P(pattern) +end +lpeg.anywhere=anywhere +function lpeg.instringchecker(p) + p=anywhere(p) + return function(str) + return lpegmatch(p,str) and true or false + end +end +function lpeg.splitter(pattern,action) + return (((1-P(pattern))^1)/action+1)^0 +end +function lpeg.tsplitter(pattern,action) + return Ct((((1-P(pattern))^1)/action+1)^0) +end +local splitters_s,splitters_m,splitters_t={},{},{} +local function splitat(separator,single) + local splitter=(single and splitters_s[separator]) or splitters_m[separator] + if not splitter then + separator=P(separator) + local other=C((1-separator)^0) + if single then + local any=anything + splitter=other*(separator*C(any^0)+"") + splitters_s[separator]=splitter + else + splitter=other*(separator*other)^0 + splitters_m[separator]=splitter + end + end + return splitter +end +local function tsplitat(separator) + local splitter=splitters_t[separator] + if not splitter then + splitter=Ct(splitat(separator)) + splitters_t[separator]=splitter + end + return splitter +end +lpeg.splitat=splitat +lpeg.tsplitat=tsplitat +function string.splitup(str,separator) + if not separator then + separator="," + end + return lpegmatch(splitters_m[separator] or splitat(separator),str) +end +local cache={} +function lpeg.split(separator,str) + local c=cache[separator] + if not c then + c=tsplitat(separator) + cache[separator]=c + end + return lpegmatch(c,str) +end +function string.split(str,separator) + if separator then + local c=cache[separator] + if not c then + c=tsplitat(separator) + cache[separator]=c + end + return lpegmatch(c,str) + else + return { str } + end +end +local spacing=patterns.spacer^0*newline +local empty=spacing*Cc("") +local nonempty=Cs((1-spacing)^1)*spacing^-1 +local content=(empty+nonempty)^1 +patterns.textline=content +local linesplitter=tsplitat(newline) +patterns.linesplitter=linesplitter +function string.splitlines(str) + return lpegmatch(linesplitter,str) +end +local cache={} +function lpeg.checkedsplit(separator,str) + local c=cache[separator] + if not c then + separator=P(separator) + local other=C((1-separator)^1) + c=Ct(separator^0*other*(separator^1*other)^0) + cache[separator]=c + end + return lpegmatch(c,str) +end +function string.checkedsplit(str,separator) + local c=cache[separator] + if not c then + separator=P(separator) + local other=C((1-separator)^1) + c=Ct(separator^0*other*(separator^1*other)^0) + cache[separator]=c + end + return lpegmatch(c,str) +end +local function f2(s) local c1,c2=byte(s,1,2) return c1*64+c2-12416 end +local function f3(s) local c1,c2,c3=byte(s,1,3) return (c1*64+c2)*64+c3-925824 end +local function f4(s) local c1,c2,c3,c4=byte(s,1,4) return ((c1*64+c2)*64+c3)*64+c4-63447168 end +local utf8byte=patterns.utf8one/byte+patterns.utf8two/f2+patterns.utf8three/f3+patterns.utf8four/f4 +patterns.utf8byte=utf8byte +local cache={} +function lpeg.stripper(str) + if type(str)=="string" then + local s=cache[str] + if not s then + s=Cs(((S(str)^1)/""+1)^0) + cache[str]=s + end + return s + else + return Cs(((str^1)/""+1)^0) + end +end +local cache={} +function lpeg.keeper(str) + if type(str)=="string" then + local s=cache[str] + if not s then + s=Cs((((1-S(str))^1)/""+1)^0) + cache[str]=s + end + return s + else + return Cs((((1-str)^1)/""+1)^0) + end +end +function lpeg.frontstripper(str) + return (P(str)+P(true))*Cs(anything^0) +end +function lpeg.endstripper(str) + return Cs((1-P(str)*endofstring)^0) +end +function lpeg.replacer(one,two,makefunction,isutf) + local pattern + local u=isutf and utf8char or 1 + if type(one)=="table" then + local no=#one + local p=P(false) + if no==0 then + for k,v in next,one do + p=p+P(k)/v + end + pattern=Cs((p+u)^0) + elseif no==1 then + local o=one[1] + one,two=P(o[1]),o[2] + pattern=Cs((one/two+u)^0) + else + for i=1,no do + local o=one[i] + p=p+P(o[1])/o[2] + end + pattern=Cs((p+u)^0) + end + else + pattern=Cs((P(one)/(two or "")+u)^0) + end + if makefunction then + return function(str) + return lpegmatch(pattern,str) + end + else + return pattern + end +end +function lpeg.finder(lst,makefunction,isutf) + local pattern + if type(lst)=="table" then + pattern=P(false) + if #lst==0 then + for k,v in next,lst do + pattern=pattern+P(k) + end + else + for i=1,#lst do + pattern=pattern+P(lst[i]) + end + end + else + pattern=P(lst) + end + if isutf then + pattern=((utf8char or 1)-pattern)^0*pattern + else + pattern=(1-pattern)^0*pattern + end + if makefunction then + return function(str) + return lpegmatch(pattern,str) + end + else + return pattern + end +end +local splitters_f,splitters_s={},{} +function lpeg.firstofsplit(separator) + local splitter=splitters_f[separator] + if not splitter then + local pattern=P(separator) + splitter=C((1-pattern)^0) + splitters_f[separator]=splitter + end + return splitter +end +function lpeg.secondofsplit(separator) + local splitter=splitters_s[separator] + if not splitter then + local pattern=P(separator) + splitter=(1-pattern)^0*pattern*C(anything^0) + splitters_s[separator]=splitter + end + return splitter +end +local splitters_s,splitters_p={},{} +function lpeg.beforesuffix(separator) + local splitter=splitters_s[separator] + if not splitter then + local pattern=P(separator) + splitter=C((1-pattern)^0)*pattern*endofstring + splitters_s[separator]=splitter + end + return splitter +end +function lpeg.afterprefix(separator) + local splitter=splitters_p[separator] + if not splitter then + local pattern=P(separator) + splitter=pattern*C(anything^0) + splitters_p[separator]=splitter + end + return splitter +end +function lpeg.balancer(left,right) + left,right=P(left),P(right) + return P { left*((1-left-right)+V(1))^0*right } +end +function lpeg.counter(pattern,action) + local n=0 + local pattern=(P(pattern)/function() n=n+1 end+anything)^0 + if action then + return function(str) n=0;lpegmatch(pattern,str);action(n) end + else + return function(str) n=0;lpegmatch(pattern,str);return n end + end +end +utf=utf or (unicode and unicode.utf8) or {} +local utfcharacters=utf and utf.characters or string.utfcharacters +local utfgmatch=utf and utf.gmatch +local utfchar=utf and utf.char +lpeg.UP=lpeg.P +if utfcharacters then + function lpeg.US(str) + local p=P(false) + for uc in utfcharacters(str) do + p=p+P(uc) + end + return p + end +elseif utfgmatch then + function lpeg.US(str) + local p=P(false) + for uc in utfgmatch(str,".") do + p=p+P(uc) + end + return p + end +else + function lpeg.US(str) + local p=P(false) + local f=function(uc) + p=p+P(uc) + end + lpegmatch((utf8char/f)^0,str) + return p + end +end +local range=utf8byte*utf8byte+Cc(false) +function lpeg.UR(str,more) + local first,last + if type(str)=="number" then + first=str + last=more or first + else + first,last=lpegmatch(range,str) + if not last then + return P(str) + end + end + if first==last then + return P(str) + elseif utfchar and (last-first<8) then + local p=P(false) + for i=first,last do + p=p+P(utfchar(i)) + end + return p + else + local f=function(b) + return b>=first and b<=last + end + return utf8byte/f + end +end +function lpeg.is_lpeg(p) + return p and lpegtype(p)=="pattern" +end +function lpeg.oneof(list,...) + if type(list)~="table" then + list={ list,... } + end + local p=P(list[1]) + for l=2,#list do + p=p+P(list[l]) + end + return p +end +local sort=table.sort +local function copyindexed(old) + local new={} + for i=1,#old do + new[i]=old + end + return new +end +local function sortedkeys(tab) + local keys,s={},0 + for key,_ in next,tab do + s=s+1 + keys[s]=key + end + sort(keys) + return keys +end +function lpeg.append(list,pp,delayed,checked) + local p=pp + if #list>0 then + local keys=copyindexed(list) + sort(keys) + for i=#keys,1,-1 do + local k=keys[i] + if p then + p=P(k)+p + else + p=P(k) + end + end + elseif delayed then + local keys=sortedkeys(list) + if p then + for i=1,#keys,1 do + local k=keys[i] + local v=list[k] + p=P(k)/list+p + end + else + for i=1,#keys do + local k=keys[i] + local v=list[k] + if p then + p=P(k)+p + else + p=P(k) + end + end + if p then + p=p/list + end + end + elseif checked then + local keys=sortedkeys(list) + for i=1,#keys do + local k=keys[i] + local v=list[k] + if p then + if k==v then + p=P(k)+p + else + p=P(k)/v+p + end + else + if k==v then + p=P(k) + else + p=P(k)/v + end + end + end + else + local keys=sortedkeys(list) + for i=1,#keys do + local k=keys[i] + local v=list[k] + if p then + p=P(k)/v+p + else + p=P(k)/v + end + end + end + return p +end +local p_false=P(false) +local p_true=P(true) +local lower=utf and utf.lower or string.lower +local upper=utf and utf.upper or string.upper +function lpeg.setutfcasers(l,u) + lower=l or lower + upper=u or upper +end +local function make1(t,rest) + local p=p_false + local keys=sortedkeys(t) + for i=1,#keys do + local k=keys[i] + if k~="" then + local v=t[k] + if v==true then + p=p+P(k)*p_true + elseif v==false then + else + p=p+P(k)*make1(v,v[""]) + end + end + end + if rest then + p=p+p_true + end + return p +end +local function make2(t,rest) + local p=p_false + local keys=sortedkeys(t) + for i=1,#keys do + local k=keys[i] + if k~="" then + local v=t[k] + if v==true then + p=p+(P(lower(k))+P(upper(k)))*p_true + elseif v==false then + else + p=p+(P(lower(k))+P(upper(k)))*make2(v,v[""]) + end + end + end + if rest then + p=p+p_true + end + return p +end +local function utfchartabletopattern(list,insensitive) + local tree={} + local n=#list + if n==0 then + for s in next,list do + local t=tree + local p,pk + for c in gmatch(s,".") do + if t==true then + t={ [c]=true,[""]=true } + p[pk]=t + p=t + t=false + elseif t==false then + t={ [c]=false } + p[pk]=t + p=t + t=false + else + local tc=t[c] + if not tc then + tc=false + t[c]=false + end + p=t + t=tc + end + pk=c + end + if t==false then + p[pk]=true + elseif t==true then + else + t[""]=true + end + end + else + for i=1,n do + local s=list[i] + local t=tree + local p,pk + for c in gmatch(s,".") do + if t==true then + t={ [c]=true,[""]=true } + p[pk]=t + p=t + t=false + elseif t==false then + t={ [c]=false } + p[pk]=t + p=t + t=false + else + local tc=t[c] + if not tc then + tc=false + t[c]=false + end + p=t + t=tc + end + pk=c + end + if t==false then + p[pk]=true + elseif t==true then + else + t[""]=true + end + end + end + return (insensitive and make2 or make1)(tree) +end +lpeg.utfchartabletopattern=utfchartabletopattern +function lpeg.utfreplacer(list,insensitive) + local pattern=Cs((utfchartabletopattern(list,insensitive)/list+utf8character)^0) + return function(str) + return lpegmatch(pattern,str) or str + end +end +patterns.containseol=lpeg.finder(eol) +local function nextstep(n,step,result) + local m=n%step + local d=floor(n/step) + if d>0 then + local v=V(tostring(step)) + local s=result.start + for i=1,d do + if s then + s=v*s + else + s=v + end + end + result.start=s + end + if step>1 and result.start then + local v=V(tostring(step/2)) + result[tostring(step)]=v*v + end + if step>0 then + return nextstep(m,step/2,result) + else + return result + end +end +function lpeg.times(pattern,n) + return P(nextstep(n,2^16,{ "start",["1"]=pattern })) +end +do + local trailingzeros=zero^0*-digit + local stripper=Cs(( + digits*( + period*trailingzeros/""+period*(digit-trailingzeros)^1*(trailingzeros/"") + )+1 + )^0) + lpeg.patterns.stripzeros=stripper + local nonzero=digit-zero + local trailingzeros=zero^1*endofstring + local stripper=Cs((1-period)^0*( + period*trailingzeros/""+period*(nonzero^1+(trailingzeros/"")+zero^1)^0+endofstring + )) + lpeg.patterns.stripzero=stripper +end +local byte_to_HEX={} +local byte_to_hex={} +local byte_to_dec={} +local hex_to_byte={} +for i=0,255 do + local H=format("%02X",i) + local h=format("%02x",i) + local d=format("%03i",i) + local c=char(i) + byte_to_HEX[c]=H + byte_to_hex[c]=h + byte_to_dec[c]=d + hex_to_byte[h]=c + hex_to_byte[H]=c +end +local hextobyte=P(2)/hex_to_byte +local bytetoHEX=P(1)/byte_to_HEX +local bytetohex=P(1)/byte_to_hex +local bytetodec=P(1)/byte_to_dec +local hextobytes=Cs(hextobyte^0) +local bytestoHEX=Cs(bytetoHEX^0) +local bytestohex=Cs(bytetohex^0) +local bytestodec=Cs(bytetodec^0) +patterns.hextobyte=hextobyte +patterns.bytetoHEX=bytetoHEX +patterns.bytetohex=bytetohex +patterns.bytetodec=bytetodec +patterns.hextobytes=hextobytes +patterns.bytestoHEX=bytestoHEX +patterns.bytestohex=bytestohex +patterns.bytestodec=bytestodec +function string.toHEX(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestoHEX,s) + end +end +function string.tohex(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestohex,s) + end +end +function string.todec(s) + if not s or s=="" then + return s + else + return lpegmatch(bytestodec,s) + end +end +function string.tobytes(s) + if not s or s=="" then + return s + else + return lpegmatch(hextobytes,s) + end +end +local patterns={} +local function containsws(what) + local p=patterns[what] + if not p then + local p1=P(what)*(whitespace+endofstring)*Cc(true) + local p2=whitespace*P(p1) + p=P(p1)+P(1-p2)^0*p2+Cc(false) + patterns[what]=p + end + return p +end +lpeg.containsws=containsws +function string.containsws(str,what) + return lpegmatch(patterns[what] or containsws(what),str) +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-functions']={ + 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" +} +functions=functions or {} +function functions.dummy() end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-string']={ + 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 string=string +local sub,gmatch,format,char,byte,rep,lower=string.sub,string.gmatch,string.format,string.char,string.byte,string.rep,string.lower +local lpegmatch,patterns=lpeg.match,lpeg.patterns +local P,S,C,Ct,Cc,Cs=lpeg.P,lpeg.S,lpeg.C,lpeg.Ct,lpeg.Cc,lpeg.Cs +local unquoted=patterns.squote*C(patterns.nosquote)*patterns.squote+patterns.dquote*C(patterns.nodquote)*patterns.dquote +function string.unquoted(str) + return lpegmatch(unquoted,str) or str +end +function string.quoted(str) + return format("%q",str) +end +function string.count(str,pattern) + local n=0 + for _ in gmatch(str,pattern) do + n=n+1 + end + return n +end +function string.limit(str,n,sentinel) + if #str>n then + sentinel=sentinel or "..." + return sub(str,1,(n-#sentinel))..sentinel + else + return str + end +end +local stripper=patterns.stripper +local fullstripper=patterns.fullstripper +local collapser=patterns.collapser +local nospacer=patterns.nospacer +local longtostring=patterns.longtostring +function string.strip(str) + return str and lpegmatch(stripper,str) or "" +end +function string.fullstrip(str) + return str and lpegmatch(fullstripper,str) or "" +end +function string.collapsespaces(str) + return str and lpegmatch(collapser,str) or "" +end +function string.nospaces(str) + return str and lpegmatch(nospacer,str) or "" +end +function string.longtostring(str) + return str and lpegmatch(longtostring,str) or "" +end +local pattern=P(" ")^0*P(-1) +function string.is_empty(str) + if not str or str=="" then + return true + else + return lpegmatch(pattern,str) and true or false + end +end +local anything=patterns.anything +local allescapes=Cc("%")*S(".-+%?()[]*") +local someescapes=Cc("%")*S(".-+%()[]") +local matchescapes=Cc(".")*S("*?") +local pattern_a=Cs ((allescapes+anything )^0 ) +local pattern_b=Cs ((someescapes+matchescapes+anything )^0 ) +local pattern_c=Cs (Cc("^")*(someescapes+matchescapes+anything )^0*Cc("$") ) +function string.escapedpattern(str,simple) + return lpegmatch(simple and pattern_b or pattern_a,str) +end +function string.topattern(str,lowercase,strict) + if str=="" or type(str)~="string" then + return ".*" + elseif strict then + str=lpegmatch(pattern_c,str) + else + str=lpegmatch(pattern_b,str) + end + if lowercase then + return lower(str) + else + return str + end +end +function string.valid(str,default) + return (type(str)=="string" and str~="" and str) or default or nil +end +string.itself=function(s) return s end +local pattern_c=Ct(C(1)^0) +local pattern_b=Ct((C(1)/byte)^0) +function string.totable(str,bytes) + return lpegmatch(bytes and pattern_b or pattern_c,str) +end +local replacer=lpeg.replacer("@","%%") +function string.tformat(fmt,...) + return format(lpegmatch(replacer,fmt),...) +end +string.quote=string.quoted +string.unquote=string.unquoted +if not string.bytetable then + local limit=5000 + function string.bytetable(str) + local n=#str + if n>limit then + local t={ byte(str,1,limit) } + for i=limit+1,n do + t[i]=byte(str,i) + end + return t + else + return { byte(str,1,n) } + end + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-table']={ + 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 type,next,tostring,tonumber,select=type,next,tostring,tonumber,select +local table,string=table,string +local concat,sort,insert,remove=table.concat,table.sort,table.insert,table.remove +local format,lower,dump=string.format,string.lower,string.dump +local getmetatable,setmetatable=getmetatable,setmetatable +local getinfo=debug.getinfo +local lpegmatch,patterns=lpeg.match,lpeg.patterns +local floor=math.floor +local stripper=patterns.stripper +function table.getn(t) + return t and #t +end +function table.strip(tab) + local lst,l={},0 + for i=1,#tab do + local s=lpegmatch(stripper,tab[i]) or "" + if s=="" then + else + l=l+1 + lst[l]=s + end + end + return lst +end +function table.keys(t) + if t then + local keys,k={},0 + for key in next,t do + k=k+1 + keys[k]=key + end + return keys + else + return {} + end +end +local function compare(a,b) + local ta=type(a) + if ta=="number" then + local tb=type(b) + if ta==tb then + return a<b + elseif tb=="string" then + return tostring(a)<b + end + elseif ta=="string" then + local tb=type(b) + if ta==tb then + return a<b + else + return a<tostring(b) + end + end + return tostring(a)<tostring(b) +end +local function sortedkeys(tab) + if tab then + local srt,category,s={},0,0 + for key in next,tab do + s=s+1 + srt[s]=key + if category==3 then + elseif category==1 then + if type(key)~="string" then + category=3 + end + elseif category==2 then + if type(key)~="number" then + category=3 + end + else + local tkey=type(key) + if tkey=="string" then + category=1 + elseif tkey=="number" then + category=2 + else + category=3 + end + end + end + if s<2 then + elseif category==3 then + sort(srt,compare) + else + sort(srt) + end + return srt + else + return {} + end +end +local function sortedhashonly(tab) + if tab then + local srt,s={},0 + for key in next,tab do + if type(key)=="string" then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt) + end + return srt + else + return {} + end +end +local function sortedindexonly(tab) + if tab then + local srt,s={},0 + for key in next,tab do + if type(key)=="number" then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt) + end + return srt + else + return {} + end +end +local function sortedhashkeys(tab,cmp) + if tab then + local srt,s={},0 + for key in next,tab do + if key then + s=s+1 + srt[s]=key + end + end + if s>1 then + sort(srt,cmp) + end + return srt + else + return {} + end +end +function table.allkeys(t) + local keys={} + for k,v in next,t do + for k in next,v do + keys[k]=true + end + end + return sortedkeys(keys) +end +table.sortedkeys=sortedkeys +table.sortedhashonly=sortedhashonly +table.sortedindexonly=sortedindexonly +table.sortedhashkeys=sortedhashkeys +local function nothing() end +local function sortedhash(t,cmp) + if t then + local s + if cmp then + s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end) + else + s=sortedkeys(t) + end + local m=#s + if m==1 then + return next,t + elseif m>0 then + local n=0 + return function() + if n<m then + n=n+1 + local k=s[n] + return k,t[k] + end + end + end + end + return nothing +end +table.sortedhash=sortedhash +table.sortedpairs=sortedhash +function table.append(t,list) + local n=#t + for i=1,#list do + n=n+1 + t[n]=list[i] + end + return t +end +function table.prepend(t,list) + local nl=#list + local nt=nl+#t + for i=#t,1,-1 do + t[nt]=t[i] + nt=nt-1 + end + for i=1,#list do + t[i]=list[i] + end + return t +end +function table.merge(t,...) + t=t or {} + for i=1,select("#",...) do + for k,v in next,(select(i,...)) do + t[k]=v + end + end + return t +end +function table.merged(...) + local t={} + for i=1,select("#",...) do + for k,v in next,(select(i,...)) do + t[k]=v + end + end + return t +end +function table.imerge(t,...) + local nt=#t + for i=1,select("#",...) do + local nst=select(i,...) + for j=1,#nst do + nt=nt+1 + t[nt]=nst[j] + end + end + return t +end +function table.imerged(...) + local tmp,ntmp={},0 + for i=1,select("#",...) do + local nst=select(i,...) + for j=1,#nst do + ntmp=ntmp+1 + tmp[ntmp]=nst[j] + end + end + return tmp +end +local function fastcopy(old,metatabletoo) + if old then + local new={} + for k,v in next,old do + if type(v)=="table" then + new[k]=fastcopy(v,metatabletoo) + else + new[k]=v + end + end + if metatabletoo then + local mt=getmetatable(old) + if mt then + setmetatable(new,mt) + end + end + return new + else + return {} + end +end +local function copy(t,tables) + tables=tables or {} + local tcopy={} + if not tables[t] then + tables[t]=tcopy + end + for i,v in next,t do + if type(i)=="table" then + if tables[i] then + i=tables[i] + else + i=copy(i,tables) + end + end + if type(v)~="table" then + tcopy[i]=v + elseif tables[v] then + tcopy[i]=tables[v] + else + tcopy[i]=copy(v,tables) + end + end + local mt=getmetatable(t) + if mt then + setmetatable(tcopy,mt) + end + return tcopy +end +table.fastcopy=fastcopy +table.copy=copy +function table.derive(parent) + local child={} + if parent then + setmetatable(child,{ __index=parent }) + end + return child +end +function table.tohash(t,value) + local h={} + if t then + if value==nil then value=true end + for _,v in next,t do + h[v]=value + end + end + return h +end +function table.fromhash(t) + local hsh,h={},0 + for k,v in next,t do + if v then + h=h+1 + hsh[h]=k + end + end + return hsh +end +local noquotes,hexify,handle,compact,inline,functions,metacheck +local reserved=table.tohash { + 'and','break','do','else','elseif','end','false','for','function','if', + 'in','local','nil','not','or','repeat','return','then','true','until','while', + 'NaN','goto', +} +local function is_simple_table(t,hexify) + local nt=#t + if nt>0 then + local n=0 + for _,v in next,t do + n=n+1 + if type(v)=="table" then + return nil + end + end + local haszero=rawget(t,0) + if n==nt then + local tt={} + for i=1,nt do + local v=t[i] + local tv=type(v) + if tv=="number" then + if hexify then + tt[i]=format("0x%X",v) + else + tt[i]=v + end + elseif tv=="string" then + tt[i]=format("%q",v) + elseif tv=="boolean" then + tt[i]=v and "true" or "false" + else + return nil + end + end + return tt + elseif haszero and (n==nt+1) then + local tt={} + for i=0,nt do + local v=t[i] + local tv=type(v) + if tv=="number" then + if hexify then + tt[i+1]=format("0x%X",v) + else + tt[i+1]=v + end + elseif tv=="string" then + tt[i+1]=format("%q",v) + elseif tv=="boolean" then + tt[i+1]=v and "true" or "false" + else + return nil + end + end + tt[1]="[0] = "..tt[1] + return tt + end + end + return nil +end +table.is_simple_table=is_simple_table +local propername=patterns.propername +local function dummy() end +local function do_serialize(root,name,depth,level,indexed) + if level>0 then + depth=depth.." " + if indexed then + handle(format("%s{",depth)) + else + local tn=type(name) + if tn=="number" then + if hexify then + handle(format("%s[0x%X]={",depth,name)) + else + handle(format("%s[%s]={",depth,name)) + end + elseif tn=="string" then + if noquotes and not reserved[name] and lpegmatch(propername,name) then + handle(format("%s%s={",depth,name)) + else + handle(format("%s[%q]={",depth,name)) + end + elseif tn=="boolean" then + handle(format("%s[%s]={",depth,name and "true" or "false")) + else + handle(format("%s{",depth)) + end + end + end + if root and next(root)~=nil then + local first,last=nil,0 + if compact then + last=#root + for k=1,last do + if rawget(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] + local tv=type(v) + local tk=type(k) + if compact and first and tk=="number" and k>=first and k<=last then + if tv=="number" then + if hexify then + handle(format("%s 0x%X,",depth,v)) + else + handle(format("%s %s,",depth,v)) + end + elseif tv=="string" then + handle(format("%s %q,",depth,v)) + elseif tv=="table" then + if next(v)==nil then + handle(format("%s {},",depth)) + elseif inline then + local st=is_simple_table(v,hexify) + if st then + handle(format("%s { %s },",depth,concat(st,", "))) + else + do_serialize(v,k,depth,level+1,true) + end + else + do_serialize(v,k,depth,level+1,true) + end + elseif tv=="boolean" then + handle(format("%s %s,",depth,v and "true" or "false")) + elseif tv=="function" then + if functions then + handle(format('%s load(%q),',depth,dump(v))) + else + handle(format('%s "function",',depth)) + end + else + handle(format("%s %q,",depth,tostring(v))) + end + elseif k=="__p__" then + if false then + handle(format("%s __p__=nil,",depth)) + end + elseif tv=="number" then + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=0x%X,",depth,k,v)) + else + handle(format("%s [%s]=%s,",depth,k,v)) + end + elseif tk=="boolean" then + if hexify then + handle(format("%s [%s]=0x%X,",depth,k and "true" or "false",v)) + else + handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) + end + elseif tk~="string" then + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + if hexify then + handle(format("%s %s=0x%X,",depth,k,v)) + else + handle(format("%s %s=%s,",depth,k,v)) + end + else + if hexify then + handle(format("%s [%q]=0x%X,",depth,k,v)) + else + handle(format("%s [%q]=%s,",depth,k,v)) + end + end + elseif tv=="string" then + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=%q,",depth,k,v)) + else + handle(format("%s [%s]=%q,",depth,k,v)) + end + elseif tk=="boolean" then + handle(format("%s [%s]=%q,",depth,k and "true" or "false",v)) + elseif tk~="string" then + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%q,",depth,k,v)) + else + handle(format("%s [%q]=%q,",depth,k,v)) + end + elseif tv=="table" then + if next(v)==nil then + if tk=="number" then + if hexify then + handle(format("%s [0x%X]={},",depth,k)) + else + handle(format("%s [%s]={},",depth,k)) + end + elseif tk=="boolean" then + handle(format("%s [%s]={},",depth,k and "true" or "false")) + elseif tk~="string" then + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s={},",depth,k)) + else + handle(format("%s [%q]={},",depth,k)) + end + elseif inline then + local st=is_simple_table(v,hexify) + if st then + if tk=="number" then + if hexify then + handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", "))) + else + handle(format("%s [%s]={ %s },",depth,k,concat(st,", "))) + end + elseif tk=="boolean" then + handle(format("%s [%s]={ %s },",depth,k and "true" or "false",concat(st,", "))) + elseif tk~="string" then + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s={ %s },",depth,k,concat(st,", "))) + else + handle(format("%s [%q]={ %s },",depth,k,concat(st,", "))) + end + else + do_serialize(v,k,depth,level+1) + end + else + do_serialize(v,k,depth,level+1) + end + elseif tv=="boolean" then + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false")) + else + handle(format("%s [%s]=%s,",depth,k,v and "true" or "false")) + end + elseif tk=="boolean" then + handle(format("%s [%s]=%s,",depth,tostring(k),v and "true" or "false")) + elseif tk~="string" then + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%s,",depth,k,v and "true" or "false")) + else + handle(format("%s [%q]=%s,",depth,k,v and "true" or "false")) + end + elseif tv=="function" then + if functions then + local f=getinfo(v).what=="C" and dump(dummy) or dump(v) + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=load(%q),",depth,k,f)) + else + handle(format("%s [%s]=load(%q),",depth,k,f)) + end + elseif tk=="boolean" then + handle(format("%s [%s]=load(%q),",depth,k and "true" or "false",f)) + elseif tk~="string" then + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=load(%q),",depth,k,f)) + else + handle(format("%s [%q]=load(%q),",depth,k,f)) + end + end + else + if tk=="number" then + if hexify then + handle(format("%s [0x%X]=%q,",depth,k,tostring(v))) + else + handle(format("%s [%s]=%q,",depth,k,tostring(v))) + end + elseif tk=="boolean" then + handle(format("%s [%s]=%q,",depth,k and "true" or "false",tostring(v))) + elseif tk~="string" then + elseif noquotes and not reserved[k] and lpegmatch(propername,k) then + handle(format("%s %s=%q,",depth,k,tostring(v))) + else + handle(format("%s [%q]=%q,",depth,k,tostring(v))) + end + end + end + end + if level>0 then + handle(format("%s},",depth)) + end +end +local function serialize(_handle,root,name,specification) + local tname=type(name) + if type(specification)=="table" then + noquotes=specification.noquotes + hexify=specification.hexify + handle=_handle or specification.handle or print + functions=specification.functions + compact=specification.compact + inline=specification.inline and compact + metacheck=specification.metacheck + if functions==nil then + functions=true + end + if compact==nil then + compact=true + end + if inline==nil then + inline=compact + end + if metacheck==nil then + metacheck=true + end + else + noquotes=false + hexify=false + handle=_handle or print + compact=true + inline=true + functions=true + metacheck=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%X]={",name)) + else + handle("["..name.."]={") + end + elseif tname=="boolean" then + if name then + handle("return {") + else + handle("{") + end + else + handle("t={") + end + if root then + if metacheck and 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)~=nil then + do_serialize(root,name,"",0) + end + end + handle("}") +end +function table.serialize(root,name,specification) + local t,n={},0 + local function flush(s) + n=n+1 + t[n]=s + end + serialize(flush,root,name,specification) + return concat(t,"\n") +end +table.tohandle=serialize +local maxtab=2*1024 +function table.tofile(filename,root,name,specification) + local f=io.open(filename,'w') + if f then + if maxtab>1 then + local t,n={},0 + local function flush(s) + n=n+1 + t[n]=s + if n>maxtab then + f:write(concat(t,"\n"),"\n") + t,n={},0 + end + end + serialize(flush,root,name,specification) + f:write(concat(t,"\n"),"\n") + else + local function flush(s) + f:write(s,"\n") + end + serialize(flush,root,name,specification) + end + f:close() + io.flush() + end +end +local function flattened(t,f,depth) + if f==nil then + f={} + depth=0xFFFF + elseif tonumber(f) then + depth=f + f={} + elseif not depth then + depth=0xFFFF + end + for k,v in next,t do + if type(k)~="number" then + if depth>0 and type(v)=="table" then + flattened(v,f,depth-1) + else + f[#f+1]=v + end + end + end + for k=1,#t do + local v=t[k] + if depth>0 and type(v)=="table" then + flattened(v,f,depth-1) + else + f[#f+1]=v + end + end + return f +end +table.flattened=flattened +local function collapsed(t,f,h) + if f==nil then + f={} + h={} + end + for k=1,#t do + local v=t[k] + if type(v)=="table" then + collapsed(v,f,h) + elseif not h[v] then + f[#f+1]=v + h[v]=true + end + end + return f +end +local function collapsedhash(t,h) + if h==nil then + h={} + end + for k=1,#t do + local v=t[k] + if type(v)=="table" then + collapsedhash(v,h) + else + h[v]=true + end + end + return h +end +table.collapsed=collapsed +table.collapsedhash=collapsedhash +local function unnest(t,f) + if not f then + f={} + end + for i=1,#t do + local v=t[i] + if type(v)=="table" then + if type(v[1])=="table" then + unnest(v,f) + else + f[#f+1]=v + end + else + f[#f+1]=v + end + end + return f +end +function table.unnest(t) + return unnest(t) +end +local function are_equal(a,b,n,m) + if a==b then + return true + elseif a and b and #a==#b then + n=n or 1 + m=m or #a + for i=n,m do + local ai,bi=a[i],b[i] + if ai==bi then + elseif type(ai)=="table" and type(bi)=="table" then + if not are_equal(ai,bi) then + return false + end + else + return false + end + end + return true + else + return false + end +end +local function identical(a,b) + if a~=b then + for ka,va in next,a do + local vb=b[ka] + if va==vb then + elseif type(va)=="table" and type(vb)=="table" then + if not identical(va,vb) then + return false + end + else + return false + end + end + end + return true +end +table.identical=identical +table.are_equal=are_equal +local function sparse(old,nest,keeptables) + local new={} + for k,v in next,old do + if not (v=="" or v==false) then + if nest and type(v)=="table" then + v=sparse(v,nest) + if keeptables or next(v)~=nil then + new[k]=v + end + else + new[k]=v + end + end + end + return new +end +table.sparse=sparse +function table.compact(t) + return sparse(t,true,true) +end +function table.contains(t,v) + if t then + for i=1,#t do + if t[i]==v then + return i + end + end + end + return false +end +function table.count(t) + local n=0 + for k,v in next,t do + n=n+1 + end + return n +end +function table.swapped(t,s) + local n={} + if s then + for k,v in next,s do + n[k]=v + end + end + for k,v in next,t do + n[v]=k + end + return n +end +function table.hashed(t) + for i=1,#t do + t[t[i]]=i + end + return t +end +function table.mirrored(t) + local n={} + for k,v in next,t do + n[v]=k + n[k]=v + end + return n +end +function table.reversed(t) + if t then + local tt,tn={},#t + if tn>0 then + local ttn=0 + for i=tn,1,-1 do + ttn=ttn+1 + tt[ttn]=t[i] + end + end + return tt + end +end +function table.reverse(t) + if t then + local n=#t + local m=n+1 + for i=1,floor(n/2) do + local j=m-i + t[i],t[j]=t[j],t[i] + end + return t + end +end +local function sequenced(t,sep,simple) + if not t then + return "" + elseif type(t)=="string" then + return t + end + local n=#t + local s={} + if n>0 then + for i=1,n do + local v=t[i] + if type(v)=="table" then + s[i]="{"..sequenced(v,sep,simple).."}" + else + s[i]=tostring(t[i]) + end + end + else + n=0 + for k,v in sortedhash(t) do + if simple then + if v==true then + n=n+1 + s[n]=k + elseif v and v~="" then + n=n+1 + if type(v)=="table" then + s[n]=k.."={"..sequenced(v,sep,simple).."}" + else + s[n]=k.."="..tostring(v) + end + end + else + n=n+1 + if type(v)=="table" then + s[n]=k.."={"..sequenced(v,sep,simple).."}" + else + s[n]=k.."="..tostring(v) + end + end + end + end + return concat(s,sep or " | ") +end +table.sequenced=sequenced +function table.print(t,...) + if type(t)~="table" then + print(tostring(t)) + else + serialize(print,t,...) + end +end +if setinspector then + setinspector("table",function(v) if type(v)=="table" then serialize(print,v,"table") return true end end) +end +function table.sub(t,i,j) + return { unpack(t,i,j) } +end +function table.is_empty(t) + return not t or next(t)==nil +end +function table.has_one_entry(t) + return t and next(t,next(t))==nil +end +function table.loweredkeys(t) + local l={} + for k,v in next,t do + l[lower(k)]=v + end + return l +end +function table.unique(old) + local hash={} + local new={} + local n=0 + for i=1,#old do + local oi=old[i] + if not hash[oi] then + n=n+1 + new[n]=oi + hash[oi]=true + end + end + return new +end +function table.sorted(t,...) + sort(t,...) + return t +end +function table.values(t,s) + if t then + local values,keys,v={},{},0 + for key,value in next,t do + if not keys[value] then + v=v+1 + values[v]=value + keys[k]=key + end + end + if s then + sort(values) + end + return values + else + return {} + end +end +function table.filtered(t,pattern,sort,cmp) + if t and type(pattern)=="string" then + if sort then + local s + if cmp then + s=sortedhashkeys(t,function(a,b) return cmp(t,a,b) end) + else + s=sortedkeys(t) + end + local n=0 + local m=#s + local function kv(s) + while n<m do + n=n+1 + local k=s[n] + if find(k,pattern) then + return k,t[k] + end + end + end + return kv,s + else + local n=next(t) + local function iterator() + while n~=nil do + local k=n + n=next(t,k) + if find(k,pattern) then + return k,t[k] + end + end + end + return iterator,t + end + else + return nothing + end +end +if not table.move then + function table.move(a1,f,e,t,a2) + if a2 and a1~=a2 then + for i=f,e do + a2[t]=a1[i] + t=t+1 + end + return a2 + else + t=t+e-f + for i=e,f,-1 do + a1[t]=a1[i] + t=t-1 + end + return a1 + end + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-boolean']={ + 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 type,tonumber=type,tonumber +boolean=boolean or {} +local boolean=boolean +function boolean.tonumber(b) + if b then return 1 else return 0 end +end +function toboolean(str,tolerant) + if str==nil then + return false + elseif str==false then + return false + elseif str==true then + return true + elseif str=="true" then + return true + elseif str=="false" then + return false + elseif not tolerant then + return false + elseif str==0 then + return false + elseif (tonumber(str) or 0)>0 then + return true + else + return str=="yes" or str=="on" or str=="t" + end +end +string.toboolean=toboolean +function string.booleanstring(str) + if str=="0" then + return false + elseif str=="1" then + return true + elseif str=="" then + return false + elseif str=="false" then + return false + elseif str=="true" then + return true + elseif (tonumber(str) or 0)>0 then + return true + else + return str=="yes" or str=="on" or str=="t" + end +end +function string.is_boolean(str,default,strict) + if type(str)=="string" then + if str=="true" or str=="yes" or str=="on" or str=="t" or (not strict and str=="1") then + return true + elseif str=="false" or str=="no" or str=="off" or str=="f" or (not strict and str=="0") then + return false + end + end + return default +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-number']={ + 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 tostring,tonumber=tostring,tonumber +local format,floor,match,rep=string.format,math.floor,string.match,string.rep +local concat,insert=table.concat,table.insert +local lpegmatch=lpeg.match +local floor=math.floor +number=number or {} +local number=number +if bit32 then + local bextract=bit32.extract + local t={ + "0","0","0","0","0","0","0","0", + "0","0","0","0","0","0","0","0", + "0","0","0","0","0","0","0","0", + "0","0","0","0","0","0","0","0", + } + function number.tobitstring(b,m) + local n=32 + for i=0,31 do + local v=bextract(b,i) + local k=32-i + if v==1 then + n=k + t[k]="1" + else + t[k]="0" + end + end + if m then + m=33-m*8 + if m<1 then + m=1 + end + return concat(t,"",m) + elseif n<8 then + return concat(t) + elseif n<16 then + return concat(t,"",9) + elseif n<24 then + return concat(t,"",17) + else + return concat(t,"",25) + end + end +else + function number.tobitstring(n,m) + if n>0 then + local t={} + while n>0 do + insert(t,1,n%2>0 and 1 or 0) + n=floor(n/2) + end + local nn=8-#t%8 + if nn>0 and nn<8 then + for i=1,nn do + insert(t,1,0) + end + end + if m then + m=m*8-#t + if m>0 then + insert(t,1,rep("0",m)) + end + end + return concat(t) + elseif m then + rep("00000000",m) + else + return "00000000" + end + end +end +function number.valid(str,default) + return tonumber(str) or default or nil +end +function number.toevenhex(n) + local s=format("%X",n) + if #s%2==0 then + return s + else + return "0"..s + end +end +function number.bytetodecimal(b) + local d=floor(b*100/255+0.5) + if d>100 then + return 100 + elseif d<-100 then + return -100 + else + return d + end +end +function number.decimaltobyte(d) + local b=floor(d*255/100+0.5) + if b>255 then + return 255 + elseif b<-255 then + return -255 + else + return b + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-math']={ + 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" +} +if not math.ceiling then + math.ceiling=math.ceil +end +if not math.round then + local floor=math.floor + function math.round(x) return floor(x+0.5) end +end +if not math.div then + local floor=math.floor + function math.div(n,m) return floor(n/m) end +end +if not math.mod then + function math.mod(n,m) return n%m end +end +if not math.sind then + local sin,cos,tan=math.sin,math.cos,math.tan + local pipi=2*math.pi/360 + function math.sind(d) return sin(d*pipi) end + function math.cosd(d) return cos(d*pipi) end + function math.tand(d) return tan(d*pipi) end +end +if not math.odd then + function math.odd (n) return n%2~=0 end + function math.even(n) return n%2==0 end +end +if not math.cosh then + local exp=math.exp + function math.cosh(x) + local xx=exp(x) + return (xx+1/xx)/2 + end + function math.sinh(x) + local xx=exp(x) + return (xx-1/xx)/2 + end + function math.tanh(x) + local xx=exp(x) + return (xx-1/xx)/(xx+1/xx) + end +end +if not math.pow then + function math.pow(x,y) + return x^y + end +end +if not math.atan2 then + math.atan2=math.atan +end +if not math.ldexp then + function math.ldexp(x,e) + return x*2.0^e + end +end +if not math.log10 then + local log=math.log + function math.log10(x) + return log(x,10) + end +end +if not math.type then + function math.type() + return "float" + end +end +if not math.tointeger then + math.mininteger=-0x4FFFFFFFFFFF + math.maxinteger=0x4FFFFFFFFFFF + local floor=math.floor + function math.tointeger(n) + local f=floor(n) + return f==n and f or nil + end +end +if not math.ult then + local floor=math.floor + function math.tointeger(m,n) + return floor(m)<floor(n) + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-io']={ + 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 io=io +local open,flush,write,read=io.open,io.flush,io.write,io.read +local byte,find,gsub,format=string.byte,string.find,string.gsub,string.format +local concat=table.concat +local type=type +if string.find(os.getenv("PATH"),";",1,true) then + io.fileseparator,io.pathseparator="\\",";" +else + io.fileseparator,io.pathseparator="/",":" +end +local large=0x01000000 +local medium=0x00100000 +local small=0x00020000 +local function readall(f) + local size=f:seek("end") + if size>0 then + f:seek("set",0) + return f:read(size) + else + return "" + end +end +io.readall=readall +function io.loaddata(filename,textmode) + local f=open(filename,(textmode and 'r') or 'rb') + if f then + local size=f:seek("end") + local data=nil + if size>0 then + f:seek("set",0) + data=f:read(size) + end + f:close() + return data + end +end +function io.copydata(source,target,action) + local f=open(source,"rb") + if f then + local g=open(target,"wb") + if g then + local size=f:seek("end") + if size>0 then + f:seek("set",0) + local data=f:read(size) + if action then + data=action(data) + end + if data then + g:write(data) + end + end + g:close() + end + f:close() + flush() + end +end +function io.savedata(filename,data,joiner) + local f=open(filename,"wb") + if f then + if type(data)=="table" then + f:write(concat(data,joiner or "")) + elseif type(data)=="function" then + data(f) + else + f:write(data or "") + end + f:close() + flush() + return true + else + return false + end +end +if fio and fio.readline then + local readline=fio.readline + function io.loadlines(filename,n) + local f=open(filename,'r') + if not f then + elseif n then + local lines={} + for i=1,n do + local line=readline(f) + if line then + lines[i]=line + else + break + end + end + f:close() + lines=concat(lines,"\n") + if #lines>0 then + return lines + end + else + local line=readline(f) + f:close() + if line and #line>0 then + return line + end + end + end +else + function io.loadlines(filename,n) + local f=open(filename,'r') + if not f then + elseif n then + local lines={} + for i=1,n do + local line=f:read("*lines") + if line then + lines[i]=line + else + break + end + end + f:close() + lines=concat(lines,"\n") + if #lines>0 then + return lines + end + else + local line=f:read("*line") or "" + f:close() + if #line>0 then + return line + end + end + end +end +function io.loadchunk(filename,n) + local f=open(filename,'rb') + if f then + local data=f:read(n or 1024) + f:close() + if #data>0 then + return data + end + end +end +function io.exists(filename) + local f=open(filename) + if f==nil then + return false + else + f:close() + return true + end +end +function io.size(filename) + local f=open(filename) + if f==nil then + return 0 + else + local s=f:seek("end") + f:close() + return s + end +end +local function noflines(f) + if type(f)=="string" then + local f=open(filename) + if f then + local n=f and noflines(f) or 0 + f:close() + return n + else + return 0 + end + else + local n=0 + for _ in f:lines() do + n=n+1 + end + f:seek('set',0) + return n + end +end +io.noflines=noflines +local nextchar={ + [ 4]=function(f) + return f:read(1,1,1,1) + end, + [ 2]=function(f) + return f:read(1,1) + end, + [ 1]=function(f) + return f:read(1) + end, + [-2]=function(f) + local a,b=f:read(1,1) + return b,a + end, + [-4]=function(f) + local a,b,c,d=f:read(1,1,1,1) + return d,c,b,a + end +} +function io.characters(f,n) + if f then + return nextchar[n or 1],f + end +end +local nextbyte={ + [4]=function(f) + local a,b,c,d=f:read(1,1,1,1) + if d then + return byte(a),byte(b),byte(c),byte(d) + end + end, + [3]=function(f) + local a,b,c=f:read(1,1,1) + if b then + return byte(a),byte(b),byte(c) + end + end, + [2]=function(f) + local a,b=f:read(1,1) + if b then + return byte(a),byte(b) + end + end, + [1]=function (f) + local a=f:read(1) + if a then + return byte(a) + end + end, + [-2]=function (f) + local a,b=f:read(1,1) + if b then + return byte(b),byte(a) + end + end, + [-3]=function(f) + local a,b,c=f:read(1,1,1) + if b then + return byte(c),byte(b),byte(a) + end + end, + [-4]=function(f) + local a,b,c,d=f:read(1,1,1,1) + if d then + return byte(d),byte(c),byte(b),byte(a) + end + end +} +function io.bytes(f,n) + if f then + return nextbyte[n or 1],f + else + return nil,nil + end +end +function io.ask(question,default,options) + while true do + write(question) + if options then + write(format(" [%s]",concat(options,"|"))) + end + if default then + write(format(" [%s]",default)) + end + write(format(" ")) + flush() + local answer=read() + answer=gsub(answer,"^%s*(.*)%s*$","%1") + if answer=="" and default then + return default + elseif not options then + return answer + else + for k=1,#options do + if options[k]==answer then + return answer + end + end + local pattern="^"..answer + for k=1,#options do + local v=options[k] + if find(v,pattern) then + return v + end + end + end + end +end +local function readnumber(f,n,m) + if m then + f:seek("set",n) + n=m + end + if n==1 then + return byte(f:read(1)) + elseif n==2 then + local a,b=byte(f:read(2),1,2) + return 0x100*a+b + elseif n==3 then + local a,b,c=byte(f:read(3),1,3) + return 0x10000*a+0x100*b+c + elseif n==4 then + local a,b,c,d=byte(f:read(4),1,4) + return 0x1000000*a+0x10000*b+0x100*c+d + elseif n==8 then + local a,b=readnumber(f,4),readnumber(f,4) + return 0x100*a+b + elseif n==12 then + local a,b,c=readnumber(f,4),readnumber(f,4),readnumber(f,4) + return 0x10000*a+0x100*b+c + elseif n==-2 then + local b,a=byte(f:read(2),1,2) + return 0x100*a+b + elseif n==-3 then + local c,b,a=byte(f:read(3),1,3) + return 0x10000*a+0x100*b+c + elseif n==-4 then + local d,c,b,a=byte(f:read(4),1,4) + return 0x1000000*a+0x10000*b+0x100*c+d + elseif n==-8 then + local h,g,f,e,d,c,b,a=byte(f:read(8),1,8) + return 0x100000000000000*a+0x1000000000000*b+0x10000000000*c+0x100000000*d+0x1000000*e+0x10000*f+0x100*g+h + else + return 0 + end +end +io.readnumber=readnumber +function io.readstring(f,n,m) + if m then + f:seek("set",n) + n=m + end + local str=gsub(f:read(n),"\000","") + return str +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-os']={ + 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 os=os +local date,time=os.date,os.time +local find,format,gsub,upper,gmatch=string.find,string.format,string.gsub,string.upper,string.gmatch +local concat=table.concat +local random,ceil,randomseed=math.random,math.ceil,math.randomseed +local rawget,rawset,type,getmetatable,setmetatable,tonumber,tostring=rawget,rawset,type,getmetatable,setmetatable,tonumber,tostring +math.initialseed=tonumber(string.sub(string.reverse(tostring(ceil(socket and socket.gettime()*10000 or time()))),1,6)) +randomseed(math.initialseed) +if not os.__getenv__ then + os.__getenv__=os.getenv + os.__setenv__=os.setenv + if os.env then + local osgetenv=os.getenv + local ossetenv=os.setenv + local osenv=os.env local _=osenv.PATH + function os.setenv(k,v) + if v==nil then + v="" + end + local K=upper(k) + osenv[K]=v + if type(v)=="table" then + v=concat(v,";") + end + ossetenv(K,v) + end + function os.getenv(k) + local K=upper(k) + local v=osenv[K] or osenv[k] or osgetenv(K) or osgetenv(k) + if v=="" then + return nil + else + return v + end + end + else + local ossetenv=os.setenv + local osgetenv=os.getenv + local osenv={} + function os.setenv(k,v) + if v==nil then + v="" + end + local K=upper(k) + osenv[K]=v + end + function os.getenv(k) + local K=upper(k) + local v=osenv[K] or osgetenv(K) or osgetenv(k) + if v=="" then + return nil + else + return v + end + end + local function __index(t,k) + return os.getenv(k) + end + local function __newindex(t,k,v) + os.setenv(k,v) + end + os.env={} + setmetatable(os.env,{ __index=__index,__newindex=__newindex } ) + end +end +local execute=os.execute +local iopopen=io.popen +local function resultof(command) + local handle=iopopen(command,"r") + if handle then + local result=handle:read("*all") or "" + handle:close() + return result + else + return "" + end +end +os.resultof=resultof +function os.pipeto(command) + return iopopen(command,"w") +end +if not io.fileseparator then + if find(os.getenv("PATH"),";",1,true) then + io.fileseparator,io.pathseparator,os.type="\\",";",os.type or "windows" + else + io.fileseparator,io.pathseparator,os.type="/",":",os.type or "unix" + end +end +os.type=os.type or (io.pathseparator==";" and "windows") or "unix" +os.name=os.name or (os.type=="windows" and "mswin" ) or "linux" +if os.type=="windows" then + os.libsuffix,os.binsuffix,os.binsuffixes='dll','exe',{ 'exe','cmd','bat' } +else + os.libsuffix,os.binsuffix,os.binsuffixes='so','',{ '' } +end +local launchers={ + windows="start %s", + macosx="open %s", + unix="xdg-open %s &> /dev/null &", +} +function os.launch(str) + execute(format(launchers[os.name] or launchers.unix,str)) +end +if not os.times then + function os.times() + return { + utime=os.gettimeofday(), + stime=0, + cutime=0, + cstime=0, + } + end +end +local gettimeofday=os.gettimeofday or os.clock +os.gettimeofday=gettimeofday +local startuptime=gettimeofday() +function os.runtime() + return gettimeofday()-startuptime +end +local resolvers=os.resolvers or {} +os.resolvers=resolvers +setmetatable(os,{ __index=function(t,k) + local r=resolvers[k] + return r and r(t,k) or nil +end }) +local name,platform=os.name or "linux",os.getenv("MTX_PLATFORM") or "" +if platform~="" then + os.platform=platform +elseif os.type=="windows" then + function resolvers.platform(t,k) + local architecture=os.getenv("PROCESSOR_ARCHITECTURE") or "" + local platform="" + if find(architecture,"AMD64",1,true) then + platform="win64" + else + platform="mswin" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +elseif name=="linux" then + function resolvers.platform(t,k) + local architecture=os.getenv("HOSTTYPE") or resultof("uname -m") or "" + local platform=os.getenv("MTX_PLATFORM") or "" + local musl=find(os.selfdir or "","linuxmusl") + if platform~="" then + elseif find(architecture,"x86_64",1,true) then + platform=musl and "linuxmusl" or "linux-64" + elseif find(architecture,"ppc",1,true) then + platform="linux-ppc" + else + platform=musl and "linuxmusl" or "linux" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +elseif name=="macosx" then + function resolvers.platform(t,k) + local architecture=resultof("echo $HOSTTYPE") or "" + local platform="" + if architecture=="" then + platform="osx-intel" + elseif find(architecture,"i386",1,true) then + platform="osx-intel" + elseif find(architecture,"x86_64",1,true) then + platform="osx-64" + else + platform="osx-ppc" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +elseif name=="sunos" then + function resolvers.platform(t,k) + local architecture=resultof("uname -m") or "" + local platform="" + if find(architecture,"sparc",1,true) then + platform="solaris-sparc" + else + platform="solaris-intel" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +elseif name=="freebsd" then + function resolvers.platform(t,k) + local architecture=resultof("uname -m") or "" + local platform="" + if find(architecture,"amd64",1,true) then + platform="freebsd-amd64" + else + platform="freebsd" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +elseif name=="kfreebsd" then + function resolvers.platform(t,k) + local architecture=os.getenv("HOSTTYPE") or resultof("uname -m") or "" + local platform="" + if find(architecture,"x86_64",1,true) then + platform="kfreebsd-amd64" + else + platform="kfreebsd-i386" + end + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +else + function resolvers.platform(t,k) + local platform="linux" + os.setenv("MTX_PLATFORM",platform) + os.platform=platform + return platform + end +end +os.newline=name=="windows" and "\013\010" or "\010" +function resolvers.bits(t,k) + local bits=find(os.platform,"64",1,true) and 64 or 32 + os.bits=bits + return bits +end +local t={ 8,9,"a","b" } +function os.uuid() + return format("%04x%04x-4%03x-%s%03x-%04x-%04x%04x%04x", + random(0xFFFF),random(0xFFFF), + random(0x0FFF), + t[ceil(random(4))] or 8,random(0x0FFF), + random(0xFFFF), + random(0xFFFF),random(0xFFFF),random(0xFFFF) + ) +end +local d +function os.timezone(delta) + d=d or tonumber(tonumber(date("%H")-date("!%H"))) + if delta then + if d>0 then + return format("+%02i:00",d) + else + return format("-%02i:00",-d) + end + else + return 1 + end +end +local timeformat=format("%%s%s",os.timezone(true)) +local dateformat="!%Y-%m-%d %H:%M:%S" +local lasttime=nil +local lastdate=nil +function os.fulltime(t,default) + t=t and tonumber(t) or 0 + if t>0 then + elseif default then + return default + else + t=time() + end + if t~=lasttime then + lasttime=t + lastdate=format(timeformat,date(dateformat)) + end + return lastdate +end +local dateformat="%Y-%m-%d %H:%M:%S" +local lasttime=nil +local lastdate=nil +function os.localtime(t,default) + t=t and tonumber(t) or 0 + if t>0 then + elseif default then + return default + else + t=time() + end + if t~=lasttime then + lasttime=t + lastdate=date(dateformat,t) + end + return lastdate +end +function os.converttime(t,default) + local t=tonumber(t) + if t and t>0 then + return date(dateformat,t) + else + return default or "-" + end +end +local memory={} +local function which(filename) + local fullname=memory[filename] + if fullname==nil then + local suffix=file.suffix(filename) + local suffixes=suffix=="" and os.binsuffixes or { suffix } + for directory in gmatch(os.getenv("PATH"),"[^"..io.pathseparator.."]+") do + local df=file.join(directory,filename) + for i=1,#suffixes do + local dfs=file.addsuffix(df,suffixes[i]) + if io.exists(dfs) then + fullname=dfs + break + end + end + end + if not fullname then + fullname=false + end + memory[filename]=fullname + end + return fullname +end +os.which=which +os.where=which +function os.today() + return date("!*t") +end +function os.now() + return date("!%Y-%m-%d %H:%M:%S") +end +if not os.sleep then + local socket=socket + function os.sleep(n) + if not socket then + socket=require("socket") + end + socket.sleep(n) + end +end +local function isleapyear(year) + return (year%4==0) and (year%100~=0 or year%400==0) +end +os.isleapyear=isleapyear +local days={ 31,28,31,30,31,30,31,31,30,31,30,31 } +local function nofdays(year,month) + if not month then + return isleapyear(year) and 365 or 364 + else + return month==2 and isleapyear(year) and 29 or days[month] + end +end +os.nofdays=nofdays +function os.weekday(day,month,year) + return date("%w",time { year=year,month=month,day=day })+1 +end +function os.validdate(year,month,day) + if month<1 then + month=1 + elseif month>12 then + month=12 + end + if day<1 then + day=1 + else + local max=nofdays(year,month) + if day>max then + day=max + end + end + return year,month,day +end +local osexit=os.exit +local exitcode=nil +function os.setexitcode(code) + exitcode=code +end +function os.exit(c) + if exitcode~=nil then + return osexit(exitcode) + end + if c~=nil then + return osexit(c) + end + return osexit() +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-file']={ + 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" +} +file=file or {} +local file=file +if not lfs then + lfs=optionalrequire("lfs") +end +local insert,concat=table.insert,table.concat +local match,find,gmatch=string.match,string.find,string.gmatch +local lpegmatch=lpeg.match +local getcurrentdir,attributes=lfs.currentdir,lfs.attributes +local checkedsplit=string.checkedsplit +local P,R,S,C,Cs,Cp,Cc,Ct=lpeg.P,lpeg.R,lpeg.S,lpeg.C,lpeg.Cs,lpeg.Cp,lpeg.Cc,lpeg.Ct +local attributes=lfs.attributes +function lfs.isdir(name) + return attributes(name,"mode")=="directory" +end +function lfs.isfile(name) + local a=attributes(name,"mode") + return a=="file" or a=="link" or nil +end +function lfs.isfound(name) + local a=attributes(name,"mode") + return (a=="file" or a=="link") and name or nil +end +if sandbox then + sandbox.redefine(lfs.isfile,"lfs.isfile") + sandbox.redefine(lfs.isdir,"lfs.isdir") + sandbox.redefine(lfs.isfound,"lfs.isfound") +end +local colon=P(":") +local period=P(".") +local periods=P("..") +local fwslash=P("/") +local bwslash=P("\\") +local slashes=S("\\/") +local noperiod=1-period +local noslashes=1-slashes +local name=noperiod^1 +local suffix=period/""*(1-period-slashes)^1*-1 +local pattern=C((1-(slashes^1*noslashes^1*-1))^1)*P(1) +local function pathpart(name,default) + return name and lpegmatch(pattern,name) or default or "" +end +local pattern=(noslashes^0*slashes)^1*C(noslashes^1)*-1 +local function basename(name) + return name and lpegmatch(pattern,name) or name +end +local pattern=(noslashes^0*slashes^1)^0*Cs((1-suffix)^1)*suffix^0 +local function nameonly(name) + return name and lpegmatch(pattern,name) or name +end +local pattern=(noslashes^0*slashes)^0*(noperiod^1*period)^1*C(noperiod^1)*-1 +local function suffixonly(name) + return name and lpegmatch(pattern,name) or "" +end +local pattern=(noslashes^0*slashes)^0*noperiod^1*((period*C(noperiod^1))^1)*-1+Cc("") +local function suffixesonly(name) + if name then + return lpegmatch(pattern,name) + else + return "" + end +end +file.pathpart=pathpart +file.basename=basename +file.nameonly=nameonly +file.suffixonly=suffixonly +file.suffix=suffixonly +file.suffixesonly=suffixesonly +file.suffixes=suffixesonly +file.dirname=pathpart +file.extname=suffixonly +local drive=C(R("az","AZ"))*colon +local path=C((noslashes^0*slashes)^0) +local suffix=period*C(P(1-period)^0*P(-1)) +local base=C((1-suffix)^0) +local rest=C(P(1)^0) +drive=drive+Cc("") +path=path+Cc("") +base=base+Cc("") +suffix=suffix+Cc("") +local pattern_a=drive*path*base*suffix +local pattern_b=path*base*suffix +local pattern_c=C(drive*path)*C(base*suffix) +local pattern_d=path*rest +function file.splitname(str,splitdrive) + if not str then + elseif splitdrive then + return lpegmatch(pattern_a,str) + else + return lpegmatch(pattern_b,str) + end +end +function file.splitbase(str) + if str then + return lpegmatch(pattern_d,str) + else + return "",str + end +end +function file.nametotable(str,splitdrive) + if str then + local path,drive,subpath,name,base,suffix=lpegmatch(pattern_c,str) + if splitdrive then + return { + path=path, + drive=drive, + subpath=subpath, + name=name, + base=base, + suffix=suffix, + } + else + return { + path=path, + name=name, + base=base, + suffix=suffix, + } + end + end +end +local pattern=Cs(((period*(1-period-slashes)^1*-1)/""+1)^1) +function file.removesuffix(name) + return name and lpegmatch(pattern,name) +end +local suffix=period/""*(1-period-slashes)^1*-1 +local pattern=Cs((noslashes^0*slashes^1)^0*((1-suffix)^1))*Cs(suffix) +function file.addsuffix(filename,suffix,criterium) + if not filename or not suffix or suffix=="" then + return filename + elseif criterium==true then + return filename.."."..suffix + elseif not criterium then + local n,s=lpegmatch(pattern,filename) + if not s or s=="" then + return filename.."."..suffix + else + return filename + end + else + local n,s=lpegmatch(pattern,filename) + if s and s~="" then + local t=type(criterium) + if t=="table" then + for i=1,#criterium do + if s==criterium[i] then + return filename + end + end + elseif t=="string" then + if s==criterium then + return filename + end + end + end + return (n or filename).."."..suffix + end +end +local suffix=period*(1-period-slashes)^1*-1 +local pattern=Cs((1-suffix)^0) +function file.replacesuffix(name,suffix) + if name and suffix and suffix~="" then + return lpegmatch(pattern,name).."."..suffix + else + return name + end +end +local reslasher=lpeg.replacer(P("\\"),"/") +function file.reslash(str) + return str and lpegmatch(reslasher,str) +end +function file.is_writable(name) + if not name then + elseif lfs.isdir(name) then + name=name.."/m_t_x_t_e_s_t.tmp" + local f=io.open(name,"wb") + if f then + f:close() + os.remove(name) + return true + end + elseif lfs.isfile(name) then + local f=io.open(name,"ab") + if f then + f:close() + return true + end + else + local f=io.open(name,"ab") + if f then + f:close() + os.remove(name) + return true + end + end + return false +end +local readable=P("r")*Cc(true) +function file.is_readable(name) + if name then + local a=attributes(name) + return a and lpegmatch(readable,a.permissions) or false + else + return false + end +end +file.isreadable=file.is_readable +file.iswritable=file.is_writable +function file.size(name) + if name then + local a=attributes(name) + return a and a.size or 0 + else + return 0 + end +end +function file.splitpath(str,separator) + return str and checkedsplit(lpegmatch(reslasher,str),separator or io.pathseparator) +end +function file.joinpath(tab,separator) + return tab and concat(tab,separator or io.pathseparator) +end +local someslash=S("\\/") +local stripper=Cs(P(fwslash)^0/""*reslasher) +local isnetwork=someslash*someslash*(1-someslash)+(1-fwslash-colon)^1*colon +local isroot=fwslash^1*-1 +local hasroot=fwslash^1 +local reslasher=lpeg.replacer(S("\\/"),"/") +local deslasher=lpeg.replacer(S("\\/")^1,"/") +function file.join(one,two,three,...) + if not two then + return one=="" and one or lpegmatch(reslasher,one) + end + if one=="" then + return lpegmatch(stripper,three and concat({ two,three,... },"/") or two) + end + if lpegmatch(isnetwork,one) then + local one=lpegmatch(reslasher,one) + local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two) + if lpegmatch(hasroot,two) then + return one..two + else + return one.."/"..two + end + elseif lpegmatch(isroot,one) then + local two=lpegmatch(deslasher,three and concat({ two,three,... },"/") or two) + if lpegmatch(hasroot,two) then + return two + else + return "/"..two + end + else + return lpegmatch(deslasher,concat({ one,two,three,... },"/")) + end +end +local drivespec=R("az","AZ")^1*colon +local anchors=fwslash+drivespec +local untouched=periods+(1-period)^1*P(-1) +local mswindrive=Cs(drivespec*(bwslash/"/"+fwslash)^0) +local mswinuncpath=(bwslash+fwslash)*(bwslash+fwslash)*Cc("//") +local splitstarter=(mswindrive+mswinuncpath+Cc(false))*Ct(lpeg.splitat(S("/\\")^1)) +local absolute=fwslash +function file.collapsepath(str,anchor) + if not str then + return + end + if anchor==true and not lpegmatch(anchors,str) then + str=getcurrentdir().."/"..str + end + if str=="" or str=="." then + return "." + elseif lpegmatch(untouched,str) then + return lpegmatch(reslasher,str) + end + local starter,oldelements=lpegmatch(splitstarter,str) + local newelements={} + local i=#oldelements + while i>0 do + local element=oldelements[i] + if element=='.' then + elseif element=='..' then + local n=i-1 + while n>0 do + local element=oldelements[n] + if element~='..' and element~='.' then + oldelements[n]='.' + break + else + n=n-1 + end + end + if n<1 then + insert(newelements,1,'..') + end + elseif element~="" then + insert(newelements,1,element) + end + i=i-1 + end + if #newelements==0 then + return starter or "." + elseif starter then + return starter..concat(newelements,'/') + elseif lpegmatch(absolute,str) then + return "/"..concat(newelements,'/') + else + newelements=concat(newelements,'/') + if anchor=="." and find(str,"^%./") then + return "./"..newelements + else + return newelements + end + end +end +local validchars=R("az","09","AZ","--","..") +local pattern_a=lpeg.replacer(1-validchars) +local pattern_a=Cs((validchars+P(1)/"-")^1) +local whatever=P("-")^0/"" +local pattern_b=Cs(whatever*(1-whatever*-1)^1) +function file.robustname(str,strict) + if str then + str=lpegmatch(pattern_a,str) or str + if strict then + return lpegmatch(pattern_b,str) or str + else + return str + end + end +end +local loaddata=io.loaddata +local savedata=io.savedata +file.readdata=loaddata +file.savedata=savedata +function file.copy(oldname,newname) + if oldname and newname then + local data=loaddata(oldname) + if data and data~="" then + savedata(newname,data) + end + end +end +local letter=R("az","AZ")+S("_-+") +local separator=P("://") +local qualified=period^0*fwslash+letter*colon+letter^1*separator+letter^1*fwslash +local rootbased=fwslash+letter*colon +lpeg.patterns.qualified=qualified +lpeg.patterns.rootbased=rootbased +function file.is_qualified_path(filename) + return filename and lpegmatch(qualified,filename)~=nil +end +function file.is_rootbased_path(filename) + return filename and lpegmatch(rootbased,filename)~=nil +end +function file.strip(name,dir) + if name then + local b,a=match(name,"^(.-)"..dir.."(.*)$") + return a~="" and a or name + end +end +function lfs.mkdirs(path) + local full="" + for sub in gmatch(path,"(/*[^\\/]+)") do + full=full..sub + lfs.mkdir(full) + end +end +function file.withinbase(path) + local l=0 + if not find(path,"^/") then + path="/"..path + end + for dir in gmatch(path,"/([^/]+)") do + if dir==".." then + l=l-1 + elseif dir~="." then + l=l+1 + end + if l<0 then + return false + end + end + return true +end +local symlinkattributes=lfs.symlinkattributes +function lfs.readlink(name) + return symlinkattributes(name,"target") or nil +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-gzip']={ + version=1.001, + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if not gzip then + return +end +local suffix,suffixes=file.suffix,file.suffixes +function gzip.load(filename) + local f=io.open(filename,"rb") + if not f then + elseif suffix(filename)=="gz" then + f:close() + local g=gzip.open(filename,"rb") + if g then + local str=g:read("*all") + g:close() + return str + end + else + local str=f:read("*all") + f:close() + return str + end +end +function gzip.save(filename,data) + if suffix(filename)~="gz" then + filename=filename..".gz" + end + local f=io.open(filename,"wb") + if f then + local s=zlib.compress(data or "",9,nil,15+16) + f:write(s) + f:close() + return #s + end +end +function gzip.suffix(filename) + local suffix,extra=suffixes(filename) + local gzipped=extra=="gz" + return suffix,gzipped +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-md5']={ + version=1.001, + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +if not md5 then + md5=optionalrequire("md5") +end +if not md5 then + md5={ + sum=function(str) print("error: md5 is not loaded (sum ignored)") return str end, + sumhexa=function(str) print("error: md5 is not loaded (sumhexa ignored)") return str end, + } +end +local md5,file=md5,file +local gsub=string.gsub +do + local patterns=lpeg and lpeg.patterns + if patterns then + local bytestoHEX=patterns.bytestoHEX + local bytestohex=patterns.bytestohex + local bytestodec=patterns.bytestodec + local lpegmatch=lpeg.match + local md5sum=md5.sum + if not md5.HEX then function md5.HEX(str) if str then return lpegmatch(bytestoHEX,md5sum(str)) end end end + if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end + if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end + md5.sumhexa=md5.hex + md5.sumHEXA=md5.HEX + end +end +function file.needsupdating(oldname,newname,threshold) + local oldtime=lfs.attributes(oldname,"modification") + if oldtime then + local newtime=lfs.attributes(newname,"modification") + if not newtime then + return true + elseif newtime>=oldtime then + return false + elseif oldtime-newtime<(threshold or 1) then + return false + else + return true + end + else + return false + end +end +file.needs_updating=file.needsupdating +function file.syncmtimes(oldname,newname) + local oldtime=lfs.attributes(oldname,"modification") + if oldtime and lfs.isfile(newname) then + lfs.touch(newname,oldtime,oldtime) + end +end +function file.checksum(name) + if md5 then + local data=io.loaddata(name) + if data then + return md5.HEX(data) + end + end + return nil +end +function file.loadchecksum(name) + if md5 then + local data=io.loaddata(name..".md5") + return data and (gsub(data,"%s","")) + end + return nil +end +function file.savechecksum(name,checksum) + if not checksum then checksum=file.checksum(name) end + if checksum then + io.savedata(name..".md5",checksum) + return checksum + end + return nil +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-dir']={ + 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 type,select=type,select +local find,gmatch,match,gsub,sub=string.find,string.gmatch,string.match,string.gsub,string.sub +local concat,insert,remove,unpack=table.concat,table.insert,table.remove,table.unpack +local lpegmatch=lpeg.match +local P,S,R,C,Cc,Cs,Ct,Cv,V=lpeg.P,lpeg.S,lpeg.R,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Cv,lpeg.V +dir=dir or {} +local dir=dir +local lfs=lfs +local attributes=lfs.attributes +local walkdir=lfs.dir +local isdir=lfs.isdir +local isfile=lfs.isfile +local currentdir=lfs.currentdir +local chdir=lfs.chdir +local mkdir=lfs.mkdir +local onwindows=os.type=="windows" or find(os.getenv("PATH"),";",1,true) +if onwindows then + local tricky=S("/\\")*P(-1) + isdir=function(name) + if lpegmatch(tricky,name) then + return attributes(name,"mode")=="directory" + else + return attributes(name.."/.","mode")=="directory" + end + end + isfile=function(name) + return attributes(name,"mode")=="file" + end + lfs.isdir=isdir + lfs.isfile=isfile +else + isdir=function(name) + return attributes(name,"mode")=="directory" + end + isfile=function(name) + return attributes(name,"mode")=="file" + end + lfs.isdir=isdir + lfs.isfile=isfile +end +function dir.current() + return (gsub(currentdir(),"\\","/")) +end +local function glob_pattern_function(path,patt,recurse,action) + if isdir(path) then + local usedpath + if path=="/" then + usedpath="/." + elseif not find(path,"/$") then + usedpath=path.."/." + path=path.."/" + else + usedpath=path + end + local dirs + local nofdirs=0 + for name in walkdir(usedpath) do + if name~="." and name~=".." then + local full=path..name + local mode=attributes(full,'mode') + if mode=='file' then + if not patt or find(full,patt) then + action(full) + end + elseif recurse and mode=="directory" then + if dirs then + nofdirs=nofdirs+1 + dirs[nofdirs]=full + else + nofdirs=1 + dirs={ full } + end + end + end + end + if dirs then + for i=1,nofdirs do + glob_pattern_function(dirs[i],patt,recurse,action) + end + end + end +end +local function glob_pattern_table(path,patt,recurse,result) + if not result then + result={} + end + local usedpath + if path=="/" then + usedpath="/." + elseif not find(path,"/$") then + usedpath=path.."/." + path=path.."/" + else + usedpath=path + end + local dirs + local nofdirs=0 + local noffiles=#result + for name,a in walkdir(usedpath) do + if name~="." and name~=".." then + local full=path..name + local mode=attributes(full,'mode') + if mode=='file' then + if not patt or find(full,patt) then + noffiles=noffiles+1 + result[noffiles]=full + end + elseif recurse and mode=="directory" then + if dirs then + nofdirs=nofdirs+1 + dirs[nofdirs]=full + else + nofdirs=1 + dirs={ full } + end + end + end + end + if dirs then + for i=1,nofdirs do + glob_pattern_table(dirs[i],patt,recurse,result) + end + end + return result +end +local function globpattern(path,patt,recurse,method) + local kind=type(method) + if patt and sub(patt,1,-3)==path then + patt=false + end + local okay=isdir(path) + if kind=="function" then + return okay and glob_pattern_function(path,patt,recurse,method) or {} + elseif kind=="table" then + return okay and glob_pattern_table(path,patt,recurse,method) or method + else + return okay and glob_pattern_table(path,patt,recurse,{}) or {} + end +end +dir.globpattern=globpattern +local function collectpattern(path,patt,recurse,result) + local ok,scanner + result=result or {} + if path=="/" then + ok,scanner,first=xpcall(function() return walkdir(path..".") end,function() end) + else + ok,scanner,first=xpcall(function() return walkdir(path) end,function() end) + end + if ok and type(scanner)=="function" then + if not find(path,"/$") then + path=path..'/' + end + for name in scanner,first do + if name=="." then + elseif name==".." then + else + local full=path..name + local attr=attributes(full) + local mode=attr.mode + if mode=='file' then + if find(full,patt) then + result[name]=attr + end + elseif recurse and mode=="directory" then + attr.list=collectpattern(full,patt,recurse) + result[name]=attr + end + end + end + end + return result +end +dir.collectpattern=collectpattern +local separator,pattern +if onwindows then + local slash=S("/\\")/"/" + pattern={ + [1]=(Cs(P(".")+slash^1)+Cs(R("az","AZ")*P(":")*slash^0)+Cc("./"))*V(2)*V(3), + [2]=Cs(((1-S("*?/\\"))^0*slash)^0), + [3]=Cs(P(1)^0) + } +else + pattern={ + [1]=(C(P(".")+P("/")^1)+Cc("./"))*V(2)*V(3), + [2]=C(((1-S("*?/"))^0*P("/"))^0), + [3]=C(P(1)^0) + } +end +local filter=Cs (( + P("**")/".*"+P("*")/"[^/]*"+P("?")/"[^/]"+P(".")/"%%."+P("+")/"%%+"+P("-")/"%%-"+P(1) +)^0 ) +local function glob(str,t) + if type(t)=="function" then + if type(str)=="table" then + for s=1,#str do + glob(str[s],t) + end + elseif isfile(str) then + t(str) + else + local root,path,base=lpegmatch(pattern,str) + if root and path and base then + local recurse=find(base,"**",1,true) + local start=root..path + local result=lpegmatch(filter,start..base) + globpattern(start,result,recurse,t) + end + end + else + if type(str)=="table" then + local t=t or {} + for s=1,#str do + glob(str[s],t) + end + return t + elseif isfile(str) then + if t then + t[#t+1]=str + return t + else + return { str } + end + else + local root,path,base=lpegmatch(pattern,str) + if root and path and base then + local recurse=find(base,"**",1,true) + local start=root..path + local result=lpegmatch(filter,start..base) + return globpattern(start,result,recurse,t) + else + return {} + end + end + end +end +dir.glob=glob +local function globfiles(path,recurse,func,files) + if type(func)=="string" then + local s=func + func=function(name) return find(name,s) end + end + files=files or {} + local noffiles=#files + for name in walkdir(path) do + if find(name,"^%.") then + else + local mode=attributes(name,'mode') + if mode=="directory" then + if recurse then + globfiles(path.."/"..name,recurse,func,files) + end + elseif mode=="file" then + if not func or func(name) then + noffiles=noffiles+1 + files[noffiles]=path.."/"..name + end + end + end + end + return files +end +dir.globfiles=globfiles +local function globdirs(path,recurse,func,files) + if type(func)=="string" then + local s=func + func=function(name) return find(name,s) end + end + files=files or {} + local noffiles=#files + for name in walkdir(path) do + if find(name,"^%.") then + else + local mode=attributes(name,'mode') + if mode=="directory" then + if not func or func(name) then + noffiles=noffiles+1 + files[noffiles]=path.."/"..name + if recurse then + globdirs(path.."/"..name,recurse,func,files) + end + end + end + end + end + return files +end +dir.globdirs=globdirs +function dir.ls(pattern) + return concat(glob(pattern),"\n") +end +local make_indeed=true +if onwindows then + function dir.mkdirs(...) + local n=select("#",...) + local str + if n==1 then + str=select(1,...) + if isdir(str) then + return str,true + end + else + str="" + for i=1,n do + local s=select(i,...) + if s=="" then + elseif str=="" then + str=s + else + str=str.."/"..s + end + end + end + local pth="" + local drive=false + local first,middle,last=match(str,"^(//)(//*)(.*)$") + if first then + else + first,last=match(str,"^(//)/*(.-)$") + if first then + middle,last=match(str,"([^/]+)/+(.-)$") + if middle then + pth="//"..middle + else + pth="//"..last + last="" + end + else + first,middle,last=match(str,"^([a-zA-Z]:)(/*)(.-)$") + if first then + pth,drive=first..middle,true + else + middle,last=match(str,"^(/*)(.-)$") + if not middle then + last=str + end + end + end + end + for s in gmatch(last,"[^/]+") do + if pth=="" then + pth=s + elseif drive then + pth,drive=pth..s,false + else + pth=pth.."/"..s + end + if make_indeed and not isdir(pth) then + mkdir(pth) + end + end + return pth,(isdir(pth)==true) + end +else + function dir.mkdirs(...) + local n=select("#",...) + local str,pth + if n==1 then + str=select(1,...) + if isdir(str) then + return str,true + end + else + str="" + for i=1,n do + local s=select(i,...) + if s and s~="" then + if str~="" then + str=str.."/"..s + else + str=s + end + end + end + end + str=gsub(str,"/+","/") + if find(str,"^/") then + pth="/" + for s in gmatch(str,"[^/]+") do + local first=(pth=="/") + if first then + pth=pth..s + else + pth=pth.."/"..s + end + if make_indeed and not first and not isdir(pth) then + mkdir(pth) + end + end + else + pth="." + for s in gmatch(str,"[^/]+") do + pth=pth.."/"..s + if make_indeed and not isdir(pth) then + mkdir(pth) + end + end + end + return pth,(isdir(pth)==true) + end +end +dir.makedirs=dir.mkdirs +do + local chdir=sandbox and sandbox.original(chdir) or chdir + if onwindows then + local xcurrentdir=dir.current + function dir.expandname(str) + local first,nothing,last=match(str,"^(//)(//*)(.*)$") + if first then + first=xcurrentdir().."/" + end + if not first then + first,last=match(str,"^(//)/*(.*)$") + end + if not first then + first,last=match(str,"^([a-zA-Z]:)(.*)$") + if first and not find(last,"^/") then + local d=currentdir() + if chdir(first) then + first=xcurrentdir() + end + chdir(d) + end + end + if not first then + first,last=xcurrentdir(),str + end + last=gsub(last,"//","/") + last=gsub(last,"/%./","/") + last=gsub(last,"^/*","") + first=gsub(first,"/*$","") + if last=="" or last=="." then + return first + else + return first.."/"..last + end + end + else + function dir.expandname(str) + if not find(str,"^/") then + str=currentdir().."/"..str + end + str=gsub(str,"//","/") + str=gsub(str,"/%./","/") + str=gsub(str,"(.)/%.$","%1") + return str + end + end +end +file.expandname=dir.expandname +local stack={} +function dir.push(newdir) + local curdir=currentdir() + insert(stack,curdir) + if newdir and newdir~="" then + chdir(newdir) + return newdir + else + return curdir + end +end +function dir.pop() + local d=remove(stack) + if d then + chdir(d) + end + return d +end +local function found(...) + for i=1,select("#",...) do + local path=select(i,...) + local kind=type(path) + if kind=="string" then + if isdir(path) then + return path + end + elseif kind=="table" then + local path=found(unpack(path)) + if path then + return path + end + end + end +end +dir.found=found + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-unicode']={ + 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" +} +utf=utf or (unicode and unicode.utf8) or {} +utf.characters=utf.characters or string.utfcharacters +utf.values=utf.values or string.utfvalues +local type=type +local char,byte,format,sub,gmatch=string.char,string.byte,string.format,string.sub,string.gmatch +local concat=table.concat +local P,C,R,Cs,Ct,Cmt,Cc,Carg,Cp=lpeg.P,lpeg.C,lpeg.R,lpeg.Cs,lpeg.Ct,lpeg.Cmt,lpeg.Cc,lpeg.Carg,lpeg.Cp +local lpegmatch=lpeg.match +local patterns=lpeg.patterns +local tabletopattern=lpeg.utfchartabletopattern +local bytepairs=string.bytepairs +local finder=lpeg.finder +local replacer=lpeg.replacer +local utfvalues=utf.values +local utfgmatch=utf.gmatch +local p_utftype=patterns.utftype +local p_utfstricttype=patterns.utfstricttype +local p_utfoffset=patterns.utfoffset +local p_utf8char=patterns.utf8character +local p_utf8byte=patterns.utf8byte +local p_utfbom=patterns.utfbom +local p_newline=patterns.newline +local p_whitespace=patterns.whitespace +if not unicode then + unicode={ utf=utf } +end +if not utf.char then + utf.char=string.utfcharacter or (utf8 and utf8.char) + if not utf.char then + local char=string.char + if bit32 then + local rshift=bit32.rshift + function utf.char(n) + if n<0x80 then + return char(n) + elseif n<0x800 then + return char( + 0xC0+rshift(n,6), + 0x80+(n%0x40) + ) + elseif n<0x10000 then + return char( + 0xE0+rshift(n,12), + 0x80+(rshift(n,6)%0x40), + 0x80+(n%0x40) + ) + elseif n<0x200000 then + return char( + 0xF0+rshift(n,18), + 0x80+(rshift(n,12)%0x40), + 0x80+(rshift(n,6)%0x40), + 0x80+(n%0x40) + ) + else + return "" + end + end + else + local floor=math.floor + function utf.char(n) + if n<0x80 then + return char(n) + elseif n<0x800 then + return char( + 0xC0+floor(n/0x40), + 0x80+(n%0x40) + ) + elseif n<0x10000 then + return char( + 0xE0+floor(n/0x1000), + 0x80+(floor(n/0x40)%0x40), + 0x80+(n%0x40) + ) + elseif n<0x200000 then + return char( + 0xF0+floor(n/0x40000), + 0x80+(floor(n/0x1000)%0x40), + 0x80+(floor(n/0x40)%0x40), + 0x80+(n%0x40) + ) + else + return "" + end + end + end + end +end +if not utf.byte then + utf.byte=string.utfvalue or (utf8 and utf8.codepoint) + if not utf.byte then + local utf8byte=patterns.utf8byte + function utf.byte(c) + return lpegmatch(utf8byte,c) + end + end +end +local utfchar,utfbyte=utf.char,utf.byte +function utf.filetype(data) + return data and lpegmatch(p_utftype,data) or "unknown" +end +local toentities=Cs ( + ( + patterns.utf8one+( + patterns.utf8two+patterns.utf8three+patterns.utf8four + )/function(s) local b=utfbyte(s) if b<127 then return s else return format("&#%X;",b) end end + )^0 +) +patterns.toentities=toentities +function utf.toentities(str) + return lpegmatch(toentities,str) +end +local one=P(1) +local two=C(1)*C(1) +local four=C(R(utfchar(0xD8),utfchar(0xFF)))*C(1)*C(1)*C(1) +local pattern=P("\254\255")*Cs(( + four/function(a,b,c,d) + local ab=0xFF*byte(a)+byte(b) + local cd=0xFF*byte(c)+byte(d) + return utfchar((ab-0xD800)*0x400+(cd-0xDC00)+0x10000) + end+two/function(a,b) + return utfchar(byte(a)*256+byte(b)) + end+one + )^1 )+P("\255\254")*Cs(( + four/function(b,a,d,c) + local ab=0xFF*byte(a)+byte(b) + local cd=0xFF*byte(c)+byte(d) + return utfchar((ab-0xD800)*0x400+(cd-0xDC00)+0x10000) + end+two/function(b,a) + return utfchar(byte(a)*256+byte(b)) + end+one + )^1 ) +function string.toutf(s) + return lpegmatch(pattern,s) or s +end +local validatedutf=Cs ( + ( + patterns.utf8one+patterns.utf8two+patterns.utf8three+patterns.utf8four+P(1)/"�" + )^0 +) +patterns.validatedutf=validatedutf +function utf.is_valid(str) + return type(str)=="string" and lpegmatch(validatedutf,str) or false +end +if not utf.len then + utf.len=string.utflength or (utf8 and utf8.len) + if not utf.len then + local n,f=0,1 + local utfcharcounter=patterns.utfbom^-1*Cmt ( + Cc(1)*patterns.utf8one^1+Cc(2)*patterns.utf8two^1+Cc(3)*patterns.utf8three^1+Cc(4)*patterns.utf8four^1, + function(_,t,d) + n=n+(t-f)/d + f=t + return true + end + )^0 + function utf.len(str) + n,f=0,1 + lpegmatch(utfcharcounter,str or "") + return n + end + end +end +utf.length=utf.len +if not utf.sub then + local utflength=utf.length + local b,e,n,first,last=0,0,0,0,0 + local function slide_zero(s,p) + n=n+1 + if n>=last then + e=p-1 + else + return p + end + end + local function slide_one(s,p) + n=n+1 + if n==first then + b=p + end + if n>=last then + e=p-1 + else + return p + end + end + local function slide_two(s,p) + n=n+1 + if n==first then + b=p + else + return true + end + end + local pattern_zero=Cmt(p_utf8char,slide_zero)^0 + local pattern_one=Cmt(p_utf8char,slide_one )^0 + local pattern_two=Cmt(p_utf8char,slide_two )^0 + local pattern_first=C(patterns.utf8character) + function utf.sub(str,start,stop) + if not start then + return str + end + if start==0 then + start=1 + end + if not stop then + if start<0 then + local l=utflength(str) + start=l+start + else + start=start-1 + end + b,n,first=0,0,start + lpegmatch(pattern_two,str) + if n>=first then + return sub(str,b) + else + return "" + end + end + if start<0 or stop<0 then + local l=utf.length(str) + if start<0 then + start=l+start + if start<=0 then + start=1 + else + start=start+1 + end + end + if stop<0 then + stop=l+stop + if stop==0 then + stop=1 + else + stop=stop+1 + end + end + end + if start==1 and stop==1 then + return lpegmatch(pattern_first,str) or "" + elseif start>stop then + return "" + elseif start>1 then + b,e,n,first,last=0,0,0,start-1,stop + lpegmatch(pattern_one,str) + if n>=first and e==0 then + e=#str + end + return sub(str,b,e) + else + b,e,n,last=1,0,0,stop + lpegmatch(pattern_zero,str) + if e==0 then + e=#str + end + return sub(str,b,e) + end + end +end +function utf.remapper(mapping,option,action) + local variant=type(mapping) + if variant=="table" then + action=action or mapping + if option=="dynamic" then + local pattern=false + table.setmetatablenewindex(mapping,function(t,k,v) rawset(t,k,v) pattern=false end) + return function(str) + if not str or str=="" then + return "" + else + if not pattern then + pattern=Cs((tabletopattern(mapping)/action+p_utf8char)^0) + end + return lpegmatch(pattern,str) + end + end + elseif option=="pattern" then + return Cs((tabletopattern(mapping)/action+p_utf8char)^0) + else + local pattern=Cs((tabletopattern(mapping)/action+p_utf8char)^0) + return function(str) + if not str or str=="" then + return "" + else + return lpegmatch(pattern,str) + end + end,pattern + end + elseif variant=="function" then + if option=="pattern" then + return Cs((p_utf8char/mapping+p_utf8char)^0) + else + local pattern=Cs((p_utf8char/mapping+p_utf8char)^0) + return function(str) + if not str or str=="" then + return "" + else + return lpegmatch(pattern,str) + end + end,pattern + end + else + return function(str) + return str or "" + end + end +end +function utf.replacer(t) + local r=replacer(t,false,false,true) + return function(str) + return lpegmatch(r,str) + end +end +function utf.subtituter(t) + local f=finder (t) + local r=replacer(t,false,false,true) + return function(str) + local i=lpegmatch(f,str) + if not i then + return str + elseif i>#str then + return str + else + return lpegmatch(r,str) + end + end +end +local utflinesplitter=p_utfbom^-1*lpeg.tsplitat(p_newline) +local utfcharsplitter_ows=p_utfbom^-1*Ct(C(p_utf8char)^0) +local utfcharsplitter_iws=p_utfbom^-1*Ct((p_whitespace^1+C(p_utf8char))^0) +local utfcharsplitter_raw=Ct(C(p_utf8char)^0) +patterns.utflinesplitter=utflinesplitter +function utf.splitlines(str) + return lpegmatch(utflinesplitter,str or "") +end +function utf.split(str,ignorewhitespace) + if ignorewhitespace then + return lpegmatch(utfcharsplitter_iws,str or "") + else + return lpegmatch(utfcharsplitter_ows,str or "") + end +end +function utf.totable(str) + return lpegmatch(utfcharsplitter_raw,str) +end +function utf.magic(f) + local str=f:read(4) or "" + local off=lpegmatch(p_utfoffset,str) + if off<4 then + f:seek('set',off) + end + return lpegmatch(p_utftype,str) +end +local utf16_to_utf8_be,utf16_to_utf8_le +local utf32_to_utf8_be,utf32_to_utf8_le +local utf_16_be_getbom=patterns.utfbom_16_be^-1 +local utf_16_le_getbom=patterns.utfbom_16_le^-1 +local utf_32_be_getbom=patterns.utfbom_32_be^-1 +local utf_32_le_getbom=patterns.utfbom_32_le^-1 +local utf_16_be_linesplitter=utf_16_be_getbom*lpeg.tsplitat(patterns.utf_16_be_nl) +local utf_16_le_linesplitter=utf_16_le_getbom*lpeg.tsplitat(patterns.utf_16_le_nl) +local utf_32_be_linesplitter=utf_32_be_getbom*lpeg.tsplitat(patterns.utf_32_be_nl) +local utf_32_le_linesplitter=utf_32_le_getbom*lpeg.tsplitat(patterns.utf_32_le_nl) +local more=0 +local p_utf16_to_utf8_be=C(1)*C(1)/function(left,right) + local now=256*byte(left)+byte(right) + if more>0 then + now=(more-0xD800)*0x400+(now-0xDC00)+0x10000 + more=0 + return utfchar(now) + elseif now>=0xD800 and now<=0xDBFF then + more=now + return "" + else + return utfchar(now) + end +end +local p_utf16_to_utf8_le=C(1)*C(1)/function(right,left) + local now=256*byte(left)+byte(right) + if more>0 then + now=(more-0xD800)*0x400+(now-0xDC00)+0x10000 + more=0 + return utfchar(now) + elseif now>=0xD800 and now<=0xDBFF then + more=now + return "" + else + return utfchar(now) + end +end +local p_utf32_to_utf8_be=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d) + return utfchar(256*256*256*byte(a)+256*256*byte(b)+256*byte(c)+byte(d)) +end +local p_utf32_to_utf8_le=C(1)*C(1)*C(1)*C(1)/function(a,b,c,d) + return utfchar(256*256*256*byte(d)+256*256*byte(c)+256*byte(b)+byte(a)) +end +p_utf16_to_utf8_be=P(true)/function() more=0 end*utf_16_be_getbom*Cs(p_utf16_to_utf8_be^0) +p_utf16_to_utf8_le=P(true)/function() more=0 end*utf_16_le_getbom*Cs(p_utf16_to_utf8_le^0) +p_utf32_to_utf8_be=P(true)/function() more=0 end*utf_32_be_getbom*Cs(p_utf32_to_utf8_be^0) +p_utf32_to_utf8_le=P(true)/function() more=0 end*utf_32_le_getbom*Cs(p_utf32_to_utf8_le^0) +patterns.utf16_to_utf8_be=p_utf16_to_utf8_be +patterns.utf16_to_utf8_le=p_utf16_to_utf8_le +patterns.utf32_to_utf8_be=p_utf32_to_utf8_be +patterns.utf32_to_utf8_le=p_utf32_to_utf8_le +utf16_to_utf8_be=function(s) + if s and s~="" then + return lpegmatch(p_utf16_to_utf8_be,s) + else + return s + end +end +local utf16_to_utf8_be_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_16_be_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf16_to_utf8_be,s) + end + end + return t +end +utf16_to_utf8_le=function(s) + if s and s~="" then + return lpegmatch(p_utf16_to_utf8_le,s) + else + return s + end +end +local utf16_to_utf8_le_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_16_le_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf16_to_utf8_le,s) + end + end + return t +end +utf32_to_utf8_be=function(s) + if s and s~="" then + return lpegmatch(p_utf32_to_utf8_be,s) + else + return s + end +end +local utf32_to_utf8_be_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_32_be_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf32_to_utf8_be,s) + end + end + return t +end +utf32_to_utf8_le=function(s) + if s and s~="" then + return lpegmatch(p_utf32_to_utf8_le,s) + else + return s + end +end +local utf32_to_utf8_le_t=function(t) + if not t then + return nil + elseif type(t)=="string" then + t=lpegmatch(utf_32_le_linesplitter,t) + end + for i=1,#t do + local s=t[i] + if s~="" then + t[i]=lpegmatch(p_utf32_to_utf8_le,s) + end + end + return t +end +utf.utf16_to_utf8_le_t=utf16_to_utf8_le_t +utf.utf16_to_utf8_be_t=utf16_to_utf8_be_t +utf.utf32_to_utf8_le_t=utf32_to_utf8_le_t +utf.utf32_to_utf8_be_t=utf32_to_utf8_be_t +utf.utf16_to_utf8_le=utf16_to_utf8_le +utf.utf16_to_utf8_be=utf16_to_utf8_be +utf.utf32_to_utf8_le=utf32_to_utf8_le +utf.utf32_to_utf8_be=utf32_to_utf8_be +function utf.utf8_to_utf8_t(t) + return type(t)=="string" and lpegmatch(utflinesplitter,t) or t +end +function utf.utf16_to_utf8_t(t,endian) + return endian and utf16_to_utf8_be_t(t) or utf16_to_utf8_le_t(t) or t +end +function utf.utf32_to_utf8_t(t,endian) + return endian and utf32_to_utf8_be_t(t) or utf32_to_utf8_le_t(t) or t +end +local function little(b) + if b<0x10000 then + return char(b%256,rshift(b,8)) + else + b=b-0x10000 + local b1=rshift(b,10)+0xD800 + local b2=b%1024+0xDC00 + return char(b1%256,rshift(b1,8),b2%256,rshift(b2,8)) + end +end +local function big(b) + if b<0x10000 then + return char(rshift(b,8),b%256) + else + b=b-0x10000 + local b1=rshift(b,10)+0xD800 + local b2=b%1024+0xDC00 + return char(rshift(b1,8),b1%256,rshift(b2,8),b2%256) + end +end +local l_remap=Cs((p_utf8byte/little+P(1)/"")^0) +local b_remap=Cs((p_utf8byte/big+P(1)/"")^0) +local function utf8_to_utf16_be(str,nobom) + if nobom then + return lpegmatch(b_remap,str) + else + return char(254,255)..lpegmatch(b_remap,str) + end +end +local function utf8_to_utf16_le(str,nobom) + if nobom then + return lpegmatch(l_remap,str) + else + return char(255,254)..lpegmatch(l_remap,str) + end +end +utf.utf8_to_utf16_be=utf8_to_utf16_be +utf.utf8_to_utf16_le=utf8_to_utf16_le +function utf.utf8_to_utf16(str,littleendian,nobom) + if littleendian then + return utf8_to_utf16_le(str,nobom) + else + return utf8_to_utf16_be(str,nobom) + end +end +local pattern=Cs ( + (p_utf8byte/function(unicode ) return format("0x%04X",unicode) end)*(p_utf8byte*Carg(1)/function(unicode,separator) return format("%s0x%04X",separator,unicode) end)^0 +) +function utf.tocodes(str,separator) + return lpegmatch(pattern,str,1,separator or " ") +end +function utf.ustring(s) + return format("U+%05X",type(s)=="number" and s or utfbyte(s)) +end +function utf.xstring(s) + return format("0x%05X",type(s)=="number" and s or utfbyte(s)) +end +function utf.toeight(str) + if not str or str=="" then + return nil + end + local utftype=lpegmatch(p_utfstricttype,str) + if utftype=="utf-8" then + return sub(str,4) + elseif utftype=="utf-16-be" then + return utf16_to_utf8_be(str) + elseif utftype=="utf-16-le" then + return utf16_to_utf8_le(str) + else + return str + end +end +local p_nany=p_utf8char/"" +if utfgmatch then + function utf.count(str,what) + if type(what)=="string" then + local n=0 + for _ in utfgmatch(str,what) do + n=n+1 + end + return n + else + return #lpegmatch(Cs((P(what)/" "+p_nany)^0),str) + end + end +else + local cache={} + function utf.count(str,what) + if type(what)=="string" then + local p=cache[what] + if not p then + p=Cs((P(what)/" "+p_nany)^0) + cache[p]=p + end + return #lpegmatch(p,str) + else + return #lpegmatch(Cs((P(what)/" "+p_nany)^0),str) + end + end +end +if not utf.characters then + function utf.characters(str) + return gmatch(str,".[\128-\191]*") + end + string.utfcharacters=utf.characters +end +if not utf.values then + local find=string.find + local dummy=function() + end + function utf.values(str) + local n=#str + if n==0 then + return dummy + elseif n==1 then + return function() return utfbyte(str) end + else + local p=1 + return function() + local b,e=find(str,".[\128-\191]*",p) + if b then + p=e+1 + return utfbyte(sub(str,b,e)) + end + end + end + end + string.utfvalues=utf.values +end +function utf.chrlen(u) + return + (u<0x80 and 1) or + (u<0xE0 and 2) or + (u<0xF0 and 3) or + (u<0xF8 and 4) or + (u<0xFC and 5) or + (u<0xFE and 6) or 0 +end +if bit32 then + local extract=bit32.extract + local char=string.char + function unicode.toutf32string(n) + if n<=0xFF then + return + char(n).."\000\000\000" + elseif n<=0xFFFF then + return + char(extract(n,0,8))..char(extract(n,8,8)).."\000\000" + elseif n<=0xFFFFFF then + return + char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8)).."\000" + else + return + char(extract(n,0,8))..char(extract(n,8,8))..char(extract(n,16,8))..char(extract(n,24,8)) + end + end +end +local len=utf.len +local rep=rep +function string.utfpadd(s,n) + if n and n~=0 then + local l=len(s) + if n>0 then + local d=n-l + if d>0 then + return rep(c or " ",d)..s + end + else + local d=- n-l + if d>0 then + return s..rep(c or " ",d) + end + end + end + return s +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-url']={ + 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 char,format,byte=string.char,string.format,string.byte +local concat=table.concat +local tonumber,type,next=tonumber,type,next +local P,C,R,S,Cs,Cc,Ct,Cf,Cg,V=lpeg.P,lpeg.C,lpeg.R,lpeg.S,lpeg.Cs,lpeg.Cc,lpeg.Ct,lpeg.Cf,lpeg.Cg,lpeg.V +local lpegmatch,lpegpatterns,replacer=lpeg.match,lpeg.patterns,lpeg.replacer +local sortedhash=table.sortedhash +url=url or {} +local url=url +local unescapes={} +local escapes={} +setmetatable(unescapes,{ __index=function(t,k) + local v=char(tonumber(k,16)) + t[k]=v + return v +end }) +setmetatable(escapes,{ __index=function(t,k) + local v=format("%%%02X",byte(k)) + t[k]=v + return v +end }) +local colon=P(":") +local qmark=P("?") +local hash=P("#") +local slash=P("/") +local atsign=P("@") +local percent=P("%") +local endofstring=P(-1) +local hexdigit=R("09","AF","af") +local plus=P("+") +local nothing=Cc("") +local okay=R("09","AZ","az")+S("-_.,:=+*~!'()@&$") +local escapedchar=(percent*C(hexdigit*hexdigit))/unescapes +local unescapedchar=P(1)/escapes +local escaped=(plus/" ")+escapedchar +local noslash=P("/")/"" +local plustospace=P("+")/" " +local decoder=Cs(( + plustospace+escapedchar+P("\r\n")/"\n"+P(1) + )^0 ) +local encoder=Cs(( + R("09","AZ","az")^1+S("-./_")^1+P(" ")/"+"+P("\n")/"\r\n"+unescapedchar + )^0 ) +lpegpatterns.urldecoder=decoder +lpegpatterns.urlencoder=encoder +function url.decode (str) return str and lpegmatch(decoder,str) or str end +function url.encode (str) return str and lpegmatch(encoder,str) or str end +function url.unescape(str) return str and lpegmatch(unescaper,str) or str end +local schemestr=Cs((escaped+(1-colon-slash-qmark-hash))^2) +local authoritystr=Cs((escaped+(1- slash-qmark-hash))^0) +local pathstr=Cs((escaped+(1- qmark-hash))^0) +local querystr=Cs(((1- hash))^0) +local fragmentstr=Cs((escaped+(1- endofstring))^0) +local scheme=schemestr*colon+nothing +local authority=slash*slash*authoritystr+nothing +local path=slash*pathstr+nothing +local query=qmark*querystr+nothing +local fragment=hash*fragmentstr+nothing +local validurl=scheme*authority*path*query*fragment +local parser=Ct(validurl) +lpegpatterns.url=validurl +lpegpatterns.urlsplitter=parser +local escaper=Cs((R("09","AZ","az")^1+P(" ")/"%%20"+S("-./_:")^1+P(1)/escapes)^0) +local unescaper=Cs((escapedchar+1)^0) +local getcleaner=Cs((P("+++")/"%%2B"+P("+")/"%%20"+P(1))^1) +lpegpatterns.urlunescaped=escapedchar +lpegpatterns.urlescaper=escaper +lpegpatterns.urlunescaper=unescaper +lpegpatterns.urlgetcleaner=getcleaner +function url.unescapeget(str) + return lpegmatch(getcleaner,str) +end +local function split(str) + return (type(str)=="string" and lpegmatch(parser,str)) or str +end +local isscheme=schemestr*colon*slash*slash +local function hasscheme(str) + if str then + local scheme=lpegmatch(isscheme,str) + return scheme~="" and scheme or false + else + return false + end +end +local rootletter=R("az","AZ")+S("_-+") +local separator=P("://") +local qualified=P(".")^0*P("/")+rootletter*P(":")+rootletter^1*separator+rootletter^1*P("/") +local rootbased=P("/")+rootletter*P(":") +local barswapper=replacer("|",":") +local backslashswapper=replacer("\\","/") +local equal=P("=") +local amp=P("&") +local key=Cs(((plustospace+escapedchar+1)-equal )^0) +local value=Cs(((plustospace+escapedchar+1)-amp-endofstring)^0) +local splitquery=Cf (Ct("")*P { "sequence", + sequence=V("pair")*(amp*V("pair"))^0, + pair=Cg(key*equal*value), +},rawset) +local userpart=(1-atsign-colon)^1 +local serverpart=(1-colon)^1 +local splitauthority=((Cs(userpart)*colon*Cs(userpart)+Cs(userpart)*Cc(nil))*atsign+Cc(nil)*Cc(nil))*Cs(serverpart)*(colon*(serverpart/tonumber)+Cc(nil)) +local function hashed(str) + if not str or str=="" then + return { + scheme="invalid", + original=str, + } + end + local detailed=split(str) + local rawscheme="" + local rawquery="" + local somescheme=false + local somequery=false + if detailed then + rawscheme=detailed[1] + rawquery=detailed[4] + somescheme=rawscheme~="" + somequery=rawquery~="" + end + if not somescheme and not somequery then + return { + scheme="file", + authority="", + path=str, + query="", + fragment="", + original=str, + noscheme=true, + filename=str, + } + end + local authority=detailed[2] + local path=detailed[3] + local filename + local username + local password + local host + local port + if authority~="" then + username,password,host,port=lpegmatch(splitauthority,authority) + end + if authority=="" then + filename=path + elseif path=="" then + filename="" + else + filename=authority.."/"..path + end + return { + scheme=rawscheme, + authority=authority, + path=path, + query=lpegmatch(unescaper,rawquery), + queries=lpegmatch(splitquery,rawquery), + fragment=detailed[5], + original=str, + noscheme=false, + filename=filename, + host=host, + port=port, + } +end +url.split=split +url.hasscheme=hasscheme +url.hashed=hashed +function url.addscheme(str,scheme) + if hasscheme(str) then + return str + elseif not scheme then + return "file:///"..str + else + return scheme..":///"..str + end +end +function url.construct(hash) + local result,r={},0 + local scheme=hash.scheme + local authority=hash.authority + local path=hash.path + local queries=hash.queries + local fragment=hash.fragment + if scheme and scheme~="" then + r=r+1;result[r]=lpegmatch(escaper,scheme) + r=r+1;result[r]="://" + end + if authority and authority~="" then + r=r+1;result[r]=lpegmatch(escaper,authority) + end + if path and path~="" then + r=r+1;result[r]="/" + r=r+1;result[r]=lpegmatch(escaper,path) + end + if queries then + local done=false + for k,v in sortedhash(queries) do + r=r+1;result[r]=done and "&" or "?" + r=r+1;result[r]=lpegmatch(escaper,k) + r=r+1;result[r]="=" + r=r+1;result[r]=lpegmatch(escaper,v) + done=true + end + end + if fragment and fragment~="" then + r=r+1;result[r]="#" + r=r+1;result[r]=lpegmatch(escaper,fragment) + end + return concat(result) +end +local pattern=Cs(slash^-1/""*R("az","AZ")*((S(":|")/":")+P(":"))*slash*P(1)^0) +function url.filename(filename) + local spec=hashed(filename) + local path=spec.path + return (spec.scheme=="file" and path and lpegmatch(pattern,path)) or filename +end +local function escapestring(str) + return lpegmatch(escaper,str) +end +url.escape=escapestring +function url.query(str) + if type(str)=="string" then + return lpegmatch(splitquery,str) or "" + else + return str + end +end +function url.toquery(data) + local td=type(data) + if td=="string" then + return #str and escape(data) or nil + elseif td=="table" then + if next(data) then + local t={} + for k,v in next,data do + t[#t+1]=format("%s=%s",k,escapestring(v)) + end + return concat(t,"&") + end + else + end +end +local pattern=Cs(noslash^0*(1-noslash*P(-1))^0) +function url.barepath(path) + if not path or path=="" then + return "" + else + return lpegmatch(pattern,path) + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['l-set']={ + 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" +} +set=set or {} +local nums={} +local tabs={} +local concat=table.concat +local next,type=next,type +set.create=table.tohash +function set.tonumber(t) + if next(t) then + local s="" + for k,v in next,t do + if v then + s=s.." "..k + end + end + local n=nums[s] + if not n then + n=#tabs+1 + tabs[n]=t + nums[s]=n + end + return n + else + return 0 + end +end +function set.totable(n) + if n==0 then + return {} + else + return tabs[n] or {} + end +end +function set.tolist(n) + if n==0 or not tabs[n] then + return "" + else + local t,n={},0 + for k,v in next,tabs[n] do + if v then + n=n+1 + t[n]=k + end + end + return concat(t," ") + end +end +function set.contains(n,s) + if type(n)=="table" then + return n[s] + elseif n==0 then + return false + else + local t=tabs[n] + return t and t[s] + end +end + +end -- closure diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic.lua index 4b302285523..0fed125d393 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-basic.lua @@ -7,7 +7,7 @@ -- lualibs.dtx (with options: `basic') -- This is a generated file. -- --- Copyright (C) 2009--2017 by +-- Copyright (C) 2009--2018 by -- PRAGMA ADE / ConTeXt Development Team -- The LuaLaTeX Dev Team -- @@ -29,8 +29,8 @@ local loadmodule = lualibs.loadmodule local lualibs_basic_module = { name = "lualibs-basic", - version = 2.5, - date = "2017-02-01", + version = 2.6, + date = "2018-09-21", description = "ConTeXt Lua libraries -- basic collection.", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL & Elie Roux & Philipp Gesang", copyright = "PRAGMA ADE / ConTeXt Development Team", @@ -40,11 +40,11 @@ local lualibs_basic_module = { local loaded = false --- track success of package loading if lualibs.prefer_merged then - info"Loading merged package for collection “basic”." + info"Loading merged package for collection ^^e2^^80^^9cbasic^^e2^^80^^9d." loaded = loadmodule('lualibs-basic-merged.lua') else info"Ignoring merged packages." - info"Falling back to individual libraries from collection “basic”." + info"Falling back to individual libraries from collection ^^e2^^80^^9cbasic^^e2^^80^^9d." end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-dir.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-dir.lua index bc691d53635..b0b2c52836a 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-dir.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-dir.lua @@ -75,7 +75,8 @@ function dir.current() return (gsub(currentdir(),"\\","/")) end --- somewhat optimized +-- The next one is somewhat optimized but still slow but it's a pitty that the iterator +-- doesn't return a mode too. local function glob_pattern_function(path,patt,recurse,action) if isdir(path) then @@ -89,6 +90,7 @@ local function glob_pattern_function(path,patt,recurse,action) usedpath = path end local dirs + local nofdirs = 0 for name in walkdir(usedpath) do if name ~= "." and name ~= ".." then local full = path .. name @@ -98,16 +100,18 @@ local function glob_pattern_function(path,patt,recurse,action) action(full) end elseif recurse and mode == "directory" then - if not dirs then - dirs = { full } + if dirs then + nofdirs = nofdirs + 1 + dirs[nofdirs] = full else - dirs[#dirs+1] = full + nofdirs = 1 + dirs = { full } end end end end if dirs then - for i=1,#dirs do + for i=1,nofdirs do glob_pattern_function(dirs[i],patt,recurse,action) end end @@ -118,38 +122,41 @@ local function glob_pattern_table(path,patt,recurse,result) if not result then result = { } end - if isdir(path) then - local usedpath - if path == "/" then - usedpath = "/." - elseif not find(path,"/$") then - usedpath = path .. "/." - path = path .. "/" - else - usedpath = path - end - local dirs - for name in walkdir(usedpath) do - if name ~= "." and name ~= ".." then - local full = path .. name - local mode = attributes(full,'mode') - if mode == 'file' then - if not patt or find(full,patt) then - result[#result+1] = full - end - elseif recurse and mode == "directory" then - if not dirs then - dirs = { full } - else - dirs[#dirs+1] = full - end + local usedpath + if path == "/" then + usedpath = "/." + elseif not find(path,"/$") then + usedpath = path .. "/." + path = path .. "/" + else + usedpath = path + end + local dirs + local nofdirs = 0 + local noffiles = #result + for name, a in walkdir(usedpath) do + if name ~= "." and name ~= ".." then + local full = path .. name + local mode = attributes(full,'mode') + if mode == 'file' then + if not patt or find(full,patt) then + noffiles = noffiles + 1 + result[noffiles] = full + end + elseif recurse and mode == "directory" then + if dirs then + nofdirs = nofdirs + 1 + dirs[nofdirs] = full + else + nofdirs = 1 + dirs = { full } end end end - if dirs then - for i=1,#dirs do - glob_pattern_table(dirs[i],patt,recurse,result) - end + end + if dirs then + for i=1,nofdirs do + glob_pattern_table(dirs[i],patt,recurse,result) end end return result @@ -160,12 +167,13 @@ local function globpattern(path,patt,recurse,method) if patt and sub(patt,1,-3) == path then patt = false end + local okay = isdir(path) if kind == "function" then - return glob_pattern_function(path,patt,recurse,method) + return okay and glob_pattern_function(path,patt,recurse,method) or { } elseif kind == "table" then - return glob_pattern_table(path,patt,recurse,method) + return okay and glob_pattern_table(path,patt,recurse,method) or method else - return glob_pattern_table(path,patt,recurse,{ }) + return okay and glob_pattern_table(path,patt,recurse,{ }) or { } end end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended-merged.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended-merged.lua new file mode 100644 index 00000000000..0175b8d8747 --- /dev/null +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended-merged.lua @@ -0,0 +1,3834 @@ +-- merged file : lualibs-extended-merged.lua +-- parent file : lualibs-extended.lua +-- merge date : Fri Sep 21 15:12:38 2018 + +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,find=string.format,string.gsub,string.rep,string.sub,string.find +local load,dump=load,string.dump +local tonumber,type,tostring,next,setmetatable=tonumber,type,tostring,next,setmetatable +local unpack,concat=table.unpack,table.concat +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,utflen=utf.char,utf.byte,utf.len +local loadstripped=nil +local oldfashioned=LUAVERSION<5.2 +if oldfashioned 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 stripzero=patterns.stripzero +local stripzeros=patterns.stripzeros +local newline=patterns.newline +local endofstring=patterns.endofstring +local anything=patterns.anything +local whitespace=patterns.whitespace +local space=patterns.space +local spacer=patterns.spacer +local spaceortab=patterns.spaceortab +local digit=patterns.digit +local sign=patterns.sign +local period=patterns.period +local ptf=1/65536 +local bpf=(7200/7227)/65536 +local function points(n) + if n==0 then + return "0pt" + end + n=tonumber(n) + if not n or n==0 then + return "0pt" + end + n=n*ptf + if n%1==0 then + return format("%ipt",n) + end + return lpegmatch(stripzeros,format("%.5fpt",n)) +end +local function basepoints(n) + if n==0 then + return "0pt" + end + n=tonumber(n) + if not n or n==0 then + return "0pt" + end + n=n*bpf + if n%1==0 then + return format("%ibp",n) + end + return lpegmatch(stripzeros,format("%.5fbp",n)) +end +number.points=points +number.basepoints=basepoints +local rubish=spaceortab^0*newline +local anyrubish=spaceortab+newline +local stripped=(spaceortab^1/"")*newline +local leading=rubish^0/"" +local trailing=(anyrubish^1*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+newline*Cp()/function(position) + extra,start=0,position + end+anything + )^1) +function strings.tabtospace(str,tab) + return lpegmatch(pattern,str,1,tab or 7) +end +function string.utfpadding(s,n) + if not n or n==0 then + return "" + end + local l=utflen(s) + if n>0 then + return nspaces[n-l] + else + return nspaces[-n-l] + end +end +local optionalspace=spacer^0 +local nospace=optionalspace/"" +local endofline=nospace*newline +local stripend=(whitespace^1*endofstring)/"" +local normalline=(nospace*((1-optionalspace*(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 intospace=whitespace^1/" " +local noleading=whitespace^1/"" +local notrailing=noleading*endofstring +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_prune_intospace=Cs (noleading*(notrailing+intospace+1 )^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, + ["prune and to space"]=p_prune_intospace, + ["retain"]=p_retain_normal, + ["retain and collapse"]=p_retain_collapse, + ["retain and no empty"]=p_retain_noempty, + ["collapse"]=patterns.collapser, +} +setmetatable(striplinepatterns,{ __index=function(t,k) return p_prune_collapse end }) +strings.striplinepatterns=striplinepatterns +function strings.striplines(str,how) + return str and lpegmatch(striplinepatterns[how],str) or str +end +function strings.collapse(str) + return str and lpegmatch(p_prune_intospace,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={ [0]= + "[null]","[soh]","[stx]","[etx]","[eot]","[enq]","[ack]","[bel]", + "[bs]","[ht]","[lf]","[vt]","[ff]","[cr]","[so]","[si]", + "[dle]","[dc1]","[dc2]","[dc3]","[dc4]","[nak]","[syn]","[etb]", + "[can]","[em]","[sub]","[esc]","[fs]","[gs]","[rs]","[us]", + "[space]", +} +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+"..(c and format("%05X",c) or "?????")..")") + end +end +function number.signed(i) + if i>0 then + return "+",i + else + return "-",-i + end +end +local two=digit*digit +local three=two*digit +local prefix=(Carg(1)*three)^1 +local splitter=Cs ( + (((1-(three^1*period))^1+C(three))*prefix+C((1-period)^1))*(anything/""*Carg(2))*C(2) +) +local splitter3=Cs ( + three*prefix*endofstring+two*prefix*endofstring+digit*prefix*endofstring+three+two+digit +) +patterns.formattednumber=splitter +function number.formatted(n,sep1,sep2) + if sep1==false then + if type(n)=="number" then + n=tostring(n) + end + return lpegmatch(splitter3,n,1,sep2 or ".") + else + if type(n)=="number" then + n=format("%0.2f",n) + end + if sep1==true then + return lpegmatch(splitter,n,1,".",",") + elseif sep1=="." then + return lpegmatch(splitter,n,1,sep1,sep2 or ",") + elseif sep1=="," then + return lpegmatch(splitter,n,1,sep1,sep2 or ".") + else + return lpegmatch(splitter,n,1,sep1 or ",",sep2 or ".") + end + end +end +local p=Cs( + P("-")^0*(P("0")^1/"")^0*(1-period)^0*(period*P("0")^1*endofstring/""+period^0)*P(1-P("0")^1*endofstring)^0 + ) +function number.compactfloat(n,fmt) + if n==0 then + return "0" + elseif n==1 then + return "1" + end + n=lpegmatch(p,format(fmt or "%0.3f",n)) + if n=="." or n=="" or n=="-" then + return "0" + end + return n +end +local zero=P("0")^1/"" +local plus=P("+")/"" +local minus=P("-") +local separator=period +local trailing=zero^1*#S("eE") +local exponent=(S("eE")*(plus+Cs((minus*zero^0*endofstring)/"")+minus)*zero^0*(endofstring*Cc("0")+anything^1)) +local pattern_a=Cs(minus^0*digit^1*(separator/""*trailing+separator*(trailing+digit)^0)*exponent) +local pattern_b=Cs((exponent+anything)^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 hf={} +local hs={} +setmetatable(hf,{ __index=function(t,k) + local v="%."..k.."f" + t[k]=v + return v +end } ) +setmetatable(hs,{ __index=function(t,k) + local v="%"..k.."s" + t[k]=v + return v +end } ) +function number.formattedfloat(n,b,a) + local s=format(hf[a],n) + local l=(b or 0)+(a or 0)+1 + if #s<l then + return format(hs[l],s) + else + return s + end +end +local template=[[ +%s +%s +return function(%s) return %s end +]] +local preamble,environment="",{} +if oldfashioned 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 utfpadding=string.utfpadding +local tracedchar=string.tracedchar +local autosingle=string.autosingle +local autodouble=string.autodouble +local sequenced=table.sequenced +local formattednumber=number.formatted +local sparseexponent=number.sparseexponent +local formattedfloat=number.formattedfloat +local stripzero=lpeg.patterns.stripzero +local stripzeros=lpeg.patterns.stripzeros + ]] +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, + utfpadding=string.utfpadding, + tracedchar=string.tracedchar, + autosingle=string.autosingle, + autodouble=string.autodouble, + sequenced=table.sequenced, + formattednumber=number.formatted, + sparseexponent=number.sparseexponent, + formattedfloat=number.formattedfloat, + stripzero=lpeg.patterns.stripzero, + stripzeros=lpeg.patterns.stripzeros, + } +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((sign+space+period+digit)^0) +local prefix_sub=(C((sign+digit)^0)+Cc(0))*period*(C((sign+digit)^0)+Cc(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_right=function(f) + n=n+1 + f=tonumber(f) + if not f or f==0 then + return format("(a%s or '')",n) + elseif f>0 then + return format("utfpadding(a%s,%i)..a%s",n,f,n) + else + return format("a%s..utfpadding(a%s,%i)",n,n,f) + end +end +local format_left=function(f) + n=n+1 + f=tonumber(f) + if not f or f==0 then + return format("(a%s or '')",n) + end + if f<0 then + return format("utfpadding(a%s,%i)..a%s",n,-f,n) + else + return format("a%s..utfpadding(a%s,%i)",n,n,-f) + end +end +local format_q=function() + n=n+1 + return format("(a%s ~= nil and format('%%q',tostring(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(f) + 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_k=function(b,a) + n=n+1 + return format("formattedfloat(a%s,%i,%i)",n,b or 0,a or 0) +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("((a%s %% 1 == 0) and format('%%i',a%s) or tostring(a%s))",n,n,n) +end +local format_N=function(f) + n=n+1 + if not f or f=="" then + f=".9" + end + return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s)))",n,n,f,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 format_m=function(f) + n=n+1 + if not f or f=="" then + f="," + end + if f=="0" then + return format([[formattednumber(a%s,false)]],n) + else + return format([[formattednumber(a%s,%q,".")]],n,f) + end +end +local format_M=function(f) + n=n+1 + if not f or f=="" then + f="." + end + if f=="0" then + return format([[formattednumber(a%s,false)]],n) + else + return format([[formattednumber(a%s,%q,",")]],n,f) + end +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 + local w=find(extension,"%.%.%.") + if f==0 then + if w then + extension=gsub(extension,"%.%.%.","") + end + return extension + elseif f==1 then + if w then + extension=gsub(extension,"%.%.%.","%%s") + end + 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 + if w then + extension=gsub(extension,"%.%.%.",rep("%%s,",f-1).."%%s") + end + local t={} + for i=1,f do + n=n+1 + t[i]="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("N") ++V("k") ++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("<") + )+V("*") + )*(endofstring+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_Q, + ["n"]=(prefix_any*P("n"))/format_n, + ["N"]=(prefix_any*P("N"))/format_N, + ["k"]=(prefix_sub*P("k"))/format_k, + ["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_any*P("m"))/format_m, + ["M"]=(prefix_any*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, + ["<"]=(prefix_any*P("<"))/format_left, + [">"]=(prefix_any*P(">"))/format_right, + ["*"]=Cs(((1-P("%"))^1+P("%%")/"%%")^1)/format_rest, + ["?"]=Cs(((1-P("%"))^1 )^1)/format_rest, + ["!"]=Carg(2)*prefix_any*P("!")*C((1-P("!"))^1)*P("!")/format_extension, +} +local xx=setmetatable({},{ __index=function(t,k) local v=format("%02x",k) t[k]=v return v end }) +local XX=setmetatable({},{ __index=function(t,k) local v=format("%02X",k) t[k]=v return v end }) +local preset={ + ["%02x"]=function(n) return xx[n] end, + ["%02X"]=function(n) return XX[n] end, +} +local direct=P("%")*(sign+space+period+digit)^0*S("sqidfgGeExXo")*endofstring/[[local format = string.format return function(str) return format("%0",str) end]] +local function make(t,str) + local f=preset[str] + if f then + return f + end + 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 oldfashioned 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('"')/"""+anything)^0) +patterns.texescape=Cs((C(S("#$%\\{}"))/"\\%1"+anything)^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 oldfashioned 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 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 +local pattern=Cs((newline/(os.newline or "\r")+1)^0) +function string.replacenewlines(str) + return lpegmatch(pattern,str) +end +function strings.newcollector() + local result,r={},0 + return + function(fmt,str,...) + r=r+1 + result[r]=str==nil and fmt or formatters[fmt](str,...) + end, + function(connector) + if result then + local str=concat(result,connector) + result,r={},0 + return str + end + end +end + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['util-fil']={ + 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 byte=string.byte +local char=string.char +utilities=utilities or {} +local files={} +utilities.files=files +local zerobased={} +function files.open(filename,zb) + local f=io.open(filename,"rb") + if f then + zerobased[f]=zb or false + end + return f +end +function files.close(f) + zerobased[f]=nil + f:close() +end +function files.size(f) + local current=f:seek() + local size=f:seek("end") + f:seek("set",current) + return size +end +files.getsize=files.size +function files.setposition(f,n) + if zerobased[f] then + f:seek("set",n) + else + f:seek("set",n-1) + end +end +function files.getposition(f) + if zerobased[f] then + return f:seek() + else + return f:seek()+1 + end +end +function files.look(f,n,chars) + local p=f:seek() + local s=f:read(n) + f:seek("set",p) + if chars then + return s + else + return byte(s,1,#s) + end +end +function files.skip(f,n) + if n==1 then + f:read(n) + else + f:seek("set",f:seek()+n) + end +end +function files.readbyte(f) + return byte(f:read(1)) +end +function files.readbytes(f,n) + return byte(f:read(n),1,n) +end +function files.readbytetable(f,n) + local s=f:read(n or 1) + return { byte(s,1,#s) } +end +function files.readchar(f) + return f:read(1) +end +function files.readstring(f,n) + return f:read(n or 1) +end +function files.readinteger1(f) + local n=byte(f:read(1)) + if n>=0x80 then + return n-0x100 + else + return n + end +end +files.readcardinal1=files.readbyte +files.readcardinal=files.readcardinal1 +files.readinteger=files.readinteger1 +files.readsignedbyte=files.readinteger1 +function files.readcardinal2(f) + local a,b=byte(f:read(2),1,2) + return 0x100*a+b +end +function files.readcardinal2le(f) + local b,a=byte(f:read(2),1,2) + return 0x100*a+b +end +function files.readinteger2(f) + local a,b=byte(f:read(2),1,2) + if a>=0x80 then + return 0x100*a+b-0x10000 + else + return 0x100*a+b + end +end +function files.readinteger2le(f) + local b,a=byte(f:read(2),1,2) + if a>=0x80 then + return 0x100*a+b-0x10000 + else + return 0x100*a+b + end +end +function files.readcardinal3(f) + local a,b,c=byte(f:read(3),1,3) + return 0x10000*a+0x100*b+c +end +function files.readcardinal3le(f) + local c,b,a=byte(f:read(3),1,3) + return 0x10000*a+0x100*b+c +end +function files.readinteger3(f) + local a,b,c=byte(f:read(3),1,3) + if a>=0x80 then + return 0x10000*a+0x100*b+c-0x1000000 + else + return 0x10000*a+0x100*b+c + end +end +function files.readinteger3le(f) + local c,b,a=byte(f:read(3),1,3) + if a>=0x80 then + return 0x10000*a+0x100*b+c-0x1000000 + else + return 0x10000*a+0x100*b+c + end +end +function files.readcardinal4(f) + local a,b,c,d=byte(f:read(4),1,4) + return 0x1000000*a+0x10000*b+0x100*c+d +end +function files.readcardinal4le(f) + local d,c,b,a=byte(f:read(4),1,4) + return 0x1000000*a+0x10000*b+0x100*c+d +end +function files.readinteger4(f) + local a,b,c,d=byte(f:read(4),1,4) + if a>=0x80 then + return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000 + else + return 0x1000000*a+0x10000*b+0x100*c+d + end +end +function files.readinteger4le(f) + local d,c,b,a=byte(f:read(4),1,4) + if a>=0x80 then + return 0x1000000*a+0x10000*b+0x100*c+d-0x100000000 + else + return 0x1000000*a+0x10000*b+0x100*c+d + end +end +function files.readfixed2(f) + local a,b=byte(f:read(2),1,2) + if a>=0x80 then + return (a-0x100)+b/0x100 + else + return (a )+b/0x100 + end +end +function files.readfixed4(f) + local a,b,c,d=byte(f:read(4),1,4) + if a>=0x80 then + return (0x100*a+b-0x10000)+(0x100*c+d)/0x10000 + else + return (0x100*a+b )+(0x100*c+d)/0x10000 + end +end +if bit32 then + local extract=bit32.extract + local band=bit32.band + function files.read2dot14(f) + local a,b=byte(f:read(2),1,2) + if a>=0x80 then + local n=-(0x100*a+b) + return-(extract(n,14,2)+(band(n,0x3FFF)/16384.0)) + else + local n=0x100*a+b + return (extract(n,14,2)+(band(n,0x3FFF)/16384.0)) + end + end +end +function files.skipshort(f,n) + f:read(2*(n or 1)) +end +function files.skiplong(f,n) + f:read(4*(n or 1)) +end +if bit32 then + local rshift=bit32.rshift + function files.writecardinal2(f,n) + local a=char(n%256) + n=rshift(n,8) + local b=char(n%256) + f:write(b,a) + end +else + local floor=math.floor + function files.writecardinal2(f,n) + local a=char(n%256) + n=floor(n/256) + local b=char(n%256) + f:write(b,a) + end +end +function files.writecardinal4(f,n) + local a=char(n%256) + n=rshift(n,8) + local b=char(n%256) + n=rshift(n,8) + local c=char(n%256) + n=rshift(n,8) + local d=char(n%256) + f:write(d,c,b,a) +end +function files.writestring(f,s) + f:write(char(byte(s,1,#s))) +end +function files.writebyte(f,b) + f:write(char(b)) +end +if fio and fio.readcardinal1 then + files.readcardinal1=fio.readcardinal1 + files.readcardinal2=fio.readcardinal2 + files.readcardinal3=fio.readcardinal3 + files.readcardinal4=fio.readcardinal4 + files.readinteger1=fio.readinteger1 + files.readinteger2=fio.readinteger2 + files.readinteger3=fio.readinteger3 + files.readinteger4=fio.readinteger4 + files.readfixed2=fio.readfixed2 + files.readfixed4=fio.readfixed4 + files.read2dot14=fio.read2dot14 + files.setposition=fio.setposition + files.getposition=fio.getposition + files.readbyte=files.readcardinal1 + files.readsignedbyte=files.readinteger1 + files.readcardinal=files.readcardinal1 + files.readinteger=files.readinteger1 + local skipposition=fio.skipposition + files.skipposition=skipposition + files.readbytes=fio.readbytes + files.readbytetable=fio.readbytetable + function files.skipshort(f,n) + skipposition(f,2*(n or 1)) + end + function files.skiplong(f,n) + skipposition(f,4*(n or 1)) + end +end +if fio and fio.readcardinaltable then + files.readcardinaltable=fio.readcardinaltable + files.readintegertable=fio.readintegertable +else + local readcardinal1=files.readcardinal1 + local readcardinal2=files.readcardinal2 + local readcardinal3=files.readcardinal3 + local readcardinal4=files.readcardinal4 + function files.readcardinaltable(f,n,b) + local t={} + if b==1 then for i=1,n do t[i]=readcardinal1(f) end + elseif b==2 then for i=1,n do t[i]=readcardinal2(f) end + elseif b==3 then for i=1,n do t[i]=readcardinal3(f) end + elseif b==4 then for i=1,n do t[i]=readcardinal4(f) end end + return t + end + local readinteger1=files.readinteger1 + local readinteger2=files.readinteger2 + local readinteger3=files.readinteger3 + local readinteger4=files.readinteger4 + function files.readintegertable(f,n,b) + local t={} + if b==1 then for i=1,n do t[i]=readinteger1(f) end + elseif b==2 then for i=1,n do t[i]=readinteger2(f) end + elseif b==3 then for i=1,n do t[i]=readinteger3(f) end + elseif b==4 then for i=1,n do t[i]=readinteger4(f) end end + return t + end +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,sort=table.concat,table.insert,table.remove,table.sort +local setmetatable,getmetatable,tonumber,tostring,rawget=setmetatable,getmetatable,tonumber,tostring,rawget +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.replacevalue(t,oldvalue,newvalue) + if oldvalue and newvalue then + for i=1,#t do + if t[i]==oldvalue then + t[i]=newvalue + 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 "," + local noffields=#fields + if specification.preamble==true then + for f=1,noffields 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,noffields do + local field=ti[fields[f]] + if type(field)=="string" then + r[f]=lpegmatch(escape,field) + else + r[f]=tostring(field) + end + end + result[i+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) + local r=#result + 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 + r=r+1 result[r]=formatters["%s<entry n='%s'>"](s,k) + toxml(v,d+step,result,step) + r=r+1 result[r]=formatters["%s</entry>"](s,k) + else + r=r+1 result[r]=formatters["%s<%s>"](s,k) + toxml(v,d+step,result,step) + r=r+1 result[r]=formatters["%s</%s>"](s,k) + end + elseif tv=="string" then + if tk=="number" then + r=r+1 result[r]=formatters["%s<entry n='%s'>%!xml!</entry>"](s,k,v,k) + else + r=r+1 result[r]=formatters["%s<%s>%!xml!</%s>"](s,k,v,k) + end + elseif tk=="number" then + r=r+1 result[r]=formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k) + else + r=r+1 result[r]=formatters["%s<%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 { "<?xml version='1.0' standalone='yes' ?>" } + 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 +local selfmapper={ __index=function(t,k) t[k]=k return k end } +function table.twowaymapper(t) + if not t then + t={} + else + local zero=rawget(t,0) + for i=zero and 0 or 1,#t do + local ti=t[i] + if ti then + local i=tostring(i) + t[i]=ti + t[ti]=i + end + end + 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_fin_seq=formatters[" %, 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 original_serialize=table.serialize +local is_simple_table=table.is_simple_table +local function serialize(root,name,specification) + if type(specification)=="table" then + return original_serialize(root,name,specification) + end + local t + local n=1 + local unknown=false + 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)~=nil then + local first=nil + local last=0 + last=#root + for k=1,last do + if rawget(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<=last and k>=first 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 next(v)==nil then + n=n+1 t[n]=f_val_not(depth) + else + local st=is_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) + elseif unknown then + n=n+1 t[n]=f_val_str(depth,tostring(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) + elseif unknown then + n=n+1 t[n]=f_key_str_value_num(depth,tostring(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) + elseif unknown then + n=n+1 t[n]=f_key_str_value_str(depth,tostring(k),v) + end + elseif tv=="table" then + if next(v)==nil then + if tk=="number" then + n=n+1 t[n]=f_key_num_value_not(depth,k) + elseif tk=="string" then + n=n+1 t[n]=f_key_str_value_not(depth,k) + elseif tk=="boolean" then + n=n+1 t[n]=f_key_boo_value_not(depth,k) + elseif unknown then + n=n+1 t[n]=f_key_str_value_not(depth,tostring(k)) + end + else + local st=is_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) + elseif unknown then + n=n+1 t[n]=f_key_str_value_seq(depth,tostring(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) + elseif unknown then + n=n+1 t[n]=f_key_str_value_boo(depth,tostring(k),v) + end + else + if tk=="number" then + n=n+1 t[n]=f_key_num_value_str(depth,k,tostring(v)) + elseif tk=="string" then + n=n+1 t[n]=f_key_str_value_str(depth,k,tostring(v)) + elseif tk=="boolean" then + n=n+1 t[n]=f_key_boo_value_str(depth,k,tostring(v)) + elseif unknown then + n=n+1 t[n]=f_key_str_value_str(depth,tostring(k),tostring(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)~=nil then + local st=is_simple_table(root) + if st then + return t[1]..f_fin_seq(st) + else + do_serialize(root,name,1,0) + end + end + end + n=n+1 + t[n]=f_table_finish() + return concat(t,"\n") +end +table.serialize=serialize +if setinspector then + setinspector("table",function(v) + if type(v)=="table" then + print(serialize(v,"table",{ metacheck=false })) + return true + end + end) +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,rawset,type=setmetatable,getmetatable,rawset,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, +} +function table.setmetatableindex(t,f) + if type(t)~="table" then + f,t=t,{} + end + local m=getmetatable(t) + local i=f_index[f] or f + if m then + m.__index=i + else + setmetatable(t,{ __index=i }) + end + return t +end +local f_index={ + ["ignore"]=f_ignore, +} +function table.setmetatablenewindex(t,f) + if type(t)~="table" then + f,t=t,{} + end + local m=getmetatable(t) + local i=f_index[f] or f + if m then + m.__newindex=i + else + setmetatable(t,{ __newindex=i }) + 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.setmetatableindices(t,f,n,c) + if type(t)~="table" then + f,t=t,{} + end + local m=getmetatable(t) + local i=f_index[f] or f + if m then + m.__index=i + m.__newindex=n + m.__call=c + else + setmetatable(t,{ + __index=i, + __newindex=n, + __call=c, + }) + 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 +function table.makeweak(t) + if not t then + t={} + end + local m=getmetatable(t) + if m then + m.__mode="v" + else + setmetatable(t,{ __mode="v" }) + end + return t +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 sortedkeys=table.sortedkeys +local tohash=table.tohash +local hashes={} +utilities.parsers.hashes=hashes +local digit=R("09") +local space=P(' ') +local equal=P("=") +local colon=P(":") +local comma=P(",") +local lbrace=P("{") +local rbrace=P("}") +local lparent=P("(") +local rparent=P(")") +local lbracket=P("[") +local rbracket=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 nobracket=1-(lbracket+rbracket) +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 nestedbrackets=P { lbracket*(nobracket+V(1))^0*rbracket } +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=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 pattern_d=(space+comma)^0*(key*(equal+colon)*value+key*C("")) +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 +local pattern_d_s=(pattern_d/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 +patterns.settings_to_hash_d=pattern_d_s +function parsers.make_settings_to_hash_pattern(set,how) + if 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 not str or str=="" then + return {} + elseif type(str)=="table" then + if existing then + for k,v in next,str do + existing[k]=v + end + return exiting + else + return str + end + else + hash=existing or {} + lpegmatch(pattern_a_s,str) + return hash + end +end +function parsers.settings_to_hash_colon_too(str) + if not str or str=="" then + return {} + elseif type(str)=="table" then + return str + else + hash={} + lpegmatch(pattern_d_s,str) + return hash + end +end +function parsers.settings_to_hash_tolerant(str,existing) + if not str or str=="" then + return {} + elseif type(str)=="table" then + if existing then + for k,v in next,str do + existing[k]=v + end + return exiting + else + return str + end + else + hash=existing or {} + lpegmatch(pattern_b_s,str) + return hash + end +end +function parsers.settings_to_hash_strict(str,existing) + if not str or str=="" then + return nil + elseif 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 + end +end +local separator=comma*space^0 +local value=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 not str or str=="" then + return {} + elseif type(str)=="table" then + return str + 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 +function parsers.settings_to_numbers(str) + if not str or str=="" then + return {} + end + if type(str)=="table" then + elseif find(str,",",1,true) then + str=lpegmatch(pattern,str) + else + return { tonumber(str) } + end + for i=1,#str do + str[i]=tonumber(str[i]) + end + return str +end +local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace+C((nestedbraces+nestedbrackets+nestedparents+(1-comma))^0) +local pattern=spaces*Ct(value*(separator*value)^0) +function parsers.settings_to_array_obey_fences(str) + return lpegmatch(pattern,str) +end +local cache_a={} +local cache_b={} +function parsers.groupedsplitat(symbol,withaction) + if not symbol then + symbol="," + end + local pattern=(withaction and cache_b or cache_a)[symbol] + if not pattern then + local symbols=S(symbol) + local separator=space^0*symbols*space^0 + local value=lbrace*C((nobrace+nestedbraces)^0)*rbrace+C((nestedbraces+(1-(space^0*(symbols+P(-1)))))^0) + if withaction then + local withvalue=Carg(1)*value/function(f,s) return f(s) end + pattern=spaces*withvalue*(separator*withvalue)^0 + cache_b[symbol]=pattern + else + pattern=spaces*Ct(value*(separator*value)^0) + cache_a[symbol]=pattern + end + end + return pattern +end +local pattern_a=parsers.groupedsplitat(",",false) +local pattern_b=parsers.groupedsplitat(",",true) +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,sortedkeys(h) + omit=omit and 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 +local pattern=Cf(Ct("")*Cg(C((1-S(", "))^1)*S(", ")^0*Cc(true))^1,rawset) +function utilities.parsers.settings_to_set(str) + return str and lpegmatch(pattern,str) or {} +end +hashes.settings_to_set=table.setmetatableindex(function(t,k) + local v=k and lpegmatch(pattern,k) or {} + t[k]=v + return v +end) +getmetatable(hashes.settings_to_set).__mode="kv" +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=Cs(lpegpatterns.unquoted)+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 splitter=lpeg.tsplitat(" ") +function utilities.parsers.options_to_array(str) + return str and lpegmatch(splitter,str) 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 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 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 morerecords=(newline^(specification.strict and -1 or 1)*record)^0 + local headeryes=Ct(morerecords) + local headernop=Ct(record*morerecords) + return function(data,getheader) + if getheader then + local header,position=lpegmatch(headerline,data) + local data=lpegmatch(headeryes,data,position) + return data,header + else + return lpegmatch(headernop,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*(cardinal*(spacers*S(":-")*spacers*(cardinal+Cc(true) )+Cc(false) )*Carg(1)*Carg(2)/ranger*S(", ")^0 )^1 +local stepper=spacers*(cardinal*(spacers*S(":-")*spacers*(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("^")/"<sup>"*lpegpatterns.integer*Cc("</sup>")+anything)^0) +function parsers.unittoxml(str) + return lpegmatch(pattern,str) +end +local cache={} +local spaces=lpegpatterns.space^0 +local dummy=function() end +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]<b[1] +end +function dimensions.__eq(a,b) + return a[1]==b[1] +end +function dimensions.__tostring(a) + return a[1]/65536 .."pt" +end +function dimensions.__index(tab,key) + local d=dimenfactors[key] + if not d then + error("illegal property of dimen: "..key) + d=1 + end + return 1/d +end + dimenfactors["ex"]=4*1/65536 + dimenfactors["em"]=10*1/65536 +local known={} setmetatable(known,{ __mode="v" }) +function dimen(a) + if a then + local ta=type(a) + if ta=="string" then + local k=known[a] + if k then + a=k + else + local value,unit=lpegmatch(dimenpair,a) + if value and unit then + k=value/unit + else + k=0 + end + known[a]=k + a=k + end + elseif ta=="table" then + a=a[1] + end + return setmetatable({ a },dimensions) + else + return setmetatable({ 0 },dimensions) + end +end +function string.todimen(str) + if type(str)=="number" then + return str + else + local k=known[str] + if not k then + local value,unit=lpegmatch(dimenpair,str) + if value and unit then + k=value/unit + else + k=0 + end + known[str]=k + end + return k + end +end +stringtodimen=string.todimen +function number.toscaled(d) + return format("%0.5f",d/0x10000) +end +function number.percent(n,d) + d=d or texget("hsize") + if type(d)=="string" then + d=stringtodimen(d) + end + return (n/100)*d +end +number["%"]=number.percent + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['util-jsn']={ + version=1.001, + comment="companion to m-json.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local P,V,R,S,C,Cc,Cs,Ct,Cf,Cg=lpeg.P,lpeg.V,lpeg.R,lpeg.S,lpeg.C,lpeg.Cc,lpeg.Cs,lpeg.Ct,lpeg.Cf,lpeg.Cg +local lpegmatch=lpeg.match +local format=string.format +local utfchar=utf.char +local concat=table.concat +local tonumber,tostring,rawset,type,next=tonumber,tostring,rawset,type,next +local json=utilities.json or {} +utilities.json=json +local lbrace=P("{") +local rbrace=P("}") +local lparent=P("[") +local rparent=P("]") +local comma=P(",") +local colon=P(":") +local dquote=P('"') +local whitespace=lpeg.patterns.whitespace +local optionalws=whitespace^0 +local escapes={ + ["b"]="\010", + ["f"]="\014", + ["n"]="\n", + ["r"]="\r", + ["t"]="\t", +} +local escape_un=C(P("\\u")/"0x"*S("09","AF","af"))/function(s) return utfchar(tonumber(s)) end +local escape_bs=P([[\]])/""*(P(1)/escapes) +local jstring=dquote*Cs((escape_un+escape_bs+(1-dquote))^0)*dquote +local jtrue=P("true")*Cc(true) +local jfalse=P("false")*Cc(false) +local jnull=P("null")*Cc(nil) +local jnumber=(1-whitespace-rparent-rbrace-comma)^1/tonumber +local key=jstring +local jsonconverter={ "value", + hash=lbrace*Cf(Ct("")*(V("pair")*(comma*V("pair"))^0+optionalws),rawset)*rbrace, + pair=Cg(optionalws*key*optionalws*colon*V("value")), + array=Ct(lparent*(V("value")*(comma*V("value"))^0+optionalws)*rparent), + value=optionalws*(jstring+V("hash")+V("array")+jtrue+jfalse+jnull+jnumber)*optionalws, +} +function json.tolua(str) + return lpegmatch(jsonconverter,str) +end +local function tojson(value,t) + local kind=type(value) + if kind=="table" then + local done=false + local size=#value + if size==0 then + for k,v in next,value do + if done then + t[#t+1]="," + else + t[#t+1]="{" + done=true + end + t[#t+1]=format("%q:",k) + tojson(v,t) + end + if done then + t[#t+1]="}" + else + t[#t+1]="{}" + end + elseif size==1 then + t[#t+1]="[" + tojson(value[1],t) + t[#t+1]="]" + else + for i=1,size do + if done then + t[#t+1]="," + else + t[#t+1]="[" + done=true + end + tojson(value[i],t) + end + t[#t+1]="]" + end + elseif kind=="string" then + t[#t+1]=format("%q",value) + elseif kind=="number" then + t[#t+1]=value + elseif kind=="boolean" then + t[#t+1]=tostring(value) + end + return t +end +function json.tostring(value) + local kind=type(value) + if kind=="table" then + return concat(tojson(value,{}),"") + elseif kind=="string" or kind=="number" then + return value + else + return tostring(value) + end +end +function json.load(filename) + local data=io.loaddata(filename) + if data then + return lpegmatch(jsonconverter,data) + end +end +return json + +end -- closure + +do -- begin closure to overcome local limits and interference + +if not modules then modules={} end modules ['trac-inf']={ + version=1.001, + comment="companion to trac-inf.mkiv", + author="Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright="PRAGMA ADE / ConTeXt Development Team", + license="see context related readme files" +} +local type,tonumber,select=type,tonumber,select +local format,lower,find=string.format,string.lower,string.find +local concat=table.concat +local clock=os.gettimeofday or os.clock +local setmetatableindex=table.setmetatableindex +local serialize=table.serialize +local formatters=string.formatters +statistics=statistics or {} +local statistics=statistics +statistics.enable=true +statistics.threshold=0.01 +local statusinfo,n,registered,timers={},0,{},{} +setmetatableindex(timers,function(t,k) + local v={ timing=0,loadtime=0 } + t[k]=v + return v +end) +local function hastiming(instance) + return instance and timers[instance] +end +local function resettiming(instance) + timers[instance or "notimer"]={ timing=0,loadtime=0 } +end +local ticks=clock +local seconds=function(n) return n or 0 end +local function starttiming(instance) + local timer=timers[instance or "notimer"] + local it=timer.timing + if it==0 then + timer.starttime=ticks() + if not timer.loadtime then + timer.loadtime=0 + end + end + timer.timing=it+1 +end +local function stoptiming(instance) + local timer=timers[instance or "notimer"] + local it=timer.timing + if it>1 then + timer.timing=it-1 + else + local starttime=timer.starttime + if starttime and starttime>0 then + local stoptime=ticks() + local loadtime=stoptime-starttime + timer.stoptime=stoptime + timer.loadtime=timer.loadtime+loadtime + timer.timing=0 + timer.starttime=0 + return loadtime + end + end + return 0 +end +local function elapsed(instance) + if type(instance)=="number" then + return instance + else + local timer=timers[instance or "notimer"] + return timer and seconds(timer.loadtime) or 0 + end +end +local function currenttime(instance) + if type(instance)=="number" then + return instance + else + local timer=timers[instance or "notimer"] + local it=timer.timing + if it>1 then + else + local starttime=timer.starttime + if starttime and starttime>0 then + return seconds(timer.loadtime+ticks()-starttime) + end + end + return 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.currenttime=currenttime +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("used engine",function() + return format("%s version %s with functionality level %s, banner: %s", + LUATEXENGINE,LUATEXVERSION,LUATEXFUNCTIONALITY,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",statistics.callbacks) + if TEXENGINE=="luajittex" and JITSUPPORTED then + local jitstatus=jit.status + if jitstatus then + local jitstatus={ jitstatus() } + if jitstatus[1] then + register("luajit options",concat(jitstatus," ",2)) + end + end + end + register("lua properties",function() + local hashchar=tonumber(status.luatex_hashchars) + local hashtype=status.luatex_hashtype + local mask=lua.mask or "ascii" + return format("engine: %s %s, used memory: %s, hash type: %s, hash chars: min(%i,40), symbol mask: %s (%s)", + jit and "luajit" or "lua", + LUAVERSION, + statistics.memused(), + 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 + +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,collectgarbage=load,loadfile,type,collectgarbage +utilities=utilities or {} +utilities.lua=utilities.lua or {} +local luautilities=utilities.lua +local report_lua=logs.reporter("system","lua") +local report_mem=logs.reporter("system","lua memory") +local tracestripping=false +local tracememory=false +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,macros) + name=name or fullname + if macros then + macros=lua.macros + end + local code,message + if macros then + code,message=macros.loaded(fullname,true,false) + 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,name,forcestrip) + 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.loadstring(code,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 + return code,0 +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 +local finalizers={} +setmetatable(finalizers,{ + __gc=function(t) + for i=1,#t do + pcall(t[i]) + end + end +} ) +function luautilities.registerfinalizer(f) + finalizers[#finalizers+1]=f +end +function luautilities.checkmemory(previous,threshold,trace) + local current=collectgarbage("count") + if previous then + local checked=(threshold or 64)*1024 + local delta=current-previous + if current-previous>checked then + collectgarbage("collect") + local afterwards=collectgarbage("count") + if trace or tracememory then + report_mem("previous %r MB, current %r MB, delta %r MB, threshold %r MB, afterwards %r MB", + previous/1024,current/1024,delta/1024,threshold,afterwards) + end + return afterwards + elseif trace or tracememory then + report_mem("previous %r MB, current %r MB, delta %r MB, threshold %r MB", + previous/1024,current/1024,delta/1024,threshold) + end + end + return current +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,sethook=debug.getinfo,debug.sethook +local type,next,tostring,tonumber=type,next,tostring,tonumber +local format,find,sub,gsub=string.format,string.find,string.sub,string.gsub +local insert,remove,sort=table.insert,table.remove,table.sort +local setmetatableindex=table.setmetatableindex +utilities=utilities or {} +local debugger=utilities.debugger or {} +utilities.debugger=debugger +local report=logs.reporter("debugger") +local ticks=os.gettimeofday or os.clock +local seconds=function(n) return n or 0 end +local overhead=0 +local dummycalls=10*1000 +local nesting=0 +local names={} +local initialize=false +if not (FFISUPPORTED and ffi) then +elseif os.type=="windows" then + initialize=function() + local kernel=ffilib("kernel32","system") + if kernel then + local tonumber=ffi.number or tonumber + ffi.cdef[[ + int QueryPerformanceFrequency(int64_t *lpFrequency); + int QueryPerformanceCounter(int64_t *lpPerformanceCount); + ]] + local target=ffi.new("__int64[1]") + ticks=function() + if kernel.QueryPerformanceCounter(target)==1 then + return tonumber(target[0]) + else + return 0 + end + end + local target=ffi.new("__int64[1]") + seconds=function(ticks) + if kernel.QueryPerformanceFrequency(target)==1 then + return ticks/tonumber(target[0]) + else + return 0 + end + end + end + initialize=false + end +elseif os.type=="unix" then + initialize=function() + local C=ffi.C + local tonumber=ffi.number or tonumber + ffi.cdef [[ + /* what a mess */ + typedef int clk_id_t; + typedef enum { CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID } clk_id; + typedef struct timespec { long sec; long nsec; } ctx_timespec; + int clock_gettime(clk_id_t timerid, struct timespec *t); + ]] + local target=ffi.new("ctx_timespec[?]",1) + local clock=C.CLOCK_PROCESS_CPUTIME_ID + ticks=function () + C.clock_gettime(clock,target) + return tonumber(target[0].sec*1000000000+target[0].nsec) + end + seconds=function(ticks) + return ticks/1000000000 + end + initialize=false + end +end +setmetatableindex(names,function(t,name) + local v=setmetatableindex(function(t,source) + local v=setmetatableindex(function(t,line) + local v={ total=0,count=0,nesting=0 } + t[line]=v + return v + end) + t[source]=v + return v + end) + t[name]=v + return v +end) +local function hook(where) + local f=getinfo(2,"nSl") + if f then + local source=f.short_src + if not source then + return + end + local line=f.linedefined or 0 + local name=f.name + if not name then + local what=f.what + if what=="C" then + name="<anonymous>" + else + name=f.namewhat or what or "<unknown>" + end + end + local data=names[name][source][line] + if where=="call" then + local nesting=data.nesting + if nesting==0 then + data.count=data.count+1 + insert(data,ticks()) + data.nesting=1 + else + data.nesting=nesting+1 + end + elseif where=="return" then + local nesting=data.nesting + if nesting==1 then + local t=remove(data) + if t then + data.total=data.total+ticks()-t + end + data.nesting=0 + else + data.nesting=nesting-1 + end + end + end +end +function debugger.showstats(printer,threshold) + local printer=printer or report + local calls=0 + local functions=0 + local dataset={} + local length=0 + local realtime=0 + local totaltime=0 + local threshold=threshold or 0 + for name,sources in next,names do + for source,lines in next,sources do + for line,data in next,lines do + local count=data.count + if count>threshold then + if #name>length then + length=#name + end + local total=data.total + local real=total + if real>0 then + real=total-(count*overhead/dummycalls) + if real<0 then + real=0 + end + realtime=realtime+real + end + totaltime=totaltime+total + if line<0 then + line=0 + end + dataset[#dataset+1]={ real,total,count,name,source,line } + end + end + end + end + sort(dataset,function(a,b) + if a[1]==b[1] then + if a[2]==b[2] then + if a[3]==b[3] then + if a[4]==b[4] then + if a[5]==b[5] then + return a[6]<b[6] + else + return a[5]<b[5] + end + else + return a[4]<b[4] + end + else + return b[3]<a[3] + end + else + return b[2]<a[2] + end + else + return b[1]<a[1] + end + end) + if length>50 then + length=50 + end + local fmt=string.formatters["%4.9k s %3.3k %% %4.9k s %3.3k %% %8i # %-"..length.."s %4i %s"] + for i=1,#dataset do + local data=dataset[i] + local real=data[1] + local total=data[2] + local count=data[3] + local name=data[4] + local source=data[5] + local line=data[6] + calls=calls+count + functions=functions+1 + name=gsub(name,"%s+"," ") + if #name>length then + name=sub(name,1,length) + end + printer(fmt(seconds(total),100*total/totaltime,seconds(real),100*real/realtime,count,name,line,source)) + end + printer("") + printer(format("functions : %i",functions)) + printer(format("calls : %i",calls)) + printer(format("overhead : %f",seconds(overhead/1000))) +end +function debugger.savestats(filename,threshold) + local f=io.open(filename,'w') + if f then + debugger.showstats(function(str) f:write(str,"\n") end,threshold) + f:close() + end +end +function debugger.enable() + if nesting==0 then + running=true + if initialize then + initialize() + end + sethook(hook,"cr") + local function dummy() end + local t=ticks() + for i=1,dummycalls do + dummy() + end + overhead=ticks()-t + end + if nesting>0 then + nesting=nesting+1 + end +end +function debugger.disable() + if nesting>0 then + nesting=nesting-1 + end + if nesting==0 then + sethook() + end +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,next=tostring,next +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=Cs(Cc("'")*sqlescape*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) + if how==false then + return replacekey(s,t,how,recurse) + else + local escaper=how and escapers[how] or luaescaper + return escaper(replacekey(s,t,how,recurse)) + end +end +local function replacekeyquoted(s,t,how,recurse) + if how==false then + return replacekey(s,t,how,recurse) + else + local escaper=how and quotedescapers[how] or quotedluaescaper + return escaper(replacekey(s,t,how,recurse)) + end +end +local function replaceoptional(l,m,r,t,how,recurse) + local v=t[l] + return v and v~="" and lpegmatch(replacer,r,1,t,how or "lua",recurse or false) or "" +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 noloptional=P("%?")/'' +local noroptional=P("?%")/'' +local nomoptional=P(":")/'' +local args=Carg(1)*Carg(2)*Carg(3) +local key=nosingle*((C((1-nosingle )^1)*args)/replacekey )*nosingle +local quoted=nolquotedq*((C((1-norquotedq )^1)*args)/replacekeyquoted )*norquotedq +local unquoted=nolquoted*((C((1-norquoted )^1)*args)/replacekeyunquoted)*norquoted +local optional=noloptional*((C((1-nomoptional)^1)*nomoptional*C((1-noroptional)^1)*args)/replaceoptional)*noroptional +local any=P(1) + replacer=Cs((unquoted+quoted+escape+optional+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 report=logs.reporter("stacker",name or nil) + 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=nil + local switch=nil + local function resolve_reset(mode) + if #tops>0 then + report("resetting %s left-over states of %a",#tops,name) + end + tops={} + top=nil + switch=nil + end + 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 and #top or 0 + 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() + if #tops>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 + 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, + resolve_reset=resolve_reset, + } + return s +end + +end -- closure diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended.lua index 9c55f8044ce..166f688ed65 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-extended.lua @@ -7,7 +7,7 @@ -- lualibs.dtx (with options: `extended') -- This is a generated file. -- --- Copyright (C) 2009--2017 by +-- Copyright (C) 2009--2018 by -- PRAGMA ADE / ConTeXt Development Team -- The LuaLaTeX Dev Team -- @@ -30,8 +30,8 @@ lualibs = lualibs or { } local lualibs_extended_module = { name = "lualibs-extended", - version = 2.5, - date = "2017-02-01", + version = 2.6, + date = "2018-09-21", description = "ConTeXt Lua libraries -- extended collection.", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL & Elie Roux & Philipp Gesang", copyright = "PRAGMA ADE / ConTeXt Development Team", @@ -111,11 +111,11 @@ fake_context() local loaded = false if lualibs.prefer_merged then - info"Loading merged package for collection “extended”." + info"Loading merged package for collection ^^e2^^80^^9cextended^^e2^^80^^9d." loaded = loadmodule('lualibs-extended-merged.lua') else info"Ignoring merged packages." - info"Falling back to individual libraries from collection “extended”." + info"Falling back to individual libraries from collection ^^e2^^80^^9cextended^^e2^^80^^9d." end if loaded == false then diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-file.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-file.lua index f2a27ad1832..46b6847d331 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-file.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-file.lua @@ -69,31 +69,34 @@ local lpegmatch = lpeg.match local getcurrentdir, attributes = lfs.currentdir, lfs.attributes local checkedsplit = string.checkedsplit --- local patterns = file.patterns or { } --- file.patterns = patterns - local P, R, S, C, Cs, Cp, Cc, Ct = lpeg.P, lpeg.R, lpeg.S, lpeg.C, lpeg.Cs, lpeg.Cp, lpeg.Cc, lpeg.Ct -- better this way: -local tricky = S("/\\") * P(-1) +----- tricky = S("/\\") * P(-1) local attributes = lfs.attributes -if sandbox then - sandbox.redefine(lfs.isfile,"lfs.isfile") - sandbox.redefine(lfs.isdir, "lfs.isdir") -end - function lfs.isdir(name) - if lpegmatch(tricky,name) then - return attributes(name,"mode") == "directory" - else - return attributes(name.."/.","mode") == "directory" - end + -- if not lpegmatch(tricky,name) then + -- name = name .. "/." + -- end + return attributes(name,"mode") == "directory" end function lfs.isfile(name) - return attributes(name,"mode") == "file" + local a = attributes(name,"mode") + return a == "file" or a == "link" or nil +end + +function lfs.isfound(name) + local a = attributes(name,"mode") + return (a == "file" or a == "link") and name or nil +end + +if sandbox then + sandbox.redefine(lfs.isfile,"lfs.isfile") + sandbox.redefine(lfs.isdir, "lfs.isdir") + sandbox.redefine(lfs.isfound, "lfs.isfound") end local colon = P(":") @@ -699,3 +702,32 @@ function lfs.mkdirs(path) lfs.mkdir(full) end end + +-- here is oen i ran into when messign around with xavante code (keppler project) +-- where it's called in_base .. no gain in using lpeg here + +function file.withinbase(path) -- don't go beyond root + local l = 0 + if not find(path,"^/") then + path = "/" .. path + end + for dir in gmatch(path,"/([^/]+)") do + if dir == ".." then + l = l - 1 + elseif dir ~= "." then + l = l + 1 + end + if l < 0 then + return false + end + end + return true +end + +-- not used in context but was in luatex once: + +local symlinkattributes = lfs.symlinkattributes + +function lfs.readlink(name) + return symlinkattributes(name,"target") or nil +end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-io.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-io.lua index 2039017b25d..fa3ad3c4cda 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-io.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-io.lua @@ -10,7 +10,7 @@ local io = io local open, flush, write, read = io.open, io.flush, io.write, io.read local byte, find, gsub, format = string.byte, string.find, string.gsub, string.format local concat = table.concat -local floor = math.floor +----- floor = math.floor local type = type if string.find(os.getenv("PATH"),";",1,true) then @@ -19,38 +19,48 @@ else io.fileseparator, io.pathseparator = "/" , ":" end -local function readall(f) - return f:read("*all") -end +-- local function readall(f) +-- return f:read("*all") +-- end -- The next one is upto 50% faster on large files and less memory consumption due -- to less intermediate large allocations. This phenomena was discussed on the -- luatex dev list. +local large = 0x01000000 -- 2^24 16.777.216 +local medium = 0x00100000 -- 2^20 1.048.576 +local small = 0x00020000 -- 2^17 131.072 + +-- local function readall(f) +-- local size = f:seek("end") +-- if size == 0 then +-- return "" +-- end +-- f:seek("set",0) +-- if size < medium then +-- return f:read('*all') +-- else +-- local step = (size > large) and large or (floor(size/(medium)) * small) +-- local data = { } +-- while true do +-- local r = f:read(step) +-- if not r then +-- return concat(data) +-- else +-- data[#data+1] = r +-- end +-- end +-- end +-- end + local function readall(f) + -- return f:read("*all") local size = f:seek("end") - if size == 0 then - return "" - end - f:seek("set",0) - if size < 1024*1024 then - return f:read('*all') + if size > 0 then + f:seek("set",0) + return f:read(size) else - local step - if size > 16*1024*1024 then - step = 16*1024*1024 - else - step = floor(size/(1024*1024)) * 1024 * 1024 / 8 - end - local data = { } - while true do - local r = f:read(step) - if not r then - return concat(data) - else - data[#data+1] = r - end - end + return "" end end @@ -59,52 +69,75 @@ io.readall = readall function io.loaddata(filename,textmode) -- return nil if empty local f = open(filename,(textmode and 'r') or 'rb') if f then - local data = readall(f) - f:close() - if #data > 0 then - return data + local size = f:seek("end") + local data = nil + if size > 0 then + -- data = f:read("*all") + f:seek("set",0) + data = f:read(size) end + f:close() + return data end end +-- function io.copydata(source,target,action) +-- local f = open(source,"rb") +-- if f then +-- local g = open(target,"wb") +-- if g then +-- local size = f:seek("end") +-- if size == 0 then +-- -- empty +-- else +-- f:seek("set",0) +-- if size < medium then +-- local data = f:read('*all') +-- if action then +-- data = action(data) +-- end +-- if data then +-- g:write(data) +-- end +-- else +-- local step = (size > large) and large or (floor(size/(medium)) * small) +-- while true do +-- local data = f:read(step) +-- if data then +-- if action then +-- data = action(data) +-- end +-- if data then +-- g:write(data) +-- end +-- else +-- break +-- end +-- end +-- end +-- end +-- g:close() +-- end +-- f:close() +-- flush() +-- end +-- end + function io.copydata(source,target,action) local f = open(source,"rb") if f then local g = open(target,"wb") if g then local size = f:seek("end") - if size == 0 then - -- empty - else + if size > 0 then + -- local data = f:read('*all') f:seek("set",0) - if size < 1024*1024 then - local data = f:read('*all') - if action then - data = action(data) - end - if data then - g:write(data) - end - else - local step - if size > 16*1024*1024 then - step = 16*1024*1024 - else - step = floor(size/(1024*1024)) * 1024 * 1024 / 8 - end - while true do - local data = f:read(step) - if data then - if action then - data = action(data) - end - if data then - g:write(data) - end - else - break - end - end + local data = f:read(size) + if action then + data = action(data) + end + if data then + g:write(data) end end g:close() @@ -134,32 +167,70 @@ end -- we can also chunk this one if needed: io.lines(filename,chunksize,"*l") -function io.loadlines(filename,n) -- return nil if empty - local f = open(filename,'r') - if not f then - -- no file - elseif n then - local lines = { } - for i=1,n do - local line = f:read("*lines") - if line then - lines[#lines+1] = line - else - break +-- ffi.readline + +if fio and fio.readline then + + local readline = fio.readline + + function io.loadlines(filename,n) -- return nil if empty + local f = open(filename,'r') + if not f then + -- no file + elseif n then + local lines = { } + for i=1,n do + local line = readline(f) + if line then + lines[i] = line + else + break + end + end + f:close() + lines = concat(lines,"\n") + if #lines > 0 then + return lines + end + else + local line = readline(f) + f:close() + if line and #line > 0 then + return line end end - f:close() - lines = concat(lines,"\n") - if #lines > 0 then - return lines - end - else - local line = f:read("*line") or "" - f:close() - if #line > 0 then - return line + end + +else + + function io.loadlines(filename,n) -- return nil if empty + local f = open(filename,'r') + if not f then + -- no file + elseif n then + local lines = { } + for i=1,n do + local line = f:read("*lines") + if line then + lines[i] = line + else + break + end + end + f:close() + lines = concat(lines,"\n") + if #lines > 0 then + return lines + end + else + local line = f:read("*line") or "" + f:close() + if #line > 0 then + return line + end end end + end function io.loadchunk(filename,n) @@ -205,6 +276,7 @@ local function noflines(f) return 0 end else + -- todo: load and lpeg local n = 0 for _ in f:lines() do n = n + 1 @@ -216,7 +288,7 @@ end io.noflines = noflines --- inlined is faster +-- inlined is faster ... beware, better use util-fil local nextchar = { [ 4] = function(f) @@ -331,7 +403,7 @@ function io.ask(question,default,options) end end -local function readnumber(f,n,m) +local function readnumber(f,n,m) -- to be replaced if m then f:seek("set",n) n = m @@ -340,38 +412,32 @@ local function readnumber(f,n,m) return byte(f:read(1)) elseif n == 2 then local a, b = byte(f:read(2),1,2) - return 256 * a + b + return 0x100 * a + b elseif n == 3 then local a, b, c = byte(f:read(3),1,3) - return 256*256 * a + 256 * b + c + return 0x10000 * a + 0x100 * b + c elseif n == 4 then local a, b, c, d = byte(f:read(4),1,4) - return 256*256*256 * a + 256*256 * b + 256 * c + d + return 0x1000000 * a + 0x10000 * b + 0x100 * c + d elseif n == 8 then local a, b = readnumber(f,4), readnumber(f,4) - return 256 * a + b + return 0x100 * a + b elseif n == 12 then local a, b, c = readnumber(f,4), readnumber(f,4), readnumber(f,4) - return 256*256 * a + 256 * b + c + return 0x10000 * a + 0x100 * b + c elseif n == -2 then local b, a = byte(f:read(2),1,2) - return 256*a + b + return 0x100 * a + b elseif n == -3 then local c, b, a = byte(f:read(3),1,3) - return 256*256 * a + 256 * b + c + return 0x10000 * a + 0x100 * b + c elseif n == -4 then local d, c, b, a = byte(f:read(4),1,4) - return 256*256*256 * a + 256*256 * b + 256*c + d + return 0x1000000 * a + 0x10000 * b + 0x100*c + d elseif n == -8 then local h, g, f, e, d, c, b, a = byte(f:read(8),1,8) - return 256*256*256*256*256*256*256 * a + - 256*256*256*256*256*256 * b + - 256*256*256*256*256 * c + - 256*256*256*256 * d + - 256*256*256 * e + - 256*256 * f + - 256 * g + - h + return 0x100000000000000 * a + 0x1000000000000 * b + 0x10000000000 * c + 0x100000000 * d + + 0x1000000 * e + 0x10000 * f + 0x100 * g + h else return 0 end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-lpeg.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-lpeg.lua index 959ca553eb8..750d5e698d7 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-lpeg.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-lpeg.lua @@ -6,6 +6,10 @@ if not modules then modules = { } end modules ['l-lpeg'] = { license = "see context related readme files" } +-- we can get too many captures (e.g. on largexml files) which makes me wonder +-- if P(foo)/"" can't be simplfied to N(foo) i.e. some direct instruction to the +-- lpeg virtual machine to ignore it + -- lpeg 12 vs lpeg 10: slower compilation, similar parsing speed (i need to check -- if i can use new features like capture / 2 and .B (at first sight the xml -- parser is some 5% slower) @@ -17,7 +21,13 @@ if not modules then modules = { } end modules ['l-lpeg'] = { -- move utf -> l-unicode -- move string -> l-string or keep it here -lpeg = require("lpeg") +-- lpeg.B : backward without consumption +-- lpeg.F = getmetatable(lpeg.P(1)).__len : forward without consumption + + +lpeg = require("lpeg") -- does lpeg register itself global? + +local lpeg = lpeg -- The latest lpeg doesn't have print any more, and even the new ones are not -- available by default (only when debug mode is enabled), which is a pitty as @@ -103,11 +113,14 @@ patterns.alwaysmatched = alwaysmatched local sign = S('+-') local zero = P('0') local digit = R('09') +local digits = digit^1 local octdigit = R("07") +local octdigits = octdigit^1 local lowercase = R("az") local uppercase = R("AZ") local underscore = P("_") local hexdigit = digit + lowercase + uppercase +local hexdigits = hexdigit^1 local cr, lf, crlf = P("\r"), P("\n"), P("\r\n") ----- newline = crlf + S("\r\n") -- cr + lf local newline = P("\r") * (P("\n") + P(true)) + P("\n") -- P("\r")^-1 * P("\n")^-1 @@ -187,18 +200,20 @@ local fullstripper = whitespace^0 * C((whitespace^0 * nonwhitespace^1)^0) ----- collapser = Cs(spacer^0/"" * ((spacer^1 * endofstring / "") + (spacer^1/" ") + P(1))^0) local collapser = Cs(spacer^0/"" * nonspacer^0 * ((spacer^0/" " * nonspacer^1)^0)) +local nospacer = Cs((whitespace^1/"" + nonwhitespace^1)^0) -local b_collapser = Cs( whitespace^0 /"" * (nonwhitespace^1 + whitespace^1/" ")^0) -local e_collapser = Cs((whitespace^1 * P(-1)/"" + nonwhitespace^1 + whitespace^1/" ")^0) -local m_collapser = Cs( (nonwhitespace^1 + whitespace^1/" ")^0) +local b_collapser = Cs( whitespace^0 /"" * (nonwhitespace^1 + whitespace^1/" ")^0) +local e_collapser = Cs((whitespace^1 * endofstring/"" + nonwhitespace^1 + whitespace^1/" ")^0) +local m_collapser = Cs( (nonwhitespace^1 + whitespace^1/" ")^0) -local b_stripper = Cs( spacer^0 /"" * (nonspacer^1 + spacer^1/" ")^0) -local e_stripper = Cs((spacer^1 * P(-1)/"" + nonspacer^1 + spacer^1/" ")^0) -local m_stripper = Cs( (nonspacer^1 + spacer^1/" ")^0) +local b_stripper = Cs( spacer^0 /"" * (nonspacer^1 + spacer^1/" ")^0) +local e_stripper = Cs((spacer^1 * endofstring/"" + nonspacer^1 + spacer^1/" ")^0) +local m_stripper = Cs( (nonspacer^1 + spacer^1/" ")^0) patterns.stripper = stripper patterns.fullstripper = fullstripper patterns.collapser = collapser +patterns.nospacer = nospacer patterns.b_collapser = b_collapser patterns.m_collapser = m_collapser @@ -238,43 +253,50 @@ patterns.doublequoted = dquote * patterns.nodquote * dquote patterns.quoted = patterns.doublequoted + patterns.singlequoted patterns.digit = digit +patterns.digits = digits patterns.octdigit = octdigit +patterns.octdigits = octdigits patterns.hexdigit = hexdigit +patterns.hexdigits = hexdigits patterns.sign = sign -patterns.cardinal = digit^1 -patterns.integer = sign^-1 * digit^1 -patterns.unsigned = digit^0 * period * digit^1 +patterns.cardinal = digits +patterns.integer = sign^-1 * digits +patterns.unsigned = digit^0 * period * digits patterns.float = sign^-1 * patterns.unsigned -patterns.cunsigned = digit^0 * comma * digit^1 -patterns.cpunsigned = digit^0 * (period + comma) * digit^1 +patterns.cunsigned = digit^0 * comma * digits +patterns.cpunsigned = digit^0 * (period + comma) * digits patterns.cfloat = sign^-1 * patterns.cunsigned patterns.cpfloat = sign^-1 * patterns.cpunsigned patterns.number = patterns.float + patterns.integer patterns.cnumber = patterns.cfloat + patterns.integer patterns.cpnumber = patterns.cpfloat + patterns.integer -patterns.oct = zero * octdigit^1 +patterns.oct = zero * octdigits -- hm is this ok patterns.octal = patterns.oct patterns.HEX = zero * P("X") * (digit+uppercase)^1 patterns.hex = zero * P("x") * (digit+lowercase)^1 -patterns.hexadecimal = zero * S("xX") * hexdigit^1 +patterns.hexadecimal = zero * S("xX") * hexdigits patterns.hexafloat = sign^-1 * zero * S("xX") - * (hexdigit^0 * period * hexdigit^1 + hexdigit^1 * period * hexdigit^0 + hexdigit^1) - * (S("pP") * sign^-1 * hexdigit^1)^-1 + * (hexdigit^0 * period * hexdigits + hexdigits * period * hexdigit^0 + hexdigits) + * (S("pP") * sign^-1 * hexdigits)^-1 patterns.decafloat = sign^-1 - * (digit^0 * period * digit^1 + digit^1 * period * digit^0 + digit^1) - * S("eE") * sign^-1 * digit^1 + * (digit^0 * period * digits + digits * period * digit^0 + digits) + * S("eE") * sign^-1 * digits patterns.propername = (uppercase + lowercase + underscore) * (uppercase + lowercase + underscore + digit)^0 * endofstring patterns.somecontent = (anything - newline - space)^1 -- (utf8char - newline - space)^1 patterns.beginline = #(1-newline) -patterns.longtostring = Cs(whitespace^0/"" * ((patterns.quoted + nonwhitespace^1 + whitespace^1/"" * (P(-1) + Cc(" ")))^0)) +patterns.longtostring = Cs(whitespace^0/"" * ((patterns.quoted + nonwhitespace^1 + whitespace^1/"" * (endofstring + Cc(" ")))^0)) -local function anywhere(pattern) --slightly adapted from website - return P { P(pattern) + 1 * V(1) } +-- local function anywhere(pattern) -- slightly adapted from website +-- return P { P(pattern) + 1 * V(1) } +-- end + +function anywhere(pattern) -- faster + return (1-P(pattern))^0 * P(pattern) end lpeg.anywhere = anywhere @@ -595,19 +617,27 @@ end -- print(7,lpegmatch(lpeg.secondofsplit(":"),"bc")) -- print(9,lpegmatch(lpeg.secondofsplit(":","123"),"bc")) --- -- slower: +-- this was slower but lpeg has been sped up in the meantime, so we no longer +-- use this (still seems somewhat faster on long strings) +-- +-- local nany = utf8char/"" -- -- function lpeg.counter(pattern) --- local n, pattern = 0, (lpeg.P(pattern)/function() n = n + 1 end + lpeg.anything)^0 --- return function(str) n = 0 ; lpegmatch(pattern,str) ; return n end +-- pattern = Cs((P(pattern)/" " + nany)^0) +-- return function(str) +-- return #lpegmatch(pattern,str) +-- end -- end -local nany = utf8char/"" - -function lpeg.counter(pattern) - pattern = Cs((P(pattern)/" " + nany)^0) - return function(str) - return #lpegmatch(pattern,str) +function lpeg.counter(pattern,action) + local n = 0 + local pattern = (P(pattern) / function() n = n + 1 end + anything)^0 + ----- pattern = (P(pattern) * (P(true) / function() n = n + 1 end) + anything)^0 + ----- pattern = (P(pattern) * P(function() n = n + 1 end) + anything)^0 + if action then + return function(str) n = 0 ; lpegmatch(pattern,str) ; action(n) end + else + return function(str) n = 0 ; lpegmatch(pattern,str) ; return n end end end @@ -839,28 +869,42 @@ end local p_false = P(false) local p_true = P(true) -local function make(t) - local function making(t) - local p = p_false - local keys = sortedkeys(t) - for i=1,#keys do - local k = keys[i] - if k ~= "" then - local v = t[k] - if v == true then - p = p + P(k) * p_true - elseif v == false then - -- can't happen - else - p = p + P(k) * making(v) - end - end - end - if t[""] then - p = p + p_true - end - return p - end +-- local function collapse(t,x) +-- if type(t) ~= "table" then +-- return t, x +-- else +-- local n = next(t) +-- if n == nil then +-- return t, x +-- elseif next(t,n) == nil then +-- -- one entry +-- local k = n +-- local v = t[k] +-- if type(v) == "table" then +-- return collapse(v,x..k) +-- else +-- return v, x .. k +-- end +-- else +-- local tt = { } +-- for k, v in next, t do +-- local vv, kk = collapse(v,k) +-- tt[kk] = vv +-- end +-- return tt, x +-- end +-- end +-- end + +local lower = utf and utf.lower or string.lower +local upper = utf and utf.upper or string.upper + +function lpeg.setutfcasers(l,u) + lower = l or lower + upper = u or upper +end + +local function make1(t,rest) local p = p_false local keys = sortedkeys(t) for i=1,#keys do @@ -872,41 +916,39 @@ local function make(t) elseif v == false then -- can't happen else - p = p + P(k) * making(v) + p = p + P(k) * make1(v,v[""]) end end end + if rest then + p = p + p_true + end return p end -local function collapse(t,x) - if type(t) ~= "table" then - return t, x - else - local n = next(t) - if n == nil then - return t, x - elseif next(t,n) == nil then - -- one entry - local k = n +local function make2(t,rest) -- only ascii + local p = p_false + local keys = sortedkeys(t) + for i=1,#keys do + local k = keys[i] + if k ~= "" then local v = t[k] - if type(v) == "table" then - return collapse(v,x..k) + if v == true then + p = p + (P(lower(k))+P(upper(k))) * p_true + elseif v == false then + -- can't happen else - return v, x .. k + p = p + (P(lower(k))+P(upper(k))) * make2(v,v[""]) end - else - local tt = { } - for k, v in next, t do - local vv, kk = collapse(v,k) - tt[kk] = vv - end - return tt, x end end + if rest then + p = p + p_true + end + return p end -function lpeg.utfchartabletopattern(list) -- goes to util-lpg +local function utfchartabletopattern(list,insensitive) -- goes to util-lpg local tree = { } local n = #list if n == 0 then @@ -979,32 +1021,42 @@ function lpeg.utfchartabletopattern(list) -- goes to util-lpg end end end --- collapse(tree,"") -- needs testing, maybe optional, slightly faster because P("x")*P("X") seems slower than P"(xX") (why) --- inspect(tree) - return make(tree) + -- collapse(tree,"") -- needs testing, maybe optional, slightly faster because P("x")*P("X") seems slower than P"(xX") (why) + -- inspect(tree) + return (insensitive and make2 or make1)(tree) +end + +lpeg.utfchartabletopattern = utfchartabletopattern + +function lpeg.utfreplacer(list,insensitive) + local pattern = Cs((utfchartabletopattern(list,insensitive)/list + utf8character)^0) + return function(str) + return lpegmatch(pattern,str) or str + end end + -- local t = { "start", "stoep", "staart", "paard" } -- local p = lpeg.Cs((lpeg.utfchartabletopattern(t)/string.upper + 1)^1) -- local t = { "a", "abc", "ac", "abe", "abxyz", "xy", "bef","aa" } -- local p = lpeg.Cs((lpeg.utfchartabletopattern(t)/string.upper + 1)^1) --- inspect(lpegmatch(p,"a")) --- inspect(lpegmatch(p,"aa")) --- inspect(lpegmatch(p,"aaaa")) --- inspect(lpegmatch(p,"ac")) --- inspect(lpegmatch(p,"bc")) --- inspect(lpegmatch(p,"zzbczz")) --- inspect(lpegmatch(p,"zzabezz")) --- inspect(lpegmatch(p,"ab")) --- inspect(lpegmatch(p,"abc")) --- inspect(lpegmatch(p,"abe")) --- inspect(lpegmatch(p,"xa")) --- inspect(lpegmatch(p,"bx")) --- inspect(lpegmatch(p,"bax")) --- inspect(lpegmatch(p,"abxyz")) --- inspect(lpegmatch(p,"foobarbefcrap")) +-- inspect(lpegmatch(p,"a")=="A") +-- inspect(lpegmatch(p,"aa")=="AA") +-- inspect(lpegmatch(p,"aaaa")=="AAAA") +-- inspect(lpegmatch(p,"ac")=="AC") +-- inspect(lpegmatch(p,"bc")=="bc") +-- inspect(lpegmatch(p,"zzbczz")=="zzbczz") +-- inspect(lpegmatch(p,"zzabezz")=="zzABEzz") +-- inspect(lpegmatch(p,"ab")=="Ab") +-- inspect(lpegmatch(p,"abc")=="ABC") +-- inspect(lpegmatch(p,"abe")=="ABE") +-- inspect(lpegmatch(p,"xa")=="xA") +-- inspect(lpegmatch(p,"bx")=="bx") +-- inspect(lpegmatch(p,"bax")=="bAx") +-- inspect(lpegmatch(p,"abxyz")=="ABXYZ") +-- inspect(lpegmatch(p,"foobarbefcrap")=="foobArBEFcrAp") -- local t = { ["^"] = 1, ["^^"] = 2, ["^^^"] = 3, ["^^^^"] = 4 } -- local p = lpeg.Cs((lpeg.utfchartabletopattern(t)/t + 1)^1) @@ -1081,25 +1133,38 @@ end -- moved here (before util-str) ------ digit = R("09") ------ period = P(".") ------ 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) +do + + local trailingzeros = zero^0 * -digit -- suggested by Roberto + local stripper = Cs(( + digits * ( + period * trailingzeros / "" + + period * (digit - trailingzeros)^1 * (trailingzeros / "") + ) + 1 + )^0) + + lpeg.patterns.stripzeros = stripper -- multiple in string -lpeg.patterns.stripzeros = stripper + local nonzero = digit - zero + local trailingzeros = zero^1 * endofstring + local stripper = Cs( (1-period)^0 * ( + period * trailingzeros/"" + + period * (nonzero^1 + (trailingzeros/"") + zero^1)^0 + + endofstring + )) --- 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.stripzero = stripper -- slightly more efficient but expects a float ! --- for practical reasone we keep this here: + -- 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)) + +end + +-- for practical reasons we keep this here: local byte_to_HEX = { } local byte_to_hex = { } @@ -1171,3 +1236,22 @@ end -- local h = "ADFE0345" -- local b = lpegmatch(patterns.hextobytes,h) -- print(h,b,string.tohex(b),string.toHEX(b)) + +local patterns = { } -- can be made weak + +local function containsws(what) + local p = patterns[what] + if not p then + local p1 = P(what) * (whitespace + endofstring) * Cc(true) + local p2 = whitespace * P(p1) + p = P(p1) + P(1-p2)^0 * p2 + Cc(false) + patterns[what] = p + end + return p +end + +lpeg.containsws = containsws + +function string.containsws(str,what) + return lpegmatch(patterns[what] or containsws(what),str) +end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-lua.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-lua.lua index 35715383635..c0a787a5c43 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-lua.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-lua.lua @@ -17,28 +17,37 @@ if not modules then modules = { } end modules ['l-lua'] = { -- utf.* -- bit32 --- compatibility hacksand helpers +local next, type, tonumber = next, type, tonumber -_MAJORVERSION, _MINORVERSION = string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") +-- compatibility hacks and helpers -_MAJORVERSION = tonumber(_MAJORVERSION) or 5 -_MINORVERSION = tonumber(_MINORVERSION) or 1 -_LUAVERSION = _MAJORVERSION + _MINORVERSION/10 +LUAMAJORVERSION, LUAMINORVERSION = string.match(_VERSION,"^[^%d]+(%d+)%.(%d+).*$") -if _LUAVERSION < 5.2 and jit then +LUAMAJORVERSION = tonumber(LUAMAJORVERSION) or 5 +LUAMINORVERSION = tonumber(LUAMINORVERSION) or 1 +LUAVERSION = LUAMAJORVERSION + LUAMINORVERSION/10 + +if LUAVERSION < 5.2 and jit then -- -- we want loadstring cum suis to behave like 5.2 -- - _MINORVERSION = 2 - _LUAVERSION = 5.2 + MINORVERSION = 2 + LUAVERSION = 5.2 end +_LUAVERSION = LUAVERSION -- for old times sake, will go away + -- lpeg if not lpeg then lpeg = require("lpeg") end +-- if utf8 then +-- utf8lua = utf8 +-- utf8 = nil +-- end + -- basics: if loadstring then @@ -188,7 +197,7 @@ if lua then lua.mask = load([[τεχ = 1]]) and "utf" or "ascii" end -local flush = io.flush +local flush = io.flush if flush then @@ -199,3 +208,45 @@ if flush then end +-- new + +FFISUPPORTED = type(ffi) == "table" and ffi.os ~= "" and ffi.arch ~= "" and ffi.load + +if not FFISUPPORTED then + + -- Maybe we should check for LUATEXENGINE but that's also a bti tricky as we still + -- can have a weird ffi library laying around. Checking for presence of 'jit' is + -- also not robust. So for now we hope for the best. + + local okay ; okay, ffi = pcall(require,"ffi") + + FFISUPPORTED = type(ffi) == "table" and ffi.os ~= "" and ffi.arch ~= "" and ffi.load + +end + +if not FFISUPPORTED then + ffi = nil +elseif not ffi.number then + ffi.number = tonumber +end + +if not bit32 then -- and utf8 then + -- bit32 = load ( [[ -- replacement code with 5.3 syntax so that 5.2 doesn't bark on it ]] ) + bit32 = require("l-bit32") +end + +-- We need this due a bug in luatex socket loading: + +-- local loaded = package.loaded +-- +-- if not loaded["socket"] then loaded["socket"] = loaded["socket.core"] end +-- if not loaded["mime"] then loaded["mime"] = loaded["mime.core"] end +-- +-- if not socket.mime then socket.mime = package.loaded["mime"] end +-- +-- if not loaded["socket.mime"] then loaded["socket.mime"] = socket.mime end +-- if not loaded["socket.http"] then loaded["socket.http"] = socket.http end +-- if not loaded["socket.ftp"] then loaded["socket.ftp"] = socket.ftp end +-- if not loaded["socket.smtp"] then loaded["socket.smtp"] = socket.smtp end +-- if not loaded["socket.tp"] then loaded["socket.tp"] = socket.tp end +-- if not loaded["socket.url"] then loaded["socket.url"] = socket.url end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-math.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-math.lua index ec62919b46a..e5668a5db0a 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-math.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-math.lua @@ -6,33 +6,144 @@ if not modules then modules = { } end modules ['l-math'] = { license = "see context related readme files" } -local floor, sin, cos, tan = math.floor, math.sin, math.cos, math.tan - if not math.ceiling then + math.ceiling = math.ceil + end if not math.round then + + local floor = math.floor + function math.round(x) return floor(x + 0.5) end + end if not math.div then + + local floor = math.floor + function math.div(n,m) return floor(n/m) end + end if not math.mod then + function math.mod(n,m) return n % m end -end -local pipi = 2*math.pi/360 +end if not math.sind then + + local sin, cos, tan = math.sin, math.cos, math.tan + + local pipi = 2*math.pi/360 + function math.sind(d) return sin(d*pipi) end function math.cosd(d) return cos(d*pipi) end function math.tand(d) return tan(d*pipi) end + end if not math.odd then + function math.odd (n) return n % 2 ~= 0 end function math.even(n) return n % 2 == 0 end + +end + +if not math.cosh then + + local exp = math.exp + + function math.cosh(x) + local xx = exp(x) + return (xx+1/xx)/2 + end + function math.sinh(x) + local xx = exp(x) + return (xx-1/xx)/2 + end + function math.tanh(x) + local xx = exp(x) + return (xx-1/xx)/(xx+1/xx) + end + +end + +if not math.pow then + + function math.pow(x,y) + return x^y + end + +end + +if not math.atan2 then + + math.atan2 = math.atan + +end + +if not math.ldexp then + + function math.ldexp(x,e) + return x * 2.0^e + end + +end + +-- if not math.frexp then +-- +-- -- not a oneliner so use a math library instead +-- +-- function math.frexp(x,e) +-- -- returns m and e such that x = m2e, e is an integer and the absolute +-- -- value of m is in the range [0.5, 1) (or zero when x is zero) +-- end +-- +-- end + +if not math.log10 then + + local log = math.log + + function math.log10(x) + return log(x,10) + end + +end + +if not math.type then + + function math.type() + return "float" + end + +end + +if not math.tointeger then + + math.mininteger = -0x4FFFFFFFFFFF + math.maxinteger = 0x4FFFFFFFFFFF + + local floor = math.floor + + function math.tointeger(n) + local f = floor(n) + return f == n and f or nil + end + +end + +if not math.ult then + + local floor = math.floor + + function math.tointeger(m,n) + -- not ok but i'm not motivated to look into it now + return floor(m) < floor(n) -- unsigned comparison needed + end + end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-md5.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-md5.lua index 00272c873f1..6758fa44441 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-md5.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-md5.lua @@ -48,6 +48,9 @@ do if not md5.hex then function md5.hex(str) if str then return lpegmatch(bytestohex,md5sum(str)) end end end if not md5.dec then function md5.dec(str) if str then return lpegmatch(bytestodec,md5sum(str)) end end end + md5.sumhexa = md5.hex + md5.sumHEXA = md5.HEX + end end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-number.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-number.lua index c6f1e33597b..a83e8f8f94a 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-number.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-number.lua @@ -18,46 +18,50 @@ local floor = math.floor number = number or { } local number = number -if bit32 then -- I wonder if this is faster +-- begin obsolete code -- - local btest, bor = bit32.btest, bit32.bor - - function number.bit(p) - return 2 ^ (p - 1) -- 1-based indexing - end - - number.hasbit = btest - number.setbit = bor - - function number.setbit(x,p) -- why not bor? - return btest(x,p) and x or x + p - end - - function number.clearbit(x,p) - return btest(x,p) and x - p or x - end - -else - - -- http://ricilake.blogspot.com/2007/10/iterating-bits-in-lua.html - - function number.bit(p) - return 2 ^ (p - 1) -- 1-based indexing - end - - function number.hasbit(x, p) -- typical call: if hasbit(x, bit(3)) then ... - return x % (p + p) >= p - end - - function number.setbit(x, p) - return (x % (p + p) >= p) and x or x + p - end - - function number.clearbit(x, p) - return (x % (p + p) >= p) and x - p or x - end +-- if bit32 then +-- +-- local btest, bor = bit32.btest, bit32.bor +-- +-- function number.bit(p) +-- return 2 ^ (p - 1) -- 1-based indexing +-- end +-- +-- number.hasbit = btest +-- number.setbit = bor +-- +-- function number.setbit(x,p) -- why not bor? +-- return btest(x,p) and x or x + p +-- end +-- +-- function number.clearbit(x,p) +-- return btest(x,p) and x - p or x +-- end +-- +-- else +-- +-- -- http://ricilake.blogspot.com/2007/10/iterating-bits-in-lua.html +-- +-- function number.bit(p) +-- return 2 ^ (p - 1) -- 1-based indexing +-- end +-- +-- function number.hasbit(x, p) -- typical call: if hasbit(x, bit(3)) then ... +-- return x % (p + p) >= p +-- end +-- +-- function number.setbit(x, p) +-- return (x % (p + p) >= p) and x or x + p +-- end +-- +-- function number.clearbit(x, p) +-- return (x % (p + p) >= p) and x - p or x +-- end +-- +-- end -end +-- end obsolete code -- -- print(number.tobitstring(8)) -- print(number.tobitstring(14)) @@ -152,60 +156,60 @@ function number.toevenhex(n) end end --- a,b,c,d,e,f = number.toset(100101) +-- -- a,b,c,d,e,f = number.toset(100101) +-- -- +-- -- function number.toset(n) +-- -- return match(tostring(n),"(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?)") +-- -- end +-- -- +-- -- -- the lpeg way is slower on 8 digits, but faster on 4 digits, some 7.5% +-- -- -- on +-- -- +-- -- for i=1,1000000 do +-- -- local a,b,c,d,e,f,g,h = number.toset(12345678) +-- -- local a,b,c,d = number.toset(1234) +-- -- local a,b,c = number.toset(123) +-- -- local a,b,c = number.toset("123") +-- -- end +-- +-- local one = lpeg.C(1-lpeg.S('')/tonumber)^1 -- -- function number.toset(n) --- return match(tostring(n),"(.?)(.?)(.?)(.?)(.?)(.?)(.?)(.?)") +-- return lpegmatch(one,tostring(n)) -- end -- --- -- the lpeg way is slower on 8 digits, but faster on 4 digits, some 7.5% --- -- on +-- -- function number.bits(n,zero) +-- -- local t, i = { }, (zero and 0) or 1 +-- -- while n > 0 do +-- -- local m = n % 2 +-- -- if m > 0 then +-- -- insert(t,1,i) +-- -- end +-- -- n = floor(n/2) +-- -- i = i + 1 +-- -- end +-- -- return t +-- -- end +-- -- +-- -- -- a bit faster -- --- for i=1,1000000 do --- local a,b,c,d,e,f,g,h = number.toset(12345678) --- local a,b,c,d = number.toset(1234) --- local a,b,c = number.toset(123) --- local a,b,c = number.toset("123") --- end - -local one = lpeg.C(1-lpeg.S('')/tonumber)^1 - -function number.toset(n) - return lpegmatch(one,tostring(n)) -end - --- function number.bits(n,zero) --- local t, i = { }, (zero and 0) or 1 --- while n > 0 do +-- local function bits(n,i,...) +-- if n > 0 then -- local m = n % 2 +-- local n = floor(n/2) -- if m > 0 then --- insert(t,1,i) +-- return bits(n, i+1, i, ...) +-- else +-- return bits(n, i+1, ...) -- end --- n = floor(n/2) --- i = i + 1 +-- else +-- return ... -- end --- return t -- end -- --- -- a bit faster - -local function bits(n,i,...) - if n > 0 then - local m = n % 2 - local n = floor(n/2) - if m > 0 then - return bits(n, i+1, i, ...) - else - return bits(n, i+1, ...) - end - else - return ... - end -end - -function number.bits(n) - return { bits(n,1) } -end +-- function number.bits(n) +-- return { bits(n,1) } +-- end function number.bytetodecimal(b) local d = floor(b * 100 / 255 + 0.5) diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-os.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-os.lua index 0a86ea6d697..c2a903f5c3f 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-os.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-os.lua @@ -32,11 +32,12 @@ local concat = table.concat local random, ceil, randomseed = math.random, math.ceil, math.randomseed local rawget, rawset, type, getmetatable, setmetatable, tonumber, tostring = rawget, rawset, type, getmetatable, setmetatable, tonumber, tostring --- The following code permits traversing the environment table, at least --- in luatex. Internally all environment names are uppercase. +-- The following code permits traversing the environment table, at least in luatex. Internally all +-- environment names are uppercase. -- The randomseed in Lua is not that random, although this depends on the operating system as well --- as the binary (Luatex is normally okay). But to be sure we set the seed anyway. +-- as the binary (Luatex is normally okay). But to be sure we set the seed anyway. It will be better +-- in Lua 5.4 (according to the announcements.) math.initialseed = tonumber(string.sub(string.reverse(tostring(ceil(socket and socket.gettime()*10000 or time()))),1,6)) @@ -119,7 +120,7 @@ end local execute = os.execute local iopopen = io.popen -function os.resultof(command) +local function resultof(command) local handle = iopopen(command,"r") -- already has flush if handle then local result = handle:read("*all") or "" @@ -130,9 +131,15 @@ function os.resultof(command) end end +os.resultof = resultof + +function os.pipeto(command) + return iopopen(command,"w") -- already has flush +end + if not io.fileseparator then if find(os.getenv("PATH"),";",1,true) then - io.fileseparator, io.pathseparator, os.type = "\\", ";", os.type or "mswin" + io.fileseparator, io.pathseparator, os.type = "\\", ";", os.type or "windows" else io.fileseparator, io.pathseparator, os.type = "/" , ":", os.type or "unix" end @@ -150,7 +157,7 @@ end local launchers = { windows = "start %s", macosx = "open %s", - unix = "$BROWSER %s &> /dev/null &", + unix = "xdg-open %s &> /dev/null &", } function os.launch(str) @@ -203,20 +210,27 @@ end }) local name, platform = os.name or "linux", os.getenv("MTX_PLATFORM") or "" -local function guess() - local architecture = os.resultof("uname -m") or "" - if architecture ~= "" then - return architecture - end - architecture = os.getenv("HOSTTYPE") or "" - if architecture ~= "" then - return architecture - end - return os.resultof("echo $HOSTTYPE") or "" -end +-- local function guess() +-- local architecture = resultof("uname -m") or "" +-- if architecture ~= "" then +-- return architecture +-- end +-- architecture = os.getenv("HOSTTYPE") or "" +-- if architecture ~= "" then +-- return architecture +-- end +-- return resultof("echo $HOSTTYPE") or "" +-- end -- os.bits = 32 | 64 +-- os.uname() +-- sysname +-- machine +-- release +-- version +-- nodename + if platform ~= "" then os.platform = platform @@ -228,10 +242,12 @@ elseif os.type == "windows" then -- PROCESSOR_ARCHITECTURE : binary platform -- PROCESSOR_ARCHITEW6432 : OS platform + -- mswin-64 is now win64 + function resolvers.platform(t,k) - local platform, architecture = "", os.getenv("PROCESSOR_ARCHITECTURE") or "" + local architecture = os.getenv("PROCESSOR_ARCHITECTURE") or "" + local platform = "" if find(architecture,"AMD64",1,true) then - -- platform = "mswin-64" platform = "win64" else platform = "mswin" @@ -245,13 +261,17 @@ elseif name == "linux" then function resolvers.platform(t,k) -- we sometimes have HOSTTYPE set so let's check that first - local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" - if find(architecture,"x86_64",1,true) then - platform = "linux-64" + local architecture = os.getenv("HOSTTYPE") or resultof("uname -m") or "" + local platform = os.getenv("MTX_PLATFORM") or "" + local musl = find(os.selfdir or "","linuxmusl") + if platform ~= "" then + -- we're done + elseif find(architecture,"x86_64",1,true) then + platform = musl and "linuxmusl" or "linux-64" elseif find(architecture,"ppc",1,true) then platform = "linux-ppc" else - platform = "linux" + platform = musl and "linuxmusl" or "linux" end os.setenv("MTX_PLATFORM",platform) os.platform = platform @@ -271,11 +291,13 @@ elseif name == "macosx" then ]]-- function resolvers.platform(t,k) - -- local platform, architecture = "", os.getenv("HOSTTYPE") or "" + -- local platform = "" + -- local architecture = os.getenv("HOSTTYPE") or "" -- if architecture == "" then - -- architecture = os.resultof("echo $HOSTTYPE") or "" + -- architecture = resultof("echo $HOSTTYPE") or "" -- end - local platform, architecture = "", os.resultof("echo $HOSTTYPE") or "" + local architecture = resultof("echo $HOSTTYPE") or "" + local platform = "" if architecture == "" then -- print("\nI have no clue what kind of OSX you're running so let's assume an 32 bit intel.\n") platform = "osx-intel" @@ -294,7 +316,8 @@ elseif name == "macosx" then elseif name == "sunos" then function resolvers.platform(t,k) - local platform, architecture = "", os.resultof("uname -m") or "" + local architecture = resultof("uname -m") or "" + local platform = "" if find(architecture,"sparc",1,true) then platform = "solaris-sparc" else -- if architecture == 'i86pc' @@ -308,7 +331,8 @@ elseif name == "sunos" then elseif name == "freebsd" then function resolvers.platform(t,k) - local platform, architecture = "", os.resultof("uname -m") or "" + local architecture = resultof("uname -m") or "" + local platform = "" if find(architecture,"amd64",1,true) then platform = "freebsd-amd64" else @@ -323,7 +347,8 @@ elseif name == "kfreebsd" then function resolvers.platform(t,k) -- we sometimes have HOSTTYPE set so let's check that first - local platform, architecture = "", os.getenv("HOSTTYPE") or os.resultof("uname -m") or "" + local architecture = os.getenv("HOSTTYPE") or resultof("uname -m") or "" + local platform = "" if find(architecture,"x86_64",1,true) then platform = "kfreebsd-amd64" else @@ -502,8 +527,10 @@ end -- These are moved from core-con.lua (as I needed them elsewhere). -local function isleapyear(year) - return (year % 400 == 0) or ((year % 100 ~= 0) and (year % 4 == 0)) +local function isleapyear(year) -- timed for bram's cs practicum + -- return (year % 400 == 0) or (year % 100 ~= 0 and year % 4 == 0) -- 3:4:1600:1900 = 9.9 : 8.2 : 5.0 : 6.8 (29.9) + return (year % 4 == 0) and (year % 100 ~= 0 or year % 400 == 0) -- 3:4:1600:1900 = 5.1 : 6.5 : 8.1 : 10.2 (29.9) + -- return (year % 4 == 0) and (year % 400 == 0 or year % 100 ~= 0) -- 3:4:1600:1900 = 5.2 : 8.5 : 6.8 : 10.1 (30.6) end os.isleapyear = isleapyear @@ -556,3 +583,20 @@ function os.validdate(year,month,day) end return year, month, day end + +local osexit = os.exit +local exitcode = nil + +function os.setexitcode(code) + exitcode = code +end + +function os.exit(c) + if exitcode ~= nil then + return osexit(exitcode) + end + if c ~= nil then + return osexit(c) + end + return osexit() +end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-package.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-package.lua index 075fcde25ae..0dd71e5eca8 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-package.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-package.lua @@ -18,11 +18,15 @@ if not modules then modules = { } end modules ['l-package'] = { local type = type local gsub, format, find = string.gsub, string.format, string.find +local insert, remove = table.insert, table.remove local P, S, Cs, lpegmatch = lpeg.P, lpeg.S, lpeg.Cs, lpeg.match -local package = package -local searchers = package.searchers or package.loaders +local package = package +local searchers = package.searchers or package.loaders + +-------.loaders = nil -- old stuff that we don't want +-------.seeall = nil -- old stuff that we don't want -- dummies @@ -191,7 +195,25 @@ local function registerpath(tag,what,target,...) add(path) end end - return paths +end + +local function pushpath(tag,what,target,path) + local path = helpers.cleanpath(path) + insert(target,1,path) + if helpers.trace then + helpers.report("pushing %s path in front: %s",tag,path) + end +end + +local function poppath(tag,what,target) + local path = remove(target,1) + if helpers.trace then + if path then + helpers.report("popping %s path from front: %s",tag,path) + else + helpers.report("no %s path to pop",tag) + end + end end helpers.registerpath = registerpath @@ -199,10 +221,22 @@ helpers.registerpath = registerpath function package.extraluapath(...) registerpath("extra lua","lua",extraluapaths,...) end +function package.pushluapath(path) + pushpath("extra lua","lua",extraluapaths,path) +end +function package.popluapath() + poppath("extra lua","lua",extraluapaths) +end function package.extralibpath(...) registerpath("extra lib","lib",extralibpaths,...) end +function package.pushlibpath(path) + pushpath("extra lib","lib",extralibpaths,path) +end +function package.poplibpath() + poppath("extra lib","lua",extralibpaths) +end -- lib loader (used elsewhere) @@ -360,3 +394,7 @@ end -- front .. table.insert(searchers,1,helpers.loaded) + +if context then + package.path = "" +end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-string.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-string.lua index be8f397aee3..8ae8d8d1d76 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-string.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-string.lua @@ -18,7 +18,7 @@ local P, S, C, Ct, Cc, Cs = lpeg.P, lpeg.S, lpeg.C, lpeg.Ct, lpeg.Cc, lpeg.Cs -- -- function string.split(str,pattern) -- local t = { } --- if #str > 0 then +-- if str ~= "" then -- local n = 1 -- for s in gmatch(str..pattern,"(.-)"..pattern) do -- t[n] = s @@ -72,6 +72,7 @@ end local stripper = patterns.stripper local fullstripper = patterns.fullstripper local collapser = patterns.collapser +local nospacer = patterns.nospacer local longtostring = patterns.longtostring function string.strip(str) @@ -86,6 +87,10 @@ function string.collapsespaces(str) return str and lpegmatch(collapser,str) or "" end +function string.nospaces(str) + return str and lpegmatch(nospacer,str) or "" +end + function string.longtostring(str) return str and lpegmatch(longtostring,str) or "" end @@ -212,3 +217,24 @@ end string.quote = string.quoted string.unquote = string.unquoted + +-- new + +if not string.bytetable then -- used in font-cff.lua + + local limit = 5000 -- we can go to 8000 in luajit and much higher in lua if needed + + function string.bytetable(str) -- from a string + local n = #str + if n > limit then + local t = { byte(str,1,limit) } + for i=limit+1,n do + t[i] = byte(str,i) + end + return t + else + return { byte(str,1,n) } + end + end + +end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-table.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-table.lua index 39357bd25e6..69f45d9b4cd 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-table.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-table.lua @@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['l-table'] = { license = "see context related readme files" } -local type, next, tostring, tonumber, ipairs, select = type, next, tostring, tonumber, ipairs, select +local type, next, tostring, tonumber, select = type, next, tostring, tonumber, select local table, string = table, string local concat, sort, insert, remove = table.concat, table.sort, table.insert, table.remove local format, lower, dump = string.format, string.lower, string.dump @@ -22,6 +22,10 @@ local floor = math.floor local stripper = patterns.stripper +function table.getn(t) + return t and #t -- for very old times sake +end + function table.strip(tab) local lst, l = { }, 0 for i=1,#tab do @@ -416,7 +420,7 @@ end -- todo : copy without metatable -local function copy(t, tables) -- taken from lua wiki, slightly adapted +local function copy(t,tables) -- taken from lua wiki, slightly adapted tables = tables or { } local tcopy = { } if not tables[t] then @@ -427,7 +431,7 @@ local function copy(t, tables) -- taken from lua wiki, slightly adapted if tables[i] then i = tables[i] else - i = copy(i, tables) + i = copy(i,tables) end end if type(v) ~= "table" then @@ -435,7 +439,7 @@ local function copy(t, tables) -- taken from lua wiki, slightly adapted elseif tables[v] then tcopy[i] = tables[v] else - tcopy[i] = copy(v, tables) + tcopy[i] = copy(v,tables) end end local mt = getmetatable(t) @@ -460,7 +464,7 @@ function table.tohash(t,value) local h = { } if t then if value == nil then value = true end - for _, v in next, t do -- no ipairs here + for _, v in next, t do h[v] = value end end @@ -469,7 +473,7 @@ end function table.fromhash(t) local hsh, h = { }, 0 - for k, v in next, t do -- no ipairs here + for k, v in next, t do if v then h = h + 1 hsh[h] = k @@ -486,7 +490,7 @@ local reserved = table.tohash { -- intercept a language inconvenience: no reserv 'NaN', 'goto', } --- local function simple_table(t) +-- local function is_simple_table(t) -- if #t > 0 then -- local n = 0 -- for _,v in next, t do @@ -520,29 +524,67 @@ local reserved = table.tohash { -- intercept a language inconvenience: no reserv -- return nil -- end -local function simple_table(t) +-- local function is_simple_table(t) +-- local nt = #t +-- if nt > 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 == nt then +-- local tt = { } +-- for i=1,nt do +-- local v = t[i] +-- local tv = type(v) +-- if tv == "number" then +-- if hexify then +-- tt[i] = format("0x%X",v) +-- else +-- tt[i] = tostring(v) -- tostring not needed +-- end +-- elseif tv == "string" then +-- tt[i] = format("%q",v) +-- elseif tv == "boolean" then +-- tt[i] = v and "true" or "false" +-- else +-- return nil +-- end +-- end +-- return tt +-- end +-- end +-- return nil +-- end + +local function is_simple_table(t,hexify) -- also used in util-tab so maybe public local nt = #t if nt > 0 then local n = 0 - for _,v in next, t do + for _, v in next, t do n = n + 1 - -- if type(v) == "table" then - -- return nil - -- end + if type(v) == "table" then + return nil + end end + -- local haszero = t[0] + local haszero = rawget(t,0) -- don't trigger meta if n == nt then local tt = { } for i=1,nt do local v = t[i] local tv = type(v) if tv == "number" then + -- tt[i] = v -- not needed tostring(v) if hexify then tt[i] = format("0x%X",v) else - tt[i] = tostring(v) -- tostring not needed + tt[i] = v -- not needed tostring(v) end elseif tv == "string" then - tt[i] = format("%q",v) + tt[i] = format("%q",v) -- f_string(v) elseif tv == "boolean" then tt[i] = v and "true" or "false" else @@ -550,11 +592,35 @@ local function simple_table(t) end end return tt + elseif haszero and (n == nt + 1) then + local tt = { } + for i=0,nt do + local v = t[i] + local tv = type(v) + if tv == "number" then + -- tt[i+1] = v -- not needed tostring(v) + if hexify then + tt[i+1] = format("0x%X",v) + else + tt[i+1] = v -- not needed tostring(v) + end + elseif tv == "string" then + tt[i+1] = format("%q",v) -- f_string(v) + elseif tv == "boolean" then + tt[i+1] = v and "true" or "false" + else + return nil + end + end + tt[1] = "[0] = " .. tt[1] + return tt end end return nil end +table.is_simple_table = is_simple_table + -- Because this is a core function of mkiv I moved some function calls -- inline. -- @@ -637,7 +703,7 @@ local function do_serialize(root,name,depth,level,indexed) if next(v) == nil then handle(format("%s {},",depth)) elseif inline then -- and #t > 0 - local st = simple_table(v) + local st = is_simple_table(v,hexify) if st then handle(format("%s { %s },",depth,concat(st,", "))) else @@ -723,7 +789,7 @@ local function do_serialize(root,name,depth,level,indexed) handle(format("%s [%q]={},",depth,k)) end elseif inline then - local st = simple_table(v) + local st = is_simple_table(v,hexify) if st then if tk == "number" then if hexify then @@ -1030,7 +1096,9 @@ function table.unnest(t) -- bad name end local function are_equal(a,b,n,m) -- indexed - if a and b and #a == #b then + if a == b then + return true + elseif a and b and #a == #b then n = n or 1 m = m or #a for i=n,m do @@ -1052,16 +1120,18 @@ local function are_equal(a,b,n,m) -- indexed end local function identical(a,b) -- assumes same structure - for ka, va in next, a do - local vb = b[ka] - if va == vb then - -- same - elseif type(va) == "table" and type(vb) == "table" then - if not identical(va,vb) then + if a ~= b then + for ka, va in next, a do + local vb = b[ka] + if va == vb then + -- same + elseif type(va) == "table" and type(vb) == "table" then + if not identical(va,vb) then + return false + end + else return false end - else - return false end end return true @@ -1155,27 +1225,35 @@ function table.reversed(t) end end -function table.reverse(t) +function table.reverse(t) -- check with 5.3 ? if t then local n = #t - for i=1,floor(n/2) do - local j = n - i + 1 + local m = n + 1 + for i=1,floor(n/2) do -- maybe just n//2 + local j = m - i t[i], t[j] = t[j], t[i] end return t end end -function table.sequenced(t,sep,simple) -- hash only +local function sequenced(t,sep,simple) if not t then return "" + elseif type(t) == "string" then + return t -- handy fallback end local n = #t local s = { } if n > 0 then -- indexed for i=1,n do - s[i] = tostring(t[i]) + local v = t[i] + if type(v) == "table" then + s[i] = "{" .. sequenced(v,sep,simple) .. "}" + else + s[i] = tostring(t[i]) + end end else -- hashed @@ -1187,17 +1265,27 @@ function table.sequenced(t,sep,simple) -- hash only s[n] = k elseif v and v~= "" then n = n + 1 - s[n] = k .. "=" .. tostring(v) + if type(v) == "table" then + s[n] = k .. "={" .. sequenced(v,sep,simple) .. "}" + else + s[n] = k .. "=" .. tostring(v) + end end else n = n + 1 - s[n] = k .. "=" .. tostring(v) + if type(v) == "table" then + s[n] = k .. "={" .. sequenced(v,sep,simple) .. "}" + else + s[n] = k .. "=" .. tostring(v) + end end end end return concat(s,sep or " | ") end +table.sequenced = sequenced + function table.print(t,...) if type(t) ~= "table" then print(tostring(t)) @@ -1326,3 +1414,26 @@ function table.filtered(t,pattern,sort,cmp) return nothing end end + +-- lua 5.3: + +if not table.move then + + function table.move(a1,f,e,t,a2) + if a2 and a1 ~= a2 then + for i=f,e do + a2[t] = a1[i] + t = t + 1 + end + return a2 + else + t = t + e - f + for i=e,f,-1 do + a1[t] = a1[i] + t = t - 1 + end + return a1 + end + end + +end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-trac-inf.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-trac-inf.lua index 12a4f646ff4..51893d585fe 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-trac-inf.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-trac-inf.lua @@ -42,11 +42,51 @@ local function resettiming(instance) timers[instance or "notimer"] = { timing = 0, loadtime = 0 } end +local ticks = clock +local seconds = function(n) return n or 0 end + +-- if FFISUPPORTED and ffi and os.type == "windows" then +-- +-- local okay, kernel = pcall(ffi.load,"kernel32") +-- +-- if kernel then +-- +-- local tonumber = ffi.number or tonumber +-- +-- ffi.cdef[[ +-- int QueryPerformanceFrequency(int64_t *lpFrequency); +-- int QueryPerformanceCounter(int64_t *lpPerformanceCount); +-- ]] +-- +-- local target = ffi.new("__int64[1]") +-- +-- ticks = function() +-- if kernel.QueryPerformanceCounter(target) == 1 then +-- return tonumber(target[0]) +-- else +-- return 0 +-- end +-- end +-- +-- local target = ffi.new("__int64[1]") +-- +-- seconds = function(ticks) +-- if kernel.QueryPerformanceFrequency(target) == 1 then +-- return ticks / tonumber(target[0]) +-- else +-- return 0 +-- end +-- end +-- +-- end +-- +-- end + local function starttiming(instance) local timer = timers[instance or "notimer"] - local it = timer.timing or 0 + local it = timer.timing if it == 0 then - timer.starttime = clock() + timer.starttime = ticks() if not timer.loadtime then timer.loadtime = 0 end @@ -62,7 +102,7 @@ local function stoptiming(instance) else local starttime = timer.starttime if starttime and starttime > 0 then - local stoptime = clock() + local stoptime = ticks() local loadtime = stoptime - starttime timer.stoptime = stoptime timer.loadtime = timer.loadtime + loadtime @@ -76,10 +116,28 @@ end local function elapsed(instance) if type(instance) == "number" then - return instance or 0 + return instance + else + local timer = timers[instance or "notimer"] + return timer and seconds(timer.loadtime) or 0 + end +end + +local function currenttime(instance) + if type(instance) == "number" then + return instance else local timer = timers[instance or "notimer"] - return timer and timer.loadtime or 0 + local it = timer.timing + if it > 1 then + -- whatever + else + local starttime = timer.starttime + if starttime and starttime > 0 then + return seconds(timer.loadtime + ticks() - starttime) + end + end + return 0 end end @@ -101,6 +159,7 @@ statistics.hastiming = hastiming statistics.resettiming = resettiming statistics.starttiming = starttiming statistics.stoptiming = stoptiming +statistics.currenttime = currenttime statistics.elapsed = elapsed statistics.elapsedtime = elapsedtime statistics.elapsedindeed = elapsedindeed @@ -127,20 +186,24 @@ function statistics.show() 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) + -- register("luatex banner", function() + -- return lower(status.banner) + -- end) + register("used engine", function() + return format("%s version %s with functionality level %s, banner: %s", + LUATEXENGINE, LUATEXVERSION, LUATEXFUNCTIONALITY, 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)) + register("callbacks", statistics.callbacks) + if TEXENGINE == "luajittex" and JITSUPPORTED then + local jitstatus = jit.status + if jitstatus then + local jitstatus = { jitstatus() } + if jitstatus[1] then + register("luajit options", concat(jitstatus," ",2)) + end end end -- so far @@ -149,8 +212,9 @@ function statistics.show() local hashchar = tonumber(status.luatex_hashchars) local hashtype = status.luatex_hashtype 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)", + return format("engine: %s %s, used memory: %s, hash type: %s, hash chars: min(%i,40), symbol mask: %s (%s)", jit and "luajit" or "lua", + LUAVERSION, statistics.memused(), hashtype or "default", hashchar and 2^hashchar or "unknown", @@ -184,7 +248,7 @@ end function statistics.runtime() stoptiming(statistics) - -- stoptiming(statistics) -- somehow we can start the timer twice, but where + -- stoptiming(statistics) -- somehow we can start the timer twice, but where return statistics.formatruntime(elapsedtime(statistics)) end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-unicode.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-unicode.lua index 3dec8001345..b5f52d3127c 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-unicode.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-unicode.lua @@ -6,6 +6,9 @@ if not modules then modules = { } end modules ['l-unicode'] = { license = "see context related readme files" } +-- floor(b/256) => rshift(b, 8) +-- floor(b/1024) => rshift(b,10) + -- in lua 5.3: -- utf8.char(···) : concatinated @@ -18,6 +21,9 @@ if not modules then modules = { } end modules ['l-unicode'] = { -- todo: utf.sub replacement (used in syst-aux) -- we put these in the utf namespace: +-- used : byte char gmatch len lower sub upper +-- not used : dump find format gfind gsub match rep reverse + utf = utf or (unicode and unicode.utf8) or { } utf.characters = utf.characters or string.utfcharacters @@ -29,6 +35,9 @@ utf.values = utf.values or string.utfvalues -- string.characterpairs -- string.bytes -- string.bytepairs +-- string.utflength +-- string.utfvalues +-- string.utfcharacters local type = type local char, byte, format, sub, gmatch = string.char, string.byte, string.format, string.sub, string.gmatch @@ -64,53 +73,103 @@ end if not utf.char then - local floor, char = math.floor, string.char - - function utf.char(n) - if n < 0x80 then - -- 0aaaaaaa : 0x80 - return char(n) - elseif n < 0x800 then - -- 110bbbaa : 0xC0 : n >> 6 - -- 10aaaaaa : 0x80 : n & 0x3F - return char( - 0xC0 + floor(n/0x40), - 0x80 + (n % 0x40) - ) - elseif n < 0x10000 then - -- 1110bbbb : 0xE0 : n >> 12 - -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F - -- 10aaaaaa : 0x80 : n & 0x3F - return char( - 0xE0 + floor(n/0x1000), - 0x80 + (floor(n/0x40) % 0x40), - 0x80 + (n % 0x40) - ) - elseif n < 0x200000 then - -- 11110ccc : 0xF0 : n >> 18 - -- 10ccbbbb : 0x80 : (n >> 12) & 0x3F - -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F - -- 10aaaaaa : 0x80 : n & 0x3F - -- dddd : ccccc - 1 - return char( - 0xF0 + floor(n/0x40000), - 0x80 + (floor(n/0x1000) % 0x40), - 0x80 + (floor(n/0x40) % 0x40), - 0x80 + (n % 0x40) - ) + utf.char = string.utfcharacter or (utf8 and utf8.char) + + if not utf.char then + + -- no multiples + + local char = string.char + + if bit32 then + + local rshift = bit32.rshift + + function utf.char(n) + if n < 0x80 then + -- 0aaaaaaa : 0x80 + return char(n) + elseif n < 0x800 then + -- 110bbbaa : 0xC0 : n >> 6 + -- 10aaaaaa : 0x80 : n & 0x3F + return char( + 0xC0 + rshift(n,6), + 0x80 + (n % 0x40) + ) + elseif n < 0x10000 then + -- 1110bbbb : 0xE0 : n >> 12 + -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F + -- 10aaaaaa : 0x80 : n & 0x3F + return char( + 0xE0 + rshift(n,12), + 0x80 + (rshift(n,6) % 0x40), + 0x80 + (n % 0x40) + ) + elseif n < 0x200000 then + -- 11110ccc : 0xF0 : n >> 18 + -- 10ccbbbb : 0x80 : (n >> 12) & 0x3F + -- 10bbbbaa : 0x80 : (n >> 6) & 0x3F + -- 10aaaaaa : 0x80 : n & 0x3F + -- dddd : ccccc - 1 + return char( + 0xF0 + rshift(n,18), + 0x80 + (rshift(n,12) % 0x40), + 0x80 + (rshift(n,6) % 0x40), + 0x80 + (n % 0x40) + ) + else + return "" + end + end + else - return "" + + local floor = math.floor + + function utf.char(n) + if n < 0x80 then + return char(n) + elseif n < 0x800 then + return char( + 0xC0 + floor(n/0x40), + 0x80 + (n % 0x40) + ) + elseif n < 0x10000 then + return char( + 0xE0 + floor(n/0x1000), + 0x80 + (floor(n/0x40) % 0x40), + 0x80 + (n % 0x40) + ) + elseif n < 0x200000 then + return char( + 0xF0 + floor(n/0x40000), + 0x80 + (floor(n/0x1000) % 0x40), + 0x80 + (floor(n/0x40) % 0x40), + 0x80 + (n % 0x40) + ) + else + return "" + end + end + end + end end if not utf.byte then - local utf8byte = patterns.utf8byte + utf.byte = string.utfvalue or (utf8 and utf8.codepoint) + + if not utf.byte then + + local utf8byte = patterns.utf8byte + + function utf.byte(c) + return lpegmatch(utf8byte,c) + end - function utf.byte(c) - return lpegmatch(utf8byte,c) end end @@ -171,43 +230,6 @@ local one = P(1) local two = C(1) * C(1) local four = C(R(utfchar(0xD8),utfchar(0xFF))) * C(1) * C(1) * C(1) --- actually one of them is already utf ... sort of useless this one - --- function utf.char(n) --- if n < 0x80 then --- return char(n) --- elseif n < 0x800 then --- return char( --- 0xC0 + floor(n/0x40), --- 0x80 + (n % 0x40) --- ) --- elseif n < 0x10000 then --- return char( --- 0xE0 + floor(n/0x1000), --- 0x80 + (floor(n/0x40) % 0x40), --- 0x80 + (n % 0x40) --- ) --- elseif n < 0x40000 then --- return char( --- 0xF0 + floor(n/0x40000), --- 0x80 + floor(n/0x1000), --- 0x80 + (floor(n/0x40) % 0x40), --- 0x80 + (n % 0x40) --- ) --- else --- -- return char( --- -- 0xF1 + floor(n/0x1000000), --- -- 0x80 + floor(n/0x40000), --- -- 0x80 + floor(n/0x1000), --- -- 0x80 + (floor(n/0x40) % 0x40), --- -- 0x80 + (n % 0x40) --- -- ) --- return "?" --- end --- end --- --- merge into: - local pattern = P("\254\255") * Cs( ( four / function(a,b,c,d) local ab = 0xFF * byte(a) + byte(b) @@ -253,83 +275,89 @@ end if not utf.len then - -- -- alternative 1: 0.77 - -- - -- local utfcharcounter = utfbom^-1 * Cs((p_utf8char/'!')^0) - -- - -- function utf.len(str) - -- return #lpegmatch(utfcharcounter,str or "") - -- end - -- - -- -- alternative 2: 1.70 - -- - -- local n = 0 - -- - -- local utfcharcounter = utfbom^-1 * (p_utf8char/function() n = n + 1 end)^0 -- slow - -- - -- function utf.length(str) - -- n = 0 - -- lpegmatch(utfcharcounter,str or "") - -- return n - -- end - -- - -- -- alternative 3: 0.24 (native unicode.utf8.len: 0.047) - - -- local n = 0 - -- - -- -- local utfcharcounter = lpeg.patterns.utfbom^-1 * P ( ( Cp() * ( - -- -- patterns.utf8one ^1 * Cc(1) - -- -- + patterns.utf8two ^1 * Cc(2) - -- -- + patterns.utf8three^1 * Cc(3) - -- -- + patterns.utf8four ^1 * Cc(4) ) * Cp() / function(f,d,t) n = n + (t - f)/d end - -- -- )^0 ) -- just as many captures as below - -- - -- -- local utfcharcounter = lpeg.patterns.utfbom^-1 * P ( ( - -- -- (Cmt(patterns.utf8one ^1,function(_,_,s) n = n + #s return true end)) - -- -- + (Cmt(patterns.utf8two ^1,function(_,_,s) n = n + #s/2 return true end)) - -- -- + (Cmt(patterns.utf8three^1,function(_,_,s) n = n + #s/3 return true end)) - -- -- + (Cmt(patterns.utf8four ^1,function(_,_,s) n = n + #s/4 return true end)) - -- -- )^0 ) -- not interesting as it creates strings but sometimes faster - -- - -- -- The best so far: - -- - -- local utfcharcounter = utfbom^-1 * P ( ( - -- Cp() * (patterns.utf8one )^1 * Cp() / function(f,t) n = n + t - f end - -- + Cp() * (patterns.utf8two )^1 * Cp() / function(f,t) n = n + (t - f)/2 end - -- + Cp() * (patterns.utf8three)^1 * Cp() / function(f,t) n = n + (t - f)/3 end - -- + Cp() * (patterns.utf8four )^1 * Cp() / function(f,t) n = n + (t - f)/4 end - -- )^0 ) - - -- function utf.len(str) - -- n = 0 - -- lpegmatch(utfcharcounter,str or "") - -- return n - -- end - - local n, f = 0, 1 + utf.len = string.utflength or (utf8 and utf8.len) + + if not utf.len then + + -- -- alternative 1: 0.77 + -- + -- local utfcharcounter = utfbom^-1 * Cs((p_utf8char/'!')^0) + -- + -- function utf.len(str) + -- return #lpegmatch(utfcharcounter,str or "") + -- end + -- + -- -- alternative 2: 1.70 + -- + -- local n = 0 + -- + -- local utfcharcounter = utfbom^-1 * (p_utf8char/function() n = n + 1 end)^0 -- slow + -- + -- function utf.length(str) + -- n = 0 + -- lpegmatch(utfcharcounter,str or "") + -- return n + -- end + -- + -- -- alternative 3: 0.24 (native unicode.utf8.len: 0.047) + + -- local n = 0 + -- + -- -- local utfcharcounter = lpeg.patterns.utfbom^-1 * P ( ( Cp() * ( + -- -- patterns.utf8one ^1 * Cc(1) + -- -- + patterns.utf8two ^1 * Cc(2) + -- -- + patterns.utf8three^1 * Cc(3) + -- -- + patterns.utf8four ^1 * Cc(4) ) * Cp() / function(f,d,t) n = n + (t - f)/d end + -- -- )^0 ) -- just as many captures as below + -- + -- -- local utfcharcounter = lpeg.patterns.utfbom^-1 * P ( ( + -- -- (Cmt(patterns.utf8one ^1,function(_,_,s) n = n + #s return true end)) + -- -- + (Cmt(patterns.utf8two ^1,function(_,_,s) n = n + #s/2 return true end)) + -- -- + (Cmt(patterns.utf8three^1,function(_,_,s) n = n + #s/3 return true end)) + -- -- + (Cmt(patterns.utf8four ^1,function(_,_,s) n = n + #s/4 return true end)) + -- -- )^0 ) -- not interesting as it creates strings but sometimes faster + -- + -- -- The best so far: + -- + -- local utfcharcounter = utfbom^-1 * P ( ( + -- Cp() * (patterns.utf8one )^1 * Cp() / function(f,t) n = n + t - f end + -- + Cp() * (patterns.utf8two )^1 * Cp() / function(f,t) n = n + (t - f)/2 end + -- + Cp() * (patterns.utf8three)^1 * Cp() / function(f,t) n = n + (t - f)/3 end + -- + Cp() * (patterns.utf8four )^1 * Cp() / function(f,t) n = n + (t - f)/4 end + -- )^0 ) + + -- function utf.len(str) + -- n = 0 + -- lpegmatch(utfcharcounter,str or "") + -- return n + -- end + + local n, f = 0, 1 + + local utfcharcounter = patterns.utfbom^-1 * Cmt ( + Cc(1) * patterns.utf8one ^1 + + Cc(2) * patterns.utf8two ^1 + + Cc(3) * patterns.utf8three^1 + + Cc(4) * patterns.utf8four ^1, + function(_,t,d) -- due to Cc no string captures, so faster + n = n + (t - f)/d + f = t + return true + end + )^0 - local utfcharcounter = patterns.utfbom^-1 * Cmt ( - Cc(1) * patterns.utf8one ^1 - + Cc(2) * patterns.utf8two ^1 - + Cc(3) * patterns.utf8three^1 - + Cc(4) * patterns.utf8four ^1, - function(_,t,d) -- due to Cc no string captures, so faster - n = n + (t - f)/d - f = t - return true + function utf.len(str) + n, f = 0, 1 + lpegmatch(utfcharcounter,str or "") + return n end - )^0 - function utf.len(str) - n, f = 0, 1 - lpegmatch(utfcharcounter,str or "") - return n - end + -- -- these are quite a bit slower: - -- -- these are quite a bit slower: + -- utfcharcounter = utfbom^-1 * (Cmt(P(1) * R("\128\191")^0, function() n = n + 1 return true end))^0 -- 50+ times slower + -- utfcharcounter = utfbom^-1 * (Cmt(P(1), function() n = n + 1 return true end) * R("\128\191")^0)^0 -- 50- times slower - -- utfcharcounter = utfbom^-1 * (Cmt(P(1) * R("\128\191")^0, function() n = n + 1 return true end))^0 -- 50+ times slower - -- utfcharcounter = utfbom^-1 * (Cmt(P(1), function() n = n + 1 return true end) * R("\128\191")^0)^0 -- 50- times slower + end end @@ -1041,21 +1069,23 @@ end local function little(b) if b < 0x10000 then - return char(b%256,b/256) + return char(b%256,rshift(b,8)) else b = b - 0x10000 - local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00 - return char(b1%256,b1/256,b2%256,b2/256) + local b1 = rshift(b,10) + 0xD800 + local b2 = b%1024 + 0xDC00 + return char(b1%256,rshift(b1,8),b2%256,rshift(b2,8)) end end local function big(b) if b < 0x10000 then - return char(b/256,b%256) + return char(rshift(b,8),b%256) else b = b - 0x10000 - local b1, b2 = b/1024 + 0xD800, b%1024 + 0xDC00 - return char(b1/256,b1%256,b2/256,b2%256) + local b1 = rshift(b,10) + 0xD800 + local b2 = b%1024 + 0xDC00 + return char(rshift(b1,8),b1%256,rshift(b2,8),b2%256) end end @@ -1270,3 +1300,62 @@ function utf.chrlen(u) -- u is number (u < 0xFC and 5) or (u < 0xFE and 6) or 0 end + +-- hashing saves a little but not that much in practice +-- +-- local utf32 = table.setmetatableindex(function(t,k) local v = toutf32(k) t[k] = v return v end) + +if bit32 then + + local extract = bit32.extract + local char = string.char + + function unicode.toutf32string(n) + if n <= 0xFF then + return + char(n) .. + "\000\000\000" + elseif n <= 0xFFFF then + return + char(extract(n, 0,8)) .. + char(extract(n, 8,8)) .. + "\000\000" + elseif n <= 0xFFFFFF then + return + char(extract(n, 0,8)) .. + char(extract(n, 8,8)) .. + char(extract(n,16,8)) .. + "\000" + else + return + char(extract(n, 0,8)) .. + char(extract(n, 8,8)) .. + char(extract(n,16,8)) .. + char(extract(n,24,8)) + end + end + +end + +-- goodie: + +local len = utf.len +local rep = rep + +function string.utfpadd(s,n) + if n and n ~= 0 then + local l = len(s) + if n > 0 then + local d = n - l + if d > 0 then + return rep(c or " ",d) .. s + end + else + local d = - n - l + if d > 0 then + return s .. rep(c or " ",d) + end + end + end + return s +end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-url.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-url.lua index b189ec5bba8..6f834d778d1 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-url.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-url.lua @@ -8,9 +8,10 @@ if not modules then modules = { } end modules ['l-url'] = { local char, format, byte = string.char, string.format, string.byte local concat = table.concat -local tonumber, type = tonumber, type +local tonumber, type, next = tonumber, type, next local P, C, R, S, Cs, Cc, Ct, Cf, Cg, V = lpeg.P, lpeg.C, lpeg.R, lpeg.S, lpeg.Cs, lpeg.Cc, lpeg.Ct, lpeg.Cf, lpeg.Cg, lpeg.V local lpegmatch, lpegpatterns, replacer = lpeg.match, lpeg.patterns, lpeg.replacer +local sortedhash = table.sortedhash -- from wikipedia: -- @@ -32,22 +33,61 @@ local lpegmatch, lpegpatterns, replacer = lpeg.match, lpeg.patterns, lpeg.replac url = url or { } local url = url -local tochar = function(s) return char(tonumber(s,16)) end +local unescapes = { } +local escapes = { } + +setmetatable(unescapes, { __index = function(t,k) + local v = char(tonumber(k,16)) + t[k] = v + return v +end }) + +setmetatable(escapes, { __index = function(t,k) + local v = format("%%%02X",byte(k)) + t[k] = v + return v +end }) + +-- okay: local colon = P(":") local qmark = P("?") local hash = P("#") local slash = P("/") +local atsign = P("@") local percent = P("%") local endofstring = P(-1) - local hexdigit = R("09","AF","af") local plus = P("+") local nothing = Cc("") -local escapedchar = (percent * C(hexdigit * hexdigit)) / tochar -local escaped = (plus / " ") + escapedchar -- so no loc://foo++.tex - -local noslash = P("/") / "" +local okay = R("09","AZ","az") + S("-_.,:=+*~!'()@&$") + +local escapedchar = (percent * C(hexdigit * hexdigit)) / unescapes +local unescapedchar = P(1) / escapes +local escaped = (plus / " ") + escapedchar -- so no loc://foo++.tex +local noslash = P("/") / "" +local plustospace = P("+")/" " + +local decoder = Cs( ( + plustospace + + escapedchar + + P("\r\n")/"\n" + + P(1) + )^0 ) +local encoder = Cs( ( + R("09","AZ","az")^1 + + S("-./_")^1 + + P(" ")/"+" + + P("\n")/"\r\n" + + unescapedchar + )^0 ) + +lpegpatterns.urldecoder = decoder +lpegpatterns.urlencoder = encoder + +function url.decode (str) return str and lpegmatch(decoder, str) or str end +function url.encode (str) return str and lpegmatch(encoder, str) or str end +function url.unescape(str) return str and lpegmatch(unescaper,str) or str end -- we assume schemes with more than 1 character (in order to avoid problems with windows disks) -- we also assume that when we have a scheme, we also have an authority @@ -73,17 +113,9 @@ local parser = Ct(validurl) lpegpatterns.url = validurl lpegpatterns.urlsplitter = parser -local escapes = { } - -setmetatable(escapes, { __index = function(t,k) - local v = format("%%%02X",byte(k)) - t[k] = v - return v -end }) - -local escaper = Cs((R("09","AZ","az")^1 + P(" ")/"%%20" + S("-./_")^1 + P(1) / escapes)^0) -- space happens most +local escaper = Cs((R("09","AZ","az")^1 + P(" ")/"%%20" + S("-./_:")^1 + P(1) / escapes)^0) -- space happens most local unescaper = Cs((escapedchar + 1)^0) -local getcleaner = Cs((P("+++") / "%%2B" + P("+") / "%%20" + P(1))^1) +local getcleaner = Cs((P("+++")/"%%2B" + P("+")/"%%20" + P(1))^1) lpegpatterns.urlunescaped = escapedchar lpegpatterns.urlescaper = escaper @@ -134,8 +166,8 @@ local backslashswapper = replacer("\\","/") local equal = P("=") local amp = P("&") -local key = Cs(((escapedchar+1)-equal )^0) -local value = Cs(((escapedchar+1)-amp -endofstring)^0) +local key = Cs(((plustospace + escapedchar + 1) - equal )^0) +local value = Cs(((plustospace + escapedchar + 1) - amp - endofstring)^0) local splitquery = Cf ( Ct("") * P { "sequence", sequence = V("pair") * (amp * V("pair"))^0, @@ -144,6 +176,11 @@ local splitquery = Cf ( Ct("") * P { "sequence", -- hasher +local userpart = (1-atsign-colon)^1 +local serverpart = (1-colon)^1 +local splitauthority = ((Cs(userpart) * colon * Cs(userpart) + Cs(userpart) * Cc(nil)) * atsign + Cc(nil) * Cc(nil)) + * Cs(serverpart) * (colon * (serverpart/tonumber) + Cc(nil)) + local function hashed(str) -- not yet ok (/test?test) if not str or str == "" then return { @@ -177,12 +214,21 @@ local function hashed(str) -- not yet ok (/test?test) -- not always a filename but handy anyway local authority = detailed[2] local path = detailed[3] - local filename = nil + local filename -- = nil + local username -- = nil + local password -- = nil + local host -- = nil + local port -- = nil + if authority ~= "" then + -- these can be invalid + username, password, host, port = lpegmatch(splitauthority,authority) + end if authority == "" then filename = path elseif path == "" then filename = "" else + -- this one can be can be invalid filename = authority .. "/" .. path end return { @@ -195,6 +241,11 @@ local function hashed(str) -- not yet ok (/test?test) original = str, noscheme = false, filename = filename, + -- + host = host, + port = port, + -- usename = username, + -- password = password, } end @@ -236,24 +287,38 @@ function url.addscheme(str,scheme) -- no authority end function url.construct(hash) -- dodo: we need to escape ! - local fullurl, f = { }, 0 - local scheme, authority, path, query, fragment = hash.scheme, hash.authority, hash.path, hash.query, hash.fragment + local result, r = { }, 0 + local scheme = hash.scheme + local authority = hash.authority + local path = hash.path + local queries = hash.queries + local fragment = hash.fragment if scheme and scheme ~= "" then - f = f + 1 ; fullurl[f] = scheme .. "://" + r = r + 1 ; result[r] = lpegmatch(escaper,scheme) + r = r + 1 ; result[r] = "://" end if authority and authority ~= "" then - f = f + 1 ; fullurl[f] = authority + r = r + 1 ; result[r] = lpegmatch(escaper,authority) end if path and path ~= "" then - f = f + 1 ; fullurl[f] = "/" .. path + r = r + 1 ; result[r] = "/" + r = r + 1 ; result[r] = lpegmatch(escaper,path) end - if query and query ~= "" then - f = f + 1 ; fullurl[f] = "?".. query + if queries then + local done = false + for k, v in sortedhash(queries) do + r = r + 1 ; result[r] = done and "&" or "?" + r = r + 1 ; result[r] = lpegmatch(escaper,k) -- is this escaped + r = r + 1 ; result[r] = "=" + r = r + 1 ; result[r] = lpegmatch(escaper,v) -- is this escaped + done = true + end end if fragment and fragment ~= "" then - f = f + 1 ; fullurl[f] = "#".. fragment + r = r + 1 ; result[r] = "#" + r = r + 1 ; result[r] = lpegmatch(escaper,fragment) end - return lpegmatch(escaper,concat(fullurl)) + return concat(result) end local pattern = Cs(slash^-1/"" * R("az","AZ") * ((S(":|")/":") + P(":")) * slash * P(1)^0) diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-deb.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-deb.lua index ee732b3b54c..9488a728b61 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-deb.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-deb.lua @@ -12,87 +12,268 @@ if not modules then modules = { } end modules ['util-deb'] = { 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 +local getinfo, sethook = debug.getinfo, debug.sethook +local type, next, tostring, tonumber = type, next, tostring, tonumber +local format, find, sub, gsub = string.format, string.find, string.sub, string.gsub +local insert, remove, sort = table.insert, table.remove, table.sort +local setmetatableindex = table.setmetatableindex utilities = utilities or { } local debugger = utilities.debugger or { } utilities.debugger = debugger -local counters = { } +local report = logs.reporter("debugger") + +local ticks = os.gettimeofday or os.clock +local seconds = function(n) return n or 0 end +local overhead = 0 +local dummycalls = 10*1000 +local nesting = 0 local names = { } -local report = logs.reporter("debugger") +local initialize = false + +if not (FFISUPPORTED and ffi) then + + -- we have no precise timer + +elseif os.type == "windows" then + + initialize = function() + local kernel = ffilib("kernel32","system") -- no checking + if kernel then + local tonumber = ffi.number or tonumber + ffi.cdef[[ + int QueryPerformanceFrequency(int64_t *lpFrequency); + int QueryPerformanceCounter(int64_t *lpPerformanceCount); + ]] + local target = ffi.new("__int64[1]") + ticks = function() + if kernel.QueryPerformanceCounter(target) == 1 then + return tonumber(target[0]) + else + return 0 + end + end + local target = ffi.new("__int64[1]") + seconds = function(ticks) + if kernel.QueryPerformanceFrequency(target) == 1 then + return ticks / tonumber(target[0]) + else + return 0 + end + end + end + initialize = false + end + +elseif os.type == "unix" then + + -- for the values: echo '#include <time.h>' > foo.h; gcc -dM -E foo.h + + initialize = function() + local C = ffi.C + local tonumber = ffi.number or tonumber + ffi.cdef [[ + /* what a mess */ + typedef int clk_id_t; + typedef enum { CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_PROCESS_CPUTIME_ID } clk_id; + typedef struct timespec { long sec; long nsec; } ctx_timespec; + int clock_gettime(clk_id_t timerid, struct timespec *t); + ]] + local target = ffi.new("ctx_timespec[?]",1) + local clock = C.CLOCK_PROCESS_CPUTIME_ID + ticks = function () + C.clock_gettime(clock,target) + return tonumber(target[0].sec*1000000000 + target[0].nsec) + end + seconds = function(ticks) + return ticks/1000000000 + end + initialize = false + end + +end --- one +setmetatableindex(names,function(t,name) + local v = setmetatableindex(function(t,source) + local v = setmetatableindex(function(t,line) + local v = { total = 0, count = 0, nesting = 0 } + t[line] = v + return v + end) + t[source] = v + return v + end) + t[name] = v + return v +end) -local function hook() - local f = getinfo(2) -- "nS" +local function hook(where) + local f = getinfo(2,"nSl") if f then - local n = "unknown" - if f.what == "C" then - n = f.name or '<anonymous>' - if not names[n] then - names[n] = format("%42s",n) + local source = f.short_src + if not source then + return + end + local line = f.linedefined or 0 + local name = f.name + if not name then + local what = f.what + if what == "C" then + name = "<anonymous>" + else + name = f.namewhat or what or "<unknown>" end - else - -- source short_src linedefined what name namewhat nups func - n = f.name or f.namewhat or f.what - if not n or n == "" then - n = "?" + end + local data = names[name][source][line] + if where == "call" then + local nesting = data.nesting + if nesting == 0 then + data.count = data.count + 1 + insert(data,ticks()) + data.nesting = 1 + else + data.nesting = nesting + 1 end - if not names[n] then - names[n] = format("%42s : % 5i : %s",n,f.linedefined or 0,f.short_src or "unknown source") + elseif where == "return" then + local nesting = data.nesting + if nesting == 1 then + local t = remove(data) + if t then + data.total = data.total + ticks() - t + end + data.nesting = 0 + else + data.nesting = nesting - 1 end end - counters[n] = (counters[n] or 0) + 1 end end -function debugger.showstats(printer,threshold) -- hm, something has changed, rubish now - 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 } +function debugger.showstats(printer,threshold) + local printer = printer or report + local calls = 0 + local functions = 0 + local dataset = { } + local length = 0 + local realtime = 0 + local totaltime = 0 + local threshold = threshold or 0 + for name, sources in next, names do + for source, lines in next, sources do + for line, data in next, lines do + local count = data.count + if count > threshold then + if #name > length then + length = #name + end + local total = data.total + local real = total + if real > 0 then + real = total - (count * overhead / dummycalls) + if real < 0 then + real = 0 + end + realtime = realtime + real + end + totaltime = totaltime + total + if line < 0 then + line = 0 + end + -- if name = "a" then + -- -- weird name + -- end + dataset[#dataset+1] = { real, total, count, name, source, line } + end + end + end 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 -- move up - printer(format("%8i %s\n", count, names[name])) - total = total + count + sort(dataset,function(a,b) + if a[1] == b[1] then + if a[2] == b[2] then + if a[3] == b[3] then + if a[4] == b[4] then + if a[5] == b[5] then + return a[6] < b[6] + else + return a[5] < b[5] + end + else + return a[4] < b[4] + end + else + return b[3] < a[3] + end + else + return b[2] < a[2] + end + else + return b[1] < a[1] end - grandtotal = grandtotal + count + end) + if length > 50 then + length = 50 + end + local fmt = string.formatters["%4.9k s %3.3k %% %4.9k s %3.3k %% %8i # %-" .. length .. "s %4i %s"] + for i=1,#dataset do + local data = dataset[i] + local real = data[1] + local total = data[2] + local count = data[3] + local name = data[4] + local source = data[5] + local line = data[6] + calls = calls + count functions = functions + 1 + name = gsub(name,"%s+"," ") + if #name > length then + name = sub(name,1,length) + end + printer(fmt(seconds(total),100*total/totaltime,seconds(real),100*real/realtime,count,name,line,source)) 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)) + printer("") + printer(format("functions : %i", functions)) + printer(format("calls : %i", calls)) + printer(format("overhead : %f", seconds(overhead/1000))) + + -- table.save("luatex-profile.lua",names) end function debugger.savestats(filename,threshold) local f = io.open(filename,'w') if f then - debugger.showstats(function(str) f:write(str) end,threshold) + debugger.showstats(function(str) f:write(str,"\n") end,threshold) f:close() end end function debugger.enable() - debug.sethook(hook,"c") + if nesting == 0 then + running = true + if initialize then + initialize() + end + sethook(hook,"cr") + local function dummy() end + local t = ticks() + for i=1,dummycalls do + dummy() + end + overhead = ticks() - t + end + if nesting > 0 then + nesting = nesting + 1 + end end function debugger.disable() - debug.sethook() - -- counters[debug.getinfo(2,"f").func] = nil + if nesting > 0 then + nesting = nesting - 1 + end + if nesting == 0 then + sethook() + end end -- debugger.enable() diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-dim.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-dim.lua index 2bdb870e7a4..fd9351a28fb 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-dim.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-dim.lua @@ -429,7 +429,7 @@ end stringtodimen = string.todimen -- local variable defined earlier function number.toscaled(d) - return format("%0.5f",d/2^16) + return format("%0.5f",d/0x10000) -- 2^16 end --[[ldx-- diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-fil.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-fil.lua index 0f9731a2656..d568a2e1fd0 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-fil.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-fil.lua @@ -6,10 +6,8 @@ if not modules then modules = { } end modules ['util-fil'] = { license = "see context related readme files" } -local byte = string.byte -local char = string.char -local extract = bit32 and bit32.extract -local floor = math.floor +local byte = string.byte +local char = string.char -- Here are a few helpers (the starting point were old ones I used for parsing -- flac files). In Lua 5.3 we can probably do this better. Some code will move @@ -35,7 +33,10 @@ function files.close(f) end function files.size(f) - return f:seek("end") + local current = f:seek() + local size = f:seek("end") + f:seek("set",current) + return size end files.getsize = files.size @@ -83,6 +84,12 @@ function files.readbytes(f,n) return byte(f:read(n),1,n) end +function files.readbytetable(f,n) + -- return { byte(f:read(n),1,n) } + local s = f:read(n or 1) + return { byte(s,1,#s) } -- best use the real length +end + function files.readchar(f) return f:read(1) end @@ -93,22 +100,23 @@ end function files.readinteger1(f) -- one byte local n = byte(f:read(1)) - if n >= 0x80 then - -- return n - 0xFF - 1 + if n >= 0x80 then return n - 0x100 else return n end end -files.readcardinal1 = files.readbyte -- one byte -files.readcardinal = files.readcardinal1 -files.readinteger = files.readinteger1 +files.readcardinal1 = files.readbyte -- one byte +files.readcardinal = files.readcardinal1 +files.readinteger = files.readinteger1 +files.readsignedbyte = files.readinteger1 function files.readcardinal2(f) local a, b = byte(f:read(2),1,2) return 0x100 * a + b end + function files.readcardinal2le(f) local b, a = byte(f:read(2),1,2) return 0x100 * a + b @@ -116,22 +124,19 @@ end function files.readinteger2(f) local a, b = byte(f:read(2),1,2) - local n = 0x100 * a + b - if n >= 0x8000 then - -- return n - 0xFFFF - 1 - return n - 0x10000 + if a >= 0x80 then + return 0x100 * a + b - 0x10000 else - return n + return 0x100 * a + b end end + function files.readinteger2le(f) local b, a = byte(f:read(2),1,2) - local n = 0x100 * a + b - if n >= 0x8000 then - -- return n - 0xFFFF - 1 - return n - 0x10000 + if a >= 0x80 then + return 0x100 * a + b - 0x10000 else - return n + return 0x100 * a + b end end @@ -139,6 +144,7 @@ function files.readcardinal3(f) local a, b, c = byte(f:read(3),1,3) return 0x10000 * a + 0x100 * b + c end + function files.readcardinal3le(f) local c, b, a = byte(f:read(3),1,3) return 0x10000 * a + 0x100 * b + c @@ -146,22 +152,19 @@ end function files.readinteger3(f) local a, b, c = byte(f:read(3),1,3) - local n = 0x10000 * a + 0x100 * b + c - if n >= 0x80000 then - -- return n - 0xFFFFFF - 1 - return n - 0x1000000 + if a >= 0x80 then + return 0x10000 * a + 0x100 * b + c - 0x1000000 else - return n + return 0x10000 * a + 0x100 * b + c end end + function files.readinteger3le(f) local c, b, a = byte(f:read(3),1,3) - local n = 0x10000 * a + 0x100 * b + c - if n >= 0x80000 then - -- return n - 0xFFFFFF - 1 - return n - 0x1000000 + if a >= 0x80 then + return 0x10000 * a + 0x100 * b + c - 0x1000000 else - return n + return 0x10000 * a + 0x100 * b + c end end @@ -169,6 +172,7 @@ function files.readcardinal4(f) local a, b, c, d = byte(f:read(4),1,4) return 0x1000000 * a + 0x10000 * b + 0x100 * c + d end + function files.readcardinal4le(f) local d, c, b, a = byte(f:read(4),1,4) return 0x1000000 * a + 0x10000 * b + 0x100 * c + d @@ -176,48 +180,75 @@ end function files.readinteger4(f) local a, b, c, d = byte(f:read(4),1,4) - local n = 0x1000000 * a + 0x10000 * b + 0x100 * c + d - if n >= 0x8000000 then - -- return n - 0xFFFFFFFF - 1 - return n - 0x100000000 + if a >= 0x80 then + return 0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000 else - return n + return 0x1000000 * a + 0x10000 * b + 0x100 * c + d end end + function files.readinteger4le(f) local d, c, b, a = byte(f:read(4),1,4) - local n = 0x1000000 * a + 0x10000 * b + 0x100 * c + d - if n >= 0x8000000 then - -- return n - 0xFFFFFFFF - 1 - return n - 0x100000000 + if a >= 0x80 then + return 0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000 else - return n + return 0x1000000 * a + 0x10000 * b + 0x100 * c + d end end +-- function files.readfixed2(f) +-- local a, b = byte(f:read(2),1,2) +-- if a >= 0x80 then +-- return (0x100 * a + b - 0x10000)/256.0 +-- else +-- return (0x100 * a + b)/256.0 +-- end +-- end + +function files.readfixed2(f) + local a, b = byte(f:read(2),1,2) + if a >= 0x80 then + return (a - 0x100) + b/0x100 + else + return (a ) + b/0x100 + end +end + +-- (real) (n>>16) + ((n&0xffff)/65536.0)) + +-- function files.readfixed4(f) +-- local a, b, c, d = byte(f:read(4),1,4) +-- if a >= 0x80 then +-- return (0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000)/65536.0 +-- else +-- return (0x1000000 * a + 0x10000 * b + 0x100 * c + d)/65536.0 +-- end +-- end + function files.readfixed4(f) local a, b, c, d = byte(f:read(4),1,4) - local n = 0x100 * a + b - if n >= 0x8000 then - -- return n - 0xFFFF - 1 + (0x100 * c + d)/0xFFFF - return n - 0x10000 + (0x100 * c + d)/0xFFFF + if a >= 0x80 then + return (0x100 * a + b - 0x10000) + (0x100 * c + d)/0x10000 else - return n + (0x100 * c + d)/0xFFFF + return (0x100 * a + b ) + (0x100 * c + d)/0x10000 end end -if extract then +-- (real) ((n<<16)>>(16+14)) + ((n&0x3fff)/16384.0)) + +if bit32 then + + local extract = bit32.extract + local band = bit32.band function files.read2dot14(f) local a, b = byte(f:read(2),1,2) - local n = 0x100 * a + b - local m = extract(n,0,30) - if n > 0x7FFF then - n = extract(n,30,2) - return m/0x4000 - 4 + if a >= 0x80 then + local n = -(0x100 * a + b) + return - (extract(n,14,2) + (band(n,0x3FFF) / 16384.0)) else - n = extract(n,30,2) - return n + m/0x4000 + local n = 0x100 * a + b + return (extract(n,14,2) + (band(n,0x3FFF) / 16384.0)) end end @@ -233,20 +264,37 @@ end -- writers (kind of slow) -function files.writecardinal2(f,n) - local a = char(n % 256) - n = floor(n/256) - local b = char(n % 256) - f:write(b,a) +if bit32 then + + local rshift = bit32.rshift + + function files.writecardinal2(f,n) + local a = char(n % 256) + n = rshift(n,8) + local b = char(n % 256) + f:write(b,a) + end + +else + + local floor = math.floor + + function files.writecardinal2(f,n) + local a = char(n % 256) + n = floor(n/256) + local b = char(n % 256) + f:write(b,a) + end + end function files.writecardinal4(f,n) local a = char(n % 256) - n = floor(n/256) + n = rshift(n,8) local b = char(n % 256) - n = floor(n/256) + n = rshift(n,8) local c = char(n % 256) - n = floor(n/256) + n = rshift(n,8) local d = char(n % 256) f:write(d,c,b,a) end @@ -259,3 +307,76 @@ function files.writebyte(f,b) f:write(char(b)) end +if fio and fio.readcardinal1 then + + files.readcardinal1 = fio.readcardinal1 + files.readcardinal2 = fio.readcardinal2 + files.readcardinal3 = fio.readcardinal3 + files.readcardinal4 = fio.readcardinal4 + files.readinteger1 = fio.readinteger1 + files.readinteger2 = fio.readinteger2 + files.readinteger3 = fio.readinteger3 + files.readinteger4 = fio.readinteger4 + files.readfixed2 = fio.readfixed2 + files.readfixed4 = fio.readfixed4 + files.read2dot14 = fio.read2dot14 + files.setposition = fio.setposition + files.getposition = fio.getposition + + files.readbyte = files.readcardinal1 + files.readsignedbyte = files.readinteger1 + files.readcardinal = files.readcardinal1 + files.readinteger = files.readinteger1 + + local skipposition = fio.skipposition + files.skipposition = skipposition + + files.readbytes = fio.readbytes + files.readbytetable = fio.readbytetable + + function files.skipshort(f,n) + skipposition(f,2*(n or 1)) + end + + function files.skiplong(f,n) + skipposition(f,4*(n or 1)) + end + +end + +if fio and fio.readcardinaltable then + + files.readcardinaltable = fio.readcardinaltable + files.readintegertable = fio.readintegertable + +else + + local readcardinal1 = files.readcardinal1 + local readcardinal2 = files.readcardinal2 + local readcardinal3 = files.readcardinal3 + local readcardinal4 = files.readcardinal4 + + function files.readcardinaltable(f,n,b) + local t = { } + if b == 1 then for i=1,n do t[i] = readcardinal1(f) end + elseif b == 2 then for i=1,n do t[i] = readcardinal2(f) end + elseif b == 3 then for i=1,n do t[i] = readcardinal3(f) end + elseif b == 4 then for i=1,n do t[i] = readcardinal4(f) end end + return t + end + + local readinteger1 = files.readinteger1 + local readinteger2 = files.readinteger2 + local readinteger3 = files.readinteger3 + local readinteger4 = files.readinteger4 + + function files.readintegertable(f,n,b) + local t = { } + if b == 1 then for i=1,n do t[i] = readinteger1(f) end + elseif b == 2 then for i=1,n do t[i] = readinteger2(f) end + elseif b == 3 then for i=1,n do t[i] = readinteger3(f) end + elseif b == 4 then for i=1,n do t[i] = readinteger4(f) end end + return t + end + +end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-jsn.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-jsn.lua index e835c07d648..e5f83e06c47 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-jsn.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-jsn.lua @@ -21,7 +21,7 @@ local format = string.format local utfchar = utf.char local concat = table.concat -local tonumber, tostring, rawset, type = tonumber, tostring, rawset, type +local tonumber, tostring, rawset, type, next = tonumber, tostring, rawset, type, next local json = utilities.json or { } utilities.json = json @@ -158,4 +158,11 @@ end -- inspect(json.tostring(true)) +function json.load(filename) + local data = io.loaddata(filename) + if data then + return lpegmatch(jsonconverter,data) + end +end + return json diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-lua.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-lua.lua index b3346006c94..1f531ef48e2 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-lua.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-lua.lua @@ -10,16 +10,17 @@ if not modules then modules = { } end modules ['util-lua'] = { -- 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 +local load, loadfile, type, collectgarbage = load, loadfile, type, collectgarbage utilities = utilities or {} utilities.lua = utilities.lua or { } local luautilities = utilities.lua local report_lua = logs.reporter("system","lua") +local report_mem = logs.reporter("system","lua memory") local tracestripping = false -local forcestupidcompile = true -- use internal bytecode compiler +local tracememory = false luautilities.stripcode = true -- support stripping when asked for luautilities.alwaysstripcode = false -- saves 1 meg on 7 meg compressed format file (2012.08.12) luautilities.nofstrippedchunks = 0 @@ -41,7 +42,7 @@ luautilities.suffixes = { -- environment.loadpreprocessedfile can be set to a preprocessor -local function register(name) +local function register(name) -- makes no sense runtime if tracestripping then report_lua("stripped bytecode from %a",name or "unknown") end @@ -71,12 +72,15 @@ end -- quite subtle ... doing this wrong incidentally can give more bytes -function luautilities.loadedluacode(fullname,forcestrip,name) +function luautilities.loadedluacode(fullname,forcestrip,name,macros) -- quite subtle ... doing this wrong incidentally can give more bytes name = name or fullname + if macros then + macros = lua.macros + end local code, message - if environment.loadpreprocessedfile then - code, message = environment.loadpreprocessedfile(fullname) + if macros then + code, message = macros.loaded(fullname,true,false) else code, message = loadfile(fullname) end @@ -103,19 +107,27 @@ function luautilities.loadedluacode(fullname,forcestrip,name) end end -function luautilities.strippedloadstring(code,forcestrip,name) -- not executed +function luautilities.strippedloadstring(code,name,forcestrip) -- not executed 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 -- not yet executes + return load(dump(code,true)), 0 -- not yet executed else return code, 0 end end +function luautilities.loadstring(code,name) -- not executed + 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 + return code, 0 +end + function luautilities.compile(luafile,lucfile,cleanup,strip,fallback) -- defaults: cleanup=false strip=true report_lua("compiling %a into %a",luafile,lucfile) os.remove(lucfile) @@ -176,3 +188,24 @@ setmetatable(finalizers, { function luautilities.registerfinalizer(f) finalizers[#finalizers+1] = f end + +function luautilities.checkmemory(previous,threshold,trace) -- threshold in MB + local current = collectgarbage("count") + if previous then + local checked = (threshold or 64)*1024 + local delta = current - previous + if current - previous > checked then + collectgarbage("collect") + local afterwards = collectgarbage("count") + if trace or tracememory then + report_mem("previous %r MB, current %r MB, delta %r MB, threshold %r MB, afterwards %r MB", + previous/1024,current/1024,delta/1024,threshold,afterwards) + end + return afterwards + elseif trace or tracememory then + report_mem("previous %r MB, current %r MB, delta %r MB, threshold %r MB", + previous/1024,current/1024,delta/1024,threshold) + end + end + return current +end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-prs.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-prs.lua index 650a7ead6ae..891f1096ac9 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-prs.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-prs.lua @@ -31,6 +31,7 @@ utilities.parsers.hashes = hashes local digit = R("09") local space = P(' ') local equal = P("=") +local colon = P(":") local comma = P(",") local lbrace = P("{") local rbrace = P("}") @@ -72,11 +73,13 @@ lpegpatterns.nested = nestedbraces -- no capture lpegpatterns.argument = argument -- argument after e.g. = lpegpatterns.content = content -- rest after e.g = -local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace) + C((nestedbraces + (1-comma))^0) +local value = 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 pattern_d = (space+comma)^0 * (key * (equal+colon) * value + key * C("")) local key = C((1-space-equal-comma)^1) local pattern_b = spaces * comma^0 * spaces * (key * ((spaces * equal * spaces * value) + C(""))) @@ -92,10 +95,12 @@ 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 +local pattern_d_s = (pattern_d/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 +patterns.settings_to_hash_d = pattern_d_s function parsers.make_settings_to_hash_pattern(set,how) if how == "strict" then @@ -126,6 +131,18 @@ function parsers.settings_to_hash(str,existing) end end +function parsers.settings_to_hash_colon_too(str) + if not str or str == "" then + return { } + elseif type(str) == "table" then + return str + else + hash = { } + lpegmatch(pattern_d_s,str) + return hash + end +end + function parsers.settings_to_hash_tolerant(str,existing) if not str or str == "" then return { } @@ -165,7 +182,7 @@ function parsers.settings_to_hash_strict(str,existing) end local separator = comma * space^0 -local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace) +local value = lbrace * C((nobrace + nestedbraces)^0) * rbrace + C((nestedbraces + (1-comma))^0) local pattern = spaces * Ct(value*(separator*value)^0) @@ -210,7 +227,7 @@ function parsers.settings_to_numbers(str) return str end -local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace) +local value = lbrace * C((nobrace + nestedbraces)^0) * rbrace + C((nestedbraces + nestedbrackets + nestedparents + (1-comma))^0) local pattern = spaces * Ct(value*(separator*value)^0) @@ -242,7 +259,7 @@ function parsers.groupedsplitat(symbol,withaction) if not pattern then local symbols = S(symbol) local separator = space^0 * symbols * space^0 - local value = P(lbrace * C((nobrace + nestedbraces)^0) * rbrace) + local value = lbrace * C((nobrace + nestedbraces)^0) * rbrace + C((nestedbraces + (1-(space^0*(symbols+P(-1)))))^0) if withaction then local withvalue = Carg(1) * value / function(f,s) return f(s) end @@ -566,14 +583,16 @@ function parsers.rfc4180splitter(specification) local field = escaped + non_escaped + Cc("") local record = Ct(field * (separator * field)^1) local headerline = record * Cp() - local wholeblob = Ct((newline^(specification.strict and -1 or 1) * record)^0) + local morerecords = (newline^(specification.strict and -1 or 1) * record)^0 + local headeryes = Ct(morerecords) + local headernop = Ct(record * morerecords) return function(data,getheader) if getheader then local header, position = lpegmatch(headerline,data) - local data = lpegmatch(wholeblob,data,position) + local data = lpegmatch(headeryes,data,position) return data, header else - return lpegmatch(wholeblob,data) + return lpegmatch(headernop,data) end end end @@ -604,10 +623,10 @@ 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) ) +local stepper = spacers * ( cardinal * ( spacers * S(":-") * spacers * ( 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) ) +local stepper = spacers * ( cardinal * ( spacers * S(":-") * spacers * ( cardinal + (P("*") + endofstring) * Cc(true) ) + Cc(false) ) * Carg(1) * Carg(2) / ranger * S(", ")^0 )^1 * endofstring -- we're sort of strict (could do without endofstring) function parsers.stepper(str,n,action) diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sta.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sta.lua index 27ab5a624b6..d140cacdcd8 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sta.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sta.lua @@ -154,7 +154,7 @@ function stacker.new(name) local function resolve_step(ti) -- keep track of changes outside function ! -- todo: optimize for n=1 etc local result = nil - local noftop = #top + local noftop = top and #top or 0 if ti > 0 then local current = list[ti] if current then @@ -289,24 +289,24 @@ end -- -- local concat = table.concat -- --- local pdfliteral = nodes.pool.pdfliteral +-- local pdfpageliteral = nodes.pool.pdfpageliteral -- -- function demostacker.start(s,t,first,last) -- local n = whatever[t[last]] -- -- s.report("start: %s",n) --- return pdfliteral(n) +-- return pdfpageliteral(n) -- end -- -- function demostacker.stop(s,t,first,last) -- local n = whatever[false] -- -- s.report("stop: %s",n) --- return pdfliteral(n) +-- return pdfpageliteral(n) -- end -- -- function demostacker.change(s,t1,first1,last1,t2,first2,last2) -- local n = whatever[t2[last2]] -- -- s.report("change: %s",n) --- return pdfliteral(n) +-- return pdfpageliteral(n) -- end -- -- demostacker.mode = "switch" @@ -325,7 +325,7 @@ end -- r[#r+1] = whatever[t[i]] -- end -- -- s.report("start: %s",concat(r," ")) --- return pdfliteral(concat(r," ")) +-- return pdfpageliteral(concat(r," ")) -- end -- -- function demostacker.stop(s,t,first,last) @@ -334,7 +334,7 @@ end -- r[#r+1] = whatever[false] -- end -- -- s.report("stop: %s",concat(r," ")) --- return pdfliteral(concat(r," ")) +-- return pdfpageliteral(concat(r," ")) -- end -- -- function demostacker.change(s,t1,first1,last1,t2,first2,last2) @@ -346,7 +346,7 @@ end -- r[#r+1] = whatever[t2[i]] -- end -- -- s.report("change: %s",concat(r," ")) --- return pdfliteral(concat(r," ")) +-- return pdfpageliteral(concat(r," ")) -- end -- -- demostacker.mode = "stack" diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sto.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sto.lua index d21267d7a8c..a08d25ced18 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sto.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-sto.lua @@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['util-sto'] = { license = "see context related readme files" } -local setmetatable, getmetatable, type = setmetatable, getmetatable, type +local setmetatable, getmetatable, rawset, type = setmetatable, getmetatable, rawset, type utilities = utilities or { } utilities.storage = utilities.storage or { } @@ -158,6 +158,29 @@ function table.setmetatablecall(t,f) return t end +-- the manual is somewhat fuzzy about this but suggests that one can best +-- set all fields before assigning a metatable + +function table.setmetatableindices(t,f,n,c) + if type(t) ~= "table" then + f, t = t, { } + end + local m = getmetatable(t) + local i = f_index[f] or f + if m then + m.__index = i + m.__newindex = n + m.__call = c + else + setmetatable(t,{ + __index = i, + __newindex = n, + __call = c, + }) + end + return t +end + function table.setmetatablekey(t,key,value) local m = getmetatable(t) if not m then @@ -172,3 +195,88 @@ function table.getmetatablekey(t,key,value) local m = getmetatable(t) return m and m[key] end + +function table.makeweak(t) + if not t then + t = { } + end + local m = getmetatable(t) + if m then + m.__mode = "v" + else + setmetatable(t,{ __mode = "v" }) + end + return t +end + +-- Problem: we have no __next (which is ok as it would probably slow down lua) so +-- we cannot loop over the keys. + +-- local parametersets = table.autokeys() +-- +-- parametersets.foo.bar = function(t,k) return "OEPS" end +-- parametersets.foo.foo = "SPEO" +-- parametersets.crap = { a = "a", b = table.autokey { function() return "b" end } } +-- +-- print(parametersets.foo.bar) +-- print(parametersets.foo.foo) +-- print(parametersets.crap.b) +-- print(parametersets.crap.b[1]) + +-- function table.autotables(t) +-- local t = t or { } +-- local m = getmetatable(t) +-- if not m then +-- m = { } +-- setmetatable(t,m) +-- end +-- m.__newindex = function(t,k,p) +-- local v = { } +-- local m = { +-- __index = function(t,k) +-- local v = p[k] +-- if type(v) == "function" then +-- return v(t,k) -- so we can have multiple arguments +-- else +-- return v +-- end +-- end, +-- __newindex = function(t,k,v) +-- p[k] = v +-- end, +-- __len = function(t) +-- return #p +-- end, +-- } +-- setmetatable(v,m) +-- rawset(t,k,v) +-- return v +-- end +-- m.__index = function(t,k) +-- local v = { } +-- t[k] = v -- calls newindex +-- return v +-- end +-- return t +-- end +-- +-- function table.autokeys(p) +-- local t = { } +-- setmetatable(t, { +-- __newindex = function(t,k,v) +-- p[k] = v +-- end, +-- __index = function(t,k) +-- local v = p[k] +-- if type(v) == "function" then +-- return v(t,k) -- so we can have multiple arguments +-- else +-- return v +-- end +-- end, +-- __len = function(t) +-- return #p +-- end, +-- }) +-- return t +-- end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-str.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-str.lua index fb510257a2f..48c265659ef 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-str.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-str.lua @@ -12,17 +12,20 @@ local strings = utilities.strings local format, gsub, rep, sub, find = string.format, string.gsub, string.rep, string.sub, string.find local load, dump = load, string.dump -local tonumber, type, tostring = tonumber, type, tostring +local tonumber, type, tostring, next, setmetatable = tonumber, type, tostring, next, setmetatable +local unpack, concat = table.unpack, table.concat 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 utfchar, utfbyte, utflen = utf.char, utf.byte, utf.len + ----- loadstripped = utilities.lua.loadstripped ----- setmetatableindex = table.setmetatableindex local loadstripped = nil +local oldfashioned = LUAVERSION < 5.2 -if _LUAVERSION < 5.2 then +if oldfashioned then loadstripped = function(str,shortcuts) return load(str) @@ -44,21 +47,60 @@ end if not number then number = { } end -- temp hack for luatex-fonts -local stripper = patterns.stripzeros +local stripzero = patterns.stripzero +local stripzeros = patterns.stripzeros local newline = patterns.newline local endofstring = patterns.endofstring +local anything = patterns.anything local whitespace = patterns.whitespace +local space = patterns.space local spacer = patterns.spacer local spaceortab = patterns.spaceortab +local digit = patterns.digit +local sign = patterns.sign +local period = patterns.period + +-- local function points(n) +-- n = tonumber(n) +-- return (not n or n == 0) and "0pt" or lpegmatch(stripzeros,format("%.5fpt",n/65536)) +-- end + +-- local function basepoints(n) +-- n = tonumber(n) +-- return (not n or n == 0) and "0bp" or lpegmatch(stripzeros,format("%.5fbp", n*(7200/7227)/65536)) +-- end + +local ptf = 1 / 65536 +local bpf = (7200/7227) / 65536 local function points(n) + if n == 0 then + return "0pt" + end n = tonumber(n) - return (not n or n == 0) and "0pt" or lpegmatch(stripper,format("%.5fpt",n/65536)) + if not n or n == 0 then + return "0pt" + end + n = n * ptf + if n % 1 == 0 then + return format("%ipt",n) + end + return lpegmatch(stripzeros,format("%.5fpt",n)) -- plural as we need to keep the pt end local function basepoints(n) + if n == 0 then + return "0pt" + end n = tonumber(n) - return (not n or n == 0) and "0bp" or lpegmatch(stripper,format("%.5fbp", n*(7200/7227)/65536)) + if not n or n == 0 then + return "0pt" + end + n = n * bpf + if n % 1 == 0 then + return format("%ibp",n) + end + return lpegmatch(stripzeros,format("%.5fbp",n)) -- plural as we need to keep the pt end number.points = points @@ -69,7 +111,6 @@ number.basepoints = basepoints local rubish = spaceortab^0 * newline local anyrubish = spaceortab + newline -local anything = patterns.anything local stripped = (spaceortab^1 / "") * newline local leading = rubish^0 / "" local trailing = (anyrubish^1 * endofstring) / "" @@ -137,13 +178,26 @@ local pattern = + newline * Cp() / function(position) extra, start = 0, position end - + patterns.anything + + anything )^1) function strings.tabtospace(str,tab) + -- no real gain in first checking if a \t is there return lpegmatch(pattern,str,1,tab or 7) end +function string.utfpadding(s,n) + if not n or n == 0 then + return "" + end + local l = utflen(s) + if n > 0 then + return nspaces[n-l] + else + return nspaces[-n-l] + end +end + -- local t = { -- "1234567123456712345671234567", -- "\tb\tc", @@ -167,27 +221,31 @@ end -- return str -- end -local space = spacer^0 -local nospace = space/"" -local endofline = nospace * newline +local optionalspace = spacer^0 +local nospace = optionalspace/"" +local endofline = nospace * newline -local stripend = (whitespace^1 * endofstring)/"" +local stripend = (whitespace^1 * endofstring)/"" -local normalline = (nospace * ((1-space*(newline+endofstring))^1) * nospace) +local normalline = (nospace * ((1-optionalspace*(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 stripempty = endofline^1/"" +local normalempty = endofline^1 +local singleempty = endofline * (endofline^0/"") +local doubleempty = endofline * endofline^-1 * (endofline^0/"") +local stripstart = stripempty^0 -local stripstart = stripempty^0 +local intospace = whitespace^1/" " +local noleading = whitespace^1/"" +local notrailing = noleading * endofstring -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 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_prune_intospace = Cs ( noleading * ( notrailing + intospace + 1 )^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 ) -- function striplines(str,prune,collapse,noempty) -- if prune then @@ -213,10 +271,11 @@ local striplinepatterns = { ["prune"] = p_prune_normal, ["prune and collapse"] = p_prune_collapse, -- default ["prune and no empty"] = p_prune_noempty, + ["prune and to space"] = p_prune_intospace, ["retain"] = p_retain_normal, ["retain and collapse"] = p_retain_collapse, ["retain and no empty"] = p_retain_noempty, - ["collapse"] = patterns.collapser, -- how about: stripper fullstripper + ["collapse"] = patterns.collapser, } setmetatable(striplinepatterns,{ __index = function(t,k) return p_prune_collapse end }) @@ -227,6 +286,10 @@ function strings.striplines(str,how) return str and lpegmatch(striplinepatterns[how],str) or str end +function strings.collapse(str) -- maybe also in strings + return str and lpegmatch(p_prune_intospace,str) or str +end + -- also see: string.collapsespaces strings.striplong = strings.striplines -- for old times sake @@ -242,13 +305,14 @@ strings.striplong = strings.striplines -- for old times sake -- " zus wim jet", -- " ", -- }, "\n") - +-- -- local str = table.concat( { -- " aaaa", -- " bb", -- " cccccc", +-- " ", -- }, "\n") - +-- -- for k, v in table.sortedhash(utilities.strings.striplinepatterns) do -- logs.report("stripper","method: %s, result: [[%s]]",k,utilities.strings.striplines(str,k)) -- end @@ -280,41 +344,49 @@ end -- -- More info can be found in cld-mkiv.pdf so here I stick to a simple list. -- --- integer %...i number --- integer %...d number --- unsigned %...u number --- character %...c number --- hexadecimal %...x number --- HEXADECIMAL %...X number --- octal %...o number --- string %...s string number --- float %...f number --- checked float %...F number --- exponential %...e number --- exponential %...E number --- autofloat %...g number --- autofloat %...G number --- utf character %...c number --- force tostring %...S any --- force tostring %Q any --- force tonumber %N number (strip leading zeros) --- signed number %I number --- rounded number %r number --- 0xhexadecimal %...h character number --- 0xHEXADECIMAL %...H character number --- U+hexadecimal %...u character number --- U+HEXADECIMAL %...U character number --- points %p number (scaled points) --- basepoints %b number (scaled points) --- table concat %...t table --- table concat %{.}t table --- serialize %...T sequenced (no nested tables) --- serialize %{.}T sequenced (no nested tables) --- boolean (logic) %l boolean --- BOOLEAN %L boolean --- whitespace %...w --- automatic %...a 'whatever' (string, table, ...) --- automatic %...A "whatever" (string, table, ...) +-- integer %...i number +-- integer %...d number +-- unsigned %...u number -- not used +-- character %...c number +-- hexadecimal %...x number +-- HEXADECIMAL %...X number +-- octal %...o number +-- string %...s string number +-- float %...f number +-- checked float %...F number +-- exponential %...e number +-- exponential %...E number +-- stripped e %...j number +-- stripped E %...J number +-- autofloat %...g number +-- autofloat %...G number +-- utf character %...c number +-- force tostring %...S any +-- force tostring %Q any +-- force tonumber %N number (strip leading zeros) +-- signed number %I number +-- rounded number %r number +-- 0xhexadecimal %...h character number +-- 0xHEXADECIMAL %...H character number +-- U+hexadecimal %...u character number +-- U+HEXADECIMAL %...U character number +-- points %p number (scaled points) +-- basepoints %b number (scaled points) +-- table concat %...t table +-- table concat %{.}t table +-- serialize %...T sequenced (no nested tables) +-- serialize %{.}T sequenced (no nested tables) +-- boolean (logic) %l boolean +-- BOOLEAN %L boolean +-- whitespace %...w number +-- whitespace %...W (fixed) +-- automatic %...a 'whatever' (string, table, ...) +-- automatic %...A "whatever" (string, table, ...) +-- zap %...z skip +-- stripped %...N %...N +-- comma/period real %...m +-- period/comma real %...M +-- formatted float %...k n.m local n = 0 @@ -387,27 +459,45 @@ end -- maybe to util-num -local digit = patterns.digit -local period = patterns.period -local three = digit * digit * digit +local two = digit * digit +local three = two * digit +local prefix = (Carg(1) * three)^1 local splitter = Cs ( - (((1 - (three^1 * period))^1 + C(three)) * (Carg(1) * three)^1 + C((1-period)^1)) - * (P(1)/"" * Carg(2)) * C(2) + (((1 - (three^1 * period))^1 + C(three)) * prefix + C((1-period)^1)) + * (anything/"" * Carg(2)) * C(2) +) + +local splitter3 = Cs ( + three * prefix * endofstring + + two * prefix * endofstring + + digit * prefix * endofstring + + three + + two + + digit ) 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 ".") + if sep1 == false then + if type(n) == "number" then + n = tostring(n) + end + return lpegmatch(splitter3,n,1,sep2 or ".") else - return lpegmatch(splitter,s,1,sep1 or ",",sep2 or ".") + if type(n) == "number" then + n = format("%0.2f",n) + end + if sep1 == true then + return lpegmatch(splitter,n,1,".",",") + elseif sep1 == "." then + return lpegmatch(splitter,n,1,sep1,sep2 or ",") + elseif sep1 == "," then + return lpegmatch(splitter,n,1,sep1,sep2 or ".") + else + return lpegmatch(splitter,n,1,sep1 or ",",sep2 or ".") + end end end @@ -420,17 +510,45 @@ end -- print(number.formatted(1234567)) -- print(number.formatted(12345678)) -- print(number.formatted(12345678,true)) +-- print(number.formatted(1,false)) +-- print(number.formatted(12,false)) +-- print(number.formatted(123,false)) +-- print(number.formatted(1234,false)) +-- print(number.formatted(12345,false)) +-- print(number.formatted(123456,false)) +-- print(number.formatted(1234567,false)) +-- print(number.formatted(12345678,false)) -- print(number.formatted(1234.56,"!","?")) +local p = Cs( + P("-")^0 + * (P("0")^1/"")^0 + * (1-period)^0 + * (period * P("0")^1 * endofstring/"" + period^0) + * P(1-P("0")^1*endofstring)^0 + ) + +function number.compactfloat(n,fmt) + if n == 0 then + return "0" + elseif n == 1 then + return "1" + end + n = lpegmatch(p,format(fmt or "%0.3f",n)) + if n == "." or n == "" or n == "-" then + return "0" + end + return n +end + local zero = P("0")^1 / "" local plus = P("+") / "" local minus = P("-") -local separator = S(".") -local digit = R("09") +local separator = period 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 exponent = (S("eE") * (plus + Cs((minus * zero^0 * endofstring)/"") + minus) * zero^0 * (endofstring * Cc("0") + anything^1)) local pattern_a = Cs(minus^0 * digit^1 * (separator/"" * trailing + separator * (trailing + digit)^0) * exponent) -local pattern_b = Cs((exponent + P(1))^0) +local pattern_b = Cs((exponent + anything)^0) function number.sparseexponent(f,n) if not n then @@ -449,6 +567,31 @@ function number.sparseexponent(f,n) return tostring(n) end +local hf = { } +local hs = { } + +setmetatable(hf, { __index = function(t,k) + local v = "%." .. k .. "f" + t[k] = v + return v +end } ) + +setmetatable(hs, { __index = function(t,k) + local v = "%" .. k .. "s" + t[k] = v + return v +end } ) + +function number.formattedfloat(n,b,a) + local s = format(hf[a],n) + local l = (b or 0) + (a or 0) + 1 + if #s < l then + return format(hs[l],s) + else + return s + end +end + local template = [[ %s %s @@ -457,7 +600,7 @@ return function(%s) return %s end local preamble, environment = "", { } -if _LUAVERSION < 5.2 then +if oldfashioned then preamble = [[ local lpeg=lpeg @@ -473,12 +616,16 @@ local utfchar=utf.char local utfbyte=utf.byte local lpegmatch=lpeg.match local nspaces=string.nspaces +local utfpadding=string.utfpadding local tracedchar=string.tracedchar local autosingle=string.autosingle local autodouble=string.autodouble local sequenced=table.sequenced local formattednumber=number.formatted local sparseexponent=number.sparseexponent +local formattedfloat=number.formattedfloat +local stripzero=lpeg.patterns.stripzero +local stripzeros=lpeg.patterns.stripzeros ]] else @@ -498,12 +645,16 @@ else utfbyte = utf.byte, lpegmatch = lpeg.match, nspaces = string.nspaces, + utfpadding = string.utfpadding, tracedchar = string.tracedchar, autosingle = string.autosingle, autodouble = string.autodouble, sequenced = table.sequenced, formattednumber = number.formatted, sparseexponent = number.sparseexponent, + formattedfloat = number.formattedfloat, + stripzero = lpeg.patterns.stripzero, + stripzeros = lpeg.patterns.stripzeros, } end @@ -520,7 +671,10 @@ setmetatable(arguments, { __index = end }) -local prefix_any = C((S("+- .") + R("09"))^0) +local prefix_any = C((sign + space + period + digit)^0) +local prefix_sub = (C((sign + digit)^0) + Cc(0)) + * period + * (C((sign + digit)^0) + Cc(0)) local prefix_tab = P("{") * C((1-P("}"))^0) * P("}") + C((1-R("az","AZ","09","%%"))^0) -- we've split all cases as then we can optimize them (let's omit the fuzzy u) @@ -545,9 +699,36 @@ local format_S = function(f) -- can be optimized end end +local format_right = function(f) + n = n + 1 + f = tonumber(f) + if not f or f == 0 then + return format("(a%s or '')",n) + elseif f > 0 then + return format("utfpadding(a%s,%i)..a%s",n,f,n) + else + return format("a%s..utfpadding(a%s,%i)",n,n,f) + end +end + +local format_left = function(f) + n = n + 1 + f = tonumber(f) + if not f or f == 0 then + return format("(a%s or '')",n) + end + if f < 0 then + return format("utfpadding(a%s,%i)..a%s",n,-f,n) + else + return format("a%s..utfpadding(a%s,%i)",n,n,-f) + end +end + local format_q = function() n = n + 1 - return format("(a%s and format('%%q',a%s) or '')",n,n) -- goodie: nil check (maybe separate lpeg, not faster) + -- lua 5.3 has a different q than lua 5.2 (which does a tostring on numbers) + -- return format("(a%s ~= nil and format('%%q',a%s) or '')",n,n) + return format("(a%s ~= nil and format('%%q',tostring(a%s)) or '')",n,n) end local format_Q = function() -- can be optimized @@ -594,6 +775,11 @@ local format_F = function(f) -- beware, no cast to number end end +local format_k = function(b,a) -- slow + n = n + 1 + return format("formattedfloat(a%s,%i,%i)",n,b or 0, a or 0) +end + local format_g = function(f) n = n + 1 return format("format('%%%sg',a%s)",f,n) @@ -732,9 +918,43 @@ local format_L = function() return format("(a%s and 'TRUE' or 'FALSE')",n) end -local format_N = function() -- strips leading zeros +local format_n = function() -- strips leading and trailing zeros and removes .0 + n = n + 1 + return format("((a%s %% 1 == 0) and format('%%i',a%s) or tostring(a%s))",n,n,n) +end + +-- local format_N = function() -- strips leading and trailing zeros (also accepts string) +-- n = n + 1 +-- return format("tostring(tonumber(a%s) or a%s)",n,n) +-- end + +-- local format_N = function(f) -- strips leading and trailing zeros +-- n = n + 1 +-- -- stripzero (singular) as we only have a number +-- if not f or f == "" then +-- return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or ((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%.9f',a%s)))",n,n,n,n,n) +-- else +-- return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s)))",n,n,f,n) +-- end +-- end + +-- local format_N = function(f) -- strips leading and trailing zeros +-- n = n + 1 +-- -- stripzero (singular) as we only have a number +-- if not f or f == "" then +-- return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or ((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or lpegmatch(stripzero,format('%%.9f',a%s)))",n,n,n,n,n) +-- else +-- return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s)))",n,n,f,n) +-- end +-- end + +local format_N = function(f) -- strips leading and trailing zeros n = n + 1 - return format("tostring(tonumber(a%s) or a%s)",n,n) + -- stripzero (singular) as we only have a number + if not f or f == "" then + f = ".9" + end -- always a leading number ! + return format("(((a%s %% 1 == 0) and format('%%i',a%s)) or lpegmatch(stripzero,format('%%%sf',a%s)))",n,n,f,n) end local format_a = function(f) @@ -774,7 +994,11 @@ local format_m = function(f) if not f or f == "" then f = "," end - return format([[formattednumber(a%s,%q,".")]],n,f) + if f == "0" then + return format([[formattednumber(a%s,false)]],n) + else + return format([[formattednumber(a%s,%q,".")]],n,f) + end end local format_M = function(f) @@ -782,7 +1006,11 @@ local format_M = function(f) if not f or f == "" then f = "." end - return format([[formattednumber(a%s,%q,",")]],n,f) + if f == "0" then + return format([[formattednumber(a%s,false)]],n) + else + return format([[formattednumber(a%s,%q,",")]],n,f) + end end -- @@ -854,7 +1082,9 @@ local builder = Cs { "start", + V("C") + V("S") -- new + V("Q") -- new + + V("n") -- new + V("N") -- new + + V("k") -- new -- + V("r") + V("h") + V("H") + V("u") + V("U") @@ -870,11 +1100,14 @@ local builder = Cs { "start", + V("m") + V("M") -- new (formatted number) + V("z") -- new -- + + V(">") -- left padding + + V("<") -- right padding + -- -- + V("?") -- ignored, probably messed up % ) + V("*") ) - * (P(-1) + Carg(1)) + * (endofstring + Carg(1)) )^0, -- ["s"] = (prefix_any * P("s")) / format_s, -- %s => regular %s (string) @@ -892,8 +1125,10 @@ local builder = Cs { "start", ["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) + ["Q"] = (prefix_any * P("Q")) / format_Q, -- %Q => %q (tostring) + ["n"] = (prefix_any * P("n")) / format_n, -- %n => tonumber (strips leading and trailing zeros, as well as .0, expects number) + ["N"] = (prefix_any * P("N")) / format_N, -- %N => tonumber (strips leading and trailing zeros, also takes string) + ["k"] = (prefix_sub * P("k")) / format_k, -- %k => like f but with n.m ["c"] = (prefix_any * P("c")) / format_c, -- %c => utf character (extension to regular) ["C"] = (prefix_any * P("C")) / format_C, -- %c => U+.... utf character -- @@ -916,48 +1151,48 @@ local builder = Cs { "start", ["j"] = (prefix_any * P("j")) / format_j, -- %j => %e (float) stripped exponent (irrational) ["J"] = (prefix_any * P("J")) / format_J, -- %J => %E (float) stripped exponent (irrational) -- - ["m"] = (prefix_tab * P("m")) / format_m, -- %m => xxx.xxx.xxx,xx (optional prefix instead of .) - ["M"] = (prefix_tab * P("M")) / format_M, -- %M => xxx,xxx,xxx.xx (optional prefix instead of ,) + ["m"] = (prefix_any * P("m")) / format_m, -- %m => xxx.xxx.xxx,xx (optional prefix instead of .) + ["M"] = (prefix_any * P("M")) / format_M, -- %M => xxx,xxx,xxx.xx (optional prefix instead of ,) -- - ["z"] = (prefix_any * P("z")) / format_z, -- %M => xxx,xxx,xxx.xx (optional prefix instead of ,) + ["z"] = (prefix_any * P("z")) / format_z, -- %z => skip n arguments -- ["a"] = (prefix_any * P("a")) / format_a, -- %a => '...' (forces tostring) ["A"] = (prefix_any * P("A")) / format_A, -- %A => "..." (forces tostring) -- + ["<"] = (prefix_any * P("<")) / format_left, + [">"] = (prefix_any * P(">")) / format_right, + -- ["*"] = Cs(((1-P("%"))^1 + P("%%")/"%%")^1) / format_rest, -- rest (including %%) ["?"] = Cs(((1-P("%"))^1 )^1) / format_rest, -- rest (including %%) -- ["!"] = Carg(2) * prefix_any * P("!") * C((1-P("!"))^1) * P("!") / format_extension, } --- we can be clever and only alias what is needed - --- local direct = Cs ( --- P("%")/"" --- * Cc([[local format = string.format return function(str) return format("%]]) --- * (S("+- .") + R("09"))^0 --- * S("sqidfgGeExXo") --- * Cc([[",str) end]]) --- * P(-1) --- ) - -local direct = Cs ( - P("%") - * (S("+- .") + R("09"))^0 - * S("sqidfgGeExXo") - * P(-1) / [[local format = string.format return function(str) return format("%0",str) end]] -) +-- We can be clever and only alias what is needed: + +local xx = setmetatable({ }, { __index = function(t,k) local v = format("%02x",k) t[k] = v return v end }) +local XX = setmetatable({ }, { __index = function(t,k) local v = format("%02X",k) t[k] = v return v end }) + +local preset = { + ["%02x"] = function(n) return xx[n] end, + ["%02X"] = function(n) return XX[n] end, +} + +local direct = + P("%") * (sign + space + period + digit)^0 * S("sqidfgGeExXo") * endofstring + / [[local format = string.format return function(str) return format("%0",str) end]] local function make(t,str) - local f - local p + local f = preset[str] + if f then + return f + end local p = lpegmatch(direct,str) if p then - -- f = loadstripped(p)() -- print("builder 1 >",p) f = loadstripped(p)() else - n = 0 + n = 0 -- used in patterns -- p = lpegmatch(builder,str,1,"..",t._extensions_) -- after this we know n p = lpegmatch(builder,str,1,t._connector_,t._extensions_) -- after this we know n if n > 0 then @@ -968,6 +1203,7 @@ local function make(t,str) f = function() return str end end end + -- if jit then jit.on(f,true) end t[str] = f return f end @@ -1020,7 +1256,7 @@ strings.formatters = { } -- _connector_ is an experiment -if _LUAVERSION < 5.2 then +if oldfashioned then function strings.formatters.new(noconcat) local t = { _type_ = "formatter", _connector_ = noconcat and "," or "..", _extensions_ = { }, _preamble_ = preamble, _environment_ = { } } @@ -1072,19 +1308,15 @@ strings.formatters.add = add -- registered in the default instance (should we fall back on this one?) -patterns.xmlescape = Cs((P("<")/"<" + P(">")/">" + P("&")/"&" + P('"')/""" + P(1))^0) -patterns.texescape = Cs((C(S("#$%\\{}"))/"\\%1" + P(1))^0) +patterns.xmlescape = Cs((P("<")/"<" + P(">")/">" + P("&")/"&" + P('"')/""" + anything)^0) +patterns.texescape = Cs((C(S("#$%\\{}"))/"\\%1" + anything)^0) patterns.luaescape = Cs(((1-S('"\n'))^1 + P('"')/'\\"' + P('\n')/'\\n"')^0) -- maybe also \0 patterns.luaquoted = Cs(Cc('"') * ((1-S('"\n'))^1 + P('"')/'\\"' + P('\n')/'\\n"')^0 * Cc('"')) -- escaping by lpeg is faster for strings without quotes, slower on a string with quotes, but -- faster again when other q-escapables are found (the ones we don't need to escape) --- 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]]) - -if _LUAVERSION < 5.2 then +if oldfashioned then add(formatters,"xml",[[lpegmatch(xmlescape,%s)]],"local xmlescape = lpeg.patterns.xmlescape") add(formatters,"tex",[[lpegmatch(texescape,%s)]],"local texescape = lpeg.patterns.texescape") @@ -1133,7 +1365,6 @@ end local dquote = patterns.dquote -- P('"') local equote = patterns.escaped + dquote / '\\"' + 1 -local space = patterns.space local cquote = Cc('"') local pattern = @@ -1149,3 +1380,21 @@ local pattern = Cs((newline / (os.newline or "\r") + 1)^0) function string.replacenewlines(str) return lpegmatch(pattern,str) end + +-- + +function strings.newcollector() + local result, r = { }, 0 + return + function(fmt,str,...) -- write + r = r + 1 + result[r] = str == nil and fmt or formatters[fmt](str,...) + end, + function(connector) -- flush + if result then + local str = concat(result,connector) + result, r = { }, 0 + return str + end + end +end diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tab.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tab.lua index 0521a2a4f14..f395f43defc 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tab.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tab.lua @@ -153,15 +153,16 @@ function table.tocsv(t,specification) fields = sortedkeys(t[1]) end local separator = specification.separator or "," + local noffields = #fields if specification.preamble == true then - for f=1,#fields do + for f=1,noffields 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 + for f=1,noffields do local field = ti[fields[f]] if type(field) == "string" then r[f] = lpegmatch(escape,field) @@ -215,30 +216,31 @@ end local nspaces = utilities.strings.newrepeater(" ") local function toxml(t,d,result,step) + local r = #result 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) + r = r + 1 result[r] = formatters["%s<entry n='%s'>"](s,k) toxml(v,d+step,result,step) - result[#result+1] = formatters["%s</entry>"](s,k) + r = r + 1 result[r] = formatters["%s</entry>"](s,k) else - result[#result+1] = formatters["%s<%s>"](s,k) + r = r + 1 result[r] = formatters["%s<%s>"](s,k) toxml(v,d+step,result,step) - result[#result+1] = formatters["%s</%s>"](s,k) + r = r + 1 result[r] = 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) + r = r + 1 result[r] = formatters["%s<entry n='%s'>%!xml!</entry>"](s,k,v,k) else - result[#result+1] = formatters["%s<%s>%!xml!</%s>"](s,k,v,k) + r = r + 1 result[r] = formatters["%s<%s>%!xml!</%s>"](s,k,v,k) end elseif tk == "number" then - result[#result+1] = formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k) + r = r + 1 result[r] = formatters["%s<entry n='%s'>%S</entry>"](s,k,v,k) else - result[#result+1] = formatters["%s<%s>%S</%s>"](s,k,v,k) + r = r + 1 result[r] = formatters["%s<%s>%S</%s>"](s,k,v,k) end end end @@ -478,11 +480,11 @@ end -- inspect(table.drop({ { a=2 }, {a=3} })) -- inspect(table.drop({ { a=2 }, {a=3} },true)) -function table.autokey(t,k) - local v = { } - t[k] = v - return v -end +-- function table.autokey(t,k) -- replaced +-- local v = { } +-- t[k] = v +-- return v +-- end local selfmapper = { __index = function(t,k) t[k] = k return k end } @@ -564,99 +566,72 @@ local original_serialize = table.serialize -- the extensive one, the one we star -- latest lua for the value of #n (with holes) .. anyway for tracing purposes we want -- indices / keys being sorted, so it will never be real fast -local function serialize(root,name,specification) - - if type(specification) == "table" then - return original_serialize(root,name,specification) -- the original one - end - - local t -- = { } - local n = 1 - local unknown = false +local is_simple_table = table.is_simple_table --- local function simple_table(t) --- local ts = #t --- if ts > 0 then --- local n = 0 --- for _, v in next, t do --- n = n + 1 --- if type(v) == "table" then +-- local function is_simple_table(t) +-- local nt = #t +-- if nt > 0 then +-- local n = 0 +-- for _, v in next, t do +-- n = n + 1 +-- if type(v) == "table" then +-- return nil +-- end +-- end +-- -- local haszero = t[0] +-- local haszero = rawget(t,0) -- don't trigger meta +-- if n == nt then +-- local tt = { } +-- for i=1,nt do +-- local v = t[i] +-- local tv = type(v) +-- if tv == "number" then +-- tt[i] = v -- not needed tostring(v) +-- elseif tv == "string" then +-- tt[i] = format("%q",v) -- f_string(v) +-- elseif tv == "boolean" then +-- tt[i] = v and "true" or "false" +-- else -- return nil -- end -- end --- if n == ts then --- local tt = { } --- local nt = 0 --- for i=1,ts 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) -- f_string(v) --- elseif tv == "boolean" then --- tt[nt] = v and "true" or "false" --- else --- return nil --- end +-- return tt +-- elseif haszero and (n == nt + 1) then +-- local tt = { } +-- for i=0,nt do +-- local v = t[i] +-- local tv = type(v) +-- if tv == "number" then +-- tt[i+1] = v -- not needed tostring(v) +-- elseif tv == "string" then +-- tt[i+1] = format("%q",v) -- f_string(v) +-- elseif tv == "boolean" then +-- tt[i+1] = v and "true" or "false" +-- else +-- return nil -- end --- return tt -- end +-- tt[1] = "[0] = " .. tt[1] +-- return tt -- end --- return nil -- end +-- return nil +-- end - local function simple_table(t) - local nt = #t - if nt > 0 then - local n = 0 - for _, v in next, t do - n = n + 1 - if type(v) == "table" then - return nil - end - end - -- local haszero = t[0] - local haszero = rawget(t,0) -- don't trigger meta - if n == nt then - local tt = { } - for i=1,nt do - local v = t[i] - local tv = type(v) - if tv == "number" then - tt[i] = v -- not needed tostring(v) - elseif tv == "string" then - tt[i] = format("%q",v) -- f_string(v) - elseif tv == "boolean" then - tt[i] = v and "true" or "false" - else - return nil - end - end - return tt - elseif haszero and (n == nt + 1) then - local tt = { } - for i=0,nt do - local v = t[i] - local tv = type(v) - if tv == "number" then - tt[i+1] = v -- not needed tostring(v) - elseif tv == "string" then - tt[i+1] = format("%q",v) -- f_string(v) - elseif tv == "boolean" then - tt[i+1] = v and "true" or "false" - else - return nil - end - end - tt[1] = "[0] = " .. tt[1] - return tt - end - end - return nil +-- In order to overcome the luajit (65K constant) limitation I tried a split approach, +-- i.e. outputting the first level tables as locals but that failed with large cjk +-- fonts too so I removed that ... just use luatex instead. + +local function serialize(root,name,specification) + + if type(specification) == "table" then + return original_serialize(root,name,specification) -- the original one end + local t -- = { } + local n = 1 + local unknown = false + local function do_serialize(root,name,depth,level,indexed) if level > 0 then n = n + 1 @@ -706,7 +681,7 @@ local function serialize(root,name,specification) if next(v) == nil then -- tricky as next is unpredictable in a hash n = n + 1 t[n] = f_val_not(depth) else - local st = simple_table(v) + local st = is_simple_table(v) if st then n = n + 1 t[n] = f_val_seq(depth,st) else @@ -750,7 +725,7 @@ local function serialize(root,name,specification) n = n + 1 t[n] = f_key_str_value_not(depth,tostring(k)) end else - local st = simple_table(v) + local st = is_simple_table(v) if not st then do_serialize(v,k,depth,level+1) elseif tk == "number" then @@ -821,7 +796,7 @@ local function serialize(root,name,specification) end -- Let's forget about empty tables. if next(root) ~= nil then - local st = simple_table(root) + local st = is_simple_table(root) if st then return t[1] .. f_fin_seq(st) -- todo: move up and in one go else @@ -844,3 +819,4 @@ if setinspector then end end) end + diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tpl.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tpl.lua index 468dd429c98..5e75a151b92 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tpl.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs-util-tpl.lua @@ -16,7 +16,7 @@ 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 tostring, next = tostring, next 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 diff --git a/Master/texmf-dist/tex/luatex/lualibs/lualibs.lua b/Master/texmf-dist/tex/luatex/lualibs/lualibs.lua index 1181bd1d840..685d4228e3d 100644 --- a/Master/texmf-dist/tex/luatex/lualibs/lualibs.lua +++ b/Master/texmf-dist/tex/luatex/lualibs/lualibs.lua @@ -7,7 +7,7 @@ -- lualibs.dtx (with options: `lualibs') -- This is a generated file. -- --- Copyright (C) 2009--2017 by +-- Copyright (C) 2009--2018 by -- PRAGMA ADE / ConTeXt Development Team -- The LuaLaTeX Dev Team -- @@ -25,8 +25,8 @@ lualibs = lualibs or { } lualibs.module_info = { name = "lualibs", - version = 2.5, - date = "2017-02-01", + version = 2.6, + date = "2018-09-21", description = "ConTeXt Lua standard libraries.", author = "Hans Hagen, PRAGMA-ADE, Hasselt NL & Elie Roux & Philipp Gesang", copyright = "PRAGMA ADE / ConTeXt Development Team", @@ -91,7 +91,7 @@ local loadmodule = loadmodule or function (name, t) if not t then t = "library" end local filepath = find_file(name, "lua") if not filepath or filepath == "" then - warn(stringformat("Could not locate %s “%s”.", t, name)) + warn(stringformat("Could not locate %s ^^e2^^80^^9c%s^^e2^^80^^9d.", t, name)) return false end dofile(filepath) |