diff options
author | Marius <mariausol@gmail.com> | 2010-10-29 13:00:23 +0300 |
---|---|---|
committer | Marius <mariausol@gmail.com> | 2010-10-29 13:00:23 +0300 |
commit | 42c4d16ce1daa37425d12be6c87d6f64a72b5094 (patch) | |
tree | 494b8c10ccef29abe26db9acf08261ce78c16cf6 | |
parent | f56f0054360a9bdfb57de9abcf0d81a2766c22b9 (diff) | |
download | context-42c4d16ce1daa37425d12be6c87d6f64a72b5094.tar.gz |
beta 2010.10.29 11:35
126 files changed, 5532 insertions, 3918 deletions
diff --git a/scripts/context/lua/mtx-chars.lua b/scripts/context/lua/mtx-chars.lua index 67243dcf3..d356bb5bb 100644 --- a/scripts/context/lua/mtx-chars.lua +++ b/scripts/context/lua/mtx-chars.lua @@ -239,13 +239,14 @@ function scripts.chars.makeencoutf() end end end + local template = "\\def\\%-".. length .. "s{\\char\"%05X } %% %s: %s\n" for i=1,#list do local code = list[i] if code > 0x5B and code <= 0xFFFF then local chr = data[code] if chr and chr.contextname then local ch = utfchar(code) - f:write(format("\\def\\%s{\\char\"%05X } %% %s: %s\n", chr.contextname:rpadd(length," "), code, chr.description, ch)) + f:write(format(template, chr.contextname, code, chr.description, ch)) end end end diff --git a/scripts/context/lua/mtx-check.lua b/scripts/context/lua/mtx-check.lua index 0c9a1708d..7704d86ae 100644 --- a/scripts/context/lua/mtx-check.lua +++ b/scripts/context/lua/mtx-check.lua @@ -11,86 +11,82 @@ scripts.checker = scripts.checker or { } local validator = { } -do - - validator.n = 1 - validator.errors = { } - validator.trace = false - validator.direct = false - - validator.printer = print - validator.tracer = print - - local message = function(position, kind) - local ve = validator.errors - ve[#ve+1] = { kind, position, validator.n } - if validator.direct then - validator.printer(string.format("%s error at position %s (line %s)", kind, position, validator.n)) - end +validator.n = 1 +validator.errors = { } +validator.trace = false +validator.direct = false + +validator.printer = print +validator.tracer = print + +local message = function(position, kind) + local ve = validator.errors + ve[#ve+1] = { kind, position, validator.n } + if validator.direct then + validator.printer(string.format("%s error at position %s (line %s)", kind, position, validator.n)) end - local progress = function(position, data, kind) - if validator.trace then - validator.tracer(string.format("%s at position %s: %s", kind, position, data or "")) - end +end +local progress = function(position, data, kind) + if validator.trace then + validator.tracer(string.format("%s at position %s: %s", kind, position, data or "")) end +end - local P, R, S, V, C, CP, CC = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cp, lpeg.Cc - - local i_m, d_m = P("$"), P("$$") - local l_s, r_s = P("["), P("]") - local l_g, r_g = P("{"), P("}") - - local okay = lpeg.P("{[}") + lpeg.P("{]}") - - local esc = P("\\") - local cr = P("\r") - local lf = P("\n") - local crlf = P("\r\n") - local space = S(" \t\f\v") - local newline = crlf + cr + lf - - local line = newline / function() validator.n = validator.n + 1 end - - -- local grammar = P { "tokens", - -- ["tokens"] = (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + V("errors") + 1)^0, - -- ["whatever"] = line + esc * 1 + C(P("%") * (1-line)^0), - -- ["grouped"] = CP() * C(l_g * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_g - r_g))^0 * r_g) * CC("group") / progress, - -- ["setup"] = CP() * C(l_s * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_s - r_s))^0 * r_s) * CC("setup") / progress, - -- ["display"] = CP() * C(d_m * (V("whatever") + V("grouped") + (1 - d_m))^0 * d_m) * CC("display") / progress, - -- ["inline"] = CP() * C(i_m * (V("whatever") + V("grouped") + (1 - i_m))^0 * i_m) * CC("inline") / progress, - -- ["errors"] = (V("gerror") + V("serror") + V("derror") + V("ierror")) * true, - -- ["gerror"] = CP() * (l_g + r_g) * CC("grouping") / message, - -- ["serror"] = CP() * (l_s + r_g) * CC("setup error") / message, - -- ["derror"] = CP() * d_m * CC("display math error") / message, - -- ["ierror"] = CP() * i_m * CC("inline math error") / message, - -- } - - local startluacode = P("\\startluacode") - local stopluacode = P("\\stopluacode") - - local somecode = startluacode * (1-stopluacode)^1 * stopluacode - - local grammar = P { "tokens", - ["tokens"] = (V("ignore") + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + V("errors") + 1)^0, - ["whatever"] = line + esc * 1 + C(P("%") * (1-line)^0), - ["grouped"] = l_g * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_g - r_g))^0 * r_g, - ["setup"] = l_s * (okay + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_s - r_s))^0 * r_s, - ["display"] = d_m * (V("whatever") + V("grouped") + (1 - d_m))^0 * d_m, - ["inline"] = i_m * (V("whatever") + V("grouped") + (1 - i_m))^0 * i_m, - ["errors"] = (V("gerror")+ V("serror") + V("derror") + V("ierror")), - ["gerror"] = CP() * (l_g + r_g) * CC("grouping") / message, - ["serror"] = CP() * (l_s + r_g) * CC("setup error") / message, - ["derror"] = CP() * d_m * CC("display math error") / message, - ["ierror"] = CP() * i_m * CC("inline math error") / message, - ["ignore"] = somecode, - } - - function validator.check(str) - validator.n = 1 - validator.errors = { } - grammar:match(str) - end +local P, R, S, V, C, CP, CC = lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.C, lpeg.Cp, lpeg.Cc + +local i_m, d_m = P("$"), P("$$") +local l_s, r_s = P("["), P("]") +local l_g, r_g = P("{"), P("}") + +local okay = lpeg.P("{[}") + lpeg.P("{]}") + +local esc = P("\\") +local cr = P("\r") +local lf = P("\n") +local crlf = P("\r\n") +local space = S(" \t\f\v") +local newline = crlf + cr + lf + +local line = newline / function() validator.n = validator.n + 1 end + +-- local grammar = P { "tokens", +-- ["tokens"] = (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + V("errors") + 1)^0, +-- ["whatever"] = line + esc * 1 + C(P("%") * (1-line)^0), +-- ["grouped"] = CP() * C(l_g * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_g - r_g))^0 * r_g) * CC("group") / progress, +-- ["setup"] = CP() * C(l_s * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_s - r_s))^0 * r_s) * CC("setup") / progress, +-- ["display"] = CP() * C(d_m * (V("whatever") + V("grouped") + (1 - d_m))^0 * d_m) * CC("display") / progress, +-- ["inline"] = CP() * C(i_m * (V("whatever") + V("grouped") + (1 - i_m))^0 * i_m) * CC("inline") / progress, +-- ["errors"] = (V("gerror") + V("serror") + V("derror") + V("ierror")) * true, +-- ["gerror"] = CP() * (l_g + r_g) * CC("grouping") / message, +-- ["serror"] = CP() * (l_s + r_g) * CC("setup error") / message, +-- ["derror"] = CP() * d_m * CC("display math error") / message, +-- ["ierror"] = CP() * i_m * CC("inline math error") / message, +-- } + +local startluacode = P("\\startluacode") +local stopluacode = P("\\stopluacode") + +local somecode = startluacode * (1-stopluacode)^1 * stopluacode + +local grammar = P { "tokens", + ["tokens"] = (V("ignore") + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + V("errors") + 1)^0, + ["whatever"] = line + esc * 1 + C(P("%") * (1-line)^0), + ["grouped"] = l_g * (V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_g - r_g))^0 * r_g, + ["setup"] = l_s * (okay + V("whatever") + V("grouped") + V("setup") + V("display") + V("inline") + (1 - l_s - r_s))^0 * r_s, + ["display"] = d_m * (V("whatever") + V("grouped") + (1 - d_m))^0 * d_m, + ["inline"] = i_m * (V("whatever") + V("grouped") + (1 - i_m))^0 * i_m, + ["errors"] = (V("gerror")+ V("serror") + V("derror") + V("ierror")), + ["gerror"] = CP() * (l_g + r_g) * CC("grouping") / message, + ["serror"] = CP() * (l_s + r_g) * CC("setup error") / message, + ["derror"] = CP() * d_m * CC("display math error") / message, + ["ierror"] = CP() * i_m * CC("inline math error") / message, + ["ignore"] = somecode, +} +function validator.check(str) + validator.n = 1 + validator.errors = { } + grammar:match(str) end --~ str = [[ @@ -117,7 +113,7 @@ function scripts.checker.check(filename) ["\t"] = " <tab> ", }) data = data:gsub("^ *","") - print(string.format("% 5i %s %s", line,string.rpadd(kind,10," "),data)) + print(string.format("% 5i %-10s %s", line, kind, data)) end else print("no error") diff --git a/scripts/context/lua/mtx-context.lua b/scripts/context/lua/mtx-context.lua index 38cffe63e..6e4eea39f 100644 --- a/scripts/context/lua/mtx-context.lua +++ b/scripts/context/lua/mtx-context.lua @@ -695,8 +695,19 @@ function scripts.context.run(ctxdata,filename) local texexec = resolvers.findfile("texexec.rb") or "" if texexec ~= "" then os.setenv("RUBYOPT","") - local command = string.format("ruby %s %s",texexec,environment.reconstructcommandline(environment.arguments_after)) - os.exec(command) + local options = environment.reconstructcommandline(environment.arguments_after) + options = string.gsub(options,"--purge","") + options = string.gsub(options,"--purgeall","") + local command = string.format("ruby %s %s",texexec,options) + if environment.argument("purge") then + os.execute(command) + scripts.context.purge_job(filename,false,true) + elseif environment.argument("purgeall") then + os.execute(command) + scripts.context.purge_job(filename,true,true) + else + os.exec(command) + end end end else @@ -1110,6 +1121,10 @@ local persistent_runfiles = { "tuo", "tub", "top", "tuc" } +local special_runfiles = { + "-mpgraph*", "-mprun*" +} + local function purge_file(dfile,cfile) if cfile and lfs.isfile(cfile) then if os.remove(dfile) then @@ -1122,31 +1137,38 @@ local function purge_file(dfile,cfile) end end -function scripts.context.purge_job(jobname,all) +local function remove_special_files(pattern) +end + +function scripts.context.purge_job(jobname,all,mkiitoo) if jobname and jobname ~= "" then jobname = file.basename(jobname) local filebase = file.removesuffix(jobname) - local deleted = { } - for i=1,#obsolete_results do - deleted[#deleted+1] = purge_file(filebase.."."..obsolete_results[i],filebase..".pdf") - end - for i=1,#temporary_runfiles do - deleted[#deleted+1] = purge_file(filebase.."."..temporary_runfiles[i]) - end - if all then - for i=1,#persistent_runfiles do - deleted[#deleted+1] = purge_file(filebase.."."..persistent_runfiles[i]) + if mkiitoo then + scripts.context.purge(all,filebase,true) -- leading "./" + else + local deleted = { } + for i=1,#obsolete_results do + deleted[#deleted+1] = purge_file(filebase.."."..obsolete_results[i],filebase..".pdf") + end + for i=1,#temporary_runfiles do + deleted[#deleted+1] = purge_file(filebase.."."..temporary_runfiles[i]) + end + if all then + for i=1,#persistent_runfiles do + deleted[#deleted+1] = purge_file(filebase.."."..persistent_runfiles[i]) + end + end + if #deleted > 0 then + logs.simple("purged files: %s", table.concat(deleted,", ")) end - end - if #deleted > 0 then - logs.simple("purged files: %s", table.concat(deleted,", ")) end end end -function scripts.context.purge(all) +function scripts.context.purge(all,pattern,mkiitoo) local all = all or environment.argument("all") - local pattern = environment.argument("pattern") or "*.*" + local pattern = environment.argument("pattern") or (pattern and (pattern.."*")) or "*.*" local files = dir.glob(pattern) local obsolete = table.tohash(obsolete_results) local temporary = table.tohash(temporary_runfiles) @@ -1159,6 +1181,12 @@ function scripts.context.purge(all) local basename = file.basename(name) if obsolete[suffix] or temporary[suffix] or persistent[suffix] or generic[basename] then deleted[#deleted+1] = purge_file(name) + elseif mkiitoo then + for i=1,#special_runfiles do + if string.find(name,special_runfiles[i]) then + deleted[#deleted+1] = purge_file(name) + end + end end end if #deleted > 0 then diff --git a/scripts/context/lua/mtx-unzip.lua b/scripts/context/lua/mtx-unzip.lua index f990f4210..85a4b9e7b 100644 --- a/scripts/context/lua/mtx-unzip.lua +++ b/scripts/context/lua/mtx-unzip.lua @@ -1,5 +1,15 @@ +if not modules then modules = { } end modules ['mtx-unzip'] = { + version = 1.001, + comment = "companion to mtxrun.lua", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + -- maybe --pattern +local format = string.format + logs.extendbanner("Simple Unzipper 0.10") messages.help = [[ @@ -36,10 +46,13 @@ function scripts.unzipper.list() if #k.filename > n then n = #k.filename end end local files, paths, compressed, uncompressed = 0, 0, 0, 0 + local template_a = "%-"..n.."s" + local template_b = "%-"..n.."s % 9i % 9i" + local template_c = "\n%-"..n.."s % 9i % 9i" for k in zipfile:files() do if k.filename:find("/$") then paths = paths + 1 - print(string.format("%s", k.filename:rpadd(n," "))) + print(format(template_a, k.filename)) else files = files + 1 local cs, us = k.compressed_size, k.uncompressed_size @@ -49,10 +62,10 @@ function scripts.unzipper.list() if us > uncompressed then uncompressed = us end - print(string.format("%s % 9i % 9i", k.filename:rpadd(n," "),cs,us)) + print(format(template_b,k.filename,cs,us)) end - end - print(string.format("\n%s % 9i % 9i", (files .. " files, " .. paths .. " directories"):rpadd(n," "),compressed,uncompressed)) + end -- check following pattern, n is not enough + print(format(template_c,files .. " files, " .. paths .. " directories",compressed,uncompressed)) end end diff --git a/scripts/context/lua/mtxrun.lua b/scripts/context/lua/mtxrun.lua index d091fa405..7af51ba30 100644 --- a/scripts/context/lua/mtxrun.lua +++ b/scripts/context/lua/mtxrun.lua @@ -54,7 +54,7 @@ if not modules then modules = { } end modules ['l-string'] = { local string = string local sub, gsub, find, match, gmatch, format, char, byte, rep, lower = string.sub, string.gsub, string.find, string.match, string.gmatch, string.format, string.char, string.byte, string.rep, string.lower -local lpegmatch = lpeg.match +local lpegmatch, S, C, Ct = lpeg.match, lpeg.S, lpeg.C, lpeg.Ct -- some functions may disappear as they are not used anywhere @@ -62,145 +62,68 @@ if not string.split then -- this will be overloaded by a faster lpeg variant - function string:split(pattern) - if #self > 0 then - local t = { } - for s in gmatch(self..pattern,"(.-)"..pattern) do - t[#t+1] = s + function string.split(str,pattern) + local t = { } + if #str > 0 then + local n = 1 + for s in gmatch(str..pattern,"(.-)"..pattern) do + t[n] = s + n = n + 1 end - return t - else - return { } end + return t end end -string.patterns = { } - -local escapes = { - ["%"] = "%%", - ["."] = "%.", - ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", - ["^"] = "%^", ["$"] = "%$", - ["["] = "%[", ["]"] = "%]", - ["("] = "%(", [")"] = "%)", - ["{"] = "%{", ["}"] = "%}" -} - -string.patterns.escapes = escapes - -function string:esc() -- variant 2 - return (gsub(self,"(.)",escapes)) -end - -function string:unquote() - return (gsub(self,"^([\"\'])(.*)%1$","%2")) +function string.unquoted(str) + return (gsub(str,"^([\"\'])(.*)%1$","%2")) end -function string:quote() -- we could use format("%q") - return format("%q",self) +function string.quoted(str) + return format("%q",str) -- always " end -function string:count(pattern) -- variant 3 +function string.count(str,pattern) -- variant 3 local n = 0 - for _ in gmatch(self,pattern) do + for _ in gmatch(str,pattern) do -- not for utf n = n + 1 end return n end -function string:limit(n,sentinel) - if #self > n then +function string.limit(str,n,sentinel) + if #str > n then sentinel = sentinel or " ..." - return sub(self,1,(n-#sentinel)) .. sentinel + return sub(str,1,(n-#sentinel)) .. sentinel else - return self - end -end - - -do -- roberto's variant: - local space = lpeg.S(" \t\v\n") - local nospace = 1 - space - local stripper = space^0 * lpeg.C((space^0 * nospace^1)^0) - function string.strip(str) - return lpegmatch(stripper,str) or "" - end -end - -function string:is_empty() - return not find(self,"%S") -end - -function string:enhance(pattern,action) - local ok, n = true, 0 - while ok do - ok = false - self = gsub(self,pattern, function(...) - ok, n = true, n + 1 - return action(...) - end) + return str end - return self, n end -if not string.characters then - - local function nextchar(str, index) - index = index + 1 - return (index <= #str) and index or nil, sub(str,index,index) - end - function string:characters() - return nextchar, self, 0 - end - local function nextbyte(str, index) - index = index + 1 - return (index <= #str) and index or nil, byte(sub(str,index,index)) - end - function string:bytes() - return nextbyte, self, 0 - end +local space = S(" \t\v\n") +local nospace = 1 - space +local stripper = space^0 * C((space^0 * nospace^1)^0) -- roberto's code +function string.strip(str) + return lpegmatch(stripper,str) or "" end -function string:rpadd(n,chr) - local m = n-#self - if m > 0 then - return self .. rep(chr or " ",m) - else - return self - end -end - -function string:lpadd(n,chr) - local m = n-#self - if m > 0 then - return rep(chr or " ",m) .. self - else - return self - end +function string.is_empty(str) + return not find(str,"%S") end -string.padd = string.rpadd - local patterns_escapes = { - ["-"] = "%-", - ["."] = "%.", - ["+"] = "%+", - ["*"] = "%*", ["%"] = "%%", - ["("] = "%)", - [")"] = "%)", - ["["] = "%[", - ["]"] = "%]", + ["."] = "%.", + ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", + ["["] = "%[", ["]"] = "%]", + ["("] = "%)", [")"] = "%)", + -- ["{"] = "%{", ["}"] = "%}" + -- ["^"] = "%^", ["$"] = "%$", } -function string:escapedpattern() - return (gsub(self,".",patterns_escapes)) -end - local simple_escapes = { ["-"] = "%-", ["."] = "%.", @@ -208,24 +131,32 @@ local simple_escapes = { ["*"] = ".*", } -function string:partialescapedpattern() - return (gsub(self,".",simple_escapes)) +function string.escapedpattern(str,simple) + if simple then + return (gsub(str,".",simple_escapes)) + else + return (gsub(str,".",patterns_escapes)) + end end -function string:tohash() - local t = { } - for s in gmatch(self,"([^, ]+)") do -- lpeg - t[s] = true +function string.topattern(str,lowercase,strict) + if str == "" then + return ".*" + else + str = gsub(str,".",simple_escapes) + if lowercase then + str = lower(str) + end + if strict then + return "^" .. str .. "$" + else + return str + end end - return t end -local pattern = lpeg.Ct(lpeg.C(1)^0) - -function string:totable() - return lpegmatch(pattern,self) -end +-- The following functions might end up in another namespace. function string.tabtospace(str,tab) -- we don't handle embedded newlines @@ -246,30 +177,17 @@ function string.tabtospace(str,tab) return str end -function string:compactlong() -- strips newlines and leading spaces - self = gsub(self,"[\n\r]+ *","") - self = gsub(self,"^ *","") - return self -end -function string:striplong() -- strips newlines and leading spaces - self = gsub(self,"^%s*","") - self = gsub(self,"[\n\r]+ *","\n") - return self +function string.striplong(str) -- strips all leading spaces + str = gsub(str,"^%s*","") + str = gsub(str,"[\n\r]+ *","\n") + return str end -function string:topattern(lowercase,strict) - if lowercase then - self = lower(self) - end - self = gsub(self,".",simple_escapes) - if self == "" then - self = ".*" - elseif strict then - self = "^" .. self .. "$" - end - return self -end +-- obsolete names: + +string.quote = string.quoted +string.unquote = string.unquoted end -- of closure @@ -292,15 +210,29 @@ local patterns = lpeg.patterns local P, R, S, Ct, C, Cs, Cc, V = lpeg.P, lpeg.R, lpeg.S, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.V local match = lpeg.match +local utfcharacters = string.utfcharacters +local utfgmatch = unicode and unicode.utf8.gmatch + local digit, sign = R('09'), S('+-') local cr, lf, crlf = P("\r"), P("\n"), P("\r\n") -local utf8byte = R("\128\191") +local utf8next = R("\128\191") +local escaped = P("\\") * P(1) +local squote = P("'") +local dquote = P('"') -patterns.utf8byte = utf8byte patterns.utf8one = R("\000\127") -patterns.utf8two = R("\194\223") * utf8byte -patterns.utf8three = R("\224\239") * utf8byte * utf8byte -patterns.utf8four = R("\240\244") * utf8byte * utf8byte * utf8byte +patterns.utf8two = R("\194\223") * utf8next +patterns.utf8three = R("\224\239") * utf8next * utf8next +patterns.utf8four = R("\240\244") * utf8next * utf8next * utf8next +patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') + +local utf8char = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four +local validutf8char = utf8char^0 * P(-1) * Cc(true) + Cc(false) + +patterns.utf8 = utf8char +patterns.utf8char = utf8char +patterns.validutf8 = validutf8char +patterns.validutf8char = validutf8char patterns.digit = digit patterns.sign = sign @@ -327,17 +259,24 @@ patterns.nonspace = 1 - patterns.space patterns.nonspacer = 1 - patterns.spacer patterns.whitespace = patterns.eol + patterns.spacer patterns.nonwhitespace = 1 - patterns.whitespace -patterns.utf8 = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four -patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') -patterns.validutf8 = patterns.utf8^0 * P(-1) * Cc(true) + Cc(false) patterns.comma = P(",") patterns.commaspacer = P(",") * patterns.spacer^0 patterns.period = P(".") - -patterns.undouble = P('"')/"" * (1-P('"'))^0 * P('"')/"" -patterns.unsingle = P("'")/"" * (1-P("'"))^0 * P("'")/"" +patterns.escaped = escaped +patterns.squote = squote +patterns.dquote = dquote +patterns.undouble = (dquote/"") * ((escaped + (1-dquote))^0) * (dquote/"") +patterns.unsingle = (squote/"") * ((escaped + (1-squote))^0) * (squote/"") +patterns.unquoted = patterns.undouble + patterns.unsingle -- more often undouble patterns.unspacer = ((patterns.spacer^1)/"")^0 +local unquoted = Cs(patterns.unquoted * P(-1)) -- not C + +function string.unquoted(str) + return match(unquoted,str) or str +end + + function lpeg.anywhere(pattern) --slightly adapted from website return P { P(pattern) + 1 * V(1) } -- why so complex? end @@ -353,8 +292,8 @@ local content = (empty + nonempty)^1 local capture = Ct(content^0) -function string:splitlines() - return match(capture,self) +function string.splitlines(str) + return match(capture,str) end patterns.textline = content @@ -366,12 +305,12 @@ 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 other, any = C((1 - separator)^0), P(1) + local any = P(1) splitter = other * (separator * C(any^0) + "") -- ? splitters_s[separator] = splitter else - local other = C((1 - separator)^0) splitter = other * (separator * other)^0 splitters_m[separator] = splitter end @@ -392,16 +331,15 @@ function lpeg.split(separator,str) return match(c,str) end -function string:split(separator) +function string.split(str,separator) local c = cache[separator] if not c then c = Ct(splitat(separator)) cache[separator] = c end - return match(c,self) + return match(c,str) end -lpeg.splitters = cache local cache = { } @@ -409,22 +347,22 @@ function lpeg.checkedsplit(separator,str) local c = cache[separator] if not c then separator = P(separator) - local other = C((1 - separator)^0) + local other = C((1 - separator)^1) c = Ct(separator^0 * other * (separator^1 * other)^0) cache[separator] = c end return match(c,str) end -function string:checkedsplit(separator) +function string.checkedsplit(str,separator) local c = cache[separator] if not c then separator = P(separator) - local other = C((1 - separator)^0) + local other = C((1 - separator)^1) c = Ct(separator^0 * other * (separator^1 * other)^0) cache[separator] = c end - return match(c,self) + return match(c,str) end @@ -435,7 +373,9 @@ local function f2(s) local c1, c2 = f1(s,1,2) return c1 * 64 + c2 local function f3(s) local c1, c2, c3 = f1(s,1,3) return (c1 * 64 + c2) * 64 + c3 - 925824 end local function f4(s) local c1, c2, c3, c4 = f1(s,1,4) return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168 end -patterns.utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 +local utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 + +patterns.utf8byte = utf8byte @@ -469,19 +409,25 @@ function lpeg.keeper(str) end end +-- Just for fun I looked at the used bytecode and +-- p = (p and p + pp) or pp gets one more (testset). + function lpeg.replacer(t) if #t > 0 then local p for i=1,#t do local ti= t[i] local pp = P(ti[1]) / ti[2] - p = (p and p + pp ) or pp + if p then + p = p + pp + else + p = pp + end end return Cs((p + 1)^0) end end - local splitters_f, splitters_s = { }, { } function lpeg.firstofsplit(separator) -- always return value @@ -505,11 +451,170 @@ function lpeg.secondofsplit(separator) -- nil if not split end function lpeg.balancer(left,right) + left, right = P(left), P(right) return P { left * ((1 - left - right) + V(1))^0 * right } end +local nany = utf8char/"" + +function lpeg.counter(pattern) + pattern = Cs((P(pattern)/" " + nany)^0) + return function(str) + return #match(pattern,str) + end +end + +if utfgmatch then + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local n = 0 + for _ in utfgmatch(str,what) do + n = n + 1 + end + return n + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +else + + local cache = { } + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local p = cache[what] + if not p then + p = Cs((P(what)/" " + nany)^0) + cache[p] = p + end + return #match(p,str) + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +end + +local patterns_escapes = { -- also defines in l-string + ["%"] = "%%", + ["."] = "%.", + ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", + ["["] = "%[", ["]"] = "%]", + ["("] = "%)", [")"] = "%)", + -- ["{"] = "%{", ["}"] = "%}" + -- ["^"] = "%^", ["$"] = "%$", +} + +local simple_escapes = { -- also defines in l-string + ["-"] = "%-", + ["."] = "%.", + ["?"] = ".", + ["*"] = ".*", +} + +local p = Cs((S("-.+*%()[]") / patterns_escapes + P(1))^0) +local s = Cs((S("-.+*%()[]") / simple_escapes + P(1))^0) + +function string.escapedpattern(str,simple) + if simple then + return match(s,str) + else + return match(p,str) + end +end + +-- utf extensies + +lpeg.UP = lpeg.P + +if utfcharacters then + + function lpeg.US(str) + local p + for uc in utfcharacters(str) do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + + +elseif utfgmatch then + + function lpeg.US(str) + local p + for uc in utfgmatch(str,".") do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + +else + + function lpeg.US(str) + local p + local f = function(uc) + if p then + p = p + P(uc) + else + p = P(uc) + end + end + match((utf8char/f)^0,str) + return p + end + +end + +local range = Cs(utf8byte) * (Cs(utf8byte) + Cc(false)) + +local utfchar = unicode and unicode.utf8 and unicode.utf8.char + +function lpeg.UR(str,more) + local first, last + if type(str) == "number" then + first = str + last = more or first + else + first, last = match(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 -- a somewhat arbitrary criterium + local p + for i=first,last do + if p then + p = p + P(utfchar(i)) + else + p = P(utfchar(i)) + end + end + return p -- nil when invalid range + else + local f = function(b) + return b >= first and b <= last + end + return utf8byte / f -- nil when invalid range + end +end + + + + end -- of closure do -- create closure to overcome 200 locals limit @@ -577,24 +682,26 @@ end -- extra functions, some might go (when not used) function table.strip(tab) - local lst = { } + local lst, l = { }, 0 for i=1,#tab do local s = gsub(tab[i],"^%s*(.-)%s*$","%1") if s == "" then -- skip this one else - lst[#lst+1] = s + l = l + 1 + lst[l] = s end end return lst end function table.keys(t) - local k = { } + local keys, k = { }, 0 for key, _ in next, t do - k[#k+1] = key + k = k + 1 + keys[k] = key end - return k + return keys end local function compare(a,b) @@ -607,9 +714,10 @@ local function compare(a,b) end local function sortedkeys(tab) - local srt, kind = { }, 0 -- 0=unknown 1=string, 2=number 3=mixed + local srt, kind, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed for key,_ in next, tab do - srt[#srt+1] = key + s = s + 1 + srt[s] = key if kind == 3 then -- no further check else @@ -632,10 +740,11 @@ local function sortedkeys(tab) end local function sortedhashkeys(tab) -- fast one - local srt = { } + local srt, s = { }, 0 for key,_ in next, tab do if key then - srt[#srt+1] = key + s= s + 1 + srt[s] = key end end sort(srt) @@ -649,8 +758,7 @@ local function nothing() end local function sortedhash(t) if t then - local s = sortedhashkeys(t) -- maybe just sortedkeys - local n = 0 + local n, s = 0, sortedkeys(t) -- the robust one local function kv(s) n = n + 1 local k = s[n] @@ -666,20 +774,30 @@ table.sortedhash = sortedhash table.sortedpairs = sortedhash function table.append(t, list) - for _,v in next, list do - insert(t,v) + local n = #t + for i=1,#list do + n = n + 1 + t[n] = list[i] end + return t end function table.prepend(t, list) - for k,v in next, list do - insert(t,k,v) + 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, ...) -- first one is target t = t or { } - local lst = {...} + local lst = { ... } for i=1,#lst do for k, v in next, lst[i] do t[k] = v @@ -689,7 +807,7 @@ function table.merge(t, ...) -- first one is target end function table.merged(...) - local tmp, lst = { }, {...} + local tmp, lst = { }, { ... } for i=1,#lst do for k, v in next, lst[i] do tmp[k] = v @@ -699,22 +817,24 @@ function table.merged(...) end function table.imerge(t, ...) - local lst = {...} + local lst, nt = { ... }, #t for i=1,#lst do local nst = lst[i] for j=1,#nst do - t[#t+1] = nst[j] + nt = nt + 1 + t[nt] = nst[j] end end return t end function table.imerged(...) - local tmp, lst = { }, {...} + local tmp, ntmp, lst = { }, 0, {...} for i=1,#lst do local nst = lst[i] for j=1,#nst do - tmp[#tmp+1] = nst[j] + ntmp = ntmp + 1 + tmp[ntmp] = nst[j] end end return tmp @@ -790,11 +910,14 @@ function table.tohash(t,value) end function table.fromhash(t) - local h = { } + local hsh, h = { }, 0 for k, v in next, t do -- no ipairs here - if v then h[#h+1] = k end + if v then + h = h + 1 + hsh[h] = k + end end - return h + return hsh end table.serialize_functions = true @@ -815,20 +938,23 @@ local function simple_table(t) n = n + 1 end if n == #t then - local tt = { } + local tt, nt = { }, 0 for i=1,#t do local v = t[i] local tv = type(v) if tv == "number" then + nt = nt + 1 if hexify then - tt[#tt+1] = format("0x%04X",v) + tt[nt] = format("0x%04X",v) else - tt[#tt+1] = tostring(v) -- tostring not needed + tt[nt] = tostring(v) -- tostring not needed end elseif tv == "boolean" then - tt[#tt+1] = tostring(v) + nt = nt + 1 + tt[nt] = tostring(v) elseif tv == "string" then - tt[#tt+1] = format("%q",v) + nt = nt + 1 + tt[nt] = format("%q",v) else tt = nil break @@ -1123,10 +1249,11 @@ local function serialize(root,name,_handle,_reduce,_noquotes,_hexify) end -function table.serialize(root,name,reduce,noquotes,hexify) - local t = { } +function table.serialize(root,name,reduce,noquotes,hexify) -- can be faster if flush == false and t as argument + local t, n = { }, 0 local function flush(s) - t[#t+1] = s + n = n + 1 + t[n] = s end serialize(root,name,flush,reduce,noquotes,hexify) return concat(t,"\n") @@ -1152,12 +1279,13 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) if f then local maxtab = table.tofile_maxtab if maxtab > 1 then - local t = { } + local t, n = { }, 0 local function flush(s) - t[#t+1] = s - if #t > maxtab then + n = n + 1 + t[n] = s + if n > maxtab then f:write(concat(t,"\n"),"\n") -- hm, write(sometable) should be nice - t = { } + t, n = { }, 0 -- we could recycle t if needed end end serialize(root,name,flush,reduce,noquotes,hexify) @@ -1173,52 +1301,66 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) end end -local function flatten(t,f,complete) -- is this used? meybe a variant with next, ... - for i=1,#t do - local v = t[i] - if type(v) == "table" then - if complete or type(v[1]) == "table" then - flatten(v,f,complete) +local function flattened(t,f,depth) + if f == nil then + f = { } + depth = 0xFFFF + elseif tonumber(f) then + -- assume then only two arguments are given + 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 + f[k] = v end + end + end + local n = #f + for k=1,#t do + local v = t[k] + if depth > 0 and type(v) == "table" then + flattened(v,f,depth-1) + n = #f else - f[#f+1] = v + n = n + 1 + f[n] = v end end -end - -function table.flatten(t) - local f = { } - flatten(t,f,true) return f end -function table.unnest(t) -- bad name - local f = { } - flatten(t,f,false) - return f -end - -table.flattenonelevel = table.unnest - --- a better one: +table.flattened = flattened -local function flattened(t,f) - if not f then +local function unnest(t,f) -- only used in mk, for old times sake + if not f then -- and only relevant for token lists f = { } end - for k, v in next, t do + for i=1,#t do + local v = t[i] if type(v) == "table" then - flattened(v,f) + if type(v[1]) == "table" then + unnest(v,f) + else + f[#f+1] = v + end else - f[k] = v + f[#f+1] = v end end return f end -table.flattened = flattened +function table.unnest(t) -- bad name + return unnest(t) +end + + local function are_equal(a,b,n,m) -- indexed if a and b and #a == #b then @@ -1244,7 +1386,7 @@ end local function identical(a,b) -- assumes same structure for ka, va in next, a do - local vb = b[k] + local vb = b[ka] if va == vb then -- same elseif type(va) == "table" and type(vb) == "table" then @@ -1258,8 +1400,8 @@ local function identical(a,b) -- assumes same structure return true end -table.are_equal = are_equal table.identical = identical +table.are_equal = are_equal -- maybe also make a combined one @@ -1285,14 +1427,14 @@ function table.contains(t, v) end function table.count(t) - local n, e = 0, next(t) - while e do - n, e = n + 1, next(t,e) + local n = 0 + for k, v in next, t do + n = n + 1 end return n end -function table.swapped(t,s) +function table.swapped(t,s) -- hash local n = { } if s then for k, v in next, s do @@ -1305,52 +1447,34 @@ function table.swapped(t,s) return n end - -function table.clone(t,p) -- t is optional or nil or table - if not p then - t, p = { }, t or { } - elseif not t then - t = { } - end - setmetatable(t, { __index = function(_,key) return p[key] end }) -- why not __index = p ? - return t -end - -function table.hexed(t,seperator) - local tt = { } - for i=1,#t do tt[i] = format("0x%04X",t[i]) end - return concat(tt,seperator or " ") -end - -function table.swaphash(h) -- needs another name - local r = { } - for k,v in next, h do - r[v] = lower(gsub(k," ","")) - end - return r -end - -function table.reverse(t) - local tt = { } - if #t > 0 then - for i=#t,1,-1 do - tt[#tt+1] = t[i] +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 - return tt end function table.sequenced(t,sep,simple) -- hash only - local s = { } + local s, n = { }, 0 for k, v in sortedhash(t) do if simple then if v == true then - s[#s+1] = k + n = n + 1 + s[n] = k elseif v and v~= "" then - s[#s+1] = k .. "=" .. tostring(v) + n = n + 1 + s[n] = k .. "=" .. tostring(v) end else - s[#s+1] = k .. "=" .. tostring(v) + n = n + 1 + s[n] = k .. "=" .. tostring(v) end end return concat(s, sep or " | ") @@ -1360,7 +1484,7 @@ function table.print(...) table.tohandle(print,...) end --- -- -- obsolete but we keep them for a while and will comment them later -- -- -- +-- -- -- obsolete but we keep them for a while and might comment them later -- -- -- -- roughly: copy-loop : unpack : sub == 0.9 : 0.4 : 0.45 (so in critical apps, use unpack) @@ -1375,14 +1499,7 @@ function table.is_empty(t) end function table.has_one_entry(t) - local n = next(t) - return n and not next(t,n) -end - -function table.replace(a,b) - for k,v in next, b do - a[k] = v - end + return t and not next(t,next(t)) end @@ -1627,6 +1744,8 @@ if not modules then modules = { } end modules ['l-number'] = { license = "see context related readme files" } +-- this module will be replaced when we have the bit library + local tostring = tostring local format, floor, insert, match = string.format, math.floor, string.match local concat, insert = table.concat, table.insert @@ -1768,10 +1887,11 @@ function set.tolist(n) if n == 0 or not tabs[n] then return "" else - local t = { } + local t, n = { }, 0 for k, v in next, tabs[n] do if v then - t[#t+1] = k + n = n + 1 + t[n] = k end end return concat(t," ") @@ -1950,7 +2070,7 @@ end -- no need for function anymore as we have more clever code and helpers now -- this metatable trickery might as well disappear -os.resolvers = os.resolvers or { } +os.resolvers = os.resolvers or { } -- will become private local resolvers = os.resolvers @@ -2279,7 +2399,7 @@ end file.isreadable = file.is_readable -- depricated file.iswritable = file.is_writable -- depricated --- todo: lpeg +-- todo: lpeg \\ / .. does not save much local checkedsplit = string.checkedsplit @@ -2295,7 +2415,7 @@ end -- we can hash them weakly -function file.collapse_path(str,anchor) +function file.collapsepath(str,anchor) if anchor and not find(str,"^/") and not find(str,"^%a:") then str = getcurrentdir() .. "/" .. str end @@ -2350,6 +2470,8 @@ function file.collapse_path(str,anchor) end end +file.collapse_path = file.collapsepath + function file.robustname(str) return (gsub(str,"[^%a%d%/%-%.\\]+","-")) @@ -2782,9 +2904,12 @@ local function glob(str,t) end return t elseif isfile(str) then - local t = t or { } - t[#t+1] = str - return t + if t then + t[#t+1] = str + return t + else + return { str } + end else local split = lpegmatch(pattern,str) if split then @@ -2812,6 +2937,7 @@ local function globfiles(path,recurse,func,files) -- func == pattern or function 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 --- skip @@ -2822,12 +2948,9 @@ local function globfiles(path,recurse,func,files) -- func == pattern or function globfiles(path .. "/" .. name,recurse,func,files) end elseif mode == "file" then - if func then - if func(name) then - files[#files+1] = path .. "/" .. name - end - else - files[#files+1] = path .. "/" .. name + if not func or func(name) then + noffiles = noffiles + 1 + files[noffiles] = path .. "/" .. name end end end @@ -3012,8 +3135,12 @@ local type, tonumber = type, tonumber boolean = boolean or { } local boolean = boolean +-- function boolean.tonumber(b) +-- return b and 1 or 0 -- test and test and return or return +-- end + function boolean.tonumber(b) - if b then return 1 else return 0 end + if b then return 1 else return 0 end -- test and return or return end function toboolean(str,tolerant) @@ -3037,6 +3164,8 @@ function toboolean(str,tolerant) end end +string.toboolean = toboolean + function string.is_boolean(str,default) if type(str) == "string" then if str == "true" or str == "yes" or str == "on" or str == "t" then @@ -3048,14 +3177,6 @@ function string.is_boolean(str,default) return default end -function boolean.alwaystrue() - return true -end - -function boolean.falsetrue() - return false -end - end -- of closure @@ -3144,30 +3265,37 @@ function unicode.utftype(f) end function unicode.utf16_to_utf8(str, endian) -- maybe a gsub is faster or an lpeg - local result, tmp, n, m, p = { }, { }, 0, 0, 0 + local result, tmp, n, m, p, r, t = { }, { }, 0, 0, 0, 0, 0 -- we reuse tmp -- lf | cr | crlf / (cr:13, lf:10) local function doit() if n == 10 then if p ~= 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = 0 end elseif n == 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = n else - tmp[#tmp+1] = utfchar(n) + t = t + 1 + tmp[t] = utfchar(n) p = 0 end end for l,r in bytepairs(str) do if r then if endian then - n = l*256 + r + n = 256*l + r else - n = r*256 + l + n = 256*r + l end if m > 0 then n = (m-0xD800)*0x400 + (n-0xDC00) + 0x10000 @@ -3180,29 +3308,36 @@ function unicode.utf16_to_utf8(str, endian) -- maybe a gsub is faster or an lpeg end end end - if #tmp > 0 then - result[#result+1] = concat(tmp) + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) end return result end function unicode.utf32_to_utf8(str, endian) - local result = { } - local tmp, n, m, p = { }, 0, -1, 0 + local result, tmp, n, m, p, r, t = { }, { }, 0, -1, 0, 0, 0 -- lf | cr | crlf / (cr:13, lf:10) local function doit() if n == 10 then if p ~= 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = 0 end elseif n == 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = n else - tmp[#tmp+1] = utfchar(n) + t = t + 1 + tmp[t] = utfchar(n) p = 0 end end @@ -3210,15 +3345,15 @@ function unicode.utf32_to_utf8(str, endian) if a and b then if m < 0 then if endian then - m = a*256*256*256 + b*256*256 + m = 256*256*256*a + 256*256*b else - m = b*256 + a + m = 256*b + a end else if endian then - n = m + a*256 + b + n = m + 256*a + b else - n = m + b*256*256*256 + a*256*256 + n = m + 256*256*256*b + 256*256*a end m = -1 doit() @@ -3228,13 +3363,14 @@ function unicode.utf32_to_utf8(str, endian) end end if #tmp > 0 then - result[#result+1] = concat(tmp) + r = r + 1 + result[r] = concat(tmp,"",1,t) end return result end local function little(c) - local b = byte(c) -- b = c:byte() + local b = byte(c) if b < 0x10000 then return char(b%256,b/256) else @@ -3264,9 +3400,10 @@ function unicode.utf8_to_utf16(str,littleendian) end function unicode.utfcodes(str) - local t = { } - for k,v in utfvalues(str) do - t[#t+1] = format("0x%04X",k) + local t, n = { }, 0 + for u in utfvalues(str) do + n = n + 1 + t[n] = format("0x%04X",u) end return concat(t,separator or " ") end @@ -3324,6 +3461,189 @@ end -- of closure do -- create closure to overcome 200 locals limit +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 = string.format, string.gmatch +local concat, insert, remove = table.concat, table.insert, table.remove +local setmetatable = setmetatable + +function tables.definetable(target) -- defines undefined tables + local composed, t, n = nil, { }, 0 + for name in gmatch(target,"([^%.]+)") do + n = n + 1 + if composed then + composed = composed .. "." .. name + else + composed = name + end + t[n] = format("%s = %s or { }",composed,composed) + end + return concat(t,"\n") +end + +function tables.accesstable(target) + local t = _G + for name in gmatch(target,"([^%.]+)") do + t = t[name] + end + return t +end + +function tables.removevalue(t,value) -- todo: n + if value then + for i=1,#t do + if t[i] == value then + remove(t,i) + -- remove all, so no: return + 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 _empty_table_ = { __index = function(t,k) return "" end } + +function table.setemptymetatable(t) + setmetatable(t,_empty_table_) +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +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 = setmetatable, getmetatable + +utilities = utilities or { } +utilities.storage = utilities.storage or { } +local storage = utilities.storage + +function storage.mark(t) + if not t then + texio.write_nl("fatal error: storage '%s' cannot be marked",t) + os.exit() + 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 + texio.write_nl("fatal error: storage '%s' has not been allocated",t) + os.exit() + end + return t +end + +function setmetatablekey(t,key,value) + local m = getmetatable(t) + if not m then + m = { } + setmetatable(t,m) + end + m[key] = value +end + +function getmetatablekey(t,key,value) + local m = getmetatable(t) + return m and m[key] +end + + +function storage.setinitializer(data,initialize) + local m = getmetatable(data) or { } + m.__index = function(data,k) + m.__index = nil -- so that we can access the entries during initializing + 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 + + +end -- of closure + +do -- create closure to overcome 200 locals limit + if not modules then modules = { } end modules ['util-mrg'] = { version = 1.001, comment = "companion to luat-lib.mkiv", @@ -3483,7 +3803,7 @@ utilities.report = utilities.report or print function utilities.lua.compile(luafile,lucfile,cleanup,strip) -- defaults: cleanup=false strip=true utilities.report("lua: compiling %s into %s",luafile,lucfile) os.remove(lucfile) - local command = "-o " .. string.quote(lucfile) .. " " .. string.quote(luafile) + local command = "-o " .. string.quoted(lucfile) .. " " .. string.quoted(luafile) if strip ~= false then command = "-s " .. command end @@ -3496,6 +3816,12 @@ function utilities.lua.compile(luafile,lucfile,cleanup,strip) -- defaults: clean end + + + + + + end -- of closure do -- create closure to overcome 200 locals limit @@ -3516,7 +3842,7 @@ parsers.patterns = parsers.patterns or { } local P, R, V, C, Ct, Carg = lpeg.P, lpeg.R, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Carg local lpegmatch = lpeg.match local concat, format, gmatch = table.concat, string.format, string.gmatch -local tostring, type, next = tostring, type, next +local tostring, type, next, setmetatable = tostring, type, next, setmetatable local sortedhash = table.sortedhash local escape, left, right = P("\\"), P('{'), P('}') @@ -3635,7 +3961,7 @@ end function parsers.hash_to_string(h,separator,yes,no,strict,omit) if h then - local t, s = { }, table.sortedkeys(h) + local t, tn, s = { }, 0, table.sortedkeys(h) omit = omit and table.tohash(omit) for i=1,#s do local key = s[i] @@ -3644,15 +3970,19 @@ function parsers.hash_to_string(h,separator,yes,no,strict,omit) if type(value) == "boolean" then if yes and no then if value then - t[#t+1] = key .. '=' .. yes + tn = tn + 1 + t[tn] = key .. '=' .. yes elseif not strict then - t[#t+1] = key .. '=' .. no + tn = tn + 1 + t[tn] = key .. '=' .. no end elseif value or not strict then - t[#t+1] = key .. '=' .. tostring(value) + tn = tn + 1 + t[tn] = key .. '=' .. tostring(value) end else - t[#t+1] = key .. '=' .. value + tn = tn + 1 + t[tn] = key .. '=' .. value end end end @@ -3679,10 +4009,11 @@ function parsers.settings_to_set(str,t) -- tohash? -- todo: lpeg -- duplicate an end function parsers.simple_hash_to_string(h, separator) - local t = { } + local t, tn = { }, 0 for k, v in sortedhash(h) do if v then - t[#t+1] = k + tn = tn + 1 + t[tn] = k end end return concat(t,separator or ",") @@ -3700,99 +4031,18 @@ end function parsers.getparameters(self,class,parentclass,settings) local sc = self[class] if not sc then - sc = table.clone(self[parent]) + sc = { } self[class] = sc - end - parsers.settings_to_hash(settings,sc) -end - - -end -- of closure - -do -- create closure to overcome 200 locals limit - -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 = string.format, string.gmatch -local concat, insert, remove = table.concat, table.insert, table.remove -local setmetatable = setmetatable - -function tables.definetable(target) -- defines undefined tables - local composed, t = nil, { } - for name in gmatch(target,"([^%.]+)") do - if composed then - composed = composed .. "." .. name - else - composed = name - end - t[#t+1] = format("%s = %s or { }",composed,composed) - end - return concat(t,"\n") -end - -function tables.accesstable(target) - local t = _G - for name in gmatch(target,"([^%.]+)") do - t = t[name] - end - return t -end - -function table.removevalue(t,value) -- todo: n - if value then - for i=1,#t do - if t[i] == value then - remove(t,i) - -- remove all, so no: return + if parentclass then + local sp = self[parentclass] + if not sp then + sp = { } + self[parentclass] = sp end + setmetatable(sc, { __index = sp }) end end -end - -function table.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 table.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 _empty_table_ = { __index = function(t,k) return "" end } - -function table.setemptymetatable(t) - setmetatable(t,_empty_table_) + parsers.settings_to_hash(settings,sc) end @@ -3831,9 +4081,9 @@ local number = digit^1 * (case_1 + case_2) local stripper = Cs((number + 1)^0) -lpeg.patterns.strip_zeros = stripper +lpeg.patterns.stripzeros = stripper -function formatters.strip_zeros(str) +function formatters.stripzeros(str) return lpegmatch(stripper,str) end @@ -4005,88 +4255,6 @@ end -- of closure do -- create closure to overcome 200 locals limit -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 = setmetatable, getmetatable - -utilities = utilities or { } -utilities.storage = utilities.storage or { } -local storage = utilities.storage - -function storage.mark(t) - if not t then - texio.write_nl("fatal error: storage '%s' cannot be marked",t) - os.exit() - 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 - texio.write_nl("fatal error: storage '%s' has not been allocated",t) - os.exit() - end - return t -end - -function setmetatablekey(t,key,value) - local m = getmetatable(t) - if not m then - m = { } - setmetatable(t,m) - end - m[key] = value -end - -function getmetatablekey(t,key,value) - local m = getmetatable(t) - return m and m[key] -end - - -function storage.setinitializer(data,initialize) - local m = getmetatable(data) or { } - m.__index = function(data,k) - m.__index = nil -- so that we can access the entries during initializing - initialize() - return data[k] - end - setmetatable(data, m) -end - - -end -- of closure - -do -- create closure to overcome 200 locals limit - if not modules then modules = { } end modules ['trac-inf'] = { version = 1.001, comment = "companion to trac-inf.mkiv", @@ -4102,6 +4270,7 @@ if not modules then modules = { } end modules ['trac-inf'] = { local format = string.format local clock = os.gettimeofday or os.clock -- should go in environment +local write_nl = texio.write_nl statistics = statistics or { } local statistics = statistics @@ -4197,7 +4366,7 @@ end function statistics.show(reporter) if statistics.enable then - if not reporter then reporter = function(tag,data,n) texio.write_nl(tag .. " " .. data) end end + if not reporter then reporter = function(tag,data,n) write_nl(tag .. " " .. data) end end -- this code will move local register = statistics.register register("luatex banner", function() @@ -4220,18 +4389,24 @@ function statistics.show(reporter) reporter(s[1],r,n) end end - texio.write_nl("") -- final newline + write_nl("") -- final newline statistics.enable = false end end + +local template, nn = nil, 0 -- we only calcute it once + function statistics.showjobstat(tag,data,n) if type(data) == "table" then for i=1,#data do statistics.showjobstat(tag,data[i],n) end else - texio.write_nl(format("%-15s: %s - %s","mkiv lua stats",tag:rpadd(n," "),data)) + if not template or n > nn then + template, n = format("%%-%ss: %%-%ss - %%s",15,n), nn + end + write_nl(format(template,"mkiv lua stats",tag,data)) end end @@ -4288,7 +4463,7 @@ if not modules then modules = { } end modules ['trac-set'] = { -- might become u local type, next, tostring = type, next, tostring local concat = table.concat -local format, find, lower, gsub, partialescapedpattern = string.format, string.find, string.lower, string.gsub, string.partialescapedpattern +local format, find, lower, gsub, escapedpattern = string.format, string.find, string.lower, string.gsub, string.escapedpattern local is_boolean = string.is_boolean local settings_to_hash = utilities.parsers.settings_to_hash local allocate = utilities.storage.allocate @@ -4368,7 +4543,7 @@ local function set(t,what,newvalue) for name, functions in next, data do if done[name] then -- prevent recursion due to wildcards - elseif find(name,partialescapedpattern(w)) then + elseif find(name,escapedpattern(w,true)) then done[name] = true for i=1,#functions do functions[i](value) @@ -5151,7 +5326,7 @@ local report_resolvers = logs.new("resolvers") local allocate, mark = utilities.storage.allocate, utilities.storage.mark local format, sub, match, gsub, find = string.format, string.sub, string.match, string.gsub, string.find -local unquote, quote = string.unquote, string.quote +local unquoted, quoted = string.unquoted, string.quoted local concat = table.concat -- precautions @@ -5219,7 +5394,7 @@ function environment.initializearguments(arg) if index > 0 then local flag, value = match(argument,"^%-+(.-)=(.-)$") if flag then - arguments[flag] = unquote(value or "") + arguments[flag] = unquoted(value or "") else flag = match(argument,"^%-+(.+)") if flag then @@ -5284,19 +5459,21 @@ end function environment.reconstructcommandline(arg,noquote) arg = arg or environment.originalarguments if noquote and #arg == 1 then + -- we could just do: return unquoted(resolvers.resolve(arg[i])) local a = arg[1] a = resolvers.resolve(a) - a = unquote(a) + a = unquoted(a) return a elseif #arg > 0 then local result = { } for i=1,#arg do + -- we could just do: result[#result+1] = format("%q",unquoted(resolvers.resolve(arg[i]))) local a = arg[i] a = resolvers.resolve(a) - a = unquote(a) + a = unquoted(a) a = gsub(a,'"','\\"') -- tricky if find(a," ") then - result[#result+1] = quote(a) + result[#result+1] = quoted(a) else result[#result+1] = a end @@ -5307,6 +5484,7 @@ function environment.reconstructcommandline(arg,noquote) end end + if arg then -- new, reconstruct quoted snippets (maybe better just remove the " then and add them later) @@ -6767,7 +6945,7 @@ apply_axis['root'] = function(list) rt = ll end end - collected[#collected+1] = rt + collected[l] = rt end return collected end @@ -6777,7 +6955,7 @@ apply_axis['self'] = function(list) end apply_axis['child'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local dt = ll.dt @@ -6786,7 +6964,8 @@ apply_axis['child'] = function(list) for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en @@ -6798,68 +6977,74 @@ apply_axis['child'] = function(list) return collected end -local function collect(list,collected) +local function collect(list,collected,c) local dt = list.dt if dt then local en = 0 for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en - collect(dk,collected) + c = collect(dk,collected,c) end end list.en = en end + return c end apply_axis['descendant'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do - collect(list[l],collected) + c = collect(list[l],collected,c) end return collected end -local function collect(list,collected) +local function collect(list,collected,c) local dt = list.dt if dt then local en = 0 for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en - collect(dk,collected) + c = collect(dk,collected,c) end end list.en = en end + return c end apply_axis['descendant-or-self'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] if ll.special ~= true then -- catch double root - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end - collect(ll,collected) + c = collect(ll,collected,c) end return collected end apply_axis['ancestor'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] while ll do ll = ll.__p__ if ll then - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end end end @@ -6867,14 +7052,16 @@ apply_axis['ancestor'] = function(list) end apply_axis['ancestor-or-self'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll while ll do ll = ll.__p__ if ll then - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end end end @@ -6882,11 +7069,12 @@ apply_axis['ancestor-or-self'] = function(list) end apply_axis['parent'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local pl = list[l].__p__ if pl then - collected[#collected+1] = pl + c = c + 1 + collected[c] = pl end end return collected @@ -6909,7 +7097,7 @@ apply_axis['preceding'] = function(list) -- incomplete end apply_axis['following-sibling'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -6917,7 +7105,8 @@ apply_axis['following-sibling'] = function(list) for i=ll.ni+1,#d do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -6925,7 +7114,7 @@ apply_axis['following-sibling'] = function(list) end apply_axis['preceding-sibling'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -6933,7 +7122,8 @@ apply_axis['preceding-sibling'] = function(list) for i=1,ll.ni-1 do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -6941,7 +7131,7 @@ apply_axis['preceding-sibling'] = function(list) end apply_axis['reverse-sibling'] = function(list) -- reverse preceding - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -6949,7 +7139,8 @@ apply_axis['reverse-sibling'] = function(list) -- reverse preceding for i=ll.ni-1,1,-1 do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -6975,7 +7166,7 @@ local function apply_nodes(list,directive,nodes) return { } end else - local collected, m, p = { }, 0, nil + local collected, c, m, p = { }, 0, 0, nil if not nns then -- only check tag for l=1,#list do local ll = list[l] @@ -6984,11 +7175,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ntg == ltg then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif ntg ~= ltg then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7000,11 +7193,13 @@ local function apply_nodes(list,directive,nodes) if directive then if lns == nns then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif lns ~= nns then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7018,11 +7213,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif not ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7030,7 +7227,7 @@ local function apply_nodes(list,directive,nodes) return collected end else - local collected, m, p = { }, 0, nil + local collected, c, m, p = { }, 0, 0, nil for l=1,#list do local ll = list[l] local ltg = ll.tg @@ -7047,11 +7244,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif not ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7062,12 +7261,13 @@ end local quit_expression = false local function apply_expression(list,expression,order) - local collected = { } + local collected, c = { }, 0 quit_expression = false for l=1,#list do local ll = list[l] if expression(list,ll,l,order) then -- nasty, order alleen valid als n=1 - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end if quit_expression then break @@ -7363,7 +7563,7 @@ local function nodesettostring(set,nodetest) if not ns or ns == "" then ns = "*" end if not tg or tg == "" then tg = "*" end tg = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) - t[#t+1] = (directive and tg) or format("not(%s)",tg) + t[i] = (directive and tg) or format("not(%s)",tg) end if nodetest == false then return format("not(%s)",concat(t,"|")) @@ -7382,7 +7582,7 @@ local function tagstostring(list) local ns, tg = li.ns, li.tg if not ns or ns == "" then ns = "*" end if not tg or tg == "" then tg = "*" end - t[#t+1] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) + t[i] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) end return concat(t," ") end @@ -8102,16 +8302,17 @@ end function xml.collect_tags(root, pattern, nonamespace) local collected = xmlapplylpath(root,pattern) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collected[c] local ns, tg = e.ns, e.tg + n = n + 1 if nonamespace then - t[#t+1] = tg + t[n] = tg elseif ns == "" then - t[#t+1] = tg + t[n] = tg else - t[#t+1] = ns .. ":" .. tg + t[n] = ns .. ":" .. tg end end return t @@ -8219,8 +8420,10 @@ local function inject_element(root,pattern,whatever,prepend) else be, af = edt, cp end + local bn = #be for i=1,#af do - be[#be+1] = af[i] + bn = bn + 1 + be[bn] = af[i] end if rri then r.dt[rri].dt = be @@ -8305,11 +8508,12 @@ local function stripelement(e,nolines,anywhere) local edt = e.dt if edt then if anywhere then - local t = { } + local t, n = { }, 0 for e=1,#edt do local str = edt[e] if type(str) ~= "string" then - t[#t+1] = str + n = n + 1 + t[n] = str elseif str ~= "" then -- todo: lpeg for each case if nolines then @@ -8317,7 +8521,8 @@ local function stripelement(e,nolines,anywhere) end str = gsub(str,"^%s*(.-)%s*$","%1") if str ~= "" then - t[#t+1] = str + n = n + 1 + t[n] = str end end end @@ -8344,9 +8549,10 @@ local function stripelement(e,nolines,anywhere) end end end - if #edt > 0 then + local nedt = #nedt + if nedt > 0 then -- strip end - local str = edt[#edt] + local str = edt[nedt] if type(str) ~= "string" then -- nothing elseif str == "" then @@ -8359,7 +8565,7 @@ local function stripelement(e,nolines,anywhere) if str == "" then remove(edt) else - edt[#edt] = str + edt[nedt] = str end end end @@ -8506,15 +8712,8 @@ local function all(collected) return collected end -local function reverse(collected) - if collected then - local reversed = { } - for c=#collected,1,-1 do - reversed[#reversed+1] = collected[c] - end - return reversed - end -end + +local reverse = table.reversed local function attribute(collected,name) if collected and #collected > 0 then @@ -8605,11 +8804,12 @@ end local function texts(collected) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collection[c] if e and e.dt then - t[#t+1] = e.dt + n = n + 1 + t[n] = e.dt end end return t @@ -8652,14 +8852,15 @@ end local function tags(collected,nonamespace) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collected[c] local ns, tg = e.ns, e.tg + n = n + 1 if nonamespace or ns == "" then - t[#t+1] = tg + t[n] = tg else - t[#t+1] = ns .. ":" .. tg + t[n] = ns .. ":" .. tg end end return t @@ -8855,9 +9056,13 @@ end do - local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or '~' + local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or '' + + if not homedir or homedir == "" then + homedir = string.char(127) -- we need a value, later we wil trigger on it + end - homedir = file.collapse_path(homedir) + homedir = file.collapsepath(homedir) ossetenv("HOME", homedir) -- can be used in unix cnf files ossetenv("USERPROFILE",homedir) -- can be used in windows cnf files @@ -8876,8 +9081,8 @@ do local ownbin = environment.ownbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luatex" local ownpath = environment.ownpath or os.selfdir - ownbin = file.collapse_path(ownbin) - ownpath = file.collapse_path(ownpath) + ownbin = file.collapsepath(ownbin) + ownpath = file.collapsepath(ownpath) if not ownpath or ownpath == "" or ownpath == "unset" then ownpath = args[-1] or arg[-1] @@ -8949,9 +9154,9 @@ do local ownpath = environment.ownpath or dir.current() if ownpath then - ossetenv('SELFAUTOLOC', file.collapse_path(ownpath)) - ossetenv('SELFAUTODIR', file.collapse_path(ownpath .. "/..")) - ossetenv('SELFAUTOPARENT', file.collapse_path(ownpath .. "/../..")) + ossetenv('SELFAUTOLOC', file.collapsepath(ownpath)) + ossetenv('SELFAUTODIR', file.collapsepath(ownpath .. "/..")) + ossetenv('SELFAUTOPARENT', file.collapsepath(ownpath .. "/../..")) else report_resolvers("error: unable to locate ownpath") os.exit() @@ -8987,7 +9192,7 @@ if not texroot or texroot == "" then ossetenv('TEXROOT',texroot) end -environment.texroot = file.collapse_path(texroot) +environment.texroot = file.collapsepath(texroot) -- Tracing. Todo ... @@ -9026,7 +9231,7 @@ local lpegCt, lpegCs, lpegP, lpegC, lpegS = lpeg.Ct, lpeg.Cs, lpeg.P, lpeg.C, lp local type, next = type, next local ostype = os.type -local collapse_path = file.collapse_path +local collapsepath = file.collapsepath local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end) local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end) @@ -9062,22 +9267,29 @@ local resolvers = resolvers local dummy_path_expr = "^!*unset/*$" local function do_first(a,b) - local t = { } - for s in gmatch(b,"[^,]+") do t[#t+1] = a .. s end + local t, n = { }, 0 + for s in gmatch(b,"[^,]+") do + n = n + 1 + t[n] = a .. s + end return "{" .. concat(t,",") .. "}" end local function do_second(a,b) - local t = { } - for s in gmatch(a,"[^,]+") do t[#t+1] = s .. b end + local t, n = { }, 0 + for s in gmatch(a,"[^,]+") do + n = n + 1 + t[n] = s .. b + end return "{" .. concat(t,",") .. "}" end local function do_both(a,b) - local t = { } + local t, n = { }, 0 for sa in gmatch(a,"[^,]+") do for sb in gmatch(b,"[^,]+") do - t[#t+1] = sa .. sb + n = n + 1 + t[n] = sa .. sb end end return "{" .. concat(t,",") .. "}" @@ -9101,6 +9313,7 @@ local function splitpathexpr(str, newlist, validate) report_resolvers("expanding variable '%s'",str) end local t, ok, done = newlist or { }, false, false + local n = #t str = lpegmatch(replacer_1,str) while true do done = false @@ -9124,11 +9337,15 @@ local function splitpathexpr(str, newlist, validate) if validate then for s in gmatch(str,"[^,]+") do s = validate(s) - if s then t[#t+1] = s end + if s then + n = n + 1 + t[n] = s + end end else for s in gmatch(str,"[^,]+") do - t[#t+1] = s + n = n + 1 + t[n] = s end end if trace_expansions then @@ -9141,7 +9358,7 @@ end local function validate(s) local isrecursive = find(s,"//$") - s = collapse_path(s) + s = collapsepath(s) if isrecursive then s = s .. "//" end @@ -9164,10 +9381,14 @@ function resolvers.expandedpathfromlist(pathlist) -- maybe not a list, just a pa splitpathexpr(pathlist[k],newlist,validate) end else + local n = 0 for k=1,#pathlist do for p in gmatch(pathlist[k],"([^,]+)") do p = validate(p) - if p ~= "" then newlist[#newlist+1] = p end + if p ~= "" then + n = n + 1 + newlist[n] = p + end end end end @@ -9176,16 +9397,42 @@ end -- We also put some cleanup code here. -local cleanup -- used recursively -cleanup = lpeg.replacer { - { "!", "" }, - { "\\", "/" }, - { "~" , function() return lpegmatch(cleanup,environment.homedir) end }, + + +local cleanup = lpeg.replacer { + { "!" , "" }, + { "\\" , "/" }, } +local homedir + function resolvers.cleanpath(str) - return str and lpegmatch(cleanup,str) + if not homedir then + homedir = lpegmatch(cleanup,environment.homedir or "") + if homedir == string.char(127) or homedir == "" or not lfs.isdir(homedir) then + if trace_expansions then + report_resolvers("no home dir set, ignoring dependent paths") + end + function resolvers.cleanpath(str) + if find(str,"~") then + return "" -- special case + else + return str and lpegmatch(cleanup,str) + end + end + else + cleanup = lpeg.replacer { + { "!" , "" }, + { "\\" , "/" }, + { "~" , homedir }, + } + function resolvers.cleanpath(str) + return str and lpegmatch(cleanup,str) + end + end + end + return resolvers.cleanpath(str) end -- This one strips quotes and funny tokens. @@ -9211,7 +9458,6 @@ end -- we join them and split them after the expansion has taken place. This -- is more convenient. - local cache = { } local splitter = lpegCt(lpeg.splitat(lpegS(ostype == "windows" and ";" or ":;"))) -- maybe add , @@ -9226,15 +9472,17 @@ local function splitconfigurationpath(str) -- beware, this can be either a path str = gsub(str,"\\","/") local split = lpegmatch(splitter,str) found = { } + local noffound = 0 for i=1,#split do local s = split[i] if not find(s,"^{*unset}*") then - found[#found+1] = s + noffound = noffound + 1 + found[noffound] = s end end if trace_expansions then report_resolvers("splitting path specification '%s'",str) - for k=1,#found do + for k=1,noffound do report_resolvers("% 4i: %s",k,found[k]) end end @@ -9357,7 +9605,6 @@ formats['enc'] = 'ENCFONTS' suffixes['enc'] = { 'enc' } formats['fmt'] = 'TEXFORMATS' suffixes['fmt'] = { 'fmt' } formats['map'] = 'TEXFONTMAPS' suffixes['map'] = { 'map' } formats['mp'] = 'MPINPUTS' suffixes['mp'] = { 'mp' } -formats['ocp'] = 'OCPINPUTS' suffixes['ocp'] = { 'ocp' } formats['ofm'] = 'OFMFONTS' suffixes['ofm'] = { 'ofm', 'tfm' } formats['otf'] = 'OPENTYPEFONTS' suffixes['otf'] = { 'otf' } formats['opl'] = 'OPLFONTS' suffixes['opl'] = { 'opl' } @@ -9582,7 +9829,7 @@ local function identify() local cachepath = texmfcaches[k] if cachepath ~= "" then cachepath = resolvers.cleanpath(cachepath) - cachepath = file.collapse_path(cachepath) + cachepath = file.collapsepath(cachepath) local valid = isdir(cachepath) if valid then if file.is_readable(cachepath) then @@ -9921,7 +10168,7 @@ function resolvers.splitmethod(filename) end function resolvers.methodhandler(what, filename, filetype) -- ... - filename = file.collapse_path(filename) + filename = file.collapsepath(filename) local specification = (type(filename) == "string" and resolvers.splitmethod(filename)) or filename -- no or { }, let it bomb local scheme = specification.scheme local resolver = resolvers[what] @@ -9966,7 +10213,7 @@ local lpegP, lpegS, lpegR, lpegC, lpegCc, lpegCs, lpegCt = lpeg.P, lpeg.S, lpeg. local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns local filedirname, filebasename, fileextname, filejoin = file.dirname, file.basename, file.extname, file.join -local collapse_path = file.collapse_path +local collapsepath = file.collapsepath local allocate = utilities.storage.allocate local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end) @@ -10169,7 +10416,7 @@ local function identify_configuration_files() expandvars(cnfpaths) --- hm local luacnfname = resolvers.luacnfname for i=1,#cnfpaths do - local filename = collapse_path(filejoin(cnfpaths[i],luacnfname)) + local filename = collapsepath(filejoin(cnfpaths[i],luacnfname)) if lfs.isfile(filename) then specification[#specification+1] = filename end @@ -10330,7 +10577,7 @@ local function locate_file_databases() -- todo: cache:// and tree:// (runtime) local texmfpaths = resolvers.expandedpathlist('TEXMF') for i=1,#texmfpaths do - local path = collapse_path(texmfpaths[i]) + local path = collapsepath(texmfpaths[i]) local stripped = gsub(path,"^!!","") local runtime = stripped == path path = resolvers.cleanpath(path) @@ -10443,11 +10690,12 @@ end function resolvers.splitexpansions() local ie = instance.expansions for k,v in next, ie do - local t, h, p = { }, { }, splitconfigurationpath(v) + local t, tn, h, p = { }, 0, { }, splitconfigurationpath(v) for kk=1,#p do local vv = p[kk] if vv ~= "" and not h[vv] then - t[#t+1] = vv + tn = tn + 1 + t[tn] = vv h[vv] = true end end @@ -10554,7 +10802,8 @@ end function resolvers.registerextrapath(paths,subpaths) local ep = instance.extra_paths or { } - local n = #ep + local oldn = #ep + local newn = oldn if paths and paths ~= "" then if subpaths and subpaths ~= "" then for p in gmatch(paths,"[^,]+") do @@ -10562,7 +10811,8 @@ function resolvers.registerextrapath(paths,subpaths) for s in gmatch(subpaths,"[^,]+") do local ps = p .. "/" .. s if not done[ps] then - ep[#ep+1] = resolvers.cleanpath(ps) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(ps) done[ps] = true end end @@ -10570,7 +10820,8 @@ function resolvers.registerextrapath(paths,subpaths) else for p in gmatch(paths,"[^,]+") do if not done[p] then - ep[#ep+1] = resolvers.cleanpath(p) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(p) done[p] = true end end @@ -10581,16 +10832,17 @@ function resolvers.registerextrapath(paths,subpaths) for s in gmatch(subpaths,"[^,]+") do local ps = ep[i] .. "/" .. s if not done[ps] then - ep[#ep+1] = resolvers.cleanpath(ps) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(ps) done[ps] = true end end end end - if #ep > 0 then + if newn > 0 then instance.extra_paths = ep -- register paths end - if #ep > n then + if newn > oldn then instance.lists = { } -- erase the cache end end @@ -10600,14 +10852,15 @@ local function made_list(instance,list) if not ep or #ep == 0 then return list else - local done, new = { }, { } + local done, new, newn = { }, { }, 0 -- honour . .. ../.. but only when at the start for k=1,#list do local v = list[k] if not done[v] then if find(v,"^[%.%/]$") then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v else break end @@ -10618,7 +10871,8 @@ local function made_list(instance,list) local v = ep[k] if not done[v] then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v end end -- next the formal paths @@ -10626,7 +10880,8 @@ local function made_list(instance,list) local v = list[k] if not done[v] then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v end end return new @@ -10637,7 +10892,7 @@ function resolvers.cleanpathlist(str) local t = resolvers.expandedpathlist(str) if t then for i=1,#t do - t[i] = collapse_path(resolvers.cleanpath(t[i])) + t[i] = collapsepath(resolvers.cleanpath(t[i])) end end return t @@ -10703,7 +10958,7 @@ resolvers.isreadable.tex = resolvers.isreadable.file -- name/name local function collect_files(names) - local filelist = { } + local filelist, noffiles = { }, 0 for k=1,#names do local fname = names[k] if trace_detail then @@ -10744,7 +10999,8 @@ local function collect_files(names) if trace_detail then report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result) end - filelist[#filelist+1] = { kind, search, result } + noffiles = noffiles + 1 + filelist[noffiles] = { kind, search, result } end else for kk=1,#blobfile do @@ -10756,7 +11012,8 @@ local function collect_files(names) if trace_detail then report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result) end - filelist[#filelist+1] = { kind, search, result } + noffiles = noffiles + 1 + filelist[noffiles] = { kind, search, result } end end end @@ -10766,7 +11023,7 @@ local function collect_files(names) end end end - return #filelist > 0 and filelist or nil + return noffiles > 0 and filelist or nil end function resolvers.registerintrees(name) @@ -10792,7 +11049,7 @@ end local function collect_instance_files(filename,collected) -- todo : plugin (scanners, checkers etc) local result = collected or { } local stamp = nil - filename = collapse_path(filename) + filename = collapsepath(filename) -- speed up / beware: format problem if instance.remember then stamp = filename .. "--" .. instance.engine .. "--" .. instance.progname .. "--" .. instance.format @@ -11045,7 +11302,7 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan end end for k=1,#result do - local rk = collapse_path(result[k]) + local rk = collapsepath(result[k]) result[k] = rk resolvers.registerintrees(rk) -- for tracing used files end @@ -12439,7 +12696,7 @@ function resolvers.load_tree(tree) local texos = "texmf-" .. os.platform local oldroot = environment.texroot - local newroot = file.collapse_path(tree) + local newroot = file.collapsepath(tree) local newtree = file.join(newroot,texos) local newpath = file.join(newtree,"bin") @@ -12668,17 +12925,17 @@ local format = string.format -- helper for mtxrun -local quote = string.quote +local quoted = string.quoted local function primaryflags() local trackers = environment.argument("trackers") local directives = environment.argument("directives") local flags = "" if trackers and trackers ~= "" then - flags = flags .. "--trackers=" .. quote(trackers) + flags = flags .. "--trackers=" .. quoted(trackers) end if directives and directives ~= "" then - flags = flags .. "--directives=" .. quote(directives) + flags = flags .. "--directives=" .. quoted(directives) end return flags end @@ -12743,7 +13000,7 @@ function environment.make_format(name) return end -- generate format - local command = format("luatex --ini %s --lua=%s %s %sdump",primaryflags(),quote(usedluastub),quote(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\") + local command = format("luatex --ini %s --lua=%s %s %sdump",primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\") logs.simple("running command: %s\n",command) os.spawn(command) -- remove related mem files @@ -12781,8 +13038,7 @@ function environment.run_format(name,data,more) logs.simple("using format name: %s",fmtname) logs.simple("no luc/lua with name: %s",barename) else - local q = string.quote - local command = format("luatex %s --fmt=%s --lua=%s %s %s",primaryflags(),quote(barename),quote(luaname),quote(data),more ~= "" and quote(more) or "") + local command = format("luatex %s --fmt=%s --lua=%s %s %s",primaryflags(),quoted(barename),quoted(luaname),quoted(data),more ~= "" and quoted(more) or "") logs.simple("running command: %s",command) os.spawn(command) end @@ -12813,13 +13069,13 @@ own.libs = { -- order can be made better 'l-unicode.lua', 'l-math.lua', + 'util-tab.lua', + 'util-sto.lua', 'util-mrg.lua', 'util-lua.lua', 'util-prs.lua', - 'util-tab.lua', 'util-fmt.lua', 'util-deb.lua', - 'util-sto.lua', 'trac-inf.lua', 'trac-set.lua', diff --git a/scripts/context/stubs/mswin/mtxrun.lua b/scripts/context/stubs/mswin/mtxrun.lua index d091fa405..7af51ba30 100644 --- a/scripts/context/stubs/mswin/mtxrun.lua +++ b/scripts/context/stubs/mswin/mtxrun.lua @@ -54,7 +54,7 @@ if not modules then modules = { } end modules ['l-string'] = { local string = string local sub, gsub, find, match, gmatch, format, char, byte, rep, lower = string.sub, string.gsub, string.find, string.match, string.gmatch, string.format, string.char, string.byte, string.rep, string.lower -local lpegmatch = lpeg.match +local lpegmatch, S, C, Ct = lpeg.match, lpeg.S, lpeg.C, lpeg.Ct -- some functions may disappear as they are not used anywhere @@ -62,145 +62,68 @@ if not string.split then -- this will be overloaded by a faster lpeg variant - function string:split(pattern) - if #self > 0 then - local t = { } - for s in gmatch(self..pattern,"(.-)"..pattern) do - t[#t+1] = s + function string.split(str,pattern) + local t = { } + if #str > 0 then + local n = 1 + for s in gmatch(str..pattern,"(.-)"..pattern) do + t[n] = s + n = n + 1 end - return t - else - return { } end + return t end end -string.patterns = { } - -local escapes = { - ["%"] = "%%", - ["."] = "%.", - ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", - ["^"] = "%^", ["$"] = "%$", - ["["] = "%[", ["]"] = "%]", - ["("] = "%(", [")"] = "%)", - ["{"] = "%{", ["}"] = "%}" -} - -string.patterns.escapes = escapes - -function string:esc() -- variant 2 - return (gsub(self,"(.)",escapes)) -end - -function string:unquote() - return (gsub(self,"^([\"\'])(.*)%1$","%2")) +function string.unquoted(str) + return (gsub(str,"^([\"\'])(.*)%1$","%2")) end -function string:quote() -- we could use format("%q") - return format("%q",self) +function string.quoted(str) + return format("%q",str) -- always " end -function string:count(pattern) -- variant 3 +function string.count(str,pattern) -- variant 3 local n = 0 - for _ in gmatch(self,pattern) do + for _ in gmatch(str,pattern) do -- not for utf n = n + 1 end return n end -function string:limit(n,sentinel) - if #self > n then +function string.limit(str,n,sentinel) + if #str > n then sentinel = sentinel or " ..." - return sub(self,1,(n-#sentinel)) .. sentinel + return sub(str,1,(n-#sentinel)) .. sentinel else - return self - end -end - - -do -- roberto's variant: - local space = lpeg.S(" \t\v\n") - local nospace = 1 - space - local stripper = space^0 * lpeg.C((space^0 * nospace^1)^0) - function string.strip(str) - return lpegmatch(stripper,str) or "" - end -end - -function string:is_empty() - return not find(self,"%S") -end - -function string:enhance(pattern,action) - local ok, n = true, 0 - while ok do - ok = false - self = gsub(self,pattern, function(...) - ok, n = true, n + 1 - return action(...) - end) + return str end - return self, n end -if not string.characters then - - local function nextchar(str, index) - index = index + 1 - return (index <= #str) and index or nil, sub(str,index,index) - end - function string:characters() - return nextchar, self, 0 - end - local function nextbyte(str, index) - index = index + 1 - return (index <= #str) and index or nil, byte(sub(str,index,index)) - end - function string:bytes() - return nextbyte, self, 0 - end +local space = S(" \t\v\n") +local nospace = 1 - space +local stripper = space^0 * C((space^0 * nospace^1)^0) -- roberto's code +function string.strip(str) + return lpegmatch(stripper,str) or "" end -function string:rpadd(n,chr) - local m = n-#self - if m > 0 then - return self .. rep(chr or " ",m) - else - return self - end -end - -function string:lpadd(n,chr) - local m = n-#self - if m > 0 then - return rep(chr or " ",m) .. self - else - return self - end +function string.is_empty(str) + return not find(str,"%S") end -string.padd = string.rpadd - local patterns_escapes = { - ["-"] = "%-", - ["."] = "%.", - ["+"] = "%+", - ["*"] = "%*", ["%"] = "%%", - ["("] = "%)", - [")"] = "%)", - ["["] = "%[", - ["]"] = "%]", + ["."] = "%.", + ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", + ["["] = "%[", ["]"] = "%]", + ["("] = "%)", [")"] = "%)", + -- ["{"] = "%{", ["}"] = "%}" + -- ["^"] = "%^", ["$"] = "%$", } -function string:escapedpattern() - return (gsub(self,".",patterns_escapes)) -end - local simple_escapes = { ["-"] = "%-", ["."] = "%.", @@ -208,24 +131,32 @@ local simple_escapes = { ["*"] = ".*", } -function string:partialescapedpattern() - return (gsub(self,".",simple_escapes)) +function string.escapedpattern(str,simple) + if simple then + return (gsub(str,".",simple_escapes)) + else + return (gsub(str,".",patterns_escapes)) + end end -function string:tohash() - local t = { } - for s in gmatch(self,"([^, ]+)") do -- lpeg - t[s] = true +function string.topattern(str,lowercase,strict) + if str == "" then + return ".*" + else + str = gsub(str,".",simple_escapes) + if lowercase then + str = lower(str) + end + if strict then + return "^" .. str .. "$" + else + return str + end end - return t end -local pattern = lpeg.Ct(lpeg.C(1)^0) - -function string:totable() - return lpegmatch(pattern,self) -end +-- The following functions might end up in another namespace. function string.tabtospace(str,tab) -- we don't handle embedded newlines @@ -246,30 +177,17 @@ function string.tabtospace(str,tab) return str end -function string:compactlong() -- strips newlines and leading spaces - self = gsub(self,"[\n\r]+ *","") - self = gsub(self,"^ *","") - return self -end -function string:striplong() -- strips newlines and leading spaces - self = gsub(self,"^%s*","") - self = gsub(self,"[\n\r]+ *","\n") - return self +function string.striplong(str) -- strips all leading spaces + str = gsub(str,"^%s*","") + str = gsub(str,"[\n\r]+ *","\n") + return str end -function string:topattern(lowercase,strict) - if lowercase then - self = lower(self) - end - self = gsub(self,".",simple_escapes) - if self == "" then - self = ".*" - elseif strict then - self = "^" .. self .. "$" - end - return self -end +-- obsolete names: + +string.quote = string.quoted +string.unquote = string.unquoted end -- of closure @@ -292,15 +210,29 @@ local patterns = lpeg.patterns local P, R, S, Ct, C, Cs, Cc, V = lpeg.P, lpeg.R, lpeg.S, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.V local match = lpeg.match +local utfcharacters = string.utfcharacters +local utfgmatch = unicode and unicode.utf8.gmatch + local digit, sign = R('09'), S('+-') local cr, lf, crlf = P("\r"), P("\n"), P("\r\n") -local utf8byte = R("\128\191") +local utf8next = R("\128\191") +local escaped = P("\\") * P(1) +local squote = P("'") +local dquote = P('"') -patterns.utf8byte = utf8byte patterns.utf8one = R("\000\127") -patterns.utf8two = R("\194\223") * utf8byte -patterns.utf8three = R("\224\239") * utf8byte * utf8byte -patterns.utf8four = R("\240\244") * utf8byte * utf8byte * utf8byte +patterns.utf8two = R("\194\223") * utf8next +patterns.utf8three = R("\224\239") * utf8next * utf8next +patterns.utf8four = R("\240\244") * utf8next * utf8next * utf8next +patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') + +local utf8char = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four +local validutf8char = utf8char^0 * P(-1) * Cc(true) + Cc(false) + +patterns.utf8 = utf8char +patterns.utf8char = utf8char +patterns.validutf8 = validutf8char +patterns.validutf8char = validutf8char patterns.digit = digit patterns.sign = sign @@ -327,17 +259,24 @@ patterns.nonspace = 1 - patterns.space patterns.nonspacer = 1 - patterns.spacer patterns.whitespace = patterns.eol + patterns.spacer patterns.nonwhitespace = 1 - patterns.whitespace -patterns.utf8 = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four -patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') -patterns.validutf8 = patterns.utf8^0 * P(-1) * Cc(true) + Cc(false) patterns.comma = P(",") patterns.commaspacer = P(",") * patterns.spacer^0 patterns.period = P(".") - -patterns.undouble = P('"')/"" * (1-P('"'))^0 * P('"')/"" -patterns.unsingle = P("'")/"" * (1-P("'"))^0 * P("'")/"" +patterns.escaped = escaped +patterns.squote = squote +patterns.dquote = dquote +patterns.undouble = (dquote/"") * ((escaped + (1-dquote))^0) * (dquote/"") +patterns.unsingle = (squote/"") * ((escaped + (1-squote))^0) * (squote/"") +patterns.unquoted = patterns.undouble + patterns.unsingle -- more often undouble patterns.unspacer = ((patterns.spacer^1)/"")^0 +local unquoted = Cs(patterns.unquoted * P(-1)) -- not C + +function string.unquoted(str) + return match(unquoted,str) or str +end + + function lpeg.anywhere(pattern) --slightly adapted from website return P { P(pattern) + 1 * V(1) } -- why so complex? end @@ -353,8 +292,8 @@ local content = (empty + nonempty)^1 local capture = Ct(content^0) -function string:splitlines() - return match(capture,self) +function string.splitlines(str) + return match(capture,str) end patterns.textline = content @@ -366,12 +305,12 @@ 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 other, any = C((1 - separator)^0), P(1) + local any = P(1) splitter = other * (separator * C(any^0) + "") -- ? splitters_s[separator] = splitter else - local other = C((1 - separator)^0) splitter = other * (separator * other)^0 splitters_m[separator] = splitter end @@ -392,16 +331,15 @@ function lpeg.split(separator,str) return match(c,str) end -function string:split(separator) +function string.split(str,separator) local c = cache[separator] if not c then c = Ct(splitat(separator)) cache[separator] = c end - return match(c,self) + return match(c,str) end -lpeg.splitters = cache local cache = { } @@ -409,22 +347,22 @@ function lpeg.checkedsplit(separator,str) local c = cache[separator] if not c then separator = P(separator) - local other = C((1 - separator)^0) + local other = C((1 - separator)^1) c = Ct(separator^0 * other * (separator^1 * other)^0) cache[separator] = c end return match(c,str) end -function string:checkedsplit(separator) +function string.checkedsplit(str,separator) local c = cache[separator] if not c then separator = P(separator) - local other = C((1 - separator)^0) + local other = C((1 - separator)^1) c = Ct(separator^0 * other * (separator^1 * other)^0) cache[separator] = c end - return match(c,self) + return match(c,str) end @@ -435,7 +373,9 @@ local function f2(s) local c1, c2 = f1(s,1,2) return c1 * 64 + c2 local function f3(s) local c1, c2, c3 = f1(s,1,3) return (c1 * 64 + c2) * 64 + c3 - 925824 end local function f4(s) local c1, c2, c3, c4 = f1(s,1,4) return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168 end -patterns.utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 +local utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 + +patterns.utf8byte = utf8byte @@ -469,19 +409,25 @@ function lpeg.keeper(str) end end +-- Just for fun I looked at the used bytecode and +-- p = (p and p + pp) or pp gets one more (testset). + function lpeg.replacer(t) if #t > 0 then local p for i=1,#t do local ti= t[i] local pp = P(ti[1]) / ti[2] - p = (p and p + pp ) or pp + if p then + p = p + pp + else + p = pp + end end return Cs((p + 1)^0) end end - local splitters_f, splitters_s = { }, { } function lpeg.firstofsplit(separator) -- always return value @@ -505,11 +451,170 @@ function lpeg.secondofsplit(separator) -- nil if not split end function lpeg.balancer(left,right) + left, right = P(left), P(right) return P { left * ((1 - left - right) + V(1))^0 * right } end +local nany = utf8char/"" + +function lpeg.counter(pattern) + pattern = Cs((P(pattern)/" " + nany)^0) + return function(str) + return #match(pattern,str) + end +end + +if utfgmatch then + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local n = 0 + for _ in utfgmatch(str,what) do + n = n + 1 + end + return n + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +else + + local cache = { } + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local p = cache[what] + if not p then + p = Cs((P(what)/" " + nany)^0) + cache[p] = p + end + return #match(p,str) + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +end + +local patterns_escapes = { -- also defines in l-string + ["%"] = "%%", + ["."] = "%.", + ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", + ["["] = "%[", ["]"] = "%]", + ["("] = "%)", [")"] = "%)", + -- ["{"] = "%{", ["}"] = "%}" + -- ["^"] = "%^", ["$"] = "%$", +} + +local simple_escapes = { -- also defines in l-string + ["-"] = "%-", + ["."] = "%.", + ["?"] = ".", + ["*"] = ".*", +} + +local p = Cs((S("-.+*%()[]") / patterns_escapes + P(1))^0) +local s = Cs((S("-.+*%()[]") / simple_escapes + P(1))^0) + +function string.escapedpattern(str,simple) + if simple then + return match(s,str) + else + return match(p,str) + end +end + +-- utf extensies + +lpeg.UP = lpeg.P + +if utfcharacters then + + function lpeg.US(str) + local p + for uc in utfcharacters(str) do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + + +elseif utfgmatch then + + function lpeg.US(str) + local p + for uc in utfgmatch(str,".") do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + +else + + function lpeg.US(str) + local p + local f = function(uc) + if p then + p = p + P(uc) + else + p = P(uc) + end + end + match((utf8char/f)^0,str) + return p + end + +end + +local range = Cs(utf8byte) * (Cs(utf8byte) + Cc(false)) + +local utfchar = unicode and unicode.utf8 and unicode.utf8.char + +function lpeg.UR(str,more) + local first, last + if type(str) == "number" then + first = str + last = more or first + else + first, last = match(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 -- a somewhat arbitrary criterium + local p + for i=first,last do + if p then + p = p + P(utfchar(i)) + else + p = P(utfchar(i)) + end + end + return p -- nil when invalid range + else + local f = function(b) + return b >= first and b <= last + end + return utf8byte / f -- nil when invalid range + end +end + + + + end -- of closure do -- create closure to overcome 200 locals limit @@ -577,24 +682,26 @@ end -- extra functions, some might go (when not used) function table.strip(tab) - local lst = { } + local lst, l = { }, 0 for i=1,#tab do local s = gsub(tab[i],"^%s*(.-)%s*$","%1") if s == "" then -- skip this one else - lst[#lst+1] = s + l = l + 1 + lst[l] = s end end return lst end function table.keys(t) - local k = { } + local keys, k = { }, 0 for key, _ in next, t do - k[#k+1] = key + k = k + 1 + keys[k] = key end - return k + return keys end local function compare(a,b) @@ -607,9 +714,10 @@ local function compare(a,b) end local function sortedkeys(tab) - local srt, kind = { }, 0 -- 0=unknown 1=string, 2=number 3=mixed + local srt, kind, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed for key,_ in next, tab do - srt[#srt+1] = key + s = s + 1 + srt[s] = key if kind == 3 then -- no further check else @@ -632,10 +740,11 @@ local function sortedkeys(tab) end local function sortedhashkeys(tab) -- fast one - local srt = { } + local srt, s = { }, 0 for key,_ in next, tab do if key then - srt[#srt+1] = key + s= s + 1 + srt[s] = key end end sort(srt) @@ -649,8 +758,7 @@ local function nothing() end local function sortedhash(t) if t then - local s = sortedhashkeys(t) -- maybe just sortedkeys - local n = 0 + local n, s = 0, sortedkeys(t) -- the robust one local function kv(s) n = n + 1 local k = s[n] @@ -666,20 +774,30 @@ table.sortedhash = sortedhash table.sortedpairs = sortedhash function table.append(t, list) - for _,v in next, list do - insert(t,v) + local n = #t + for i=1,#list do + n = n + 1 + t[n] = list[i] end + return t end function table.prepend(t, list) - for k,v in next, list do - insert(t,k,v) + 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, ...) -- first one is target t = t or { } - local lst = {...} + local lst = { ... } for i=1,#lst do for k, v in next, lst[i] do t[k] = v @@ -689,7 +807,7 @@ function table.merge(t, ...) -- first one is target end function table.merged(...) - local tmp, lst = { }, {...} + local tmp, lst = { }, { ... } for i=1,#lst do for k, v in next, lst[i] do tmp[k] = v @@ -699,22 +817,24 @@ function table.merged(...) end function table.imerge(t, ...) - local lst = {...} + local lst, nt = { ... }, #t for i=1,#lst do local nst = lst[i] for j=1,#nst do - t[#t+1] = nst[j] + nt = nt + 1 + t[nt] = nst[j] end end return t end function table.imerged(...) - local tmp, lst = { }, {...} + local tmp, ntmp, lst = { }, 0, {...} for i=1,#lst do local nst = lst[i] for j=1,#nst do - tmp[#tmp+1] = nst[j] + ntmp = ntmp + 1 + tmp[ntmp] = nst[j] end end return tmp @@ -790,11 +910,14 @@ function table.tohash(t,value) end function table.fromhash(t) - local h = { } + local hsh, h = { }, 0 for k, v in next, t do -- no ipairs here - if v then h[#h+1] = k end + if v then + h = h + 1 + hsh[h] = k + end end - return h + return hsh end table.serialize_functions = true @@ -815,20 +938,23 @@ local function simple_table(t) n = n + 1 end if n == #t then - local tt = { } + local tt, nt = { }, 0 for i=1,#t do local v = t[i] local tv = type(v) if tv == "number" then + nt = nt + 1 if hexify then - tt[#tt+1] = format("0x%04X",v) + tt[nt] = format("0x%04X",v) else - tt[#tt+1] = tostring(v) -- tostring not needed + tt[nt] = tostring(v) -- tostring not needed end elseif tv == "boolean" then - tt[#tt+1] = tostring(v) + nt = nt + 1 + tt[nt] = tostring(v) elseif tv == "string" then - tt[#tt+1] = format("%q",v) + nt = nt + 1 + tt[nt] = format("%q",v) else tt = nil break @@ -1123,10 +1249,11 @@ local function serialize(root,name,_handle,_reduce,_noquotes,_hexify) end -function table.serialize(root,name,reduce,noquotes,hexify) - local t = { } +function table.serialize(root,name,reduce,noquotes,hexify) -- can be faster if flush == false and t as argument + local t, n = { }, 0 local function flush(s) - t[#t+1] = s + n = n + 1 + t[n] = s end serialize(root,name,flush,reduce,noquotes,hexify) return concat(t,"\n") @@ -1152,12 +1279,13 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) if f then local maxtab = table.tofile_maxtab if maxtab > 1 then - local t = { } + local t, n = { }, 0 local function flush(s) - t[#t+1] = s - if #t > maxtab then + n = n + 1 + t[n] = s + if n > maxtab then f:write(concat(t,"\n"),"\n") -- hm, write(sometable) should be nice - t = { } + t, n = { }, 0 -- we could recycle t if needed end end serialize(root,name,flush,reduce,noquotes,hexify) @@ -1173,52 +1301,66 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) end end -local function flatten(t,f,complete) -- is this used? meybe a variant with next, ... - for i=1,#t do - local v = t[i] - if type(v) == "table" then - if complete or type(v[1]) == "table" then - flatten(v,f,complete) +local function flattened(t,f,depth) + if f == nil then + f = { } + depth = 0xFFFF + elseif tonumber(f) then + -- assume then only two arguments are given + 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 + f[k] = v end + end + end + local n = #f + for k=1,#t do + local v = t[k] + if depth > 0 and type(v) == "table" then + flattened(v,f,depth-1) + n = #f else - f[#f+1] = v + n = n + 1 + f[n] = v end end -end - -function table.flatten(t) - local f = { } - flatten(t,f,true) return f end -function table.unnest(t) -- bad name - local f = { } - flatten(t,f,false) - return f -end - -table.flattenonelevel = table.unnest - --- a better one: +table.flattened = flattened -local function flattened(t,f) - if not f then +local function unnest(t,f) -- only used in mk, for old times sake + if not f then -- and only relevant for token lists f = { } end - for k, v in next, t do + for i=1,#t do + local v = t[i] if type(v) == "table" then - flattened(v,f) + if type(v[1]) == "table" then + unnest(v,f) + else + f[#f+1] = v + end else - f[k] = v + f[#f+1] = v end end return f end -table.flattened = flattened +function table.unnest(t) -- bad name + return unnest(t) +end + + local function are_equal(a,b,n,m) -- indexed if a and b and #a == #b then @@ -1244,7 +1386,7 @@ end local function identical(a,b) -- assumes same structure for ka, va in next, a do - local vb = b[k] + local vb = b[ka] if va == vb then -- same elseif type(va) == "table" and type(vb) == "table" then @@ -1258,8 +1400,8 @@ local function identical(a,b) -- assumes same structure return true end -table.are_equal = are_equal table.identical = identical +table.are_equal = are_equal -- maybe also make a combined one @@ -1285,14 +1427,14 @@ function table.contains(t, v) end function table.count(t) - local n, e = 0, next(t) - while e do - n, e = n + 1, next(t,e) + local n = 0 + for k, v in next, t do + n = n + 1 end return n end -function table.swapped(t,s) +function table.swapped(t,s) -- hash local n = { } if s then for k, v in next, s do @@ -1305,52 +1447,34 @@ function table.swapped(t,s) return n end - -function table.clone(t,p) -- t is optional or nil or table - if not p then - t, p = { }, t or { } - elseif not t then - t = { } - end - setmetatable(t, { __index = function(_,key) return p[key] end }) -- why not __index = p ? - return t -end - -function table.hexed(t,seperator) - local tt = { } - for i=1,#t do tt[i] = format("0x%04X",t[i]) end - return concat(tt,seperator or " ") -end - -function table.swaphash(h) -- needs another name - local r = { } - for k,v in next, h do - r[v] = lower(gsub(k," ","")) - end - return r -end - -function table.reverse(t) - local tt = { } - if #t > 0 then - for i=#t,1,-1 do - tt[#tt+1] = t[i] +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 - return tt end function table.sequenced(t,sep,simple) -- hash only - local s = { } + local s, n = { }, 0 for k, v in sortedhash(t) do if simple then if v == true then - s[#s+1] = k + n = n + 1 + s[n] = k elseif v and v~= "" then - s[#s+1] = k .. "=" .. tostring(v) + n = n + 1 + s[n] = k .. "=" .. tostring(v) end else - s[#s+1] = k .. "=" .. tostring(v) + n = n + 1 + s[n] = k .. "=" .. tostring(v) end end return concat(s, sep or " | ") @@ -1360,7 +1484,7 @@ function table.print(...) table.tohandle(print,...) end --- -- -- obsolete but we keep them for a while and will comment them later -- -- -- +-- -- -- obsolete but we keep them for a while and might comment them later -- -- -- -- roughly: copy-loop : unpack : sub == 0.9 : 0.4 : 0.45 (so in critical apps, use unpack) @@ -1375,14 +1499,7 @@ function table.is_empty(t) end function table.has_one_entry(t) - local n = next(t) - return n and not next(t,n) -end - -function table.replace(a,b) - for k,v in next, b do - a[k] = v - end + return t and not next(t,next(t)) end @@ -1627,6 +1744,8 @@ if not modules then modules = { } end modules ['l-number'] = { license = "see context related readme files" } +-- this module will be replaced when we have the bit library + local tostring = tostring local format, floor, insert, match = string.format, math.floor, string.match local concat, insert = table.concat, table.insert @@ -1768,10 +1887,11 @@ function set.tolist(n) if n == 0 or not tabs[n] then return "" else - local t = { } + local t, n = { }, 0 for k, v in next, tabs[n] do if v then - t[#t+1] = k + n = n + 1 + t[n] = k end end return concat(t," ") @@ -1950,7 +2070,7 @@ end -- no need for function anymore as we have more clever code and helpers now -- this metatable trickery might as well disappear -os.resolvers = os.resolvers or { } +os.resolvers = os.resolvers or { } -- will become private local resolvers = os.resolvers @@ -2279,7 +2399,7 @@ end file.isreadable = file.is_readable -- depricated file.iswritable = file.is_writable -- depricated --- todo: lpeg +-- todo: lpeg \\ / .. does not save much local checkedsplit = string.checkedsplit @@ -2295,7 +2415,7 @@ end -- we can hash them weakly -function file.collapse_path(str,anchor) +function file.collapsepath(str,anchor) if anchor and not find(str,"^/") and not find(str,"^%a:") then str = getcurrentdir() .. "/" .. str end @@ -2350,6 +2470,8 @@ function file.collapse_path(str,anchor) end end +file.collapse_path = file.collapsepath + function file.robustname(str) return (gsub(str,"[^%a%d%/%-%.\\]+","-")) @@ -2782,9 +2904,12 @@ local function glob(str,t) end return t elseif isfile(str) then - local t = t or { } - t[#t+1] = str - return t + if t then + t[#t+1] = str + return t + else + return { str } + end else local split = lpegmatch(pattern,str) if split then @@ -2812,6 +2937,7 @@ local function globfiles(path,recurse,func,files) -- func == pattern or function 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 --- skip @@ -2822,12 +2948,9 @@ local function globfiles(path,recurse,func,files) -- func == pattern or function globfiles(path .. "/" .. name,recurse,func,files) end elseif mode == "file" then - if func then - if func(name) then - files[#files+1] = path .. "/" .. name - end - else - files[#files+1] = path .. "/" .. name + if not func or func(name) then + noffiles = noffiles + 1 + files[noffiles] = path .. "/" .. name end end end @@ -3012,8 +3135,12 @@ local type, tonumber = type, tonumber boolean = boolean or { } local boolean = boolean +-- function boolean.tonumber(b) +-- return b and 1 or 0 -- test and test and return or return +-- end + function boolean.tonumber(b) - if b then return 1 else return 0 end + if b then return 1 else return 0 end -- test and return or return end function toboolean(str,tolerant) @@ -3037,6 +3164,8 @@ function toboolean(str,tolerant) end end +string.toboolean = toboolean + function string.is_boolean(str,default) if type(str) == "string" then if str == "true" or str == "yes" or str == "on" or str == "t" then @@ -3048,14 +3177,6 @@ function string.is_boolean(str,default) return default end -function boolean.alwaystrue() - return true -end - -function boolean.falsetrue() - return false -end - end -- of closure @@ -3144,30 +3265,37 @@ function unicode.utftype(f) end function unicode.utf16_to_utf8(str, endian) -- maybe a gsub is faster or an lpeg - local result, tmp, n, m, p = { }, { }, 0, 0, 0 + local result, tmp, n, m, p, r, t = { }, { }, 0, 0, 0, 0, 0 -- we reuse tmp -- lf | cr | crlf / (cr:13, lf:10) local function doit() if n == 10 then if p ~= 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = 0 end elseif n == 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = n else - tmp[#tmp+1] = utfchar(n) + t = t + 1 + tmp[t] = utfchar(n) p = 0 end end for l,r in bytepairs(str) do if r then if endian then - n = l*256 + r + n = 256*l + r else - n = r*256 + l + n = 256*r + l end if m > 0 then n = (m-0xD800)*0x400 + (n-0xDC00) + 0x10000 @@ -3180,29 +3308,36 @@ function unicode.utf16_to_utf8(str, endian) -- maybe a gsub is faster or an lpeg end end end - if #tmp > 0 then - result[#result+1] = concat(tmp) + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) end return result end function unicode.utf32_to_utf8(str, endian) - local result = { } - local tmp, n, m, p = { }, 0, -1, 0 + local result, tmp, n, m, p, r, t = { }, { }, 0, -1, 0, 0, 0 -- lf | cr | crlf / (cr:13, lf:10) local function doit() if n == 10 then if p ~= 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = 0 end elseif n == 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = n else - tmp[#tmp+1] = utfchar(n) + t = t + 1 + tmp[t] = utfchar(n) p = 0 end end @@ -3210,15 +3345,15 @@ function unicode.utf32_to_utf8(str, endian) if a and b then if m < 0 then if endian then - m = a*256*256*256 + b*256*256 + m = 256*256*256*a + 256*256*b else - m = b*256 + a + m = 256*b + a end else if endian then - n = m + a*256 + b + n = m + 256*a + b else - n = m + b*256*256*256 + a*256*256 + n = m + 256*256*256*b + 256*256*a end m = -1 doit() @@ -3228,13 +3363,14 @@ function unicode.utf32_to_utf8(str, endian) end end if #tmp > 0 then - result[#result+1] = concat(tmp) + r = r + 1 + result[r] = concat(tmp,"",1,t) end return result end local function little(c) - local b = byte(c) -- b = c:byte() + local b = byte(c) if b < 0x10000 then return char(b%256,b/256) else @@ -3264,9 +3400,10 @@ function unicode.utf8_to_utf16(str,littleendian) end function unicode.utfcodes(str) - local t = { } - for k,v in utfvalues(str) do - t[#t+1] = format("0x%04X",k) + local t, n = { }, 0 + for u in utfvalues(str) do + n = n + 1 + t[n] = format("0x%04X",u) end return concat(t,separator or " ") end @@ -3324,6 +3461,189 @@ end -- of closure do -- create closure to overcome 200 locals limit +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 = string.format, string.gmatch +local concat, insert, remove = table.concat, table.insert, table.remove +local setmetatable = setmetatable + +function tables.definetable(target) -- defines undefined tables + local composed, t, n = nil, { }, 0 + for name in gmatch(target,"([^%.]+)") do + n = n + 1 + if composed then + composed = composed .. "." .. name + else + composed = name + end + t[n] = format("%s = %s or { }",composed,composed) + end + return concat(t,"\n") +end + +function tables.accesstable(target) + local t = _G + for name in gmatch(target,"([^%.]+)") do + t = t[name] + end + return t +end + +function tables.removevalue(t,value) -- todo: n + if value then + for i=1,#t do + if t[i] == value then + remove(t,i) + -- remove all, so no: return + 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 _empty_table_ = { __index = function(t,k) return "" end } + +function table.setemptymetatable(t) + setmetatable(t,_empty_table_) +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +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 = setmetatable, getmetatable + +utilities = utilities or { } +utilities.storage = utilities.storage or { } +local storage = utilities.storage + +function storage.mark(t) + if not t then + texio.write_nl("fatal error: storage '%s' cannot be marked",t) + os.exit() + 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 + texio.write_nl("fatal error: storage '%s' has not been allocated",t) + os.exit() + end + return t +end + +function setmetatablekey(t,key,value) + local m = getmetatable(t) + if not m then + m = { } + setmetatable(t,m) + end + m[key] = value +end + +function getmetatablekey(t,key,value) + local m = getmetatable(t) + return m and m[key] +end + + +function storage.setinitializer(data,initialize) + local m = getmetatable(data) or { } + m.__index = function(data,k) + m.__index = nil -- so that we can access the entries during initializing + 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 + + +end -- of closure + +do -- create closure to overcome 200 locals limit + if not modules then modules = { } end modules ['util-mrg'] = { version = 1.001, comment = "companion to luat-lib.mkiv", @@ -3483,7 +3803,7 @@ utilities.report = utilities.report or print function utilities.lua.compile(luafile,lucfile,cleanup,strip) -- defaults: cleanup=false strip=true utilities.report("lua: compiling %s into %s",luafile,lucfile) os.remove(lucfile) - local command = "-o " .. string.quote(lucfile) .. " " .. string.quote(luafile) + local command = "-o " .. string.quoted(lucfile) .. " " .. string.quoted(luafile) if strip ~= false then command = "-s " .. command end @@ -3496,6 +3816,12 @@ function utilities.lua.compile(luafile,lucfile,cleanup,strip) -- defaults: clean end + + + + + + end -- of closure do -- create closure to overcome 200 locals limit @@ -3516,7 +3842,7 @@ parsers.patterns = parsers.patterns or { } local P, R, V, C, Ct, Carg = lpeg.P, lpeg.R, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Carg local lpegmatch = lpeg.match local concat, format, gmatch = table.concat, string.format, string.gmatch -local tostring, type, next = tostring, type, next +local tostring, type, next, setmetatable = tostring, type, next, setmetatable local sortedhash = table.sortedhash local escape, left, right = P("\\"), P('{'), P('}') @@ -3635,7 +3961,7 @@ end function parsers.hash_to_string(h,separator,yes,no,strict,omit) if h then - local t, s = { }, table.sortedkeys(h) + local t, tn, s = { }, 0, table.sortedkeys(h) omit = omit and table.tohash(omit) for i=1,#s do local key = s[i] @@ -3644,15 +3970,19 @@ function parsers.hash_to_string(h,separator,yes,no,strict,omit) if type(value) == "boolean" then if yes and no then if value then - t[#t+1] = key .. '=' .. yes + tn = tn + 1 + t[tn] = key .. '=' .. yes elseif not strict then - t[#t+1] = key .. '=' .. no + tn = tn + 1 + t[tn] = key .. '=' .. no end elseif value or not strict then - t[#t+1] = key .. '=' .. tostring(value) + tn = tn + 1 + t[tn] = key .. '=' .. tostring(value) end else - t[#t+1] = key .. '=' .. value + tn = tn + 1 + t[tn] = key .. '=' .. value end end end @@ -3679,10 +4009,11 @@ function parsers.settings_to_set(str,t) -- tohash? -- todo: lpeg -- duplicate an end function parsers.simple_hash_to_string(h, separator) - local t = { } + local t, tn = { }, 0 for k, v in sortedhash(h) do if v then - t[#t+1] = k + tn = tn + 1 + t[tn] = k end end return concat(t,separator or ",") @@ -3700,99 +4031,18 @@ end function parsers.getparameters(self,class,parentclass,settings) local sc = self[class] if not sc then - sc = table.clone(self[parent]) + sc = { } self[class] = sc - end - parsers.settings_to_hash(settings,sc) -end - - -end -- of closure - -do -- create closure to overcome 200 locals limit - -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 = string.format, string.gmatch -local concat, insert, remove = table.concat, table.insert, table.remove -local setmetatable = setmetatable - -function tables.definetable(target) -- defines undefined tables - local composed, t = nil, { } - for name in gmatch(target,"([^%.]+)") do - if composed then - composed = composed .. "." .. name - else - composed = name - end - t[#t+1] = format("%s = %s or { }",composed,composed) - end - return concat(t,"\n") -end - -function tables.accesstable(target) - local t = _G - for name in gmatch(target,"([^%.]+)") do - t = t[name] - end - return t -end - -function table.removevalue(t,value) -- todo: n - if value then - for i=1,#t do - if t[i] == value then - remove(t,i) - -- remove all, so no: return + if parentclass then + local sp = self[parentclass] + if not sp then + sp = { } + self[parentclass] = sp end + setmetatable(sc, { __index = sp }) end end -end - -function table.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 table.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 _empty_table_ = { __index = function(t,k) return "" end } - -function table.setemptymetatable(t) - setmetatable(t,_empty_table_) + parsers.settings_to_hash(settings,sc) end @@ -3831,9 +4081,9 @@ local number = digit^1 * (case_1 + case_2) local stripper = Cs((number + 1)^0) -lpeg.patterns.strip_zeros = stripper +lpeg.patterns.stripzeros = stripper -function formatters.strip_zeros(str) +function formatters.stripzeros(str) return lpegmatch(stripper,str) end @@ -4005,88 +4255,6 @@ end -- of closure do -- create closure to overcome 200 locals limit -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 = setmetatable, getmetatable - -utilities = utilities or { } -utilities.storage = utilities.storage or { } -local storage = utilities.storage - -function storage.mark(t) - if not t then - texio.write_nl("fatal error: storage '%s' cannot be marked",t) - os.exit() - 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 - texio.write_nl("fatal error: storage '%s' has not been allocated",t) - os.exit() - end - return t -end - -function setmetatablekey(t,key,value) - local m = getmetatable(t) - if not m then - m = { } - setmetatable(t,m) - end - m[key] = value -end - -function getmetatablekey(t,key,value) - local m = getmetatable(t) - return m and m[key] -end - - -function storage.setinitializer(data,initialize) - local m = getmetatable(data) or { } - m.__index = function(data,k) - m.__index = nil -- so that we can access the entries during initializing - initialize() - return data[k] - end - setmetatable(data, m) -end - - -end -- of closure - -do -- create closure to overcome 200 locals limit - if not modules then modules = { } end modules ['trac-inf'] = { version = 1.001, comment = "companion to trac-inf.mkiv", @@ -4102,6 +4270,7 @@ if not modules then modules = { } end modules ['trac-inf'] = { local format = string.format local clock = os.gettimeofday or os.clock -- should go in environment +local write_nl = texio.write_nl statistics = statistics or { } local statistics = statistics @@ -4197,7 +4366,7 @@ end function statistics.show(reporter) if statistics.enable then - if not reporter then reporter = function(tag,data,n) texio.write_nl(tag .. " " .. data) end end + if not reporter then reporter = function(tag,data,n) write_nl(tag .. " " .. data) end end -- this code will move local register = statistics.register register("luatex banner", function() @@ -4220,18 +4389,24 @@ function statistics.show(reporter) reporter(s[1],r,n) end end - texio.write_nl("") -- final newline + write_nl("") -- final newline statistics.enable = false end end + +local template, nn = nil, 0 -- we only calcute it once + function statistics.showjobstat(tag,data,n) if type(data) == "table" then for i=1,#data do statistics.showjobstat(tag,data[i],n) end else - texio.write_nl(format("%-15s: %s - %s","mkiv lua stats",tag:rpadd(n," "),data)) + if not template or n > nn then + template, n = format("%%-%ss: %%-%ss - %%s",15,n), nn + end + write_nl(format(template,"mkiv lua stats",tag,data)) end end @@ -4288,7 +4463,7 @@ if not modules then modules = { } end modules ['trac-set'] = { -- might become u local type, next, tostring = type, next, tostring local concat = table.concat -local format, find, lower, gsub, partialescapedpattern = string.format, string.find, string.lower, string.gsub, string.partialescapedpattern +local format, find, lower, gsub, escapedpattern = string.format, string.find, string.lower, string.gsub, string.escapedpattern local is_boolean = string.is_boolean local settings_to_hash = utilities.parsers.settings_to_hash local allocate = utilities.storage.allocate @@ -4368,7 +4543,7 @@ local function set(t,what,newvalue) for name, functions in next, data do if done[name] then -- prevent recursion due to wildcards - elseif find(name,partialescapedpattern(w)) then + elseif find(name,escapedpattern(w,true)) then done[name] = true for i=1,#functions do functions[i](value) @@ -5151,7 +5326,7 @@ local report_resolvers = logs.new("resolvers") local allocate, mark = utilities.storage.allocate, utilities.storage.mark local format, sub, match, gsub, find = string.format, string.sub, string.match, string.gsub, string.find -local unquote, quote = string.unquote, string.quote +local unquoted, quoted = string.unquoted, string.quoted local concat = table.concat -- precautions @@ -5219,7 +5394,7 @@ function environment.initializearguments(arg) if index > 0 then local flag, value = match(argument,"^%-+(.-)=(.-)$") if flag then - arguments[flag] = unquote(value or "") + arguments[flag] = unquoted(value or "") else flag = match(argument,"^%-+(.+)") if flag then @@ -5284,19 +5459,21 @@ end function environment.reconstructcommandline(arg,noquote) arg = arg or environment.originalarguments if noquote and #arg == 1 then + -- we could just do: return unquoted(resolvers.resolve(arg[i])) local a = arg[1] a = resolvers.resolve(a) - a = unquote(a) + a = unquoted(a) return a elseif #arg > 0 then local result = { } for i=1,#arg do + -- we could just do: result[#result+1] = format("%q",unquoted(resolvers.resolve(arg[i]))) local a = arg[i] a = resolvers.resolve(a) - a = unquote(a) + a = unquoted(a) a = gsub(a,'"','\\"') -- tricky if find(a," ") then - result[#result+1] = quote(a) + result[#result+1] = quoted(a) else result[#result+1] = a end @@ -5307,6 +5484,7 @@ function environment.reconstructcommandline(arg,noquote) end end + if arg then -- new, reconstruct quoted snippets (maybe better just remove the " then and add them later) @@ -6767,7 +6945,7 @@ apply_axis['root'] = function(list) rt = ll end end - collected[#collected+1] = rt + collected[l] = rt end return collected end @@ -6777,7 +6955,7 @@ apply_axis['self'] = function(list) end apply_axis['child'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local dt = ll.dt @@ -6786,7 +6964,8 @@ apply_axis['child'] = function(list) for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en @@ -6798,68 +6977,74 @@ apply_axis['child'] = function(list) return collected end -local function collect(list,collected) +local function collect(list,collected,c) local dt = list.dt if dt then local en = 0 for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en - collect(dk,collected) + c = collect(dk,collected,c) end end list.en = en end + return c end apply_axis['descendant'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do - collect(list[l],collected) + c = collect(list[l],collected,c) end return collected end -local function collect(list,collected) +local function collect(list,collected,c) local dt = list.dt if dt then local en = 0 for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en - collect(dk,collected) + c = collect(dk,collected,c) end end list.en = en end + return c end apply_axis['descendant-or-self'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] if ll.special ~= true then -- catch double root - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end - collect(ll,collected) + c = collect(ll,collected,c) end return collected end apply_axis['ancestor'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] while ll do ll = ll.__p__ if ll then - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end end end @@ -6867,14 +7052,16 @@ apply_axis['ancestor'] = function(list) end apply_axis['ancestor-or-self'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll while ll do ll = ll.__p__ if ll then - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end end end @@ -6882,11 +7069,12 @@ apply_axis['ancestor-or-self'] = function(list) end apply_axis['parent'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local pl = list[l].__p__ if pl then - collected[#collected+1] = pl + c = c + 1 + collected[c] = pl end end return collected @@ -6909,7 +7097,7 @@ apply_axis['preceding'] = function(list) -- incomplete end apply_axis['following-sibling'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -6917,7 +7105,8 @@ apply_axis['following-sibling'] = function(list) for i=ll.ni+1,#d do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -6925,7 +7114,7 @@ apply_axis['following-sibling'] = function(list) end apply_axis['preceding-sibling'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -6933,7 +7122,8 @@ apply_axis['preceding-sibling'] = function(list) for i=1,ll.ni-1 do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -6941,7 +7131,7 @@ apply_axis['preceding-sibling'] = function(list) end apply_axis['reverse-sibling'] = function(list) -- reverse preceding - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -6949,7 +7139,8 @@ apply_axis['reverse-sibling'] = function(list) -- reverse preceding for i=ll.ni-1,1,-1 do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -6975,7 +7166,7 @@ local function apply_nodes(list,directive,nodes) return { } end else - local collected, m, p = { }, 0, nil + local collected, c, m, p = { }, 0, 0, nil if not nns then -- only check tag for l=1,#list do local ll = list[l] @@ -6984,11 +7175,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ntg == ltg then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif ntg ~= ltg then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7000,11 +7193,13 @@ local function apply_nodes(list,directive,nodes) if directive then if lns == nns then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif lns ~= nns then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7018,11 +7213,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif not ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7030,7 +7227,7 @@ local function apply_nodes(list,directive,nodes) return collected end else - local collected, m, p = { }, 0, nil + local collected, c, m, p = { }, 0, 0, nil for l=1,#list do local ll = list[l] local ltg = ll.tg @@ -7047,11 +7244,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif not ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7062,12 +7261,13 @@ end local quit_expression = false local function apply_expression(list,expression,order) - local collected = { } + local collected, c = { }, 0 quit_expression = false for l=1,#list do local ll = list[l] if expression(list,ll,l,order) then -- nasty, order alleen valid als n=1 - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end if quit_expression then break @@ -7363,7 +7563,7 @@ local function nodesettostring(set,nodetest) if not ns or ns == "" then ns = "*" end if not tg or tg == "" then tg = "*" end tg = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) - t[#t+1] = (directive and tg) or format("not(%s)",tg) + t[i] = (directive and tg) or format("not(%s)",tg) end if nodetest == false then return format("not(%s)",concat(t,"|")) @@ -7382,7 +7582,7 @@ local function tagstostring(list) local ns, tg = li.ns, li.tg if not ns or ns == "" then ns = "*" end if not tg or tg == "" then tg = "*" end - t[#t+1] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) + t[i] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) end return concat(t," ") end @@ -8102,16 +8302,17 @@ end function xml.collect_tags(root, pattern, nonamespace) local collected = xmlapplylpath(root,pattern) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collected[c] local ns, tg = e.ns, e.tg + n = n + 1 if nonamespace then - t[#t+1] = tg + t[n] = tg elseif ns == "" then - t[#t+1] = tg + t[n] = tg else - t[#t+1] = ns .. ":" .. tg + t[n] = ns .. ":" .. tg end end return t @@ -8219,8 +8420,10 @@ local function inject_element(root,pattern,whatever,prepend) else be, af = edt, cp end + local bn = #be for i=1,#af do - be[#be+1] = af[i] + bn = bn + 1 + be[bn] = af[i] end if rri then r.dt[rri].dt = be @@ -8305,11 +8508,12 @@ local function stripelement(e,nolines,anywhere) local edt = e.dt if edt then if anywhere then - local t = { } + local t, n = { }, 0 for e=1,#edt do local str = edt[e] if type(str) ~= "string" then - t[#t+1] = str + n = n + 1 + t[n] = str elseif str ~= "" then -- todo: lpeg for each case if nolines then @@ -8317,7 +8521,8 @@ local function stripelement(e,nolines,anywhere) end str = gsub(str,"^%s*(.-)%s*$","%1") if str ~= "" then - t[#t+1] = str + n = n + 1 + t[n] = str end end end @@ -8344,9 +8549,10 @@ local function stripelement(e,nolines,anywhere) end end end - if #edt > 0 then + local nedt = #nedt + if nedt > 0 then -- strip end - local str = edt[#edt] + local str = edt[nedt] if type(str) ~= "string" then -- nothing elseif str == "" then @@ -8359,7 +8565,7 @@ local function stripelement(e,nolines,anywhere) if str == "" then remove(edt) else - edt[#edt] = str + edt[nedt] = str end end end @@ -8506,15 +8712,8 @@ local function all(collected) return collected end -local function reverse(collected) - if collected then - local reversed = { } - for c=#collected,1,-1 do - reversed[#reversed+1] = collected[c] - end - return reversed - end -end + +local reverse = table.reversed local function attribute(collected,name) if collected and #collected > 0 then @@ -8605,11 +8804,12 @@ end local function texts(collected) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collection[c] if e and e.dt then - t[#t+1] = e.dt + n = n + 1 + t[n] = e.dt end end return t @@ -8652,14 +8852,15 @@ end local function tags(collected,nonamespace) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collected[c] local ns, tg = e.ns, e.tg + n = n + 1 if nonamespace or ns == "" then - t[#t+1] = tg + t[n] = tg else - t[#t+1] = ns .. ":" .. tg + t[n] = ns .. ":" .. tg end end return t @@ -8855,9 +9056,13 @@ end do - local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or '~' + local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or '' + + if not homedir or homedir == "" then + homedir = string.char(127) -- we need a value, later we wil trigger on it + end - homedir = file.collapse_path(homedir) + homedir = file.collapsepath(homedir) ossetenv("HOME", homedir) -- can be used in unix cnf files ossetenv("USERPROFILE",homedir) -- can be used in windows cnf files @@ -8876,8 +9081,8 @@ do local ownbin = environment.ownbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luatex" local ownpath = environment.ownpath or os.selfdir - ownbin = file.collapse_path(ownbin) - ownpath = file.collapse_path(ownpath) + ownbin = file.collapsepath(ownbin) + ownpath = file.collapsepath(ownpath) if not ownpath or ownpath == "" or ownpath == "unset" then ownpath = args[-1] or arg[-1] @@ -8949,9 +9154,9 @@ do local ownpath = environment.ownpath or dir.current() if ownpath then - ossetenv('SELFAUTOLOC', file.collapse_path(ownpath)) - ossetenv('SELFAUTODIR', file.collapse_path(ownpath .. "/..")) - ossetenv('SELFAUTOPARENT', file.collapse_path(ownpath .. "/../..")) + ossetenv('SELFAUTOLOC', file.collapsepath(ownpath)) + ossetenv('SELFAUTODIR', file.collapsepath(ownpath .. "/..")) + ossetenv('SELFAUTOPARENT', file.collapsepath(ownpath .. "/../..")) else report_resolvers("error: unable to locate ownpath") os.exit() @@ -8987,7 +9192,7 @@ if not texroot or texroot == "" then ossetenv('TEXROOT',texroot) end -environment.texroot = file.collapse_path(texroot) +environment.texroot = file.collapsepath(texroot) -- Tracing. Todo ... @@ -9026,7 +9231,7 @@ local lpegCt, lpegCs, lpegP, lpegC, lpegS = lpeg.Ct, lpeg.Cs, lpeg.P, lpeg.C, lp local type, next = type, next local ostype = os.type -local collapse_path = file.collapse_path +local collapsepath = file.collapsepath local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end) local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end) @@ -9062,22 +9267,29 @@ local resolvers = resolvers local dummy_path_expr = "^!*unset/*$" local function do_first(a,b) - local t = { } - for s in gmatch(b,"[^,]+") do t[#t+1] = a .. s end + local t, n = { }, 0 + for s in gmatch(b,"[^,]+") do + n = n + 1 + t[n] = a .. s + end return "{" .. concat(t,",") .. "}" end local function do_second(a,b) - local t = { } - for s in gmatch(a,"[^,]+") do t[#t+1] = s .. b end + local t, n = { }, 0 + for s in gmatch(a,"[^,]+") do + n = n + 1 + t[n] = s .. b + end return "{" .. concat(t,",") .. "}" end local function do_both(a,b) - local t = { } + local t, n = { }, 0 for sa in gmatch(a,"[^,]+") do for sb in gmatch(b,"[^,]+") do - t[#t+1] = sa .. sb + n = n + 1 + t[n] = sa .. sb end end return "{" .. concat(t,",") .. "}" @@ -9101,6 +9313,7 @@ local function splitpathexpr(str, newlist, validate) report_resolvers("expanding variable '%s'",str) end local t, ok, done = newlist or { }, false, false + local n = #t str = lpegmatch(replacer_1,str) while true do done = false @@ -9124,11 +9337,15 @@ local function splitpathexpr(str, newlist, validate) if validate then for s in gmatch(str,"[^,]+") do s = validate(s) - if s then t[#t+1] = s end + if s then + n = n + 1 + t[n] = s + end end else for s in gmatch(str,"[^,]+") do - t[#t+1] = s + n = n + 1 + t[n] = s end end if trace_expansions then @@ -9141,7 +9358,7 @@ end local function validate(s) local isrecursive = find(s,"//$") - s = collapse_path(s) + s = collapsepath(s) if isrecursive then s = s .. "//" end @@ -9164,10 +9381,14 @@ function resolvers.expandedpathfromlist(pathlist) -- maybe not a list, just a pa splitpathexpr(pathlist[k],newlist,validate) end else + local n = 0 for k=1,#pathlist do for p in gmatch(pathlist[k],"([^,]+)") do p = validate(p) - if p ~= "" then newlist[#newlist+1] = p end + if p ~= "" then + n = n + 1 + newlist[n] = p + end end end end @@ -9176,16 +9397,42 @@ end -- We also put some cleanup code here. -local cleanup -- used recursively -cleanup = lpeg.replacer { - { "!", "" }, - { "\\", "/" }, - { "~" , function() return lpegmatch(cleanup,environment.homedir) end }, + + +local cleanup = lpeg.replacer { + { "!" , "" }, + { "\\" , "/" }, } +local homedir + function resolvers.cleanpath(str) - return str and lpegmatch(cleanup,str) + if not homedir then + homedir = lpegmatch(cleanup,environment.homedir or "") + if homedir == string.char(127) or homedir == "" or not lfs.isdir(homedir) then + if trace_expansions then + report_resolvers("no home dir set, ignoring dependent paths") + end + function resolvers.cleanpath(str) + if find(str,"~") then + return "" -- special case + else + return str and lpegmatch(cleanup,str) + end + end + else + cleanup = lpeg.replacer { + { "!" , "" }, + { "\\" , "/" }, + { "~" , homedir }, + } + function resolvers.cleanpath(str) + return str and lpegmatch(cleanup,str) + end + end + end + return resolvers.cleanpath(str) end -- This one strips quotes and funny tokens. @@ -9211,7 +9458,6 @@ end -- we join them and split them after the expansion has taken place. This -- is more convenient. - local cache = { } local splitter = lpegCt(lpeg.splitat(lpegS(ostype == "windows" and ";" or ":;"))) -- maybe add , @@ -9226,15 +9472,17 @@ local function splitconfigurationpath(str) -- beware, this can be either a path str = gsub(str,"\\","/") local split = lpegmatch(splitter,str) found = { } + local noffound = 0 for i=1,#split do local s = split[i] if not find(s,"^{*unset}*") then - found[#found+1] = s + noffound = noffound + 1 + found[noffound] = s end end if trace_expansions then report_resolvers("splitting path specification '%s'",str) - for k=1,#found do + for k=1,noffound do report_resolvers("% 4i: %s",k,found[k]) end end @@ -9357,7 +9605,6 @@ formats['enc'] = 'ENCFONTS' suffixes['enc'] = { 'enc' } formats['fmt'] = 'TEXFORMATS' suffixes['fmt'] = { 'fmt' } formats['map'] = 'TEXFONTMAPS' suffixes['map'] = { 'map' } formats['mp'] = 'MPINPUTS' suffixes['mp'] = { 'mp' } -formats['ocp'] = 'OCPINPUTS' suffixes['ocp'] = { 'ocp' } formats['ofm'] = 'OFMFONTS' suffixes['ofm'] = { 'ofm', 'tfm' } formats['otf'] = 'OPENTYPEFONTS' suffixes['otf'] = { 'otf' } formats['opl'] = 'OPLFONTS' suffixes['opl'] = { 'opl' } @@ -9582,7 +9829,7 @@ local function identify() local cachepath = texmfcaches[k] if cachepath ~= "" then cachepath = resolvers.cleanpath(cachepath) - cachepath = file.collapse_path(cachepath) + cachepath = file.collapsepath(cachepath) local valid = isdir(cachepath) if valid then if file.is_readable(cachepath) then @@ -9921,7 +10168,7 @@ function resolvers.splitmethod(filename) end function resolvers.methodhandler(what, filename, filetype) -- ... - filename = file.collapse_path(filename) + filename = file.collapsepath(filename) local specification = (type(filename) == "string" and resolvers.splitmethod(filename)) or filename -- no or { }, let it bomb local scheme = specification.scheme local resolver = resolvers[what] @@ -9966,7 +10213,7 @@ local lpegP, lpegS, lpegR, lpegC, lpegCc, lpegCs, lpegCt = lpeg.P, lpeg.S, lpeg. local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns local filedirname, filebasename, fileextname, filejoin = file.dirname, file.basename, file.extname, file.join -local collapse_path = file.collapse_path +local collapsepath = file.collapsepath local allocate = utilities.storage.allocate local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end) @@ -10169,7 +10416,7 @@ local function identify_configuration_files() expandvars(cnfpaths) --- hm local luacnfname = resolvers.luacnfname for i=1,#cnfpaths do - local filename = collapse_path(filejoin(cnfpaths[i],luacnfname)) + local filename = collapsepath(filejoin(cnfpaths[i],luacnfname)) if lfs.isfile(filename) then specification[#specification+1] = filename end @@ -10330,7 +10577,7 @@ local function locate_file_databases() -- todo: cache:// and tree:// (runtime) local texmfpaths = resolvers.expandedpathlist('TEXMF') for i=1,#texmfpaths do - local path = collapse_path(texmfpaths[i]) + local path = collapsepath(texmfpaths[i]) local stripped = gsub(path,"^!!","") local runtime = stripped == path path = resolvers.cleanpath(path) @@ -10443,11 +10690,12 @@ end function resolvers.splitexpansions() local ie = instance.expansions for k,v in next, ie do - local t, h, p = { }, { }, splitconfigurationpath(v) + local t, tn, h, p = { }, 0, { }, splitconfigurationpath(v) for kk=1,#p do local vv = p[kk] if vv ~= "" and not h[vv] then - t[#t+1] = vv + tn = tn + 1 + t[tn] = vv h[vv] = true end end @@ -10554,7 +10802,8 @@ end function resolvers.registerextrapath(paths,subpaths) local ep = instance.extra_paths or { } - local n = #ep + local oldn = #ep + local newn = oldn if paths and paths ~= "" then if subpaths and subpaths ~= "" then for p in gmatch(paths,"[^,]+") do @@ -10562,7 +10811,8 @@ function resolvers.registerextrapath(paths,subpaths) for s in gmatch(subpaths,"[^,]+") do local ps = p .. "/" .. s if not done[ps] then - ep[#ep+1] = resolvers.cleanpath(ps) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(ps) done[ps] = true end end @@ -10570,7 +10820,8 @@ function resolvers.registerextrapath(paths,subpaths) else for p in gmatch(paths,"[^,]+") do if not done[p] then - ep[#ep+1] = resolvers.cleanpath(p) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(p) done[p] = true end end @@ -10581,16 +10832,17 @@ function resolvers.registerextrapath(paths,subpaths) for s in gmatch(subpaths,"[^,]+") do local ps = ep[i] .. "/" .. s if not done[ps] then - ep[#ep+1] = resolvers.cleanpath(ps) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(ps) done[ps] = true end end end end - if #ep > 0 then + if newn > 0 then instance.extra_paths = ep -- register paths end - if #ep > n then + if newn > oldn then instance.lists = { } -- erase the cache end end @@ -10600,14 +10852,15 @@ local function made_list(instance,list) if not ep or #ep == 0 then return list else - local done, new = { }, { } + local done, new, newn = { }, { }, 0 -- honour . .. ../.. but only when at the start for k=1,#list do local v = list[k] if not done[v] then if find(v,"^[%.%/]$") then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v else break end @@ -10618,7 +10871,8 @@ local function made_list(instance,list) local v = ep[k] if not done[v] then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v end end -- next the formal paths @@ -10626,7 +10880,8 @@ local function made_list(instance,list) local v = list[k] if not done[v] then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v end end return new @@ -10637,7 +10892,7 @@ function resolvers.cleanpathlist(str) local t = resolvers.expandedpathlist(str) if t then for i=1,#t do - t[i] = collapse_path(resolvers.cleanpath(t[i])) + t[i] = collapsepath(resolvers.cleanpath(t[i])) end end return t @@ -10703,7 +10958,7 @@ resolvers.isreadable.tex = resolvers.isreadable.file -- name/name local function collect_files(names) - local filelist = { } + local filelist, noffiles = { }, 0 for k=1,#names do local fname = names[k] if trace_detail then @@ -10744,7 +10999,8 @@ local function collect_files(names) if trace_detail then report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result) end - filelist[#filelist+1] = { kind, search, result } + noffiles = noffiles + 1 + filelist[noffiles] = { kind, search, result } end else for kk=1,#blobfile do @@ -10756,7 +11012,8 @@ local function collect_files(names) if trace_detail then report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result) end - filelist[#filelist+1] = { kind, search, result } + noffiles = noffiles + 1 + filelist[noffiles] = { kind, search, result } end end end @@ -10766,7 +11023,7 @@ local function collect_files(names) end end end - return #filelist > 0 and filelist or nil + return noffiles > 0 and filelist or nil end function resolvers.registerintrees(name) @@ -10792,7 +11049,7 @@ end local function collect_instance_files(filename,collected) -- todo : plugin (scanners, checkers etc) local result = collected or { } local stamp = nil - filename = collapse_path(filename) + filename = collapsepath(filename) -- speed up / beware: format problem if instance.remember then stamp = filename .. "--" .. instance.engine .. "--" .. instance.progname .. "--" .. instance.format @@ -11045,7 +11302,7 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan end end for k=1,#result do - local rk = collapse_path(result[k]) + local rk = collapsepath(result[k]) result[k] = rk resolvers.registerintrees(rk) -- for tracing used files end @@ -12439,7 +12696,7 @@ function resolvers.load_tree(tree) local texos = "texmf-" .. os.platform local oldroot = environment.texroot - local newroot = file.collapse_path(tree) + local newroot = file.collapsepath(tree) local newtree = file.join(newroot,texos) local newpath = file.join(newtree,"bin") @@ -12668,17 +12925,17 @@ local format = string.format -- helper for mtxrun -local quote = string.quote +local quoted = string.quoted local function primaryflags() local trackers = environment.argument("trackers") local directives = environment.argument("directives") local flags = "" if trackers and trackers ~= "" then - flags = flags .. "--trackers=" .. quote(trackers) + flags = flags .. "--trackers=" .. quoted(trackers) end if directives and directives ~= "" then - flags = flags .. "--directives=" .. quote(directives) + flags = flags .. "--directives=" .. quoted(directives) end return flags end @@ -12743,7 +13000,7 @@ function environment.make_format(name) return end -- generate format - local command = format("luatex --ini %s --lua=%s %s %sdump",primaryflags(),quote(usedluastub),quote(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\") + local command = format("luatex --ini %s --lua=%s %s %sdump",primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\") logs.simple("running command: %s\n",command) os.spawn(command) -- remove related mem files @@ -12781,8 +13038,7 @@ function environment.run_format(name,data,more) logs.simple("using format name: %s",fmtname) logs.simple("no luc/lua with name: %s",barename) else - local q = string.quote - local command = format("luatex %s --fmt=%s --lua=%s %s %s",primaryflags(),quote(barename),quote(luaname),quote(data),more ~= "" and quote(more) or "") + local command = format("luatex %s --fmt=%s --lua=%s %s %s",primaryflags(),quoted(barename),quoted(luaname),quoted(data),more ~= "" and quoted(more) or "") logs.simple("running command: %s",command) os.spawn(command) end @@ -12813,13 +13069,13 @@ own.libs = { -- order can be made better 'l-unicode.lua', 'l-math.lua', + 'util-tab.lua', + 'util-sto.lua', 'util-mrg.lua', 'util-lua.lua', 'util-prs.lua', - 'util-tab.lua', 'util-fmt.lua', 'util-deb.lua', - 'util-sto.lua', 'trac-inf.lua', 'trac-set.lua', diff --git a/scripts/context/stubs/unix/mtxrun b/scripts/context/stubs/unix/mtxrun index d091fa405..7af51ba30 100644 --- a/scripts/context/stubs/unix/mtxrun +++ b/scripts/context/stubs/unix/mtxrun @@ -54,7 +54,7 @@ if not modules then modules = { } end modules ['l-string'] = { local string = string local sub, gsub, find, match, gmatch, format, char, byte, rep, lower = string.sub, string.gsub, string.find, string.match, string.gmatch, string.format, string.char, string.byte, string.rep, string.lower -local lpegmatch = lpeg.match +local lpegmatch, S, C, Ct = lpeg.match, lpeg.S, lpeg.C, lpeg.Ct -- some functions may disappear as they are not used anywhere @@ -62,145 +62,68 @@ if not string.split then -- this will be overloaded by a faster lpeg variant - function string:split(pattern) - if #self > 0 then - local t = { } - for s in gmatch(self..pattern,"(.-)"..pattern) do - t[#t+1] = s + function string.split(str,pattern) + local t = { } + if #str > 0 then + local n = 1 + for s in gmatch(str..pattern,"(.-)"..pattern) do + t[n] = s + n = n + 1 end - return t - else - return { } end + return t end end -string.patterns = { } - -local escapes = { - ["%"] = "%%", - ["."] = "%.", - ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", - ["^"] = "%^", ["$"] = "%$", - ["["] = "%[", ["]"] = "%]", - ["("] = "%(", [")"] = "%)", - ["{"] = "%{", ["}"] = "%}" -} - -string.patterns.escapes = escapes - -function string:esc() -- variant 2 - return (gsub(self,"(.)",escapes)) -end - -function string:unquote() - return (gsub(self,"^([\"\'])(.*)%1$","%2")) +function string.unquoted(str) + return (gsub(str,"^([\"\'])(.*)%1$","%2")) end -function string:quote() -- we could use format("%q") - return format("%q",self) +function string.quoted(str) + return format("%q",str) -- always " end -function string:count(pattern) -- variant 3 +function string.count(str,pattern) -- variant 3 local n = 0 - for _ in gmatch(self,pattern) do + for _ in gmatch(str,pattern) do -- not for utf n = n + 1 end return n end -function string:limit(n,sentinel) - if #self > n then +function string.limit(str,n,sentinel) + if #str > n then sentinel = sentinel or " ..." - return sub(self,1,(n-#sentinel)) .. sentinel + return sub(str,1,(n-#sentinel)) .. sentinel else - return self - end -end - - -do -- roberto's variant: - local space = lpeg.S(" \t\v\n") - local nospace = 1 - space - local stripper = space^0 * lpeg.C((space^0 * nospace^1)^0) - function string.strip(str) - return lpegmatch(stripper,str) or "" - end -end - -function string:is_empty() - return not find(self,"%S") -end - -function string:enhance(pattern,action) - local ok, n = true, 0 - while ok do - ok = false - self = gsub(self,pattern, function(...) - ok, n = true, n + 1 - return action(...) - end) + return str end - return self, n end -if not string.characters then - - local function nextchar(str, index) - index = index + 1 - return (index <= #str) and index or nil, sub(str,index,index) - end - function string:characters() - return nextchar, self, 0 - end - local function nextbyte(str, index) - index = index + 1 - return (index <= #str) and index or nil, byte(sub(str,index,index)) - end - function string:bytes() - return nextbyte, self, 0 - end +local space = S(" \t\v\n") +local nospace = 1 - space +local stripper = space^0 * C((space^0 * nospace^1)^0) -- roberto's code +function string.strip(str) + return lpegmatch(stripper,str) or "" end -function string:rpadd(n,chr) - local m = n-#self - if m > 0 then - return self .. rep(chr or " ",m) - else - return self - end -end - -function string:lpadd(n,chr) - local m = n-#self - if m > 0 then - return rep(chr or " ",m) .. self - else - return self - end +function string.is_empty(str) + return not find(str,"%S") end -string.padd = string.rpadd - local patterns_escapes = { - ["-"] = "%-", - ["."] = "%.", - ["+"] = "%+", - ["*"] = "%*", ["%"] = "%%", - ["("] = "%)", - [")"] = "%)", - ["["] = "%[", - ["]"] = "%]", + ["."] = "%.", + ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", + ["["] = "%[", ["]"] = "%]", + ["("] = "%)", [")"] = "%)", + -- ["{"] = "%{", ["}"] = "%}" + -- ["^"] = "%^", ["$"] = "%$", } -function string:escapedpattern() - return (gsub(self,".",patterns_escapes)) -end - local simple_escapes = { ["-"] = "%-", ["."] = "%.", @@ -208,24 +131,32 @@ local simple_escapes = { ["*"] = ".*", } -function string:partialescapedpattern() - return (gsub(self,".",simple_escapes)) +function string.escapedpattern(str,simple) + if simple then + return (gsub(str,".",simple_escapes)) + else + return (gsub(str,".",patterns_escapes)) + end end -function string:tohash() - local t = { } - for s in gmatch(self,"([^, ]+)") do -- lpeg - t[s] = true +function string.topattern(str,lowercase,strict) + if str == "" then + return ".*" + else + str = gsub(str,".",simple_escapes) + if lowercase then + str = lower(str) + end + if strict then + return "^" .. str .. "$" + else + return str + end end - return t end -local pattern = lpeg.Ct(lpeg.C(1)^0) - -function string:totable() - return lpegmatch(pattern,self) -end +-- The following functions might end up in another namespace. function string.tabtospace(str,tab) -- we don't handle embedded newlines @@ -246,30 +177,17 @@ function string.tabtospace(str,tab) return str end -function string:compactlong() -- strips newlines and leading spaces - self = gsub(self,"[\n\r]+ *","") - self = gsub(self,"^ *","") - return self -end -function string:striplong() -- strips newlines and leading spaces - self = gsub(self,"^%s*","") - self = gsub(self,"[\n\r]+ *","\n") - return self +function string.striplong(str) -- strips all leading spaces + str = gsub(str,"^%s*","") + str = gsub(str,"[\n\r]+ *","\n") + return str end -function string:topattern(lowercase,strict) - if lowercase then - self = lower(self) - end - self = gsub(self,".",simple_escapes) - if self == "" then - self = ".*" - elseif strict then - self = "^" .. self .. "$" - end - return self -end +-- obsolete names: + +string.quote = string.quoted +string.unquote = string.unquoted end -- of closure @@ -292,15 +210,29 @@ local patterns = lpeg.patterns local P, R, S, Ct, C, Cs, Cc, V = lpeg.P, lpeg.R, lpeg.S, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.V local match = lpeg.match +local utfcharacters = string.utfcharacters +local utfgmatch = unicode and unicode.utf8.gmatch + local digit, sign = R('09'), S('+-') local cr, lf, crlf = P("\r"), P("\n"), P("\r\n") -local utf8byte = R("\128\191") +local utf8next = R("\128\191") +local escaped = P("\\") * P(1) +local squote = P("'") +local dquote = P('"') -patterns.utf8byte = utf8byte patterns.utf8one = R("\000\127") -patterns.utf8two = R("\194\223") * utf8byte -patterns.utf8three = R("\224\239") * utf8byte * utf8byte -patterns.utf8four = R("\240\244") * utf8byte * utf8byte * utf8byte +patterns.utf8two = R("\194\223") * utf8next +patterns.utf8three = R("\224\239") * utf8next * utf8next +patterns.utf8four = R("\240\244") * utf8next * utf8next * utf8next +patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') + +local utf8char = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four +local validutf8char = utf8char^0 * P(-1) * Cc(true) + Cc(false) + +patterns.utf8 = utf8char +patterns.utf8char = utf8char +patterns.validutf8 = validutf8char +patterns.validutf8char = validutf8char patterns.digit = digit patterns.sign = sign @@ -327,17 +259,24 @@ patterns.nonspace = 1 - patterns.space patterns.nonspacer = 1 - patterns.spacer patterns.whitespace = patterns.eol + patterns.spacer patterns.nonwhitespace = 1 - patterns.whitespace -patterns.utf8 = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four -patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') -patterns.validutf8 = patterns.utf8^0 * P(-1) * Cc(true) + Cc(false) patterns.comma = P(",") patterns.commaspacer = P(",") * patterns.spacer^0 patterns.period = P(".") - -patterns.undouble = P('"')/"" * (1-P('"'))^0 * P('"')/"" -patterns.unsingle = P("'")/"" * (1-P("'"))^0 * P("'")/"" +patterns.escaped = escaped +patterns.squote = squote +patterns.dquote = dquote +patterns.undouble = (dquote/"") * ((escaped + (1-dquote))^0) * (dquote/"") +patterns.unsingle = (squote/"") * ((escaped + (1-squote))^0) * (squote/"") +patterns.unquoted = patterns.undouble + patterns.unsingle -- more often undouble patterns.unspacer = ((patterns.spacer^1)/"")^0 +local unquoted = Cs(patterns.unquoted * P(-1)) -- not C + +function string.unquoted(str) + return match(unquoted,str) or str +end + + function lpeg.anywhere(pattern) --slightly adapted from website return P { P(pattern) + 1 * V(1) } -- why so complex? end @@ -353,8 +292,8 @@ local content = (empty + nonempty)^1 local capture = Ct(content^0) -function string:splitlines() - return match(capture,self) +function string.splitlines(str) + return match(capture,str) end patterns.textline = content @@ -366,12 +305,12 @@ 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 other, any = C((1 - separator)^0), P(1) + local any = P(1) splitter = other * (separator * C(any^0) + "") -- ? splitters_s[separator] = splitter else - local other = C((1 - separator)^0) splitter = other * (separator * other)^0 splitters_m[separator] = splitter end @@ -392,16 +331,15 @@ function lpeg.split(separator,str) return match(c,str) end -function string:split(separator) +function string.split(str,separator) local c = cache[separator] if not c then c = Ct(splitat(separator)) cache[separator] = c end - return match(c,self) + return match(c,str) end -lpeg.splitters = cache local cache = { } @@ -409,22 +347,22 @@ function lpeg.checkedsplit(separator,str) local c = cache[separator] if not c then separator = P(separator) - local other = C((1 - separator)^0) + local other = C((1 - separator)^1) c = Ct(separator^0 * other * (separator^1 * other)^0) cache[separator] = c end return match(c,str) end -function string:checkedsplit(separator) +function string.checkedsplit(str,separator) local c = cache[separator] if not c then separator = P(separator) - local other = C((1 - separator)^0) + local other = C((1 - separator)^1) c = Ct(separator^0 * other * (separator^1 * other)^0) cache[separator] = c end - return match(c,self) + return match(c,str) end @@ -435,7 +373,9 @@ local function f2(s) local c1, c2 = f1(s,1,2) return c1 * 64 + c2 local function f3(s) local c1, c2, c3 = f1(s,1,3) return (c1 * 64 + c2) * 64 + c3 - 925824 end local function f4(s) local c1, c2, c3, c4 = f1(s,1,4) return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168 end -patterns.utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 +local utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 + +patterns.utf8byte = utf8byte @@ -469,19 +409,25 @@ function lpeg.keeper(str) end end +-- Just for fun I looked at the used bytecode and +-- p = (p and p + pp) or pp gets one more (testset). + function lpeg.replacer(t) if #t > 0 then local p for i=1,#t do local ti= t[i] local pp = P(ti[1]) / ti[2] - p = (p and p + pp ) or pp + if p then + p = p + pp + else + p = pp + end end return Cs((p + 1)^0) end end - local splitters_f, splitters_s = { }, { } function lpeg.firstofsplit(separator) -- always return value @@ -505,11 +451,170 @@ function lpeg.secondofsplit(separator) -- nil if not split end function lpeg.balancer(left,right) + left, right = P(left), P(right) return P { left * ((1 - left - right) + V(1))^0 * right } end +local nany = utf8char/"" + +function lpeg.counter(pattern) + pattern = Cs((P(pattern)/" " + nany)^0) + return function(str) + return #match(pattern,str) + end +end + +if utfgmatch then + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local n = 0 + for _ in utfgmatch(str,what) do + n = n + 1 + end + return n + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +else + + local cache = { } + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local p = cache[what] + if not p then + p = Cs((P(what)/" " + nany)^0) + cache[p] = p + end + return #match(p,str) + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +end + +local patterns_escapes = { -- also defines in l-string + ["%"] = "%%", + ["."] = "%.", + ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", + ["["] = "%[", ["]"] = "%]", + ["("] = "%)", [")"] = "%)", + -- ["{"] = "%{", ["}"] = "%}" + -- ["^"] = "%^", ["$"] = "%$", +} + +local simple_escapes = { -- also defines in l-string + ["-"] = "%-", + ["."] = "%.", + ["?"] = ".", + ["*"] = ".*", +} + +local p = Cs((S("-.+*%()[]") / patterns_escapes + P(1))^0) +local s = Cs((S("-.+*%()[]") / simple_escapes + P(1))^0) + +function string.escapedpattern(str,simple) + if simple then + return match(s,str) + else + return match(p,str) + end +end + +-- utf extensies + +lpeg.UP = lpeg.P + +if utfcharacters then + + function lpeg.US(str) + local p + for uc in utfcharacters(str) do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + + +elseif utfgmatch then + + function lpeg.US(str) + local p + for uc in utfgmatch(str,".") do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + +else + + function lpeg.US(str) + local p + local f = function(uc) + if p then + p = p + P(uc) + else + p = P(uc) + end + end + match((utf8char/f)^0,str) + return p + end + +end + +local range = Cs(utf8byte) * (Cs(utf8byte) + Cc(false)) + +local utfchar = unicode and unicode.utf8 and unicode.utf8.char + +function lpeg.UR(str,more) + local first, last + if type(str) == "number" then + first = str + last = more or first + else + first, last = match(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 -- a somewhat arbitrary criterium + local p + for i=first,last do + if p then + p = p + P(utfchar(i)) + else + p = P(utfchar(i)) + end + end + return p -- nil when invalid range + else + local f = function(b) + return b >= first and b <= last + end + return utf8byte / f -- nil when invalid range + end +end + + + + end -- of closure do -- create closure to overcome 200 locals limit @@ -577,24 +682,26 @@ end -- extra functions, some might go (when not used) function table.strip(tab) - local lst = { } + local lst, l = { }, 0 for i=1,#tab do local s = gsub(tab[i],"^%s*(.-)%s*$","%1") if s == "" then -- skip this one else - lst[#lst+1] = s + l = l + 1 + lst[l] = s end end return lst end function table.keys(t) - local k = { } + local keys, k = { }, 0 for key, _ in next, t do - k[#k+1] = key + k = k + 1 + keys[k] = key end - return k + return keys end local function compare(a,b) @@ -607,9 +714,10 @@ local function compare(a,b) end local function sortedkeys(tab) - local srt, kind = { }, 0 -- 0=unknown 1=string, 2=number 3=mixed + local srt, kind, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed for key,_ in next, tab do - srt[#srt+1] = key + s = s + 1 + srt[s] = key if kind == 3 then -- no further check else @@ -632,10 +740,11 @@ local function sortedkeys(tab) end local function sortedhashkeys(tab) -- fast one - local srt = { } + local srt, s = { }, 0 for key,_ in next, tab do if key then - srt[#srt+1] = key + s= s + 1 + srt[s] = key end end sort(srt) @@ -649,8 +758,7 @@ local function nothing() end local function sortedhash(t) if t then - local s = sortedhashkeys(t) -- maybe just sortedkeys - local n = 0 + local n, s = 0, sortedkeys(t) -- the robust one local function kv(s) n = n + 1 local k = s[n] @@ -666,20 +774,30 @@ table.sortedhash = sortedhash table.sortedpairs = sortedhash function table.append(t, list) - for _,v in next, list do - insert(t,v) + local n = #t + for i=1,#list do + n = n + 1 + t[n] = list[i] end + return t end function table.prepend(t, list) - for k,v in next, list do - insert(t,k,v) + 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, ...) -- first one is target t = t or { } - local lst = {...} + local lst = { ... } for i=1,#lst do for k, v in next, lst[i] do t[k] = v @@ -689,7 +807,7 @@ function table.merge(t, ...) -- first one is target end function table.merged(...) - local tmp, lst = { }, {...} + local tmp, lst = { }, { ... } for i=1,#lst do for k, v in next, lst[i] do tmp[k] = v @@ -699,22 +817,24 @@ function table.merged(...) end function table.imerge(t, ...) - local lst = {...} + local lst, nt = { ... }, #t for i=1,#lst do local nst = lst[i] for j=1,#nst do - t[#t+1] = nst[j] + nt = nt + 1 + t[nt] = nst[j] end end return t end function table.imerged(...) - local tmp, lst = { }, {...} + local tmp, ntmp, lst = { }, 0, {...} for i=1,#lst do local nst = lst[i] for j=1,#nst do - tmp[#tmp+1] = nst[j] + ntmp = ntmp + 1 + tmp[ntmp] = nst[j] end end return tmp @@ -790,11 +910,14 @@ function table.tohash(t,value) end function table.fromhash(t) - local h = { } + local hsh, h = { }, 0 for k, v in next, t do -- no ipairs here - if v then h[#h+1] = k end + if v then + h = h + 1 + hsh[h] = k + end end - return h + return hsh end table.serialize_functions = true @@ -815,20 +938,23 @@ local function simple_table(t) n = n + 1 end if n == #t then - local tt = { } + local tt, nt = { }, 0 for i=1,#t do local v = t[i] local tv = type(v) if tv == "number" then + nt = nt + 1 if hexify then - tt[#tt+1] = format("0x%04X",v) + tt[nt] = format("0x%04X",v) else - tt[#tt+1] = tostring(v) -- tostring not needed + tt[nt] = tostring(v) -- tostring not needed end elseif tv == "boolean" then - tt[#tt+1] = tostring(v) + nt = nt + 1 + tt[nt] = tostring(v) elseif tv == "string" then - tt[#tt+1] = format("%q",v) + nt = nt + 1 + tt[nt] = format("%q",v) else tt = nil break @@ -1123,10 +1249,11 @@ local function serialize(root,name,_handle,_reduce,_noquotes,_hexify) end -function table.serialize(root,name,reduce,noquotes,hexify) - local t = { } +function table.serialize(root,name,reduce,noquotes,hexify) -- can be faster if flush == false and t as argument + local t, n = { }, 0 local function flush(s) - t[#t+1] = s + n = n + 1 + t[n] = s end serialize(root,name,flush,reduce,noquotes,hexify) return concat(t,"\n") @@ -1152,12 +1279,13 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) if f then local maxtab = table.tofile_maxtab if maxtab > 1 then - local t = { } + local t, n = { }, 0 local function flush(s) - t[#t+1] = s - if #t > maxtab then + n = n + 1 + t[n] = s + if n > maxtab then f:write(concat(t,"\n"),"\n") -- hm, write(sometable) should be nice - t = { } + t, n = { }, 0 -- we could recycle t if needed end end serialize(root,name,flush,reduce,noquotes,hexify) @@ -1173,52 +1301,66 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) end end -local function flatten(t,f,complete) -- is this used? meybe a variant with next, ... - for i=1,#t do - local v = t[i] - if type(v) == "table" then - if complete or type(v[1]) == "table" then - flatten(v,f,complete) +local function flattened(t,f,depth) + if f == nil then + f = { } + depth = 0xFFFF + elseif tonumber(f) then + -- assume then only two arguments are given + 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 + f[k] = v end + end + end + local n = #f + for k=1,#t do + local v = t[k] + if depth > 0 and type(v) == "table" then + flattened(v,f,depth-1) + n = #f else - f[#f+1] = v + n = n + 1 + f[n] = v end end -end - -function table.flatten(t) - local f = { } - flatten(t,f,true) return f end -function table.unnest(t) -- bad name - local f = { } - flatten(t,f,false) - return f -end - -table.flattenonelevel = table.unnest - --- a better one: +table.flattened = flattened -local function flattened(t,f) - if not f then +local function unnest(t,f) -- only used in mk, for old times sake + if not f then -- and only relevant for token lists f = { } end - for k, v in next, t do + for i=1,#t do + local v = t[i] if type(v) == "table" then - flattened(v,f) + if type(v[1]) == "table" then + unnest(v,f) + else + f[#f+1] = v + end else - f[k] = v + f[#f+1] = v end end return f end -table.flattened = flattened +function table.unnest(t) -- bad name + return unnest(t) +end + + local function are_equal(a,b,n,m) -- indexed if a and b and #a == #b then @@ -1244,7 +1386,7 @@ end local function identical(a,b) -- assumes same structure for ka, va in next, a do - local vb = b[k] + local vb = b[ka] if va == vb then -- same elseif type(va) == "table" and type(vb) == "table" then @@ -1258,8 +1400,8 @@ local function identical(a,b) -- assumes same structure return true end -table.are_equal = are_equal table.identical = identical +table.are_equal = are_equal -- maybe also make a combined one @@ -1285,14 +1427,14 @@ function table.contains(t, v) end function table.count(t) - local n, e = 0, next(t) - while e do - n, e = n + 1, next(t,e) + local n = 0 + for k, v in next, t do + n = n + 1 end return n end -function table.swapped(t,s) +function table.swapped(t,s) -- hash local n = { } if s then for k, v in next, s do @@ -1305,52 +1447,34 @@ function table.swapped(t,s) return n end - -function table.clone(t,p) -- t is optional or nil or table - if not p then - t, p = { }, t or { } - elseif not t then - t = { } - end - setmetatable(t, { __index = function(_,key) return p[key] end }) -- why not __index = p ? - return t -end - -function table.hexed(t,seperator) - local tt = { } - for i=1,#t do tt[i] = format("0x%04X",t[i]) end - return concat(tt,seperator or " ") -end - -function table.swaphash(h) -- needs another name - local r = { } - for k,v in next, h do - r[v] = lower(gsub(k," ","")) - end - return r -end - -function table.reverse(t) - local tt = { } - if #t > 0 then - for i=#t,1,-1 do - tt[#tt+1] = t[i] +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 - return tt end function table.sequenced(t,sep,simple) -- hash only - local s = { } + local s, n = { }, 0 for k, v in sortedhash(t) do if simple then if v == true then - s[#s+1] = k + n = n + 1 + s[n] = k elseif v and v~= "" then - s[#s+1] = k .. "=" .. tostring(v) + n = n + 1 + s[n] = k .. "=" .. tostring(v) end else - s[#s+1] = k .. "=" .. tostring(v) + n = n + 1 + s[n] = k .. "=" .. tostring(v) end end return concat(s, sep or " | ") @@ -1360,7 +1484,7 @@ function table.print(...) table.tohandle(print,...) end --- -- -- obsolete but we keep them for a while and will comment them later -- -- -- +-- -- -- obsolete but we keep them for a while and might comment them later -- -- -- -- roughly: copy-loop : unpack : sub == 0.9 : 0.4 : 0.45 (so in critical apps, use unpack) @@ -1375,14 +1499,7 @@ function table.is_empty(t) end function table.has_one_entry(t) - local n = next(t) - return n and not next(t,n) -end - -function table.replace(a,b) - for k,v in next, b do - a[k] = v - end + return t and not next(t,next(t)) end @@ -1627,6 +1744,8 @@ if not modules then modules = { } end modules ['l-number'] = { license = "see context related readme files" } +-- this module will be replaced when we have the bit library + local tostring = tostring local format, floor, insert, match = string.format, math.floor, string.match local concat, insert = table.concat, table.insert @@ -1768,10 +1887,11 @@ function set.tolist(n) if n == 0 or not tabs[n] then return "" else - local t = { } + local t, n = { }, 0 for k, v in next, tabs[n] do if v then - t[#t+1] = k + n = n + 1 + t[n] = k end end return concat(t," ") @@ -1950,7 +2070,7 @@ end -- no need for function anymore as we have more clever code and helpers now -- this metatable trickery might as well disappear -os.resolvers = os.resolvers or { } +os.resolvers = os.resolvers or { } -- will become private local resolvers = os.resolvers @@ -2279,7 +2399,7 @@ end file.isreadable = file.is_readable -- depricated file.iswritable = file.is_writable -- depricated --- todo: lpeg +-- todo: lpeg \\ / .. does not save much local checkedsplit = string.checkedsplit @@ -2295,7 +2415,7 @@ end -- we can hash them weakly -function file.collapse_path(str,anchor) +function file.collapsepath(str,anchor) if anchor and not find(str,"^/") and not find(str,"^%a:") then str = getcurrentdir() .. "/" .. str end @@ -2350,6 +2470,8 @@ function file.collapse_path(str,anchor) end end +file.collapse_path = file.collapsepath + function file.robustname(str) return (gsub(str,"[^%a%d%/%-%.\\]+","-")) @@ -2782,9 +2904,12 @@ local function glob(str,t) end return t elseif isfile(str) then - local t = t or { } - t[#t+1] = str - return t + if t then + t[#t+1] = str + return t + else + return { str } + end else local split = lpegmatch(pattern,str) if split then @@ -2812,6 +2937,7 @@ local function globfiles(path,recurse,func,files) -- func == pattern or function 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 --- skip @@ -2822,12 +2948,9 @@ local function globfiles(path,recurse,func,files) -- func == pattern or function globfiles(path .. "/" .. name,recurse,func,files) end elseif mode == "file" then - if func then - if func(name) then - files[#files+1] = path .. "/" .. name - end - else - files[#files+1] = path .. "/" .. name + if not func or func(name) then + noffiles = noffiles + 1 + files[noffiles] = path .. "/" .. name end end end @@ -3012,8 +3135,12 @@ local type, tonumber = type, tonumber boolean = boolean or { } local boolean = boolean +-- function boolean.tonumber(b) +-- return b and 1 or 0 -- test and test and return or return +-- end + function boolean.tonumber(b) - if b then return 1 else return 0 end + if b then return 1 else return 0 end -- test and return or return end function toboolean(str,tolerant) @@ -3037,6 +3164,8 @@ function toboolean(str,tolerant) end end +string.toboolean = toboolean + function string.is_boolean(str,default) if type(str) == "string" then if str == "true" or str == "yes" or str == "on" or str == "t" then @@ -3048,14 +3177,6 @@ function string.is_boolean(str,default) return default end -function boolean.alwaystrue() - return true -end - -function boolean.falsetrue() - return false -end - end -- of closure @@ -3144,30 +3265,37 @@ function unicode.utftype(f) end function unicode.utf16_to_utf8(str, endian) -- maybe a gsub is faster or an lpeg - local result, tmp, n, m, p = { }, { }, 0, 0, 0 + local result, tmp, n, m, p, r, t = { }, { }, 0, 0, 0, 0, 0 -- we reuse tmp -- lf | cr | crlf / (cr:13, lf:10) local function doit() if n == 10 then if p ~= 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = 0 end elseif n == 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = n else - tmp[#tmp+1] = utfchar(n) + t = t + 1 + tmp[t] = utfchar(n) p = 0 end end for l,r in bytepairs(str) do if r then if endian then - n = l*256 + r + n = 256*l + r else - n = r*256 + l + n = 256*r + l end if m > 0 then n = (m-0xD800)*0x400 + (n-0xDC00) + 0x10000 @@ -3180,29 +3308,36 @@ function unicode.utf16_to_utf8(str, endian) -- maybe a gsub is faster or an lpeg end end end - if #tmp > 0 then - result[#result+1] = concat(tmp) + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) end return result end function unicode.utf32_to_utf8(str, endian) - local result = { } - local tmp, n, m, p = { }, 0, -1, 0 + local result, tmp, n, m, p, r, t = { }, { }, 0, -1, 0, 0, 0 -- lf | cr | crlf / (cr:13, lf:10) local function doit() if n == 10 then if p ~= 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = 0 end elseif n == 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = n else - tmp[#tmp+1] = utfchar(n) + t = t + 1 + tmp[t] = utfchar(n) p = 0 end end @@ -3210,15 +3345,15 @@ function unicode.utf32_to_utf8(str, endian) if a and b then if m < 0 then if endian then - m = a*256*256*256 + b*256*256 + m = 256*256*256*a + 256*256*b else - m = b*256 + a + m = 256*b + a end else if endian then - n = m + a*256 + b + n = m + 256*a + b else - n = m + b*256*256*256 + a*256*256 + n = m + 256*256*256*b + 256*256*a end m = -1 doit() @@ -3228,13 +3363,14 @@ function unicode.utf32_to_utf8(str, endian) end end if #tmp > 0 then - result[#result+1] = concat(tmp) + r = r + 1 + result[r] = concat(tmp,"",1,t) end return result end local function little(c) - local b = byte(c) -- b = c:byte() + local b = byte(c) if b < 0x10000 then return char(b%256,b/256) else @@ -3264,9 +3400,10 @@ function unicode.utf8_to_utf16(str,littleendian) end function unicode.utfcodes(str) - local t = { } - for k,v in utfvalues(str) do - t[#t+1] = format("0x%04X",k) + local t, n = { }, 0 + for u in utfvalues(str) do + n = n + 1 + t[n] = format("0x%04X",u) end return concat(t,separator or " ") end @@ -3324,6 +3461,189 @@ end -- of closure do -- create closure to overcome 200 locals limit +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 = string.format, string.gmatch +local concat, insert, remove = table.concat, table.insert, table.remove +local setmetatable = setmetatable + +function tables.definetable(target) -- defines undefined tables + local composed, t, n = nil, { }, 0 + for name in gmatch(target,"([^%.]+)") do + n = n + 1 + if composed then + composed = composed .. "." .. name + else + composed = name + end + t[n] = format("%s = %s or { }",composed,composed) + end + return concat(t,"\n") +end + +function tables.accesstable(target) + local t = _G + for name in gmatch(target,"([^%.]+)") do + t = t[name] + end + return t +end + +function tables.removevalue(t,value) -- todo: n + if value then + for i=1,#t do + if t[i] == value then + remove(t,i) + -- remove all, so no: return + 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 _empty_table_ = { __index = function(t,k) return "" end } + +function table.setemptymetatable(t) + setmetatable(t,_empty_table_) +end + + +end -- of closure + +do -- create closure to overcome 200 locals limit + +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 = setmetatable, getmetatable + +utilities = utilities or { } +utilities.storage = utilities.storage or { } +local storage = utilities.storage + +function storage.mark(t) + if not t then + texio.write_nl("fatal error: storage '%s' cannot be marked",t) + os.exit() + 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 + texio.write_nl("fatal error: storage '%s' has not been allocated",t) + os.exit() + end + return t +end + +function setmetatablekey(t,key,value) + local m = getmetatable(t) + if not m then + m = { } + setmetatable(t,m) + end + m[key] = value +end + +function getmetatablekey(t,key,value) + local m = getmetatable(t) + return m and m[key] +end + + +function storage.setinitializer(data,initialize) + local m = getmetatable(data) or { } + m.__index = function(data,k) + m.__index = nil -- so that we can access the entries during initializing + 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 + + +end -- of closure + +do -- create closure to overcome 200 locals limit + if not modules then modules = { } end modules ['util-mrg'] = { version = 1.001, comment = "companion to luat-lib.mkiv", @@ -3483,7 +3803,7 @@ utilities.report = utilities.report or print function utilities.lua.compile(luafile,lucfile,cleanup,strip) -- defaults: cleanup=false strip=true utilities.report("lua: compiling %s into %s",luafile,lucfile) os.remove(lucfile) - local command = "-o " .. string.quote(lucfile) .. " " .. string.quote(luafile) + local command = "-o " .. string.quoted(lucfile) .. " " .. string.quoted(luafile) if strip ~= false then command = "-s " .. command end @@ -3496,6 +3816,12 @@ function utilities.lua.compile(luafile,lucfile,cleanup,strip) -- defaults: clean end + + + + + + end -- of closure do -- create closure to overcome 200 locals limit @@ -3516,7 +3842,7 @@ parsers.patterns = parsers.patterns or { } local P, R, V, C, Ct, Carg = lpeg.P, lpeg.R, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Carg local lpegmatch = lpeg.match local concat, format, gmatch = table.concat, string.format, string.gmatch -local tostring, type, next = tostring, type, next +local tostring, type, next, setmetatable = tostring, type, next, setmetatable local sortedhash = table.sortedhash local escape, left, right = P("\\"), P('{'), P('}') @@ -3635,7 +3961,7 @@ end function parsers.hash_to_string(h,separator,yes,no,strict,omit) if h then - local t, s = { }, table.sortedkeys(h) + local t, tn, s = { }, 0, table.sortedkeys(h) omit = omit and table.tohash(omit) for i=1,#s do local key = s[i] @@ -3644,15 +3970,19 @@ function parsers.hash_to_string(h,separator,yes,no,strict,omit) if type(value) == "boolean" then if yes and no then if value then - t[#t+1] = key .. '=' .. yes + tn = tn + 1 + t[tn] = key .. '=' .. yes elseif not strict then - t[#t+1] = key .. '=' .. no + tn = tn + 1 + t[tn] = key .. '=' .. no end elseif value or not strict then - t[#t+1] = key .. '=' .. tostring(value) + tn = tn + 1 + t[tn] = key .. '=' .. tostring(value) end else - t[#t+1] = key .. '=' .. value + tn = tn + 1 + t[tn] = key .. '=' .. value end end end @@ -3679,10 +4009,11 @@ function parsers.settings_to_set(str,t) -- tohash? -- todo: lpeg -- duplicate an end function parsers.simple_hash_to_string(h, separator) - local t = { } + local t, tn = { }, 0 for k, v in sortedhash(h) do if v then - t[#t+1] = k + tn = tn + 1 + t[tn] = k end end return concat(t,separator or ",") @@ -3700,99 +4031,18 @@ end function parsers.getparameters(self,class,parentclass,settings) local sc = self[class] if not sc then - sc = table.clone(self[parent]) + sc = { } self[class] = sc - end - parsers.settings_to_hash(settings,sc) -end - - -end -- of closure - -do -- create closure to overcome 200 locals limit - -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 = string.format, string.gmatch -local concat, insert, remove = table.concat, table.insert, table.remove -local setmetatable = setmetatable - -function tables.definetable(target) -- defines undefined tables - local composed, t = nil, { } - for name in gmatch(target,"([^%.]+)") do - if composed then - composed = composed .. "." .. name - else - composed = name - end - t[#t+1] = format("%s = %s or { }",composed,composed) - end - return concat(t,"\n") -end - -function tables.accesstable(target) - local t = _G - for name in gmatch(target,"([^%.]+)") do - t = t[name] - end - return t -end - -function table.removevalue(t,value) -- todo: n - if value then - for i=1,#t do - if t[i] == value then - remove(t,i) - -- remove all, so no: return + if parentclass then + local sp = self[parentclass] + if not sp then + sp = { } + self[parentclass] = sp end + setmetatable(sc, { __index = sp }) end end -end - -function table.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 table.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 _empty_table_ = { __index = function(t,k) return "" end } - -function table.setemptymetatable(t) - setmetatable(t,_empty_table_) + parsers.settings_to_hash(settings,sc) end @@ -3831,9 +4081,9 @@ local number = digit^1 * (case_1 + case_2) local stripper = Cs((number + 1)^0) -lpeg.patterns.strip_zeros = stripper +lpeg.patterns.stripzeros = stripper -function formatters.strip_zeros(str) +function formatters.stripzeros(str) return lpegmatch(stripper,str) end @@ -4005,88 +4255,6 @@ end -- of closure do -- create closure to overcome 200 locals limit -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 = setmetatable, getmetatable - -utilities = utilities or { } -utilities.storage = utilities.storage or { } -local storage = utilities.storage - -function storage.mark(t) - if not t then - texio.write_nl("fatal error: storage '%s' cannot be marked",t) - os.exit() - 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 - texio.write_nl("fatal error: storage '%s' has not been allocated",t) - os.exit() - end - return t -end - -function setmetatablekey(t,key,value) - local m = getmetatable(t) - if not m then - m = { } - setmetatable(t,m) - end - m[key] = value -end - -function getmetatablekey(t,key,value) - local m = getmetatable(t) - return m and m[key] -end - - -function storage.setinitializer(data,initialize) - local m = getmetatable(data) or { } - m.__index = function(data,k) - m.__index = nil -- so that we can access the entries during initializing - initialize() - return data[k] - end - setmetatable(data, m) -end - - -end -- of closure - -do -- create closure to overcome 200 locals limit - if not modules then modules = { } end modules ['trac-inf'] = { version = 1.001, comment = "companion to trac-inf.mkiv", @@ -4102,6 +4270,7 @@ if not modules then modules = { } end modules ['trac-inf'] = { local format = string.format local clock = os.gettimeofday or os.clock -- should go in environment +local write_nl = texio.write_nl statistics = statistics or { } local statistics = statistics @@ -4197,7 +4366,7 @@ end function statistics.show(reporter) if statistics.enable then - if not reporter then reporter = function(tag,data,n) texio.write_nl(tag .. " " .. data) end end + if not reporter then reporter = function(tag,data,n) write_nl(tag .. " " .. data) end end -- this code will move local register = statistics.register register("luatex banner", function() @@ -4220,18 +4389,24 @@ function statistics.show(reporter) reporter(s[1],r,n) end end - texio.write_nl("") -- final newline + write_nl("") -- final newline statistics.enable = false end end + +local template, nn = nil, 0 -- we only calcute it once + function statistics.showjobstat(tag,data,n) if type(data) == "table" then for i=1,#data do statistics.showjobstat(tag,data[i],n) end else - texio.write_nl(format("%-15s: %s - %s","mkiv lua stats",tag:rpadd(n," "),data)) + if not template or n > nn then + template, n = format("%%-%ss: %%-%ss - %%s",15,n), nn + end + write_nl(format(template,"mkiv lua stats",tag,data)) end end @@ -4288,7 +4463,7 @@ if not modules then modules = { } end modules ['trac-set'] = { -- might become u local type, next, tostring = type, next, tostring local concat = table.concat -local format, find, lower, gsub, partialescapedpattern = string.format, string.find, string.lower, string.gsub, string.partialescapedpattern +local format, find, lower, gsub, escapedpattern = string.format, string.find, string.lower, string.gsub, string.escapedpattern local is_boolean = string.is_boolean local settings_to_hash = utilities.parsers.settings_to_hash local allocate = utilities.storage.allocate @@ -4368,7 +4543,7 @@ local function set(t,what,newvalue) for name, functions in next, data do if done[name] then -- prevent recursion due to wildcards - elseif find(name,partialescapedpattern(w)) then + elseif find(name,escapedpattern(w,true)) then done[name] = true for i=1,#functions do functions[i](value) @@ -5151,7 +5326,7 @@ local report_resolvers = logs.new("resolvers") local allocate, mark = utilities.storage.allocate, utilities.storage.mark local format, sub, match, gsub, find = string.format, string.sub, string.match, string.gsub, string.find -local unquote, quote = string.unquote, string.quote +local unquoted, quoted = string.unquoted, string.quoted local concat = table.concat -- precautions @@ -5219,7 +5394,7 @@ function environment.initializearguments(arg) if index > 0 then local flag, value = match(argument,"^%-+(.-)=(.-)$") if flag then - arguments[flag] = unquote(value or "") + arguments[flag] = unquoted(value or "") else flag = match(argument,"^%-+(.+)") if flag then @@ -5284,19 +5459,21 @@ end function environment.reconstructcommandline(arg,noquote) arg = arg or environment.originalarguments if noquote and #arg == 1 then + -- we could just do: return unquoted(resolvers.resolve(arg[i])) local a = arg[1] a = resolvers.resolve(a) - a = unquote(a) + a = unquoted(a) return a elseif #arg > 0 then local result = { } for i=1,#arg do + -- we could just do: result[#result+1] = format("%q",unquoted(resolvers.resolve(arg[i]))) local a = arg[i] a = resolvers.resolve(a) - a = unquote(a) + a = unquoted(a) a = gsub(a,'"','\\"') -- tricky if find(a," ") then - result[#result+1] = quote(a) + result[#result+1] = quoted(a) else result[#result+1] = a end @@ -5307,6 +5484,7 @@ function environment.reconstructcommandline(arg,noquote) end end + if arg then -- new, reconstruct quoted snippets (maybe better just remove the " then and add them later) @@ -6767,7 +6945,7 @@ apply_axis['root'] = function(list) rt = ll end end - collected[#collected+1] = rt + collected[l] = rt end return collected end @@ -6777,7 +6955,7 @@ apply_axis['self'] = function(list) end apply_axis['child'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local dt = ll.dt @@ -6786,7 +6964,8 @@ apply_axis['child'] = function(list) for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en @@ -6798,68 +6977,74 @@ apply_axis['child'] = function(list) return collected end -local function collect(list,collected) +local function collect(list,collected,c) local dt = list.dt if dt then local en = 0 for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en - collect(dk,collected) + c = collect(dk,collected,c) end end list.en = en end + return c end apply_axis['descendant'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do - collect(list[l],collected) + c = collect(list[l],collected,c) end return collected end -local function collect(list,collected) +local function collect(list,collected,c) local dt = list.dt if dt then local en = 0 for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en - collect(dk,collected) + c = collect(dk,collected,c) end end list.en = en end + return c end apply_axis['descendant-or-self'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] if ll.special ~= true then -- catch double root - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end - collect(ll,collected) + c = collect(ll,collected,c) end return collected end apply_axis['ancestor'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] while ll do ll = ll.__p__ if ll then - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end end end @@ -6867,14 +7052,16 @@ apply_axis['ancestor'] = function(list) end apply_axis['ancestor-or-self'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll while ll do ll = ll.__p__ if ll then - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end end end @@ -6882,11 +7069,12 @@ apply_axis['ancestor-or-self'] = function(list) end apply_axis['parent'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local pl = list[l].__p__ if pl then - collected[#collected+1] = pl + c = c + 1 + collected[c] = pl end end return collected @@ -6909,7 +7097,7 @@ apply_axis['preceding'] = function(list) -- incomplete end apply_axis['following-sibling'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -6917,7 +7105,8 @@ apply_axis['following-sibling'] = function(list) for i=ll.ni+1,#d do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -6925,7 +7114,7 @@ apply_axis['following-sibling'] = function(list) end apply_axis['preceding-sibling'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -6933,7 +7122,8 @@ apply_axis['preceding-sibling'] = function(list) for i=1,ll.ni-1 do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -6941,7 +7131,7 @@ apply_axis['preceding-sibling'] = function(list) end apply_axis['reverse-sibling'] = function(list) -- reverse preceding - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -6949,7 +7139,8 @@ apply_axis['reverse-sibling'] = function(list) -- reverse preceding for i=ll.ni-1,1,-1 do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -6975,7 +7166,7 @@ local function apply_nodes(list,directive,nodes) return { } end else - local collected, m, p = { }, 0, nil + local collected, c, m, p = { }, 0, 0, nil if not nns then -- only check tag for l=1,#list do local ll = list[l] @@ -6984,11 +7175,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ntg == ltg then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif ntg ~= ltg then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7000,11 +7193,13 @@ local function apply_nodes(list,directive,nodes) if directive then if lns == nns then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif lns ~= nns then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7018,11 +7213,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif not ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7030,7 +7227,7 @@ local function apply_nodes(list,directive,nodes) return collected end else - local collected, m, p = { }, 0, nil + local collected, c, m, p = { }, 0, 0, nil for l=1,#list do local ll = list[l] local ltg = ll.tg @@ -7047,11 +7244,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif not ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -7062,12 +7261,13 @@ end local quit_expression = false local function apply_expression(list,expression,order) - local collected = { } + local collected, c = { }, 0 quit_expression = false for l=1,#list do local ll = list[l] if expression(list,ll,l,order) then -- nasty, order alleen valid als n=1 - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end if quit_expression then break @@ -7363,7 +7563,7 @@ local function nodesettostring(set,nodetest) if not ns or ns == "" then ns = "*" end if not tg or tg == "" then tg = "*" end tg = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) - t[#t+1] = (directive and tg) or format("not(%s)",tg) + t[i] = (directive and tg) or format("not(%s)",tg) end if nodetest == false then return format("not(%s)",concat(t,"|")) @@ -7382,7 +7582,7 @@ local function tagstostring(list) local ns, tg = li.ns, li.tg if not ns or ns == "" then ns = "*" end if not tg or tg == "" then tg = "*" end - t[#t+1] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) + t[i] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) end return concat(t," ") end @@ -8102,16 +8302,17 @@ end function xml.collect_tags(root, pattern, nonamespace) local collected = xmlapplylpath(root,pattern) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collected[c] local ns, tg = e.ns, e.tg + n = n + 1 if nonamespace then - t[#t+1] = tg + t[n] = tg elseif ns == "" then - t[#t+1] = tg + t[n] = tg else - t[#t+1] = ns .. ":" .. tg + t[n] = ns .. ":" .. tg end end return t @@ -8219,8 +8420,10 @@ local function inject_element(root,pattern,whatever,prepend) else be, af = edt, cp end + local bn = #be for i=1,#af do - be[#be+1] = af[i] + bn = bn + 1 + be[bn] = af[i] end if rri then r.dt[rri].dt = be @@ -8305,11 +8508,12 @@ local function stripelement(e,nolines,anywhere) local edt = e.dt if edt then if anywhere then - local t = { } + local t, n = { }, 0 for e=1,#edt do local str = edt[e] if type(str) ~= "string" then - t[#t+1] = str + n = n + 1 + t[n] = str elseif str ~= "" then -- todo: lpeg for each case if nolines then @@ -8317,7 +8521,8 @@ local function stripelement(e,nolines,anywhere) end str = gsub(str,"^%s*(.-)%s*$","%1") if str ~= "" then - t[#t+1] = str + n = n + 1 + t[n] = str end end end @@ -8344,9 +8549,10 @@ local function stripelement(e,nolines,anywhere) end end end - if #edt > 0 then + local nedt = #nedt + if nedt > 0 then -- strip end - local str = edt[#edt] + local str = edt[nedt] if type(str) ~= "string" then -- nothing elseif str == "" then @@ -8359,7 +8565,7 @@ local function stripelement(e,nolines,anywhere) if str == "" then remove(edt) else - edt[#edt] = str + edt[nedt] = str end end end @@ -8506,15 +8712,8 @@ local function all(collected) return collected end -local function reverse(collected) - if collected then - local reversed = { } - for c=#collected,1,-1 do - reversed[#reversed+1] = collected[c] - end - return reversed - end -end + +local reverse = table.reversed local function attribute(collected,name) if collected and #collected > 0 then @@ -8605,11 +8804,12 @@ end local function texts(collected) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collection[c] if e and e.dt then - t[#t+1] = e.dt + n = n + 1 + t[n] = e.dt end end return t @@ -8652,14 +8852,15 @@ end local function tags(collected,nonamespace) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collected[c] local ns, tg = e.ns, e.tg + n = n + 1 if nonamespace or ns == "" then - t[#t+1] = tg + t[n] = tg else - t[#t+1] = ns .. ":" .. tg + t[n] = ns .. ":" .. tg end end return t @@ -8855,9 +9056,13 @@ end do - local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or '~' + local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or '' + + if not homedir or homedir == "" then + homedir = string.char(127) -- we need a value, later we wil trigger on it + end - homedir = file.collapse_path(homedir) + homedir = file.collapsepath(homedir) ossetenv("HOME", homedir) -- can be used in unix cnf files ossetenv("USERPROFILE",homedir) -- can be used in windows cnf files @@ -8876,8 +9081,8 @@ do local ownbin = environment.ownbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luatex" local ownpath = environment.ownpath or os.selfdir - ownbin = file.collapse_path(ownbin) - ownpath = file.collapse_path(ownpath) + ownbin = file.collapsepath(ownbin) + ownpath = file.collapsepath(ownpath) if not ownpath or ownpath == "" or ownpath == "unset" then ownpath = args[-1] or arg[-1] @@ -8949,9 +9154,9 @@ do local ownpath = environment.ownpath or dir.current() if ownpath then - ossetenv('SELFAUTOLOC', file.collapse_path(ownpath)) - ossetenv('SELFAUTODIR', file.collapse_path(ownpath .. "/..")) - ossetenv('SELFAUTOPARENT', file.collapse_path(ownpath .. "/../..")) + ossetenv('SELFAUTOLOC', file.collapsepath(ownpath)) + ossetenv('SELFAUTODIR', file.collapsepath(ownpath .. "/..")) + ossetenv('SELFAUTOPARENT', file.collapsepath(ownpath .. "/../..")) else report_resolvers("error: unable to locate ownpath") os.exit() @@ -8987,7 +9192,7 @@ if not texroot or texroot == "" then ossetenv('TEXROOT',texroot) end -environment.texroot = file.collapse_path(texroot) +environment.texroot = file.collapsepath(texroot) -- Tracing. Todo ... @@ -9026,7 +9231,7 @@ local lpegCt, lpegCs, lpegP, lpegC, lpegS = lpeg.Ct, lpeg.Cs, lpeg.P, lpeg.C, lp local type, next = type, next local ostype = os.type -local collapse_path = file.collapse_path +local collapsepath = file.collapsepath local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end) local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end) @@ -9062,22 +9267,29 @@ local resolvers = resolvers local dummy_path_expr = "^!*unset/*$" local function do_first(a,b) - local t = { } - for s in gmatch(b,"[^,]+") do t[#t+1] = a .. s end + local t, n = { }, 0 + for s in gmatch(b,"[^,]+") do + n = n + 1 + t[n] = a .. s + end return "{" .. concat(t,",") .. "}" end local function do_second(a,b) - local t = { } - for s in gmatch(a,"[^,]+") do t[#t+1] = s .. b end + local t, n = { }, 0 + for s in gmatch(a,"[^,]+") do + n = n + 1 + t[n] = s .. b + end return "{" .. concat(t,",") .. "}" end local function do_both(a,b) - local t = { } + local t, n = { }, 0 for sa in gmatch(a,"[^,]+") do for sb in gmatch(b,"[^,]+") do - t[#t+1] = sa .. sb + n = n + 1 + t[n] = sa .. sb end end return "{" .. concat(t,",") .. "}" @@ -9101,6 +9313,7 @@ local function splitpathexpr(str, newlist, validate) report_resolvers("expanding variable '%s'",str) end local t, ok, done = newlist or { }, false, false + local n = #t str = lpegmatch(replacer_1,str) while true do done = false @@ -9124,11 +9337,15 @@ local function splitpathexpr(str, newlist, validate) if validate then for s in gmatch(str,"[^,]+") do s = validate(s) - if s then t[#t+1] = s end + if s then + n = n + 1 + t[n] = s + end end else for s in gmatch(str,"[^,]+") do - t[#t+1] = s + n = n + 1 + t[n] = s end end if trace_expansions then @@ -9141,7 +9358,7 @@ end local function validate(s) local isrecursive = find(s,"//$") - s = collapse_path(s) + s = collapsepath(s) if isrecursive then s = s .. "//" end @@ -9164,10 +9381,14 @@ function resolvers.expandedpathfromlist(pathlist) -- maybe not a list, just a pa splitpathexpr(pathlist[k],newlist,validate) end else + local n = 0 for k=1,#pathlist do for p in gmatch(pathlist[k],"([^,]+)") do p = validate(p) - if p ~= "" then newlist[#newlist+1] = p end + if p ~= "" then + n = n + 1 + newlist[n] = p + end end end end @@ -9176,16 +9397,42 @@ end -- We also put some cleanup code here. -local cleanup -- used recursively -cleanup = lpeg.replacer { - { "!", "" }, - { "\\", "/" }, - { "~" , function() return lpegmatch(cleanup,environment.homedir) end }, + + +local cleanup = lpeg.replacer { + { "!" , "" }, + { "\\" , "/" }, } +local homedir + function resolvers.cleanpath(str) - return str and lpegmatch(cleanup,str) + if not homedir then + homedir = lpegmatch(cleanup,environment.homedir or "") + if homedir == string.char(127) or homedir == "" or not lfs.isdir(homedir) then + if trace_expansions then + report_resolvers("no home dir set, ignoring dependent paths") + end + function resolvers.cleanpath(str) + if find(str,"~") then + return "" -- special case + else + return str and lpegmatch(cleanup,str) + end + end + else + cleanup = lpeg.replacer { + { "!" , "" }, + { "\\" , "/" }, + { "~" , homedir }, + } + function resolvers.cleanpath(str) + return str and lpegmatch(cleanup,str) + end + end + end + return resolvers.cleanpath(str) end -- This one strips quotes and funny tokens. @@ -9211,7 +9458,6 @@ end -- we join them and split them after the expansion has taken place. This -- is more convenient. - local cache = { } local splitter = lpegCt(lpeg.splitat(lpegS(ostype == "windows" and ";" or ":;"))) -- maybe add , @@ -9226,15 +9472,17 @@ local function splitconfigurationpath(str) -- beware, this can be either a path str = gsub(str,"\\","/") local split = lpegmatch(splitter,str) found = { } + local noffound = 0 for i=1,#split do local s = split[i] if not find(s,"^{*unset}*") then - found[#found+1] = s + noffound = noffound + 1 + found[noffound] = s end end if trace_expansions then report_resolvers("splitting path specification '%s'",str) - for k=1,#found do + for k=1,noffound do report_resolvers("% 4i: %s",k,found[k]) end end @@ -9357,7 +9605,6 @@ formats['enc'] = 'ENCFONTS' suffixes['enc'] = { 'enc' } formats['fmt'] = 'TEXFORMATS' suffixes['fmt'] = { 'fmt' } formats['map'] = 'TEXFONTMAPS' suffixes['map'] = { 'map' } formats['mp'] = 'MPINPUTS' suffixes['mp'] = { 'mp' } -formats['ocp'] = 'OCPINPUTS' suffixes['ocp'] = { 'ocp' } formats['ofm'] = 'OFMFONTS' suffixes['ofm'] = { 'ofm', 'tfm' } formats['otf'] = 'OPENTYPEFONTS' suffixes['otf'] = { 'otf' } formats['opl'] = 'OPLFONTS' suffixes['opl'] = { 'opl' } @@ -9582,7 +9829,7 @@ local function identify() local cachepath = texmfcaches[k] if cachepath ~= "" then cachepath = resolvers.cleanpath(cachepath) - cachepath = file.collapse_path(cachepath) + cachepath = file.collapsepath(cachepath) local valid = isdir(cachepath) if valid then if file.is_readable(cachepath) then @@ -9921,7 +10168,7 @@ function resolvers.splitmethod(filename) end function resolvers.methodhandler(what, filename, filetype) -- ... - filename = file.collapse_path(filename) + filename = file.collapsepath(filename) local specification = (type(filename) == "string" and resolvers.splitmethod(filename)) or filename -- no or { }, let it bomb local scheme = specification.scheme local resolver = resolvers[what] @@ -9966,7 +10213,7 @@ local lpegP, lpegS, lpegR, lpegC, lpegCc, lpegCs, lpegCt = lpeg.P, lpeg.S, lpeg. local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns local filedirname, filebasename, fileextname, filejoin = file.dirname, file.basename, file.extname, file.join -local collapse_path = file.collapse_path +local collapsepath = file.collapsepath local allocate = utilities.storage.allocate local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end) @@ -10169,7 +10416,7 @@ local function identify_configuration_files() expandvars(cnfpaths) --- hm local luacnfname = resolvers.luacnfname for i=1,#cnfpaths do - local filename = collapse_path(filejoin(cnfpaths[i],luacnfname)) + local filename = collapsepath(filejoin(cnfpaths[i],luacnfname)) if lfs.isfile(filename) then specification[#specification+1] = filename end @@ -10330,7 +10577,7 @@ local function locate_file_databases() -- todo: cache:// and tree:// (runtime) local texmfpaths = resolvers.expandedpathlist('TEXMF') for i=1,#texmfpaths do - local path = collapse_path(texmfpaths[i]) + local path = collapsepath(texmfpaths[i]) local stripped = gsub(path,"^!!","") local runtime = stripped == path path = resolvers.cleanpath(path) @@ -10443,11 +10690,12 @@ end function resolvers.splitexpansions() local ie = instance.expansions for k,v in next, ie do - local t, h, p = { }, { }, splitconfigurationpath(v) + local t, tn, h, p = { }, 0, { }, splitconfigurationpath(v) for kk=1,#p do local vv = p[kk] if vv ~= "" and not h[vv] then - t[#t+1] = vv + tn = tn + 1 + t[tn] = vv h[vv] = true end end @@ -10554,7 +10802,8 @@ end function resolvers.registerextrapath(paths,subpaths) local ep = instance.extra_paths or { } - local n = #ep + local oldn = #ep + local newn = oldn if paths and paths ~= "" then if subpaths and subpaths ~= "" then for p in gmatch(paths,"[^,]+") do @@ -10562,7 +10811,8 @@ function resolvers.registerextrapath(paths,subpaths) for s in gmatch(subpaths,"[^,]+") do local ps = p .. "/" .. s if not done[ps] then - ep[#ep+1] = resolvers.cleanpath(ps) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(ps) done[ps] = true end end @@ -10570,7 +10820,8 @@ function resolvers.registerextrapath(paths,subpaths) else for p in gmatch(paths,"[^,]+") do if not done[p] then - ep[#ep+1] = resolvers.cleanpath(p) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(p) done[p] = true end end @@ -10581,16 +10832,17 @@ function resolvers.registerextrapath(paths,subpaths) for s in gmatch(subpaths,"[^,]+") do local ps = ep[i] .. "/" .. s if not done[ps] then - ep[#ep+1] = resolvers.cleanpath(ps) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(ps) done[ps] = true end end end end - if #ep > 0 then + if newn > 0 then instance.extra_paths = ep -- register paths end - if #ep > n then + if newn > oldn then instance.lists = { } -- erase the cache end end @@ -10600,14 +10852,15 @@ local function made_list(instance,list) if not ep or #ep == 0 then return list else - local done, new = { }, { } + local done, new, newn = { }, { }, 0 -- honour . .. ../.. but only when at the start for k=1,#list do local v = list[k] if not done[v] then if find(v,"^[%.%/]$") then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v else break end @@ -10618,7 +10871,8 @@ local function made_list(instance,list) local v = ep[k] if not done[v] then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v end end -- next the formal paths @@ -10626,7 +10880,8 @@ local function made_list(instance,list) local v = list[k] if not done[v] then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v end end return new @@ -10637,7 +10892,7 @@ function resolvers.cleanpathlist(str) local t = resolvers.expandedpathlist(str) if t then for i=1,#t do - t[i] = collapse_path(resolvers.cleanpath(t[i])) + t[i] = collapsepath(resolvers.cleanpath(t[i])) end end return t @@ -10703,7 +10958,7 @@ resolvers.isreadable.tex = resolvers.isreadable.file -- name/name local function collect_files(names) - local filelist = { } + local filelist, noffiles = { }, 0 for k=1,#names do local fname = names[k] if trace_detail then @@ -10744,7 +10999,8 @@ local function collect_files(names) if trace_detail then report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result) end - filelist[#filelist+1] = { kind, search, result } + noffiles = noffiles + 1 + filelist[noffiles] = { kind, search, result } end else for kk=1,#blobfile do @@ -10756,7 +11012,8 @@ local function collect_files(names) if trace_detail then report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result) end - filelist[#filelist+1] = { kind, search, result } + noffiles = noffiles + 1 + filelist[noffiles] = { kind, search, result } end end end @@ -10766,7 +11023,7 @@ local function collect_files(names) end end end - return #filelist > 0 and filelist or nil + return noffiles > 0 and filelist or nil end function resolvers.registerintrees(name) @@ -10792,7 +11049,7 @@ end local function collect_instance_files(filename,collected) -- todo : plugin (scanners, checkers etc) local result = collected or { } local stamp = nil - filename = collapse_path(filename) + filename = collapsepath(filename) -- speed up / beware: format problem if instance.remember then stamp = filename .. "--" .. instance.engine .. "--" .. instance.progname .. "--" .. instance.format @@ -11045,7 +11302,7 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan end end for k=1,#result do - local rk = collapse_path(result[k]) + local rk = collapsepath(result[k]) result[k] = rk resolvers.registerintrees(rk) -- for tracing used files end @@ -12439,7 +12696,7 @@ function resolvers.load_tree(tree) local texos = "texmf-" .. os.platform local oldroot = environment.texroot - local newroot = file.collapse_path(tree) + local newroot = file.collapsepath(tree) local newtree = file.join(newroot,texos) local newpath = file.join(newtree,"bin") @@ -12668,17 +12925,17 @@ local format = string.format -- helper for mtxrun -local quote = string.quote +local quoted = string.quoted local function primaryflags() local trackers = environment.argument("trackers") local directives = environment.argument("directives") local flags = "" if trackers and trackers ~= "" then - flags = flags .. "--trackers=" .. quote(trackers) + flags = flags .. "--trackers=" .. quoted(trackers) end if directives and directives ~= "" then - flags = flags .. "--directives=" .. quote(directives) + flags = flags .. "--directives=" .. quoted(directives) end return flags end @@ -12743,7 +13000,7 @@ function environment.make_format(name) return end -- generate format - local command = format("luatex --ini %s --lua=%s %s %sdump",primaryflags(),quote(usedluastub),quote(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\") + local command = format("luatex --ini %s --lua=%s %s %sdump",primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\") logs.simple("running command: %s\n",command) os.spawn(command) -- remove related mem files @@ -12781,8 +13038,7 @@ function environment.run_format(name,data,more) logs.simple("using format name: %s",fmtname) logs.simple("no luc/lua with name: %s",barename) else - local q = string.quote - local command = format("luatex %s --fmt=%s --lua=%s %s %s",primaryflags(),quote(barename),quote(luaname),quote(data),more ~= "" and quote(more) or "") + local command = format("luatex %s --fmt=%s --lua=%s %s %s",primaryflags(),quoted(barename),quoted(luaname),quoted(data),more ~= "" and quoted(more) or "") logs.simple("running command: %s",command) os.spawn(command) end @@ -12813,13 +13069,13 @@ own.libs = { -- order can be made better 'l-unicode.lua', 'l-math.lua', + 'util-tab.lua', + 'util-sto.lua', 'util-mrg.lua', 'util-lua.lua', 'util-prs.lua', - 'util-tab.lua', 'util-fmt.lua', 'util-deb.lua', - 'util-sto.lua', 'trac-inf.lua', 'trac-set.lua', diff --git a/tex/context/base/back-exp.lua b/tex/context/base/back-exp.lua index 83c7b6198..28cb5c18c 100644 --- a/tex/context/base/back-exp.lua +++ b/tex/context/base/back-exp.lua @@ -740,6 +740,7 @@ end local function collapsetree() for k, v in next, treehash do local d = v[1].data + local nd = #d for i=2,#v do local vi = v[i] local vd = vi.data @@ -753,26 +754,35 @@ local function collapsetree() -- experiment, should be improved -- can be simplified ... lpn instead of done if done then - d[#d+1] = joiner_1 + nd = nd + 1 + d[nd] = joiner_1 else done = true local pn = vi.parnumber if not pn then - d[#d+1], lpn = joiner_2, nil + nd = nd + 1 + d[nd] = joiner_2 + lpn = nil elseif not lpn then - d[#d+1], lpn = joiner_3, pn + nd = nd + 1 + d[nd] = joiner_3 + lpn = pn elseif pn and pn ~= lpn then - d[#d+1], lpn = makebreaknode(vi), pn + nd = nd + 1 + d[nd] = makebreaknode(vi) + lpn = pn else - -- d[#d+1] = joiner_4 -- we need to be more clever + -- nd = nd + 1 + -- d[nd] = joiner_4 -- we need to be more clever end end else -- lpn = nil end -if vdj ~= "" then - d[#d+1] = vdj -- hm, any? -end + if vdj ~= "" then + nd = nd + 1 + d[nd] = vdj -- hm, any? + end vd[j] = false end v[i].collapsed = true @@ -784,19 +794,21 @@ local function prunetree(tree) if not tree.collapsed then local data = tree.data if data then - local p = { } + local p, np = { }, 0 for i=1,#data do local d = data[i] if type(d) == "table" then if not d.collapsed then prunetree(d) - p[#p+1] = d + np = np + 1 + p[np] = d end elseif type(d) == "string" then - p[#p+1] = d + np = np + 1 + p[np] = d end end - tree.data = #p > 0 and p + tree.data = np > 0 and p end end end diff --git a/tex/context/base/back-ini.lua b/tex/context/base/back-ini.lua index dadaaa837..1bceea691 100644 --- a/tex/context/base/back-ini.lua +++ b/tex/context/base/back-ini.lua @@ -117,6 +117,8 @@ backends.codeinjections = { finishreference = nothing, + getoutputfilename = nothing, + } backends.registrations = { diff --git a/tex/context/base/back-pdf.lua b/tex/context/base/back-pdf.lua index 425e1ad15..c9c0982f0 100644 --- a/tex/context/base/back-pdf.lua +++ b/tex/context/base/back-pdf.lua @@ -464,4 +464,13 @@ function codeinjections.setfigurealternative(data,figure) end end +local outputfilename + +function codeinjections.getoutputfilename() + if not outputfilename then + outputfilename = file.addsuffix(tex.jobname,"pdf") + end + return outputfilename +end + backends.install("pdf") diff --git a/tex/context/base/back-pdf.mkiv b/tex/context/base/back-pdf.mkiv index 94b93cb39..1a99a91a2 100644 --- a/tex/context/base/back-pdf.mkiv +++ b/tex/context/base/back-pdf.mkiv @@ -90,6 +90,8 @@ \def\pdfbackendcurrentresources {\ctxlua{lpdf.collectedresources()}} +\def\pdfcolor #1{\ctxlua{lpdf.pdfcolor(\thecolorattribute{#1})}} \let\PDFcolor\pdfcolor + %D Let's block these: \let\pdfcatalog \relax \newtoks\pdfcatalog diff --git a/tex/context/base/bibl-bib.lua b/tex/context/base/bibl-bib.lua index 5e759eff2..f301d9689 100644 --- a/tex/context/base/bibl-bib.lua +++ b/tex/context/base/bibl-bib.lua @@ -198,12 +198,12 @@ function bibtex.toxml(session,options) -- we can always speed this up if needed -- format slows down things a bit but who cares statistics.starttiming(bibtex) - local result = { } + local result, r = { }, 0 local options = settings_to_hash(options) local convert = options.convert -- todo: interface local strip = options.strip -- todo: interface local entries = session.entries - result[#result+1] = format("<?xml version='1.0' standalone='yes'?>") + r = r + 1 ; result[r] = format("<?xml version='1.0' standalone='yes'?>") result[#result+1] = format("<bibtex>") for id, categories in next, session.data do id = lower(gsub(id,"^@","")) @@ -223,14 +223,14 @@ function bibtex.toxml(session,options) -- kind of hackery ... bibtex databases are quite unportable value = lpegmatch(filter,value) or value end - result[#result+1] = format(" <field name='%s'>%s</field>",key,value) + r = r + 1 ; result[r] = format(" <field name='%s'>%s</field>",key,value) end end - result[#result+1] = format("</entry>") + r = r + 1 ; result[r] = format("</entry>") end end end - result[#result+1] = format("</bibtex>") + r = r + 1 ; result[r] = format("</bibtex>") result = concat(result,"\n") -- alternatively we could use lxml.convert session.xml = xml.convert(result, { diff --git a/tex/context/base/bibl-tra.lua b/tex/context/base/bibl-tra.lua index afc42a610..87300aa74 100644 --- a/tex/context/base/bibl-tra.lua +++ b/tex/context/base/bibl-tra.lua @@ -126,7 +126,7 @@ end function hacks.resolve(prefix,block,reference) -- maybe already feed it split local subset = references.collected[prefix or ""] or references.collected[""] if subset then - local result, done = { }, { } + local result, nofresult, done = { }, 0, { } block = tonumber(block) for rest in gmatch(reference,"([^,%s]+)") do local blk, tag, found = block, nil, nil @@ -152,7 +152,8 @@ function hacks.resolve(prefix,block,reference) -- maybe already feed it split if found then local current = found.entries and found.entries.text if current and not done[current] then - result[#result+1] = { blk, rest, current } + nofresult = nofresult + 1 + result[nofresult] = { blk, rest, current } done[current] = true end end @@ -160,8 +161,8 @@ function hacks.resolve(prefix,block,reference) -- maybe already feed it split -- todo: ranges so the interface will change sort(result,compare) local first, last, firsti, lasti, firstr, lastr - local collected = { } - for i=1,#result do + local collected, nofcollected = { }, 0 + for i=1,nofresult do local r = result[i] local current = r[3] if not first then @@ -170,11 +171,14 @@ function hacks.resolve(prefix,block,reference) -- maybe already feed it split last, lasti, lastr = current, i, r else if last > first + 1 then - collected[#collected+1] = { firstr[1], firstr[2], lastr[1], lastr[2] } + nofcollected = nofcollected + 1 + collected[nofcollected] = { firstr[1], firstr[2], lastr[1], lastr[2] } else - collected[#collected+1] = { firstr[1], firstr[2] } + nofcollected = nofcollected + 1 + collected[nofcollected] = { firstr[1], firstr[2] } if last > first then - collected[#collected+1] = { lastr[1], lastr[2] } + nofcollected = nofcollected + 1 + collected[nofcollected] = { lastr[1], lastr[2] } end end first, last, firsti, lasti, firstr, lastr = current, current, i, i, r, r @@ -182,16 +186,19 @@ function hacks.resolve(prefix,block,reference) -- maybe already feed it split end if first then if last > first + 1 then - collected[#collected+1] = { firstr[1], firstr[2], lastr[1], lastr[2] } + nofcollected = nofcollected + 1 + collected[nofcollected] = { firstr[1], firstr[2], lastr[1], lastr[2] } else - collected[#collected+1] = { firstr[1], firstr[2] } + nofcollected = nofcollected + 1 + collected[nofcollected] = { firstr[1], firstr[2] } if last > first then - collected[#collected+1] = { lastr[1], lastr[2] } + nofcollected = nofcollected + 1 + collected[nofcollected] = { lastr[1], lastr[2] } end end end - if #collected > 0 then - for i=1,#collected do + if nofcollected > 0 then + for i=1,nofcollected do local c = collected[i] if c[3] then context.dowithbibtexnumrefrange(#collected,i,prefix,c[1],c[2],c[3],c[4]) diff --git a/tex/context/base/blob-ini.lua b/tex/context/base/blob-ini.lua index fec304f7d..537e8bdd3 100644 --- a/tex/context/base/blob-ini.lua +++ b/tex/context/base/blob-ini.lua @@ -120,15 +120,18 @@ function blobs.append(t,str) local list = t.list if kind == "string" then local pars = lpegmatch(ctxtextcapture,str) + local noflist = #list for p=1,#pars do local str = pars[p] if #str == 0 then - list[#list+1 ] = { head = nil, tail = nil } + noflist = noflist + 1 + list[noflist] = { head = nil, tail = nil } else - local l = list[#list] + local l = list[noflist] if not l then l = { head = nil, tail = nil } - list[#list+1 ] = l + noflist = noflist + 1 + list[noflist] = l end local head, tail = tonodes(str,nil,nil) if head then diff --git a/tex/context/base/buff-ini.lua b/tex/context/base/buff-ini.lua index cb41b48cf..7c916f60e 100644 --- a/tex/context/base/buff-ini.lua +++ b/tex/context/base/buff-ini.lua @@ -27,6 +27,7 @@ local utfbyte, utffind, utfgsub = utf.byte, utf.find, utf.gsub local type, next = type, next local huge = math.huge local byte, sub, find, char, gsub, rep, lower, format, gmatch, match, count = string.byte, string.sub, string.find, string.char, string.gsub, string.rep, string.lower, string.format, string.gmatch, string.match, string.count +local splitlines, escapedpattern = string.splitlines, string.escapedpattern local utfcharacters, utfvalues = string.utfcharacters, string.utfvalues local ctxcatcodes = tex.ctxcatcodes local variables = interfaces.variables @@ -92,7 +93,7 @@ function buffers.grab(name,begintag,endtag,bufferdata) end dn = gsub(dn,"[\010\013]$","") if flags.storeastable then - dn = dn:splitlines() + dn = splitlines(dn) end end data[name] = dn @@ -178,7 +179,7 @@ function buffers.range(lines,first,last,range) -- 1,3 1,+3 fromhere,tothere else for i=first,last do if find(lines[i],r_first) then - first, strip = i + 1 + first = i + 1 break end end @@ -212,7 +213,7 @@ function buffers.type(name,realign,range) local action = buffers.typeline if lines then if type(lines) == "string" then - lines = lines:splitlines() + lines = splitlines(lines) data[name] = lines end if realign then @@ -260,7 +261,7 @@ function buffers.typefile(name,realign,range,regime) -- still somewhat messy, si str = regimes.translate(str,regime) end if str and str~= "" then - local lines = str:splitlines() + local lines = splitlines(str) if realign then lines = buffers.realign(lines,realign) end @@ -384,11 +385,12 @@ function buffers.collect(names,separator) -- no print if type(names) == "string" then names = settings_to_array(names) end - local t = { } + local t, n = { }, 0 for i=1,#names do local c = content(names[i],separator) if c ~= "" then - t[#t+1] = c + n = n + 1 + t[n] = c end end return concat(t,separator or "\r") -- "\n" is safer due to comments and such @@ -397,7 +399,7 @@ end function buffers.feedback(names,separator) -- don't change the texprint into texsprint as it fails on mp buffers -- because (p<nl>enddef) becomes penddef then - texprint(ctxcatcodes,string.splitlines(buffers.collect(names,separator))) + texprint(ctxcatcodes,splitlines(buffers.collect(names,separator))) end local function tobyte(c) @@ -689,7 +691,7 @@ function buffers.realign(name,forced_n) -- no, auto, <number> if type(name) == "string" then d = data[name] if type(d) == "string" then - d = d:splitlines() + d = splitlines(d) end else d = name -- already a buffer @@ -760,7 +762,7 @@ function buffers.setescapepair(name,pair) start, stop = "/BTEX", "/ETEX" else pair = string.split(pair,",") - start, stop = string.esc(pair[1] or ""), string.esc(pair[2] or "") + start, stop = escapedpattern(pair[1] or ""), escapedpattern(pair[2] or "") end if start ~= "" then local pattern diff --git a/tex/context/base/catc-ctx.tex b/tex/context/base/catc-ctx.tex index 77e8fe88c..79e89a69b 100644 --- a/tex/context/base/catc-ctx.tex +++ b/tex/context/base/catc-ctx.tex @@ -183,7 +183,6 @@ \catcode`\\ = 0 \catcode`\{ = 1 \catcode`\} = 2 - \catcode`\% = 14 \stopcatcodetable \letcatcodecommand \ctxcatcodes `\| \relax diff --git a/tex/context/base/char-def.lua b/tex/context/base/char-def.lua index 4c72c4772..c496b4a7a 100644 --- a/tex/context/base/char-def.lua +++ b/tex/context/base/char-def.lua @@ -83872,8 +83872,8 @@ characters.data={ description="<CJK Ideograph Extension A>", direction="l", linebreak="id", - unicodeslot=0x3400, - range= 0x4DB5, +--unicodeslot=0x3400, + range={ first=0x3400, last=0x4DB5 }, }, [0x4DC0]={ category="so", @@ -84329,8 +84329,8 @@ characters.data={ description="<CJK Ideograph>", direction="l", linebreak="id", - unicodeslot=0x4E00, - range=0x9FBB, +--unicodeslot=0x4E00, + range={ first=0x4E00, last=0x9FBB }, }, [0xA000]={ category="lo", @@ -95001,8 +95001,8 @@ characters.data={ description="<Hangul Syllable>", direction="l", linebreak="h2", - unicodeslot=0xAC00, - range=0xD7A3, +--unicodeslot=0xAC00, + range={ first=0xAC00, last=0xD7A3 }, }, [0xD800]={ category="cs", @@ -133602,8 +133602,8 @@ characters.data={ description="<CJK Ideograph Extension B>", direction="l", linebreak="id", - unicodeslot=0x20000, - range=0x2A6D6, +--unicodeslot=0x20000, + range={ first=0x20000, last=0x2A6D6 }, }, [0x2F800]={ category="lo", diff --git a/tex/context/base/char-ini.lua b/tex/context/base/char-ini.lua index 0cce35a1a..868e25b3f 100644 --- a/tex/context/base/char-ini.lua +++ b/tex/context/base/char-ini.lua @@ -48,10 +48,13 @@ else end if not characters.ranges then - local ranges = allocate { } + local ranges, r = allocate { }, 0 characters.ranges = ranges for k, v in next, data do - ranges[#ranges+1] = k + if v.range then + r = r + 1 + ranges[r] = v + end end end @@ -75,6 +78,10 @@ end characters.tonumber = chartonumber +local private = { + description = "PRIVATE SLOT", +} + setmetatablekey(data, "__index", function(t,k) if type(k) == "string" then k = lpegmatch(pattern,k) or utfbyte(k) @@ -86,19 +93,23 @@ setmetatablekey(data, "__index", function(t,k) -- goes to ranges end else - return nil + return private end end - for r=1,#ranges do - local rr = ranges[r] -- first in range - if k > rr and k <= data[rr].range then - t[k] = t[rr] - return t[k] + if k < 0xF0000 then + for r=1,#ranges do + local rr = ranges[r].range + if k >= rr.first and k <= rr.last then + t[k] = rr + return rr + end end end - return nil + return private -- handy for when we loop over characters in fonts and check for a property end ) +--~ setmetatable(data,{ __index = function(t,k) return "" end }) -- quite old, obsolete + characters.blocks = allocate { ["aegeannumbers"] = { 0x10100, 0x1013F, "Aegean Numbers" }, ["alphabeticpresentationforms"] = { 0x0FB00, 0x0FB4F, "Alphabetic Presentation Forms" }, @@ -380,8 +391,6 @@ characters.bidi = allocate { on = "Other Neutrals", } -table.setemptymetatable(data) -- so each key resolves to "" - --[[ldx-- <p>At this point we assume that the big data table is loaded. From this table we derive a few more.</p> @@ -445,13 +454,14 @@ if texsetcatcode then report_defining("defining active character commands") end - local activated = { } + local activated, a = { }, 0 for u, chr in next, data do -- these will be commands local fallback = chr.fallback if fallback then texsprint("{\\catcode",u,"=13\\unexpanded\\gdef ",utfchar(u),"{\\checkedchar{",u,"}{",fallback,"}}}") -- no texprint - activated[#activated+1] = u + a = a + 1 + activated[a] = u else local contextname = chr.contextname if contextname then @@ -464,7 +474,8 @@ if texsetcatcode then end elseif is_command[category] then texsprint("{\\catcode",u,"=13\\unexpanded\\gdef ",utfchar(u),"{\\"..contextname,"}}") -- no texprint - activated[#activated+1] = u + a = a + 1 + activated[a] = u end end end @@ -481,8 +492,9 @@ if texsetcatcode then if not chr.fallback and is_letter[chr.category] and u >= 128 and u <= 65536 then texsetcatcode(u,11) end - if chr.range then - for i=1,u,chr.range do + local range = chr.range + if range then + for i=1,range.first,range.last do texsetcatcode(i,11) end end @@ -521,14 +533,15 @@ else -- char-obs local template_b = "\\chardef\\l=11\\chardef\\a=13\\let\\c\\catcode%s\\let\\a\\undefined\\let\\l\\undefined\\let\\c\\undefined" function characters.define(tobelettered, tobeactivated) -- catcodetables - local lettered, activated = { }, { } + local lettered, activated, l, a = { }, { }, 0, 0 for u, chr in next, data do -- we can use a macro instead of direct settings local fallback = chr.fallback if fallback then -- texprint(format("{\\catcode %s=13\\unexpanded\\gdef %s{\\checkedchar{%s}{%s}}}",u,utfchar(u),u,fallback)) texsprint("{\\catcode",u,"=13\\unexpanded\\gdef ",utfchar(u),"{\\checkedchar{",u,"}{",fallback,"}}}") -- no texprint - activated[#activated+1] = "\\c"..u.."\\a" + a = a + 1 + activated[a] = "\\c"..u.."\\a" else local contextname = chr.contextname local category = chr.category @@ -541,28 +554,35 @@ else -- char-obs else texprint(ctxcatcodes,format("\\let\\%s=%s",contextname,utfchar(u))) if is_letter[category] then - lettered[#lettered+1] = "\\c"..u.."\\l" + l = l + 1 + lettered[l] = "\\c"..u.."\\l" end end elseif is_command[category] then -- this might change: contextcommand ipv contextname -- texprint(format("{\\catcode %s=13\\unexpanded\\gdef %s{\\%s}}",u,utfchar(u),contextname)) texsprint("{\\catcode",u,"=13\\unexpanded\\gdef ",utfchar(u),"{\\"..contextname,"}}") -- no texprint - activated[#activated+1] = "\\c"..u.."\\a" + a = a + 1 + activated[a] = "\\c"..u.."\\a" end elseif is_letter[category] then if u >= 128 and u <= 65536 then -- catch private mess - lettered[#lettered+1] = "\\c"..u.."\\l" + l = l + 1 + lettered[l] = "\\c"..u.."\\l" end end end - if chr.range then - lettered[#lettered+1] = format('\\dofastrecurse{"%05X}{"%05X}{1}{\\c\\fastrecursecounter\\l}',u,chr.range) + local range = chr.range + if range then + l = l + 1 + lettered[l] = format('\\dofastrecurse{"%05X}{"%05X}{1}{\\c\\fastrecursecounter\\l}',range.first,range.last) end end -- if false then - lettered[#lettered+1] = "\\c"..0x200C.."\\l" -- non-joiner - lettered[#lettered+1] = "\\c"..0x200D.."\\l" -- joiner + l = l + 1 + lettered[l] = "\\c"..0x200C.."\\l" -- non-joiner + l = l + 1 + lettered[l] = "\\c"..0x200D.."\\l" -- joiner -- fi if tobelettered then lettered = concat(lettered) @@ -617,10 +637,13 @@ if texsetcatcode then if cc == "lu" then texsetsfcode(code,999) end - elseif cc == "lo" and chr.range then - for i=code,chr.range do - texsetcatcode(code,11) -- letter - texsetlccode(code,code,code) -- self self + elseif cc == "lo" then + local range = chr.range + if range then + for i=range.first,range.last do + texsetcatcode(i,11) -- letter + texsetlccode(i,i,i) -- self self + end end end end @@ -640,8 +663,11 @@ else -- char-obs if cc == "lu" then texprint(ctxcatcodes,"\\sfcode ",code,"999 ") end - if cc == "lo" and chr.range then - texsprint(ctxcatcodes,format('\\dofastrecurse{"%05X}{"%05X}{1}{\\setcclcucself\\fastrecursecounter}',code,chr.range)) + if cc == "lo" then + local range = chr.range + if range then + texsprint(ctxcatcodes,format('\\dofastrecurse{"%05X}{"%05X}{1}{\\setcclcucself\\fastrecursecounter}',range.first,range.last)) + end end end end @@ -770,12 +796,13 @@ setmetatable(specialchars, { __index = function(t,u) local c = data[utfbyte(u)] local s = c and c.specials if s then - local t = { } + local t, tn = { }, 0 for i=2,#s do local si = s[i] local c = data[si] if is_letter[c.category] then - t[#t+1] = utfchar(si) + tn = tn + 1 + t[tn] = utfchar(si) end end c = concat(t) @@ -789,27 +816,30 @@ setmetatable(specialchars, { __index = function(t,u) end } ) function characters.lower(str) - local new = { } + local new, n = { }, 0 for u in utfvalues(str) do - new[#new+1] = utfchar(lccodes[u]) + n = n + 1 + new[n] = utfchar(lccodes[u]) end return concat(new) end function characters.upper(str) - local new = { } + local new, n = { }, 0 for u in utfvalues(str) do - new[#new+1] = utfchar(uccodes[u]) + n = n + 1 + new[n] = utfchar(uccodes[u]) end return concat(new) end function characters.lettered(str) - local new = { } + local new, n = { }, 0 for u in utfvalues(str) do local d = data[u] if is_letter[d.category] then - new[#new+1] = utfchar(lccodes[u]) + n = n + 1 + new[n] = utfchar(lccodes[u]) end end return concat(new) diff --git a/tex/context/base/char-utf.lua b/tex/context/base/char-utf.lua index b3ed728f1..7384f98c3 100644 --- a/tex/context/base/char-utf.lua +++ b/tex/context/base/char-utf.lua @@ -101,20 +101,22 @@ end --~ if initialize then -- saves a call --~ initialize() --~ end ---~ local tokens, first, done = { }, false, false +--~ local tokens, n, first, done = { }, 0, false, false --~ for second in utfcharacters(str) do --~ local cgf = graphemes[first] --~ if cgf and cgf[second] then --~ first, done = cgf[second], true --~ elseif first then ---~ tokens[#tokens+1] = first +--~ n + n + 1 +--~ tokens[n] = first --~ first = second --~ else --~ first = second --~ end --~ end --~ if done then ---~ tokens[#tokens+1] = first +--~ n + n + 1 +--~ tokens[n] = first --~ return concat(tokens) --~ end --~ end @@ -194,17 +196,19 @@ not collecting tokens is not only faster but also saves garbage collecting. function utffilters.collapse(str) -- not really tested (we could preallocate a table) if utffilters.collapsing and str then - if #str > 1 then + local nstr = #str + if nstr > 1 then if initialize then -- saves a call initialize() end - local tokens, first, done, n = { }, false, false, 0 + local tokens, t, first, done, n = { }, 0, false, false, 0 for second in utfcharacters(str) do if done then local crs = high[second] if crs then if first then - tokens[#tokens+1] = first + t = t + 1 + tokens[t] = first end first = crs else @@ -212,7 +216,8 @@ function utffilters.collapse(str) -- not really tested (we could preallocate a t if cgf and cgf[second] then first = cgf[second] elseif first then - tokens[#tokens+1] = first + t = t + 1 + tokens[t] = first first = second else first = second @@ -225,13 +230,17 @@ function utffilters.collapse(str) -- not really tested (we could preallocate a t if n == 1 then break else - tokens[#tokens+1], n = s, n - 1 + t = t + 1 + tokens[t] = s + n = n -1 end end if first then - tokens[#tokens+1] = first + t = t + 1 + tokens[t] = first end - first, done = crs, true + first = crs + done = true else local cgf = graphemes[first] if cgf and cgf[second] then @@ -239,21 +248,26 @@ function utffilters.collapse(str) -- not really tested (we could preallocate a t if n == 1 then break else - tokens[#tokens+1], n = s, n -1 + t = t + 1 + tokens[t] = s + n = n -1 end end - first, done = cgf[second], true + first = cgf[second] + done = true else - first, n = second, n + 1 + first = second + n = n + 1 end end end end if done then - tokens[#tokens+1] = first + t = t + 1 + tokens[t] = first return concat(tokens) -- seldom called end - elseif #str > 0 then + elseif nstr > 0 then return high[str] or str end end @@ -275,9 +289,10 @@ commands = commands or { } --ldx]]-- function utf.split(str) - local t = { } + local t, n = { }, 0 for snippet in utfcharacters(str) do - t[#t+1] = snippet + n = n + 1 + t[n+1] = snippet end return t end diff --git a/tex/context/base/chem-str.lua b/tex/context/base/chem-str.lua index f574a8ac4..0e5f36434 100644 --- a/tex/context/base/chem-str.lua +++ b/tex/context/base/chem-str.lua @@ -214,6 +214,7 @@ local pattern = local function process(spec,text,n,rulethickness,rulecolor,offset) insert(stack,{ spec=spec, text=text, n=n }) local txt = #stack + local m = #metacode for i=1,#spec do local s = spec[i] local d = definitions[s] @@ -226,7 +227,7 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) local rep, operation, special, index, upto, set, text = lpegmatch(pattern,s) if operation == "pb" then insert(pstack,kind) - metacode[#metacode+1] = syntax.pb.direct + m = m + 1 ; metacode[m] = syntax.pb.direct if keys[special] == "text" and index then if keys["c"..special] == "text" then -- can be option: auto ... metacode[#metacode+1] = format('chem_c%s(%s,%s,"");',special,bonds,index) @@ -236,21 +237,21 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) end elseif operation == "save" then insert(pstack,kind) - metacode[#metacode+1] = syntax.save.direct + m = m + 1 ; metacode[m] = syntax.save.direct elseif operation == "pe" or operation == "restore" then kind = remove(pstack) local ss = syntax[kind] local prev = bonds or 6 keys, bonds, max, rot = ss.keys, ss.n, ss.max, 1 - metacode[#metacode+1] = syntax[operation].direct - metacode[#metacode+1] = format("chem_set(%s,%s) ;",prev,bonds) + m = m + 1 ; metacode[m] = syntax[operation].direct + m = m + 1 ; metacode[m] = format("chem_set(%s,%s) ;",prev,bonds) elseif operation == "front" then if syntax[kind .. "_front"] then kind = kind .. "_front" local ss = syntax[kind] local prev = bonds or 6 keys, bonds, max, rot = ss.keys, ss.n, ss.max, 1 - metacode[#metacode+1] = format("chem_set(%s,%s) ;",prev,bonds) + m = m + 1 ; metacode[m] = format("chem_set(%s,%s) ;",prev,bonds) end elseif operation then local ss = syntax[operation] @@ -260,18 +261,18 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) local sa = ss.arguments if sa == 1 then local one ; txt, one = fetch(txt) - metacode[#metacode+1] = format(ds,one or "") + m = m + 1 ; metacode[m] = format(ds,one or "") elseif sa ==2 then local one ; txt, one = fetch(txt) local two ; txt, two = fetch(txt) - metacode[#metacode+1] = format(ds,one or "",two or "") + m = m + 1 ; metacode[m] = format(ds,one or "",two or "") else - metacode[#metacode+1] = ds + m = m + 1 ; metacode[m] = ds end elseif ss.keys then local prev = bonds or 6 kind, keys, bonds, max, rot = s, ss.keys, ss.n, ss.max, 1 - metacode[#metacode+1] = format("chem_set(%s,%s) ;",prev,bonds) + m = m + 1 ; metacode[m] = format("chem_set(%s,%s) ;",prev,bonds) end else local what = keys[operation] @@ -279,31 +280,31 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) if set then for i=1,#set do local si = set[i] - metacode[#metacode+1] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,si,si,rulethickness,rulecolor) + m = m + 1 ; metacode[m] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,si,si,rulethickness,rulecolor) end elseif upto then - metacode[#metacode+1] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,index,upto,rulethickness,rulecolor) + m = m + 1 ; metacode[m] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,index,upto,rulethickness,rulecolor) elseif index then - metacode[#metacode+1] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,index,index,rulethickness,rulecolor) + m = m + 1 ; metacode[m] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,index,index,rulethickness,rulecolor) else - metacode[#metacode+1] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,1,max,rulethickness,rulecolor) + m = m + 1 ; metacode[m] = format("chem_%s(%s,%s,%s,%s,%s);",operation,bonds,1,max,rulethickness,rulecolor) end elseif what == "number" then if set then for i=1,#set do local si = set[i] - metacode[#metacode+1] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,si,si) + m = m + 1 ; metacode[m] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,si,si) end elseif upto then for i=index,upto do local si = set[i] - metacode[#metacode+1] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,si,si) + m = m + 1 ; metacode[m] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,si,si) end elseif index then - metacode[#metacode+1] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,index,index) + m = m + 1 ; metacode[m] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,index,index) else for i=1,max do - metacode[#metacode+1] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,i,i) + m = m + 1 ; metacode[m] = format('chem_%s(%s,%s,"\\dochemicaltext{%s}");',operation,bonds,i,i) end end elseif what == "text" then @@ -318,7 +319,7 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) if t then local a = align and align[si] if a then a = "." .. a else a = "" end - metacode[#metacode+1] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,a,bonds,si,molecule(apply(t))) + m = m + 1 ; metacode[m] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,a,bonds,si,molecule(apply(t))) end end elseif upto then @@ -328,14 +329,14 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) if t then local s = align and align[i] if s then s = "." .. s else s = "" end - metacode[#metacode+1] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,s,bonds,i,molecule(apply(t))) + m = m + 1 ; metacode[m] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,s,bonds,i,molecule(apply(t))) end end elseif index == 0 then local t = text if not t then txt, t = fetch(txt) end if t then - metacode[#metacode+1] = format('chem_%s_zero("\\dochemicaltext{%s}");',operation,molecule(apply(t))) + m = m + 1 ; metacode[m] = format('chem_%s_zero("\\dochemicaltext{%s}");',operation,molecule(apply(t))) end elseif index then local t = text @@ -343,7 +344,7 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) if t then local s = align and align[index] if s then s = "." .. s else s = "" end - metacode[#metacode+1] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,s,bonds,index,molecule(apply(t))) + m = m + 1 ; metacode[m] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,s,bonds,index,molecule(apply(t))) end else for i=1,max do @@ -352,21 +353,21 @@ local function process(spec,text,n,rulethickness,rulecolor,offset) if t then local s = align and align[i] if s then s = "." .. s else s = "" end - metacode[#metacode+1] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,s,bonds,i,molecule(apply(t))) + m = m + 1 ; metacode[m] = format('chem_%s%s(%s,%s,"\\dochemicaltext{%s}");',operation,s,bonds,i,molecule(apply(t))) end end end elseif what == "transform" then if index then for r=1,rep do - metacode[#metacode+1] = format('chem_%s(%s,%s);',operation,bonds,index) + m = m + 1 ; metacode[m] = format('chem_%s(%s,%s);',operation,bonds,index) end if operation == "rot" then rot = index end end elseif what == "fixed" then - metacode[#metacode+1] = format("chem_%s(%s,%s,%s);",operation,bonds,rulethickness,rulecolor) + m = m + 1 ; metacode[m] = format("chem_%s(%s,%s,%s);",operation,bonds,rulethickness,rulecolor) end end end diff --git a/tex/context/base/colo-ini.lua b/tex/context/base/colo-ini.lua index 77b2ea888..cf8a997cf 100644 --- a/tex/context/base/colo-ini.lua +++ b/tex/context/base/colo-ini.lua @@ -345,24 +345,22 @@ function colors.registerspotcolor(parent, str) end function colors.definemultitonecolor(name,multispec,colorspec,selfspec) - local dd, pp, nn = { }, { }, { } + local dd, pp, nn, max = { }, { }, { }, 0 for k,v in gmatch(multispec,"(%a+)=([^%,]*)") do - dd[#dd+1] = k - pp[#pp+1] = v - nn[#nn+1] = k - nn[#nn+1] = format("%1.3g",tonumber(v) or 0) -- 0 can't happen + max = max + 1 + dd[max] = k + pp[max] = v + nn[max] = format("%s_%1.3g",k,tonumber(v) or 0) -- 0 can't happen end ---~ v = tonumber(v) * p - local nof = #dd - if nof > 0 then + if max > 0 then dd, pp, nn = concat(dd,','), concat(pp,','), concat(nn,'_') local parent = gsub(lower(nn),"[^%d%a%.]+","_") colors.defineprocesscolor(parent,colorspec..","..selfspec,true,true) local cp = attributes_list[a_color][parent] if cp then - do_registerspotcolor(parent, name, cp, "", nof, dd, pp) - do_registermultitonecolor(parent, name, cp, "", nof, dd, pp) - definecolor(name, register_color(name, 'spot', parent, nof, dd, pp), true) + do_registerspotcolor(parent, name, cp, "", max, dd, pp) + do_registermultitonecolor(parent, name, cp, "", max, dd, pp) + definecolor(name, register_color(name, 'spot', parent, max, dd, pp), true) local t = settings_to_hash_strict(selfspec) if t and t.a and t.t then definetransparent(name, transparencies.register(name,transparent[t.a] or tonumber(t.a) or 1,tonumber(t.t) or 1), global) @@ -412,7 +410,7 @@ end function colors.formatcolor(ca,separator) local cv = colors.value(ca) if cv then - local c, f, t, model = { }, 13, 13, cv[1] + local c, cn, f, t, model = { }, 0, 13, 13, cv[1] if model == 2 then f, t = 2, 2 elseif model == 3 then @@ -421,7 +419,8 @@ function colors.formatcolor(ca,separator) f, t = 6, 9 end for i=f,t do - c[#c+1] = format("%0.3f",cv[i]) + cn = cn + 1 + c[cn] = format("%0.3f",cv[i]) end return concat(c,separator) else diff --git a/tex/context/base/cont-new.tex b/tex/context/base/cont-new.tex index f429b372f..bfb881759 100644 --- a/tex/context/base/cont-new.tex +++ b/tex/context/base/cont-new.tex @@ -11,7 +11,7 @@ %C therefore copyrighted by \PRAGMA. See mreadme.pdf for %C details. -\newcontextversion{2010.10.22 16:46} +\newcontextversion{2010.10.29 11:35} %D This file is loaded at runtime, thereby providing an %D excellent place for hacks, patches, extensions and new diff --git a/tex/context/base/context.tex b/tex/context/base/context.tex index 3ec7d4bc8..94e5edbcc 100644 --- a/tex/context/base/context.tex +++ b/tex/context/base/context.tex @@ -20,7 +20,7 @@ %D your styles an modules. \edef\contextformat {\jobname} -\edef\contextversion{2010.10.22 16:46} +\edef\contextversion{2010.10.29 11:35} %D For those who want to use this: diff --git a/tex/context/base/core-con.lua b/tex/context/base/core-con.lua index 8f90eac16..efdb0e1bc 100644 --- a/tex/context/base/core-con.lua +++ b/tex/context/base/core-con.lua @@ -435,71 +435,71 @@ local vector = { local function tochinese(n,name) -- normal, caps, all -- improved version by Li Yanrui - local result = { } + local result, r = { }, 0 local vector = vector[name] or vector.normal while true do if n == 0 then break elseif n >= 100000000 then local m = floor(n/100000000) - result[#result+1] = tochinese(m,name) - result[#result+1] = vector[100000000] + r = r + 1 ; result[r] = tochinese(m,name) + r = r + 1 ; result[r] = vector[100000000] local z = n - m * 100000000 - if z > 0 and z < 10000000 then result[#result+1] = vector[0] end + if z > 0 and z < 10000000 then r = r + 1 ; result[r] = vector[0] end n = n % 100000000 elseif n >= 10000000 then local m = floor(n/10000) - result[#result+1] = tochinese(m,name) - result[#result+1] = vector[10000] + r = r + 1 ; result[r] = tochinese(m,name) + r = r + 1 ; result[r] = vector[10000] local z = n - m * 10000 - if z > 0 and z < 1000 then result[#result+1] = vector[0] end + if z > 0 and z < 1000 then r = r + 1 ; result[r] = vector[0] end n = n % 10000 elseif n >= 1000000 then local m = floor(n/10000) - result[#result+1] = tochinese(m,name) - result[#result+1] = vector[10000] + r = r + 1 ; result[r] = tochinese(m,name) + r = r + 1 ; result[r] = vector[10000] local z = n - m * 10000 - if z > 0 and z < 1000 then result[#result+1] = vector[0] end + if z > 0 and z < 1000 then r = r + 1 ; result[r] = vector[0] end n = n % 10000 elseif n >= 100000 then local m = floor(n/10000) - result[#result+1] = tochinese(m,name) - result[#result+1] = vector[10000] + r = r + 1 ; result[r] = tochinese(m,name) + r = r + 1 ; result[r] = vector[10000] local z = n - m * 10000 - if z > 0 and z < 1000 then result[#result+1] = vector[0] end + if z > 0 and z < 1000 then r = r + 1 ; result[r] = vector[0] end n = n % 10000 elseif n >= 10000 then local m = floor(n/10000) - result[#result+1] = vector[m] - result[#result+1] = vector[10000] + r = r + 1 ; result[r] = vector[m] + r = r + 1 ; result[r] = vector[10000] local z = n - m * 10000 - if z > 0 and z < 1000 then result[#result+1] = vector[0] end + if z > 0 and z < 1000 then r = r + 1 ; result[r] = vector[0] end n = n % 10000 elseif n >= 1000 then local m = floor(n/1000) - result[#result+1] = vector[m] - result[#result+1] = vector[1000] + r = r + 1 ; result[r] = vector[m] + r = r + 1 ; result[r] = vector[1000] local z = n - m * 1000 - if z > 0 and z < 100 then result[#result+1] = vector[0] end + if z > 0 and z < 100 then r = r + 1 ; result[r] = vector[0] end n = n % 1000 elseif n >= 100 then local m = floor(n/100) - result[#result+1] = vector[m] - result[#result+1] = vector[100] + r = r + 1 ; result[r] = vector[m] + r = r + 1 ; result[r] = vector[100] local z = n - m * 100 - if z > 0 and z < 10 then result[#result+1] = vector[0] end + if z > 0 and z < 10 then r = r + 1 ; result[r] = vector[0] end n = n % 100 elseif n >= 10 then local m = floor(n/10) if m > 1 and vector[m*10] then - result[#result+1] = vector[m*10] + r = r + 1 ; result[r] = vector[m*10] else - result[#result+1] = vector[m] - result[#result+1] = vector[10] + r = r + 1 ; result[r] = vector[m] + r = r + 1 ; result[r] = vector[10] end n = n % 10 else - result[#result+1] = vector[n] + r = r + 1 ; result[r] = vector[n] break end end diff --git a/tex/context/base/core-ctx.lua b/tex/context/base/core-ctx.lua index b1427f0e0..111c125da 100644 --- a/tex/context/base/core-ctx.lua +++ b/tex/context/base/core-ctx.lua @@ -56,7 +56,7 @@ local function found(name) -- used in resolve end local function resolve(name) -- used a few times later on - local filename = file.collapse_path(name) + local filename = file.collapsepath(name) local prepname = islocal and found(file.basename(name)) if prepname then return prepname diff --git a/tex/context/base/core-sys.lua b/tex/context/base/core-sys.lua index e4e49762d..47a5c340d 100644 --- a/tex/context/base/core-sys.lua +++ b/tex/context/base/core-sys.lua @@ -20,5 +20,6 @@ function commands.updatefilenames(inputfilename,outputfilename) end statistics.register("result saved in file", function() + -- suffix will be fetched from backend return string.format( "%s.%s", environment.outputfilename, (tex.pdfoutput>0 and "pdf") or "dvi") end) diff --git a/tex/context/base/data-env.lua b/tex/context/base/data-env.lua index ea6faeb6c..ac476593d 100644 --- a/tex/context/base/data-env.lua +++ b/tex/context/base/data-env.lua @@ -21,7 +21,6 @@ formats['enc'] = 'ENCFONTS' suffixes['enc'] = { 'enc' } formats['fmt'] = 'TEXFORMATS' suffixes['fmt'] = { 'fmt' } formats['map'] = 'TEXFONTMAPS' suffixes['map'] = { 'map' } formats['mp'] = 'MPINPUTS' suffixes['mp'] = { 'mp' } -formats['ocp'] = 'OCPINPUTS' suffixes['ocp'] = { 'ocp' } formats['ofm'] = 'OFMFONTS' suffixes['ofm'] = { 'ofm', 'tfm' } formats['otf'] = 'OPENTYPEFONTS' suffixes['otf'] = { 'otf' } formats['opl'] = 'OPLFONTS' suffixes['opl'] = { 'opl' } diff --git a/tex/context/base/data-exp.lua b/tex/context/base/data-exp.lua index 6d15a1cd7..98fbe9f95 100644 --- a/tex/context/base/data-exp.lua +++ b/tex/context/base/data-exp.lua @@ -13,7 +13,7 @@ local lpegCt, lpegCs, lpegP, lpegC, lpegS = lpeg.Ct, lpeg.Cs, lpeg.P, lpeg.C, lp local type, next = type, next local ostype = os.type -local collapse_path = file.collapse_path +local collapsepath = file.collapsepath local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end) local trace_expansions = false trackers.register("resolvers.expansions", function(v) trace_expansions = v end) @@ -49,22 +49,29 @@ local resolvers = resolvers local dummy_path_expr = "^!*unset/*$" local function do_first(a,b) - local t = { } - for s in gmatch(b,"[^,]+") do t[#t+1] = a .. s end + local t, n = { }, 0 + for s in gmatch(b,"[^,]+") do + n = n + 1 + t[n] = a .. s + end return "{" .. concat(t,",") .. "}" end local function do_second(a,b) - local t = { } - for s in gmatch(a,"[^,]+") do t[#t+1] = s .. b end + local t, n = { }, 0 + for s in gmatch(a,"[^,]+") do + n = n + 1 + t[n] = s .. b + end return "{" .. concat(t,",") .. "}" end local function do_both(a,b) - local t = { } + local t, n = { }, 0 for sa in gmatch(a,"[^,]+") do for sb in gmatch(b,"[^,]+") do - t[#t+1] = sa .. sb + n = n + 1 + t[n] = sa .. sb end end return "{" .. concat(t,",") .. "}" @@ -88,6 +95,7 @@ local function splitpathexpr(str, newlist, validate) report_resolvers("expanding variable '%s'",str) end local t, ok, done = newlist or { }, false, false + local n = #t str = lpegmatch(replacer_1,str) while true do done = false @@ -111,11 +119,15 @@ local function splitpathexpr(str, newlist, validate) if validate then for s in gmatch(str,"[^,]+") do s = validate(s) - if s then t[#t+1] = s end + if s then + n = n + 1 + t[n] = s + end end else for s in gmatch(str,"[^,]+") do - t[#t+1] = s + n = n + 1 + t[n] = s end end if trace_expansions then @@ -128,7 +140,7 @@ end local function validate(s) local isrecursive = find(s,"//$") - s = collapse_path(s) + s = collapsepath(s) if isrecursive then s = s .. "//" end @@ -151,11 +163,14 @@ function resolvers.expandedpathfromlist(pathlist) -- maybe not a list, just a pa splitpathexpr(pathlist[k],newlist,validate) end else + local n = 0 for k=1,#pathlist do for p in gmatch(pathlist[k],"([^,]+)") do ---~ p = collapse_path(p) p = validate(p) - if p ~= "" then newlist[#newlist+1] = p end + if p ~= "" then + n = n + 1 + newlist[n] = p + end end end end @@ -164,16 +179,63 @@ end -- We also put some cleanup code here. -local cleanup -- used recursively +--~ local cleanup -- used recursively +--~ local homedir + +--~ cleanup = lpeg.replacer { +--~ { +--~ "!", +--~ "" +--~ }, +--~ { +--~ "\\", +--~ "/" +--~ }, +--~ { +--~ "~" , +--~ function() +--~ return lpegmatch(cleanup,environment.homedir) +--~ end +--~ }, +--~ } + +--~ function resolvers.cleanpath(str) +--~ return str and lpegmatch(cleanup,str) +--~ end -cleanup = lpeg.replacer { - { "!", "" }, - { "\\", "/" }, - { "~" , function() return lpegmatch(cleanup,environment.homedir) end }, +local cleanup = lpeg.replacer { + { "!" , "" }, + { "\\" , "/" }, } +local homedir + function resolvers.cleanpath(str) - return str and lpegmatch(cleanup,str) + if not homedir then + homedir = lpegmatch(cleanup,environment.homedir or "") + if homedir == string.char(127) or homedir == "" or not lfs.isdir(homedir) then + if trace_expansions then + report_resolvers("no home dir set, ignoring dependent paths") + end + function resolvers.cleanpath(str) + if find(str,"~") then + return "" -- special case + else + return str and lpegmatch(cleanup,str) + end + end + else + cleanup = lpeg.replacer { + { "!" , "" }, + { "\\" , "/" }, + { "~" , homedir }, + } + function resolvers.cleanpath(str) + return str and lpegmatch(cleanup,str) + end + end + end + return resolvers.cleanpath(str) end -- This one strips quotes and funny tokens. @@ -203,8 +265,6 @@ end -- we join them and split them after the expansion has taken place. This -- is more convenient. ---~ local checkedsplit = string.checkedsplit - local cache = { } local splitter = lpegCt(lpeg.splitat(lpegS(ostype == "windows" and ";" or ":;"))) -- maybe add , @@ -219,15 +279,17 @@ local function splitconfigurationpath(str) -- beware, this can be either a path str = gsub(str,"\\","/") local split = lpegmatch(splitter,str) found = { } + local noffound = 0 for i=1,#split do local s = split[i] if not find(s,"^{*unset}*") then - found[#found+1] = s + noffound = noffound + 1 + found[noffound] = s end end if trace_expansions then report_resolvers("splitting path specification '%s'",str) - for k=1,#found do + for k=1,noffound do report_resolvers("% 4i: %s",k,found[k]) end end diff --git a/tex/context/base/data-ini.lua b/tex/context/base/data-ini.lua index 9550c1e78..ed36b6992 100644 --- a/tex/context/base/data-ini.lua +++ b/tex/context/base/data-ini.lua @@ -65,9 +65,13 @@ end do - local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or '~' + local homedir = osgetenv(ostype == "windows" and 'USERPROFILE' or 'HOME') or '' - homedir = file.collapse_path(homedir) + if not homedir or homedir == "" then + homedir = string.char(127) -- we need a value, later we wil trigger on it + end + + homedir = file.collapsepath(homedir) ossetenv("HOME", homedir) -- can be used in unix cnf files ossetenv("USERPROFILE",homedir) -- can be used in windows cnf files @@ -86,8 +90,8 @@ do local ownbin = environment.ownbin or args[-2] or arg[-2] or args[-1] or arg[-1] or arg[0] or "luatex" local ownpath = environment.ownpath or os.selfdir - ownbin = file.collapse_path(ownbin) - ownpath = file.collapse_path(ownpath) + ownbin = file.collapsepath(ownbin) + ownpath = file.collapsepath(ownpath) if not ownpath or ownpath == "" or ownpath == "unset" then ownpath = args[-1] or arg[-1] @@ -159,9 +163,9 @@ do local ownpath = environment.ownpath or dir.current() if ownpath then - ossetenv('SELFAUTOLOC', file.collapse_path(ownpath)) - ossetenv('SELFAUTODIR', file.collapse_path(ownpath .. "/..")) - ossetenv('SELFAUTOPARENT', file.collapse_path(ownpath .. "/../..")) + ossetenv('SELFAUTOLOC', file.collapsepath(ownpath)) + ossetenv('SELFAUTODIR', file.collapsepath(ownpath .. "/..")) + ossetenv('SELFAUTOPARENT', file.collapsepath(ownpath .. "/../..")) else report_resolvers("error: unable to locate ownpath") os.exit() @@ -197,7 +201,7 @@ if not texroot or texroot == "" then ossetenv('TEXROOT',texroot) end -environment.texroot = file.collapse_path(texroot) +environment.texroot = file.collapsepath(texroot) -- Tracing. Todo ... diff --git a/tex/context/base/data-met.lua b/tex/context/base/data-met.lua index 06c810fc4..b51c8b57b 100644 --- a/tex/context/base/data-met.lua +++ b/tex/context/base/data-met.lua @@ -34,7 +34,7 @@ function resolvers.splitmethod(filename) end function resolvers.methodhandler(what, filename, filetype) -- ... - filename = file.collapse_path(filename) + filename = file.collapsepath(filename) local specification = (type(filename) == "string" and resolvers.splitmethod(filename)) or filename -- no or { }, let it bomb local scheme = specification.scheme local resolver = resolvers[what] diff --git a/tex/context/base/data-res.lua b/tex/context/base/data-res.lua index 97ab8ce5d..8e3428b2f 100644 --- a/tex/context/base/data-res.lua +++ b/tex/context/base/data-res.lua @@ -23,7 +23,7 @@ local lpegP, lpegS, lpegR, lpegC, lpegCc, lpegCs, lpegCt = lpeg.P, lpeg.S, lpeg. local lpegmatch, lpegpatterns = lpeg.match, lpeg.patterns local filedirname, filebasename, fileextname, filejoin = file.dirname, file.basename, file.extname, file.join -local collapse_path = file.collapse_path +local collapsepath = file.collapsepath local allocate = utilities.storage.allocate local trace_locating = false trackers.register("resolvers.locating", function(v) trace_locating = v end) @@ -148,7 +148,6 @@ local function expandvars(lst) -- simple vars var = gsub(var,"%$([%a%d%_%-]+)",resolve) var = gsub(var,";+",";") var = gsub(var,";[!{}/\\]+;",";") ---~ var = gsub(var,"~",resolvers.homedir) lst[k] = var end end @@ -170,7 +169,6 @@ local function expandedvariable(var) -- simple vars var = gsub(var,"%$([%a%d%_%-]+)",resolve) var = gsub(var,";+",";") var = gsub(var,";[!{}/\\]+;",";") ---~ var = gsub(var,"~",resolvers.homedir) return var end @@ -228,7 +226,7 @@ local function identify_configuration_files() expandvars(cnfpaths) --- hm local luacnfname = resolvers.luacnfname for i=1,#cnfpaths do - local filename = collapse_path(filejoin(cnfpaths[i],luacnfname)) + local filename = collapsepath(filejoin(cnfpaths[i],luacnfname)) if lfs.isfile(filename) then specification[#specification+1] = filename end @@ -389,7 +387,7 @@ local function locate_file_databases() -- todo: cache:// and tree:// (runtime) local texmfpaths = resolvers.expandedpathlist('TEXMF') for i=1,#texmfpaths do - local path = collapse_path(texmfpaths[i]) + local path = collapsepath(texmfpaths[i]) local stripped = gsub(path,"^!!","") local runtime = stripped == path path = resolvers.cleanpath(path) @@ -502,11 +500,12 @@ end function resolvers.splitexpansions() local ie = instance.expansions for k,v in next, ie do - local t, h, p = { }, { }, splitconfigurationpath(v) + local t, tn, h, p = { }, 0, { }, splitconfigurationpath(v) for kk=1,#p do local vv = p[kk] if vv ~= "" and not h[vv] then - t[#t+1] = vv + tn = tn + 1 + t[tn] = vv h[vv] = true end end @@ -613,7 +612,8 @@ end function resolvers.registerextrapath(paths,subpaths) local ep = instance.extra_paths or { } - local n = #ep + local oldn = #ep + local newn = oldn if paths and paths ~= "" then if subpaths and subpaths ~= "" then for p in gmatch(paths,"[^,]+") do @@ -621,7 +621,8 @@ function resolvers.registerextrapath(paths,subpaths) for s in gmatch(subpaths,"[^,]+") do local ps = p .. "/" .. s if not done[ps] then - ep[#ep+1] = resolvers.cleanpath(ps) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(ps) done[ps] = true end end @@ -629,7 +630,8 @@ function resolvers.registerextrapath(paths,subpaths) else for p in gmatch(paths,"[^,]+") do if not done[p] then - ep[#ep+1] = resolvers.cleanpath(p) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(p) done[p] = true end end @@ -640,16 +642,17 @@ function resolvers.registerextrapath(paths,subpaths) for s in gmatch(subpaths,"[^,]+") do local ps = ep[i] .. "/" .. s if not done[ps] then - ep[#ep+1] = resolvers.cleanpath(ps) + newn = newn + 1 + ep[newn] = resolvers.cleanpath(ps) done[ps] = true end end end end - if #ep > 0 then + if newn > 0 then instance.extra_paths = ep -- register paths end - if #ep > n then + if newn > oldn then instance.lists = { } -- erase the cache end end @@ -659,14 +662,15 @@ local function made_list(instance,list) if not ep or #ep == 0 then return list else - local done, new = { }, { } + local done, new, newn = { }, { }, 0 -- honour . .. ../.. but only when at the start for k=1,#list do local v = list[k] if not done[v] then if find(v,"^[%.%/]$") then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v else break end @@ -677,7 +681,8 @@ local function made_list(instance,list) local v = ep[k] if not done[v] then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v end end -- next the formal paths @@ -685,7 +690,8 @@ local function made_list(instance,list) local v = list[k] if not done[v] then done[v] = true - new[#new+1] = v + newn = newn + 1 + new[newn] = v end end return new @@ -696,7 +702,7 @@ function resolvers.cleanpathlist(str) local t = resolvers.expandedpathlist(str) if t then for i=1,#t do - t[i] = collapse_path(resolvers.cleanpath(t[i])) + t[i] = collapsepath(resolvers.cleanpath(t[i])) end end return t @@ -762,7 +768,7 @@ resolvers.isreadable.tex = resolvers.isreadable.file -- name/name local function collect_files(names) - local filelist = { } + local filelist, noffiles = { }, 0 for k=1,#names do local fname = names[k] if trace_detail then @@ -803,7 +809,8 @@ local function collect_files(names) if trace_detail then report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result) end - filelist[#filelist+1] = { kind, search, result } + noffiles = noffiles + 1 + filelist[noffiles] = { kind, search, result } end else for kk=1,#blobfile do @@ -815,7 +822,8 @@ local function collect_files(names) if trace_detail then report_resolvers("match: kind '%s', search '%s', result '%s'",kind,search,result) end - filelist[#filelist+1] = { kind, search, result } + noffiles = noffiles + 1 + filelist[noffiles] = { kind, search, result } end end end @@ -825,7 +833,7 @@ local function collect_files(names) end end end - return #filelist > 0 and filelist or nil + return noffiles > 0 and filelist or nil end function resolvers.registerintrees(name) @@ -851,7 +859,7 @@ end local function collect_instance_files(filename,collected) -- todo : plugin (scanners, checkers etc) local result = collected or { } local stamp = nil - filename = collapse_path(filename) + filename = collapsepath(filename) -- speed up / beware: format problem if instance.remember then stamp = filename .. "--" .. instance.engine .. "--" .. instance.progname .. "--" .. instance.format @@ -1107,7 +1115,7 @@ local function collect_instance_files(filename,collected) -- todo : plugin (scan end end for k=1,#result do - local rk = collapse_path(result[k]) + local rk = collapsepath(result[k]) result[k] = rk resolvers.registerintrees(rk) -- for tracing used files end diff --git a/tex/context/base/data-sch.lua b/tex/context/base/data-sch.lua index 232722749..c8d209798 100644 --- a/tex/context/base/data-sch.lua +++ b/tex/context/base/data-sch.lua @@ -116,21 +116,23 @@ schemes.install('https') schemes.install('ftp') statistics.register("scheme handling time", function() - local l, r = { }, { } + local l, r, nl, nr = { }, { }, 0, 0 for k, v in table.sortedhash(loaded) do if v > 0 then - l[#l+1] = k .. ":" .. v + nl = nl + 1 + l[nl] = k .. ":" .. v end end for k, v in table.sortedhash(reused) do if v > 0 then - r[#r+1] = k .. ":" .. v + nr = nr + 1 + r[nr] = k .. ":" .. v end end - local n = #l + #r + local n = nl + nr if n > 0 then - l = (#l > 0 and concat(l)) or "none" - r = (#r > 0 and concat(r)) or "none" + l = (nl > 0 and concat(l)) or "none" + r = (nr > 0 and concat(r)) or "none" return format("%s seconds, %s processed, threshold %s seconds, loaded: %s, reused: %s", statistics.elapsedtime(schemes), n, schemes.threshold, l, r) else diff --git a/tex/context/base/data-tmf.lua b/tex/context/base/data-tmf.lua index d98e0ec66..e343e041b 100644 --- a/tex/context/base/data-tmf.lua +++ b/tex/context/base/data-tmf.lua @@ -22,7 +22,7 @@ function resolvers.load_tree(tree) local texos = "texmf-" .. os.platform local oldroot = environment.texroot - local newroot = file.collapse_path(tree) + local newroot = file.collapsepath(tree) local newtree = file.join(newroot,texos) local newpath = file.join(newtree,"bin") diff --git a/tex/context/base/data-tmp.lua b/tex/context/base/data-tmp.lua index 526d271bd..5ed6e4e1c 100644 --- a/tex/context/base/data-tmp.lua +++ b/tex/context/base/data-tmp.lua @@ -72,7 +72,7 @@ local function identify() local cachepath = texmfcaches[k] if cachepath ~= "" then cachepath = resolvers.cleanpath(cachepath) - cachepath = file.collapse_path(cachepath) + cachepath = file.collapsepath(cachepath) local valid = isdir(cachepath) if valid then if file.is_readable(cachepath) then diff --git a/tex/context/base/font-ctx.lua b/tex/context/base/font-ctx.lua index 8802ccb64..6101644e0 100644 --- a/tex/context/base/font-ctx.lua +++ b/tex/context/base/font-ctx.lua @@ -26,11 +26,13 @@ local report_define = logs.new("define fonts") local report_usage = logs.new("fonts usage") local report_mapfiles = logs.new("mapfiles") -local fonts = fonts -local tfm = fonts.tfm -local fontdata = fonts.identifiers -local definers = fonts.definers -local specifiers = definers.specifiers +local fonts = fonts +local tfm = fonts.tfm +local fontdata = fonts.identifiers +local definers = fonts.definers +local specifiers = definers.specifiers +local currentfont = font.current +local texattribute = tex.attribute specifiers.contextsetups = specifiers.contextsetups or { } specifiers.contextnumbers = specifiers.contextnumbers or { } @@ -226,31 +228,28 @@ specifiers.contextnumber = contextnumber specifiers.mergecontext = mergecontext specifiers.registercontext = registercontext -local current_font = font.current -local tex_attribute = tex.attribute - local cache = { } -- concat might be less efficient than nested tables function fonts.withset(name,what) - local zero = tex_attribute[0] + local zero = texattribute[0] local hash = zero .. "+" .. name .. "*" .. what local done = cache[hash] if not done then done = mergecontext(zero,name,what) cache[hash] = done end - tex_attribute[0] = done + texattribute[0] = done end function fonts.withfnt(name,what) - local font = current_font() + local font = currentfont() local hash = font .. "*" .. name .. "*" .. what local done = cache[hash] if not done then done = registercontext(font,name,what) cache[hash] = done end - tex_attribute[0] = done + texattribute[0] = done end function specifiers.showcontext(name) @@ -579,7 +578,7 @@ end local p, f = 1, "%0.1fpt" -- normally this value is changed only once -local stripper = lpeg.patterns.strip_zeros +local stripper = lpeg.patterns.stripzeros function fonts.nbfs(amount,precision) if precision ~= p then @@ -663,8 +662,8 @@ fonts.map.reset() -- resets the default file local nounicode = byte("?") -local function nametoslot(name) -- maybe some day rawdata - local tfmdata = fonts.ids[font.current()] +local function nametoslot(name,all) -- maybe some day rawdata + local tfmdata = fontdata[currentfont()] local shared = tfmdata and tfmdata.shared local fntdata = shared and shared.otfdata or shared.afmdata if fntdata then @@ -673,7 +672,9 @@ local function nametoslot(name) -- maybe some day rawdata return nounicode elseif type(unicode) == "number" then return unicode - else -- multiple unicodes + elseif all then + return unicode + else return unicode[1] end end @@ -682,11 +683,12 @@ end fonts.nametoslot = nametoslot -function fonts.char(n) -- todo: afm en tfm +function fonts.char(n,all) -- todo: afm en tfm if type(n) == "string" then - n = nametoslot(n) + n = nametoslot(n,all) end - if type(n) == "number" then + -- if type(n) == "number" then + if n then context.char(n) end end @@ -702,7 +704,7 @@ fonts.afm.char = fonts.char -- this will change ... function fonts.showchardata(n) - local tfmdata = fonts.ids[font.current()] + local tfmdata = fontdata[currentfont()] if tfmdata then if type(n) == "string" then n = utf.byte(n) @@ -715,7 +717,7 @@ function fonts.showchardata(n) end function fonts.showfontparameters() - local tfmdata = fonts.ids[font.current()] + local tfmdata = fontdata[currentfont()] if tfmdata then local parameters, mathconstants = tfmdata.parameters, tfmdata.MathConstants local hasparameters, hasmathconstants = parameters and next(parameters), mathconstants and next(mathconstants) @@ -733,9 +735,10 @@ end function fonts.reportdefinedfonts() if trace_usage then - local t = { } - for id, data in table.sortedhash(fonts.ids) do - t[#t+1] = { + local t, tn = { }, 0 + for id, data in table.sortedhash(fontdata) do + tn = tn + 1 + t[tn] = { format("%03i",id), format("%09i",data.size or 0), data.type or "real", @@ -755,7 +758,7 @@ function fonts.reportdefinedfonts() report_usage() report_usage("defined fonts:") report_usage() - for k=1,#t do + for k=1,tn do report_usage(t[k]) end end @@ -766,20 +769,20 @@ luatex.registerstopactions(fonts.reportdefinedfonts) function fonts.reportusedfeatures() -- numbers, setups, merged if trace_usage then - local t = { } - for i=1,#numbers do + local t, n = { }, #numbers + for i=1,n do local name = numbers[i] local setup = setups[name] local n = setup.number setup.number = nil -- we have no reason to show this - t[#t+1] = { i, name, table.sequenced(setup,false,true) } -- simple mode + t[i] = { i, name, table.sequenced(setup,false,true) } -- simple mode setup.number = n -- restore it (normally not needed as we're done anyway) end formatcolumns(t," ") report_usage() report_usage("defined featuresets:") report_usage() - for k=1,#t do + for k=1,n do report_usage(t[k]) end end @@ -844,3 +847,7 @@ function fonts.definetypeface(name,t) local settings = table.sequenced({ features= t.features },",") context.dofastdefinetypeface(name, shortcut, shape, size, settings) end + +function fonts.current(id) -- todo: also handle name + return fontdata[currentfont()] or fontdata[0] +end diff --git a/tex/context/base/font-def.lua b/tex/context/base/font-def.lua index 713e5b68d..2f530f022 100644 --- a/tex/context/base/font-def.lua +++ b/tex/context/base/font-def.lua @@ -170,14 +170,15 @@ local sortedhashkeys = table.sortedhashkeys function tfm.hashfeatures(specification) local features = specification.features if features then - local t = { } + local t, tn = { }, 0 local normal = features.normal if normal and next(normal) then local f = sortedhashkeys(normal) for i=1,#f do local v = f[i] if v ~= "number" and v ~= "features" then -- i need to figure this out, features - t[#t+1] = v .. '=' .. tostring(normal[v]) + tn = tn + 1 + t[tn] = v .. '=' .. tostring(normal[v]) end end end @@ -186,13 +187,15 @@ function tfm.hashfeatures(specification) local f = sortedhashkeys(vtf) for i=1,#f do local v = f[i] - t[#t+1] = v .. '=' .. tostring(vtf[v]) + tn = tn + 1 + t[tn] = v .. '=' .. tostring(vtf[v]) end end ---~ if specification.mathsize then ---~ t[#t+1] = "mathsize=" .. specification.mathsize ---~ end - if #t > 0 then + --~ if specification.mathsize then + --~ tn = tn + 1 + --~ t[tn] = "mathsize=" .. specification.mathsize + --~ end + if tn > 0 then return concat(t,"+") end end diff --git a/tex/context/base/font-enh.lua b/tex/context/base/font-enh.lua index 27e4e8b75..183ca848b 100644 --- a/tex/context/base/font-enh.lua +++ b/tex/context/base/font-enh.lua @@ -33,20 +33,26 @@ supplied by <l n='luatex'/>.</p> -- auto complete font with missing composed characters -table.insert(fonts.manipulators,"compose") - -function fonts.initializers.common.compose(tfmdata,value) - if value then - fonts.vf.aux.compose_characters(tfmdata) - end -end - -- tfm features, experimental tfm.features = tfm.features or { } tfm.features.list = tfm.features.list or { } tfm.features.default = tfm.features.default or { } +local initializers = fonts.initializers +local triggers = fonts.triggers +local manipulators = fonts.manipulators +local featurelist = tfm.features.list +local defaultfeaturelist = tfm.features.default + +table.insert(manipulators,"compose") + +function initializers.common.compose(tfmdata,value) + if value then + fonts.vf.aux.compose_characters(tfmdata) + end +end + function tfm.enhance(tfmdata,specification) -- we don't really share tfm data because we always reload -- but this is more in sycn with afm and such @@ -72,52 +78,67 @@ function tfm.setfeatures(tfmdata) local features = shared.features if features and next(features) then local mode = tfmdata.mode or features.mode or "base" - local fi = fonts.initializers[mode] + local fi = initializers[mode] if fi and fi.tfm then local function initialize(list) -- using tex lig and kerning if list then + -- fi adapts ! for i=1,#list do local f = list[i] local value = features[f] - if value and fi.tfm[f] then -- brr - if tfm.trace_features then - report_define("initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.name or 'unknown') + if value then + local fitfmf = fi.tfm[f] -- brr + if fitfmf then + if tfm.trace_features then + report_define("initializing feature %s to %s for mode %s for font %s",f,tostring(value),mode or 'unknown',tfmdata.name or 'unknown') + end + fitfmf(tfmdata,value) + mode = tfmdata.mode or features.mode or "base" + fi = initializers[mode] end - fi.tfm[f](tfmdata,value) - mode = tfmdata.mode or features.mode or "base" - fi = fonts.initializers[mode] end end end end - initialize(fonts.triggers) - initialize(tfm.features.list) - initialize(fonts.manipulators) + initialize(triggers) + initialize(featurelist) + initialize(manipulators) end local fm = fonts.methods[mode] - if fm and fm.tfm then - local function register(list) -- node manipulations - if list then - for i=1,#list do - local f = list[i] - if features[f] and fm.tfm[f] then -- brr - if not shared.processors then -- maybe also predefine - shared.processors = { fm.tfm[f] } - else - shared.processors[#shared.processors+1] = fm.tfm[f] + if fm then + local fmtfm = fm.tfm + if fmtfm then + local function register(list) -- node manipulations + if list then + local sp = shared.processors + local ns = sp and #sp + for i=1,#list do + local f = list[i] + if features[f] then + local fmtfmf = fmtfm[f] + if not fmtfmf then + -- brr + elseif not sp then + sp = { fmtfmf } + ns = 1 + shared.processors = sp + else + ns = ns + 1 + sp[ns] = fmtfmf + end end end end end + register(featurelist) end - register(tfm.features.list) end end end function tfm.features.register(name,default) - tfm.features.list[#tfm.features.list+1] = name - tfm.features.default[name] = default + featurelist[#tfm.features.list+1] = name + defaultfeaturelist[name] = default end function tfm.reencode(tfmdata,encoding) @@ -143,8 +164,8 @@ end tfm.features.register('reencode') -fonts.initializers.base.tfm.reencode = tfm.reencode -fonts.initializers.node.tfm.reencode = tfm.reencode +initializers.base.tfm.reencode = tfm.reencode +initializers.node.tfm.reencode = tfm.reencode fonts.enc = fonts.enc or { } fonts.enc.remappings = fonts.enc.remappings or { } @@ -173,8 +194,8 @@ end tfm.features.register('remap') -fonts.initializers.base.tfm.remap = tfm.remap -fonts.initializers.node.tfm.remap = tfm.remap +initializers.base.tfm.remap = tfm.remap +initializers.node.tfm.remap = tfm.remap --~ obsolete --~ diff --git a/tex/context/base/font-ext.lua b/tex/context/base/font-ext.lua index 79d0f0955..c17516653 100644 --- a/tex/context/base/font-ext.lua +++ b/tex/context/base/font-ext.lua @@ -164,21 +164,6 @@ vectors['default'] = { vectors['quality'] = vectors['default'] -- metatable ? ---~ function table.locator(...) ---~ local k = { ... } ---~ return function(t) ---~ for i=1,#k do ---~ t = t[k[i]] ---~ if not k then ---~ return false ---~ end ---~ end ---~ return t ---~ end ---~ end - ---~ local locate = table.locator { "goodies", "expansions" } - function initializers.common.expansion(tfmdata,value) if value then local class, vector = get_class_and_vector(tfmdata,value,"expansions") diff --git a/tex/context/base/font-log.lua b/tex/context/base/font-log.lua index 3f9e111b3..e16a945f9 100644 --- a/tex/context/base/font-log.lua +++ b/tex/context/base/font-log.lua @@ -37,12 +37,13 @@ function logger.save(tfmtable,source,specification) -- save file name in spec he end function logger.report(complete) - local t = { } + local t, n = { }, 0 for name, used in table.sortedhash(fonts.used) do + n = n + 1 if complete then - t[#t+1] = used .. "->" .. file.basename(name) + t[n] = used .. "->" .. file.basename(name) else - t[#t+1] = file.basename(name) + t[n] = file.basename(name) end end return t diff --git a/tex/context/base/font-map.lua b/tex/context/base/font-map.lua index 46e84ada2..569f3df81 100644 --- a/tex/context/base/font-map.lua +++ b/tex/context/base/font-map.lua @@ -219,21 +219,23 @@ fonts.map.addtounicode = function(data,filename) originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 end else - local t = { } + local t, n = { }, 0 for l=1,nplit do local base = split[l] local u = unicodes[base] or (aglmap and aglmap[base]) if not u then break elseif type(u) == "table" then - t[#t+1] = u[1] + n = n + 1 + t[n] = u[1] else - t[#t+1] = u + n = n + 1 + t[n] = u end end - if #t == 0 then -- done then + if n == 0 then -- done then -- nothing - elseif #t == 1 then + elseif n == 1 then originals[index], tounicode[index], nl, unicode = t[1], tounicode16(t[1]), nl + 1, true else originals[index], tounicode[index], nl, unicode = t, tounicode16sequence(t), nl + 1, true diff --git a/tex/context/base/font-mis.lua b/tex/context/base/font-mis.lua index 911ffd3fb..964378a8a 100644 --- a/tex/context/base/font-mis.lua +++ b/tex/context/base/font-mis.lua @@ -11,7 +11,7 @@ local lower, strip = string.lower, string.strip fonts.otf = fonts.otf or { } -fonts.otf.version = fonts.otf.version or 2.705 +fonts.otf.version = fonts.otf.version or 2.706 fonts.otf.cache = containers.define("fonts", "otf", fonts.otf.version, true) function fonts.otf.loadcached(filename,format,sub) diff --git a/tex/context/base/font-otf.lua b/tex/context/base/font-otf.lua index 085950dfc..2a19dc5f6 100644 --- a/tex/context/base/font-otf.lua +++ b/tex/context/base/font-otf.lua @@ -13,13 +13,13 @@ if not modules then modules = { } end modules ['font-otf'] = { local utf = unicode.utf8 -local concat, utfbyte = table.concat, utf.byte +local utfbyte = utf.byte local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip local type, next, tonumber, tostring = type, next, tonumber, tostring local abs = math.abs local getn = table.getn local lpegmatch = lpeg.match -local reverse = table.reverse +local reversed, concat = table.reversed, table.concat local ioflush = io.flush local allocate = utilities.storage.allocate @@ -57,7 +57,7 @@ local definers = fonts.definers otf.glists = { "gsub", "gpos" } -otf.version = 2.705 -- beware: also sync font-mis.lua +otf.version = 2.706 -- beware: also sync font-mis.lua otf.cache = containers.define("fonts", "otf", otf.version, true) local loadmethod = "table" -- table, mixed, sparse @@ -968,6 +968,7 @@ actions["prepare unicodes"] = function(data,filename,raw) local luatex = data.luatex local indices, unicodes, multiples, internals = { }, { }, { }, { } local mapmap = data.map or raw.map + local mapenc = nil -- will go away if not mapmap then report_otf("no map in %s",filename) mapmap = { } @@ -977,6 +978,7 @@ actions["prepare unicodes"] = function(data,filename,raw) mapmap = { } data.map.map = mapmap else + mapenc = mapmap.enc -- will go away mapmap = mapmap.map end local criterium = fonts.privateoffset @@ -1000,41 +1002,53 @@ actions["prepare unicodes"] = function(data,filename,raw) indices[unicode] = index unicodes[name] = unicode end + -- maybe deal with altuni here in the future but first we need + -- to encounter a proper font that sets them else -- message that something is wrong end end end -- beware: the indices table is used to initialize the tfm table - for unicode, index in next, mapmap do - if not internals[index] then - local name = glyphs[index].name - if name then - local un = unicodes[name] - if not un then - unicodes[name] = unicode -- or 0 - elseif type(un) == "number" then -- tonumber(un) - if un ~= unicode then - multiples[#multiples+1] = name - unicodes[name] = { un, unicode } - indices[unicode] = index - end - else - local ok = false - for u=1,#un do - if un[u] == unicode then - ok = true - break + local encname = lower(data.enc_name or (mapenc and mapenc[1] and mapenc[1].enc_name) or "") -- mapenc will go away + -- will become: local encname = lower(data.enc_name or "") + if encname == "" or encname == "unicodebmp" or encname == "unicodefull" then -- maybe find(encname,"unicode") + if trace_loading then + report_otf("using extra unicode map") + end + -- ok -- we can also consider using the altuni + for unicode, index in next, mapmap do + if not internals[index] then + local name = glyphs[index].name + if name then + local un = unicodes[name] + if not un then + unicodes[name] = unicode -- or 0 + elseif type(un) == "number" then -- tonumber(un) + if un ~= unicode then + multiples[#multiples+1] = name + unicodes[name] = { un, unicode } + indices[unicode] = index + end + else + local ok = false + for u=1,#un do + if un[u] == unicode then + ok = true + break + end + end + if not ok then + multiples[#multiples+1] = name + un[#un+1] = unicode + indices[unicode] = index end - end - if not ok then - multiples[#multiples+1] = name - un[#un+1] = unicode - indices[unicode] = index end end end end + else + report_otf("warning: non unicode map '%s', only using glyph unicode data",encname or "whatever") end if trace_loading then if #multiples > 0 then @@ -1063,7 +1077,7 @@ actions["reorganize lookups"] = function(data,filename,raw) for _, vv in next, v.rules do local c = vv.coverage if c and c.before then - c.before = reverse(c.before) + c.before = reversed(c.before) end end end @@ -1582,8 +1596,8 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th local glyphs, pfminfo, metadata = data.glyphs or { }, data.pfminfo or { }, data.metadata or { } local luatex = data.luatex local unicodes = luatex.unicodes -- names to unicodes - local indices = luatex.indices local mode = data.mode or "base" - + local indices = luatex.indices + local mode = data.mode or "base" local characters, parameters, math_parameters, descriptions = { }, { }, { }, { } local designsize = metadata.designsize or metadata.design_size or 100 if designsize == 0 then diff --git a/tex/context/base/font-otn.lua b/tex/context/base/font-otn.lua index 2bccc3c1b..ec246d20d 100644 --- a/tex/context/base/font-otn.lua +++ b/tex/context/base/font-otn.lua @@ -176,7 +176,7 @@ local zwj = 0x200D local wildcard = "*" local default = "dflt" -local split_at_space = lpeg.splitters[" "] or lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway +local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway local nodecodes = nodes.nodecodes local whatcodes = nodes.whatcodes @@ -282,9 +282,9 @@ local function gref(n) local num, nam = { }, { } for i=1,#n do local ni = n[i] - num[#num+1] = format("U+%04X",ni) - local dni = descriptions[ni] - nam[#num] = (dni and dni.name) or "?" + local di = descriptions[ni] + num[i] = format("U+%04X",ni) + nam[i] = di and di.name or "?" end return format("%s (%s)",concat(num," "), concat(nam," ")) end @@ -2254,13 +2254,14 @@ otf.features.prepare = { } local function split(replacement,original,cache,unicodes) -- we can cache this too, but not the same (although unicode is a unique enough hash) - local o, t, n = { }, { }, 0 + local o, t, n, no = { }, { }, 0, 0 for s in gmatch(original,"[^ ]+") do local us = unicodes[s] + no = no + 1 if type(us) == "number" then -- tonumber(us) - o[#o+1] = us + o[no] = us else - o[#o+1] = us[1] + o[no] = us[1] end end for s in gmatch(replacement,"[^ ]+") do @@ -2277,9 +2278,11 @@ end local function uncover(covers,result,cache,unicodes) -- lpeg hardly faster (.005 sec on mk) + local nofresults = #result for n=1,#covers do local c = covers[n] local cc = cache[c] + nofresults = nofresults + 1 if not cc then local t = { } for s in gmatch(c,"[^ ]+") do @@ -2293,9 +2296,9 @@ local function uncover(covers,result,cache,unicodes) end end cache[c] = t - result[#result+1] = t + result[nofresults] = t else - result[#result+1] = cc + result[nofresults] = cc end end end @@ -2337,16 +2340,17 @@ local function prepare_lookups(tfmdata) --~ end end, multiple = function (p,lookup,glyph,unicode) - local old, new = unicode, { } + local old, new, nnew = unicode, { }, 0 local m = multiple[lookup] if not m then m = { } multiple[lookup] = m end m[old] = new for pc in gmatch(p[2],"[^ ]+") do local upc = unicodes[pc] + nnew = nnew + 1 if type(upc) == "number" then - new[#new+1] = upc + new[nnew] = upc else - new[#new+1] = upc[1] + new[nnew] = upc[1] end end --~ if trace_lookups then @@ -2354,16 +2358,17 @@ local function prepare_lookups(tfmdata) --~ end end, alternate = function(p,lookup,glyph,unicode) - local old, new = unicode, { } + local old, new, nnew = unicode, { }, 0 local a = alternate[lookup] if not a then a = { } alternate[lookup] = a end a[old] = new for pc in gmatch(p[2],"[^ ]+") do local upc = unicodes[pc] + nnew = nnew + 1 if type(upc) == "number" then - new[#new+1] = upc + new[nnew] = upc else - new[#new+1] = upc[1] + new[nnew] = upc[1] end end --~ if trace_lookups then @@ -2557,7 +2562,7 @@ local function prepare_contextchains(tfmdata) contexts = { } contextchain[lookupname] = contexts end - local t = { } + local t, nt = { }, 0 for nofrules=1,#rules do -- does #rules>1 happen often? local rule = rules[nofrules] local coverage = rule.coverage @@ -2573,7 +2578,8 @@ local function prepare_contextchains(tfmdata) uncover(after,sequence,cache,unicodes) end if sequence[1] then - t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } for unic, _ in next, sequence[start] do local cu = contexts[unic] if not cu then @@ -2593,7 +2599,7 @@ local function prepare_contextchains(tfmdata) contexts = { } reversecontextchain[lookupname] = contexts end - local t = { } + local t, nt = { }, 0 for nofrules=1,#rules do local rule = rules[nofrules] local reversecoverage = rule.reversecoverage @@ -2613,7 +2619,8 @@ local function prepare_contextchains(tfmdata) end if sequence[1] then -- this is different from normal coverage, we assume only replacements - t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } for unic, _ in next, sequence[start] do local cu = contexts[unic] if not cu then @@ -2633,7 +2640,7 @@ local function prepare_contextchains(tfmdata) contexts = { } contextchain[lookupname] = contexts end - local t = { } + local t, nt = { }, 0 for nofrules=1,#rules do -- nearly the same as coverage so we could as well rename it local rule = rules[nofrules] @@ -2653,7 +2660,8 @@ local function prepare_contextchains(tfmdata) uncover(back,sequence,cache,unicodes) end if sequence[1] then - t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } for unic, _ in next, sequence[start] do local cu = contexts[unic] if not cu then diff --git a/tex/context/base/font-ott.lua b/tex/context/base/font-ott.lua index a5a0df37e..ec915b878 100644 --- a/tex/context/base/font-ott.lua +++ b/tex/context/base/font-ott.lua @@ -638,9 +638,18 @@ local baselines = allocate { ['romn'] = 'Roman baseline' } -local verbosescripts = allocate(table.swaphash(scripts )) -local verboselanguages = allocate(table.swaphash(languages)) -local verbosefeatures = allocate(table.swaphash(features )) + +local function swap(h) -- can be a tables.swap when we get a better name + local r = { } + for k, v in next, h do + r[v] = lower(gsub(k," ","")) + end + return r +end + +local verbosescripts = allocate(swap(scripts )) +local verboselanguages = allocate(swap(languages)) +local verbosefeatures = allocate(swap(features )) tables.scripts = scripts tables.languages = languages @@ -661,12 +670,6 @@ for k, v in next, verbosefeatures do verbosefeatures[lower(k)] = v end --- can be sped up by local tables - -function tables.totag(id) -- not used - return format("%4s",lower(id)) -end - local function resolve(tab,id) if tab and id then id = lower(id) diff --git a/tex/context/base/font-syn.lua b/tex/context/base/font-syn.lua index 7cd4b2a37..a0dbd4f7e 100644 --- a/tex/context/base/font-syn.lua +++ b/tex/context/base/font-syn.lua @@ -6,17 +6,6 @@ if not modules then modules = { } end modules ['font-syn'] = { license = "see context related readme files" } -local keyisvalue = { __index = function(t,k) - t[k] = k - return k -end } - -function table.initialysparse(t) - t = t or { } - setmetatable(t,keyisvalue) - return t -end - -- todo: subs in lookups requests local utf = unicode.utf8 @@ -29,6 +18,7 @@ local utfgsub, utflower = utf.gsub, utf.lower local unpack = unpack or table.unpack local allocate = utilities.storage.allocate +local sparse = utilities.storage.sparse local trace_names = false trackers.register("fonts.names", function(v) trace_names = v end) local trace_warnings = false trackers.register("fonts.warnings", function(v) trace_warnings = v end) @@ -90,7 +80,7 @@ local weights = Cs ( -- not extra + P("regular") / "normal" ) -local normalized_weights = table.initialysparse { +local normalized_weights = sparse { regular = "normal", } @@ -105,7 +95,7 @@ local styles = Cs ( + P("ita") / "italic" ) -local normalized_styles = table.initialysparse { +local normalized_styles = sparse { reverseoblique = "reverseitalic", regular = "normal", oblique = "italic", @@ -120,7 +110,7 @@ local widths = Cs( + P("book") / "normal" ) -local normalized_widths = table.initialysparse() +local normalized_widths = sparse() local variants = Cs( -- fax casual P("smallcaps") @@ -128,7 +118,7 @@ local variants = Cs( -- fax casual + P("caps") / "smallcaps" ) -local normalized_variants = table.initialysparse() +local normalized_variants = sparse() local any = P(1) @@ -247,7 +237,7 @@ filters.paths = { } filters.names = { } function names.getpaths(trace) - local hash, result = { }, { } + local hash, result, r = { }, { }, 0 local function collect(t,where) for i=1, #t do local v = resolvers.cleanpath(t[i]) @@ -255,7 +245,9 @@ function names.getpaths(trace) local key = lower(v) report_names("adding path from %s: %s",where,v) if not hash[key] then - hash[key], result[#result+1] = true, v + r = r + 1 + result[r] = v + hash[key] = true end end end @@ -783,7 +775,7 @@ local function addfilenames() end local function rejectclashes() -- just to be sure, so no explicit afm will be found then - local specifications, used, okay = names.data.specifications, { }, { } + local specifications, used, okay, o = names.data.specifications, { }, { }, 0 for i=1,#specifications do local s = specifications[i] local f = s.fontname @@ -794,10 +786,13 @@ local function rejectclashes() -- just to be sure, so no explicit afm will be fo report_names( "fontname '%s' clashes, rejecting '%s' in favor of '%s'",f,fnm,fnd) end else - used[f], okay[#okay+1] = fnm, s + used[f] = fnm + o = o + 1 + okay[o] = s end else - okay[#okay+1] = s + o = o + 1 + okay[o] = s end end local d = #specifications - #okay @@ -1333,7 +1328,7 @@ local function heuristic(name,weight,style,width,variant,all) -- todo: fallbacks if nf then local t = { } for i=1,nf do - t[#t+1] = format("'%s'",found[i].fontname) + t[i] = format("'%s'",found[i].fontname) end report_names("name '%s' resolved to %s instances: %s",name,nf,concat(t," ")) else @@ -1388,7 +1383,6 @@ end function names.collectfiles(askedname,reload) -- no all if askedname and askedname ~= "" and names.enabled then ---~ askedname = lower(askedname) -- or cleanname askedname = cleanname(askedname) -- or cleanname names.load(reload) local list = { } @@ -1396,10 +1390,8 @@ function names.collectfiles(askedname,reload) -- no all local specifications = names.data.specifications for i=1,#specifications do local s = specifications[i] ---~ if find(lower(basename(s.filename)),askedname) then if find(cleanname(basename(s.filename)),askedname) then list[#list+1] = s - end end return list @@ -1495,20 +1487,22 @@ function names.lookup(pattern,name,reload) -- todo: find end if lookups then for key, value in gmatch(pattern,"([^=,]+)=([^=,]+)") do - local t = { } + local t, n = { }, 0 if find(value,"*") then value = string.topattern(value) for i=1,#lookups do local s = lookups[i] if find(s[key],value) then - t[#t+1] = lookups[i] + n = n + 1 + t[n] = lookups[i] end end else for i=1,#lookups do local s = lookups[i] if s[key] == value then - t[#t+1] = lookups[i] + n = n + 1 + t[n] = lookups[i] end end end diff --git a/tex/context/base/font-tfm.lua b/tex/context/base/font-tfm.lua index 4d80f07e1..7a16b284f 100644 --- a/tex/context/base/font-tfm.lua +++ b/tex/context/base/font-tfm.lua @@ -564,13 +564,13 @@ function tfm.scale(tfmtable, scaledpoints, relativeid) local ivc = vc[i] local key = ivc[1] if key == "right" then - tt[#tt+1] = { key, ivc[2]*hdelta } + tt[i] = { key, ivc[2]*hdelta } elseif key == "down" then - tt[#tt+1] = { key, ivc[2]*vdelta } + tt[i] = { key, ivc[2]*vdelta } elseif key == "rule" then - tt[#tt+1] = { key, ivc[2]*vdelta, ivc[3]*hdelta } + tt[i] = { key, ivc[2]*vdelta, ivc[3]*hdelta } else -- not comment - tt[#tt+1] = ivc -- shared since in cache and untouched + tt[i] = ivc -- shared since in cache and untouched end end chr.commands = tt diff --git a/tex/context/base/font-xtx.lua b/tex/context/base/font-xtx.lua index 5a31d8c5e..5e9fea12f 100644 --- a/tex/context/base/font-xtx.lua +++ b/tex/context/base/font-xtx.lua @@ -74,7 +74,7 @@ local function colonized(specification) -- xetex mode list = { } lpegmatch(pattern,specification.specification) -- for k, v in next, list do - -- list[k] = v:is_boolean() + -- list[k] = is_boolean(v) -- if type(list[a]) == "nil" then -- list[k] = v -- end diff --git a/tex/context/base/grph-inc.lua b/tex/context/base/grph-inc.lua index 240a84e6b..e4256af32 100644 --- a/tex/context/base/grph-inc.lua +++ b/tex/context/base/grph-inc.lua @@ -44,7 +44,7 @@ local texbox = tex.box local contains = table.contains local concat = table.concat local todimen = string.todimen -local settings_to_array = utilities.parsers.settings_to_array +local settings_to_array, settings_to_hash = utilities.parsers.settings_to_array, utilities.parsers.settings_to_hash local allocate = utilities.storage.allocate local variables = interfaces.variables @@ -215,7 +215,7 @@ function figures.setpaths(locationset,pathlist) -- this function can be called each graphic so we provide this optimization return end - local iv, t, h = interfaces.variables, figures.paths, locationset:tohash() + local iv, t, h = interfaces.variables, figures.paths, settings_to_hash(locationset) if last_locationset ~= locationset then -- change == reset (actually, a 'reset' would indeed reset if h[iv["local"]] then @@ -359,114 +359,135 @@ local defaultprefix = "m_k_i_v_" -- todo: local path or cache path +local function forbiddenname(filename) + if not filename or filename == "" then + return false + end + local expandedfullname = file.collapsepath(filename,true) + local expandedinputname = file.collapsepath(file.addsuffix(environment.jobfilename,environment.jobfilesuffix),true) + if expandedfullname == expandedinputname then + report_graphics("skipping graphic with same name as input filename (%s), enforce suffix",expandedinputname) + return true + end + local expandedoutputname = file.collapsepath(codeinjections.getoutputfilename(),true) + if expandedfullname == expandedoutputname then + report_graphics("skipping graphic with same name as output filename (%s), enforce suffix",expandedoutputname) + return true + end +end + local function register(askedname,specification) if specification then - local format = specification.format - if format then - local conversion = specification.conversion - local resolution = specification.resolution - if conversion == "" then - conversion = nil - end - if resolution == "" then - resolution = nil - end - local newformat = conversion - if not newformat or newformat == "" then - newformat = defaultformat - end - if trace_conversion then - report_graphics("checking conversion of '%s': old format '%s', new format '%s', conversion '%s', resolution '%s'", - askedname,format,newformat,conversion or "default",resolution or "default") - end - local converter = (newformat ~= format) and converters[format] - if converter then - if converter[newformat] then - converter = converter[newformat] - else + if forbiddenname(specification.fullname) then + specification = { } + else + local format = specification.format + if format then + local conversion = specification.conversion + local resolution = specification.resolution + if conversion == "" then + conversion = nil + end + if resolution == "" then + resolution = nil + end + local newformat = conversion + if not newformat or newformat == "" then newformat = defaultformat + end + if trace_conversion then + report_graphics("checking conversion of '%s': old format '%s', new format '%s', conversion '%s', resolution '%s'", + askedname,format,newformat,conversion or "default",resolution or "default") + end + local converter = (newformat ~= format) and converters[format] + if converter then if converter[newformat] then converter = converter[newformat] else - converter = nil newformat = defaultformat + if converter[newformat] then + converter = converter[newformat] + else + converter = nil + newformat = defaultformat + end end + elseif trace_conversion then + report_graphics("no converter for '%s' -> '%s'",format,newformat) end - elseif trace_conversion then - report_graphics("no converter for '%s' -> '%s'",format,newformat) - end - if converter then - local oldname = specification.fullname -local oldname = specification.foundname - local newpath = file.dirname(oldname) - local oldbase = file.basename(oldname) - local newbase = file.removesuffix(oldbase) - local fc = specification.cache or figures.cachepaths.path - if fc and fc ~= "" and fc ~= "." then - newpath = fc - else - newbase = defaultprefix .. newbase - end - if not file.is_writable(newpath) then - if trace_conversion then - report_graphics("[ath '%s'is not writable, forcing conversion path '.' ",newpath) + if converter then + -- local oldname = specification.fullname + local oldname = specification.foundname + local newpath = file.dirname(oldname) + local oldbase = file.basename(oldname) + local newbase = file.removesuffix(oldbase) + local fc = specification.cache or figures.cachepaths.path + if fc and fc ~= "" and fc ~= "." then + newpath = fc + else + newbase = defaultprefix .. newbase end - newpath = "." - end - local subpath = specification.subpath or figures.cachepaths.subpath - if subpath and subpath ~= "" and subpath ~= "." then - newpath = newpath .. "/" .. subpath - end - local prefix = specification.prefix or figures.cachepaths.prefix - if prefix and prefix ~= "" then - newbase = prefix .. newbase - end - if resolution and resolution ~= "" then -- the order might change - newbase = newbase .. "_" .. resolution - end - local newbase = file.addsuffix(newbase,newformat) - local newname = file.join(newpath,newbase) - dir.makedirs(newpath) - oldname = file.collapse_path(oldname) - newname = file.collapse_path(newname) - local oldtime = lfs.attributes(oldname,'modification') or 0 - local newtime = lfs.attributes(newname,'modification') or 0 - if newtime == 0 or oldtime > newtime then - if trace_conversion then - report_graphics("converting '%s' from '%s' to '%s'",askedname,format,newformat) + if not file.is_writable(newpath) then + if trace_conversion then + report_graphics("[ath '%s'is not writable, forcing conversion path '.' ",newpath) + end + newpath = "." end - converter(oldname,newname,resolution or "") - else - if trace_conversion then - report_graphics("no need to convert '%s' from '%s' to '%s'",askedname,format,newformat) + local subpath = specification.subpath or figures.cachepaths.subpath + if subpath and subpath ~= "" and subpath ~= "." then + newpath = newpath .. "/" .. subpath + end + local prefix = specification.prefix or figures.cachepaths.prefix + if prefix and prefix ~= "" then + newbase = prefix .. newbase + end + if resolution and resolution ~= "" then -- the order might change + newbase = newbase .. "_" .. resolution + end + local newbase = file.addsuffix(newbase,newformat) + local newname = file.join(newpath,newbase) + dir.makedirs(newpath) + oldname = file.collapsepath(oldname) + newname = file.collapsepath(newname) + local oldtime = lfs.attributes(oldname,'modification') or 0 + local newtime = lfs.attributes(newname,'modification') or 0 + if newtime == 0 or oldtime > newtime then + if trace_conversion then + report_graphics("converting '%s' from '%s' to '%s'",askedname,format,newformat) + end + converter(oldname,newname,resolution or "") + else + if trace_conversion then + report_graphics("no need to convert '%s' from '%s' to '%s'",askedname,format,newformat) + end + end + if io.exists(newname) then + specification.foundname = oldname + specification.fullname = newname + specification.prefix = prefix + specification.subpath = subpath + specification.converted = true + format = newformat + elseif io.exists(oldname) then + specification.fullname = newname + specification.converted = false end end - if io.exists(newname) then - specification.foundname = oldname - specification.fullname = newname - specification.prefix = prefix - specification.subpath = subpath - specification.converted = true - format = newformat - elseif io.exists(oldname) then - specification.fullname = newname - specification.converted = false - end - end - end - local found = figures.suffixes[format] -- validtypes[format] - if not found then - specification.found = false - if trace_figures then - commands.writestatus("figures","format not supported: %s",format) end - else - specification.found = true - if trace_figures then - if validtypes[format] then - commands.writestatus("figures","format natively supported by backend: %s",format) - else - commands.writestatus("figures","format supported by output file format: %s",format) + local found = figures.suffixes[format] -- validtypes[format] + if not found then + specification.found = false + if trace_figures then + commands.writestatus("figures","format not supported: %s",format) + end + else + specification.found = true + if trace_figures then + if validtypes[format] then + commands.writestatus("figures","format natively supported by backend: %s",format) + else + commands.writestatus("figures","format supported by output file format: %s",format) + end end end end diff --git a/tex/context/base/grph-inc.mkiv b/tex/context/base/grph-inc.mkiv index 730fe1ee5..80a9c3187 100644 --- a/tex/context/base/grph-inc.mkiv +++ b/tex/context/base/grph-inc.mkiv @@ -218,7 +218,7 @@ {\global\setbox\foundexternalfigure\vbox{\cldprocessfile{#1}}} \def\docheckfigurebuffer #1{\doprocesstexlikefigure{\getbuffer[#1]}} -\def\docheckfiguretex #1{\doprocesstexlikefigure{\input#1\relax}} +\def\docheckfiguretex #1{\doprocesstexlikefigure{\input{#1}}} \def\docheckfigurecld #1{\doprocesscldlikefigure{#1}} % we can always add cldrun \def\docheckfiguremps #1{\doprocessmpslikefigure{#1}} \def\docheckfiguremprun #1#2{\doprocesstexlikefigure{\useMPrun{#1}{#2}}} diff --git a/tex/context/base/l-boolean.lua b/tex/context/base/l-boolean.lua index cf92d2f41..3fff7c126 100644 --- a/tex/context/base/l-boolean.lua +++ b/tex/context/base/l-boolean.lua @@ -11,8 +11,12 @@ local type, tonumber = type, tonumber boolean = boolean or { } local boolean = boolean +-- function boolean.tonumber(b) +-- return b and 1 or 0 -- test and test and return or return +-- end + function boolean.tonumber(b) - if b then return 1 else return 0 end + if b then return 1 else return 0 end -- test and return or return end function toboolean(str,tolerant) @@ -36,6 +40,8 @@ function toboolean(str,tolerant) end end +string.toboolean = toboolean + function string.is_boolean(str,default) if type(str) == "string" then if str == "true" or str == "yes" or str == "on" or str == "t" then @@ -46,11 +52,3 @@ function string.is_boolean(str,default) end return default end - -function boolean.alwaystrue() - return true -end - -function boolean.falsetrue() - return false -end diff --git a/tex/context/base/l-dimen.lua b/tex/context/base/l-dimen.lua index 35fca56da..cb97e35f6 100644 --- a/tex/context/base/l-dimen.lua +++ b/tex/context/base/l-dimen.lua @@ -81,9 +81,7 @@ local dimenfactors = { format (string) is implemented using this table.</p> --ldx]]-- --- was: - -local function todimen(n,unit,fmt) +local function numbertodimen(n,unit,fmt) if type(n) == 'string' then return n else @@ -102,21 +100,21 @@ end --ldx]]-- number.maxdimen = 1073741823 -number.todimen = todimen +number.todimen = numbertodimen number.dimenfactors = dimenfactors -function number.topoints (n) return todimen(n,"pt") end -function number.toinches (n) return todimen(n,"in") end -function number.tocentimeters (n) return todimen(n,"cm") end -function number.tomillimeters (n) return todimen(n,"mm") end -function number.toscaledpoints(n) return todimen(n,"sp") end -function number.toscaledpoints(n) return n .. "sp" end -function number.tobasepoints (n) return todimen(n,"bp") end -function number.topicas (n) return todimen(n "pc") end -function number.todidots (n) return todimen(n,"dd") end -function number.tociceros (n) return todimen(n,"cc") end -function number.tonewdidots (n) return todimen(n,"nd") end -function number.tonewciceros (n) return todimen(n,"nc") end +function number.topoints (n) return numbertodimen(n,"pt") end +function number.toinches (n) return numbertodimen(n,"in") end +function number.tocentimeters (n) return numbertodimen(n,"cm") end +function number.tomillimeters (n) return numbertodimen(n,"mm") end +function number.toscaledpoints(n) return numbertodimen(n,"sp") end +function number.toscaledpoints(n) return n .. "sp" end +function number.tobasepoints (n) return numbertodimen(n,"bp") end +function number.topicas (n) return numbertodimen(n "pc") end +function number.todidots (n) return numbertodimen(n,"dd") end +function number.tociceros (n) return numbertodimen(n,"cc") end +function number.tonewdidots (n) return numbertodimen(n,"nd") end +function number.tonewciceros (n) return numbertodimen(n,"nc") end --[[ldx-- <p>More interesting it to implement a (sort of) dimen datatype, one @@ -146,14 +144,24 @@ mt.__index = function(t,s) return false end -function string:todimen() - if type(self) == "number" then - return self - else - local value, unit = lpegmatch(dimenpair,self) - return value/unit - end -end + +--[[ldx-- +<p>We redefine the following function later on, so we comment it +here (which saves us bytecodes.</p> +--ldx]]-- + +-- function string.todimen(str) +-- if type(str) == "number" then +-- return str +-- else +-- local value, unit = lpegmatch(dimenpair,str) +-- return value/unit +-- end +-- end +-- +-- local stringtodimen = string.todimen + +local stringtodimen -- assigned later (commenting saves bytecode) 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") + @@ -175,12 +183,6 @@ string.todimen("10pt") string.todimen("10.0pt") </typing> -<p>And of course the often more efficient:</p> - -<typing> -somestring:todimen("12.3cm") -</typing> - <p>With this in place, we can now implement a proper datatype for dimensions, one that permits us to do this:</p> @@ -198,28 +200,28 @@ local dimensions = { } <p>The main (and globally) visible representation of a dimen is defined next: it is a one-element table. The unit that is returned from the match is normally a number (one of the previously defined factors) but we also accept functions. Later we will -see why.</p> +see why. This function is redefined later.</p> --ldx]]-- -function dimen(a) - if a then - local ta= type(a) - if ta == "string" then - local value, unit = lpegmatch(pattern,a) - if type(unit) == "function" then - k = value/unit() - else - k = value/unit - end - a = k - elseif ta == "table" then - a = a[1] - end - return setmetatable({ a }, dimensions) - else - return setmetatable({ 0 }, dimensions) - end -end +-- function dimen(a) +-- if a then +-- local ta= type(a) +-- if ta == "string" then +-- local value, unit = lpegmatch(pattern,a) +-- if type(unit) == "function" then +-- k = value/unit() +-- else +-- k = value/unit +-- end +-- a = k +-- elseif ta == "table" then +-- a = a[1] +-- end +-- return setmetatable({ a }, dimensions) +-- else +-- return setmetatable({ 0 }, dimensions) +-- end +-- end --[[ldx-- <p>This function return a small hash with a metatable attached. It is @@ -229,35 +231,35 @@ shared some of the code but for reasons of speed we don't.</p> function dimensions.__add(a, b) local ta, tb = type(a), type(b) - if ta == "string" then a = a:todimen() elseif ta == "table" then a = a[1] end - if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end + 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 = a:todimen() elseif ta == "table" then a = a[1] end - if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end + 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 = a:todimen() elseif ta == "table" then a = a[1] end - if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end + 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 = a:todimen() elseif ta == "table" then a = a[1] end - if tb == "string" then b = b:todimen() elseif tb == "table" then b = b[1] end + 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 = a:todimen() elseif ta == "table" then a = a[1] end + if ta == "string" then a = stringtodimen(a) elseif ta == "table" then a = a[1] end return setmetatable({ - a }, dimensions) end @@ -390,25 +392,27 @@ function dimen(a) end end -function string:todimen() - if type(self) == "number" then - return self +function string.todimen(str) + if type(str) == "number" then + return str else - local k = known[self] + local k = known[str] if not k then - local value, unit = lpegmatch(dimenpair,self) + local value, unit = lpegmatch(dimenpair,str) if value and unit then k = value/unit else k = 0 end - -- print(self,value,unit) - known[self] = k + -- print(str,value,unit) + known[str] = k end return k end end +stringtodimen = string.todimen -- local variable defined earlier + function number.toscaled(d) return format("%0.5f",d/2^16) end @@ -425,7 +429,7 @@ probably use a hash instead of a one-element table.</p> function number.percent(n) -- will be cleaned up once luatex 0.30 is out local hsize = tex.hsize if type(hsize) == "string" then - hsize = hsize:todimen() + hsize = stringtodimen(hsize) end return (n/100) * hsize end diff --git a/tex/context/base/l-dir.lua b/tex/context/base/l-dir.lua index 455f71b53..be21fb985 100644 --- a/tex/context/base/l-dir.lua +++ b/tex/context/base/l-dir.lua @@ -131,9 +131,12 @@ local function glob(str,t) end return t elseif isfile(str) then - local t = t or { } - t[#t+1] = str - return t + if t then + t[#t+1] = str + return t + else + return { str } + end else local split = lpegmatch(pattern,str) if split then @@ -166,6 +169,7 @@ local function globfiles(path,recurse,func,files) -- func == pattern or function 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 --- skip @@ -176,12 +180,9 @@ local function globfiles(path,recurse,func,files) -- func == pattern or function globfiles(path .. "/" .. name,recurse,func,files) end elseif mode == "file" then - if func then - if func(name) then - files[#files+1] = path .. "/" .. name - end - else - files[#files+1] = path .. "/" .. name + if not func or func(name) then + noffiles = noffiles + 1 + files[noffiles] = path .. "/" .. name end end end diff --git a/tex/context/base/l-file.lua b/tex/context/base/l-file.lua index edf3f8a06..7c5b9c67f 100644 --- a/tex/context/base/l-file.lua +++ b/tex/context/base/l-file.lua @@ -159,7 +159,7 @@ end file.isreadable = file.is_readable -- depricated file.iswritable = file.is_writable -- depricated --- todo: lpeg +-- todo: lpeg \\ / .. does not save much local checkedsplit = string.checkedsplit @@ -174,7 +174,7 @@ end -- we can hash them weakly ---~ function file.old_collapse_path(str) -- fails on b.c/.. +--~ function file.collapsepath(str) -- fails on b.c/.. --~ str = gsub(str,"\\","/") --~ if find(str,"/") then --~ str = gsub(str,"^%./",(gsub(getcurrentdir(),"\\","/")) .. "/") -- ./xx in qualified @@ -198,7 +198,7 @@ end --~ Of course there are some optimizations too. Finally we had to deal with --~ windows drive prefixes and thinsg like sys://. -function file.collapse_path(str,anchor) +function file.collapsepath(str,anchor) if anchor and not find(str,"^/") and not find(str,"^%a:") then str = getcurrentdir() .. "/" .. str end @@ -253,8 +253,10 @@ function file.collapse_path(str,anchor) end end +file.collapse_path = file.collapsepath + --~ local function test(str) ---~ print(string.format("%-20s %-15s %-15s",str,file.collapse_path(str),file.collapse_path(str,true))) +--~ print(string.format("%-20s %-15s %-15s",str,file.collapsepath(str),file.collapsepath(str,true))) --~ end --~ test("a/b.c/d") test("b.c/d") test("b.c/..") --~ test("/") test("c:/..") test("sys://..") diff --git a/tex/context/base/l-lpeg.lua b/tex/context/base/l-lpeg.lua index 0a4ee0ba3..5c53fe710 100644 --- a/tex/context/base/l-lpeg.lua +++ b/tex/context/base/l-lpeg.lua @@ -14,15 +14,29 @@ local patterns = lpeg.patterns local P, R, S, Ct, C, Cs, Cc, V = lpeg.P, lpeg.R, lpeg.S, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.V local match = lpeg.match +local utfcharacters = string.utfcharacters +local utfgmatch = unicode and unicode.utf8.gmatch + local digit, sign = R('09'), S('+-') local cr, lf, crlf = P("\r"), P("\n"), P("\r\n") -local utf8byte = R("\128\191") +local utf8next = R("\128\191") +local escaped = P("\\") * P(1) +local squote = P("'") +local dquote = P('"') -patterns.utf8byte = utf8byte patterns.utf8one = R("\000\127") -patterns.utf8two = R("\194\223") * utf8byte -patterns.utf8three = R("\224\239") * utf8byte * utf8byte -patterns.utf8four = R("\240\244") * utf8byte * utf8byte * utf8byte +patterns.utf8two = R("\194\223") * utf8next +patterns.utf8three = R("\224\239") * utf8next * utf8next +patterns.utf8four = R("\240\244") * utf8next * utf8next * utf8next +patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') + +local utf8char = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four +local validutf8char = utf8char^0 * P(-1) * Cc(true) + Cc(false) + +patterns.utf8 = utf8char +patterns.utf8char = utf8char +patterns.validutf8 = validutf8char +patterns.validutf8char = validutf8char patterns.digit = digit patterns.sign = sign @@ -49,17 +63,28 @@ patterns.nonspace = 1 - patterns.space patterns.nonspacer = 1 - patterns.spacer patterns.whitespace = patterns.eol + patterns.spacer patterns.nonwhitespace = 1 - patterns.whitespace -patterns.utf8 = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four -patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') -patterns.validutf8 = patterns.utf8^0 * P(-1) * Cc(true) + Cc(false) patterns.comma = P(",") patterns.commaspacer = P(",") * patterns.spacer^0 patterns.period = P(".") - -patterns.undouble = P('"')/"" * (1-P('"'))^0 * P('"')/"" -patterns.unsingle = P("'")/"" * (1-P("'"))^0 * P("'")/"" +patterns.escaped = escaped +patterns.squote = squote +patterns.dquote = dquote +patterns.undouble = (dquote/"") * ((escaped + (1-dquote))^0) * (dquote/"") +patterns.unsingle = (squote/"") * ((escaped + (1-squote))^0) * (squote/"") +patterns.unquoted = patterns.undouble + patterns.unsingle -- more often undouble patterns.unspacer = ((patterns.spacer^1)/"")^0 +local unquoted = Cs(patterns.unquoted * P(-1)) -- not C + +function string.unquoted(str) + return match(unquoted,str) or str +end + +--~ print(string.unquoted("test")) +--~ print(string.unquoted([["t\"est"]])) +--~ print(string.unquoted([["t\"est"x]])) +--~ print(string.unquoted("\'test\'")) + function lpeg.anywhere(pattern) --slightly adapted from website return P { P(pattern) + 1 * V(1) } -- why so complex? end @@ -75,8 +100,8 @@ local content = (empty + nonempty)^1 local capture = Ct(content^0) -function string:splitlines() - return match(capture,self) +function string.splitlines(str) + return match(capture,str) end patterns.textline = content @@ -92,12 +117,12 @@ 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 other, any = C((1 - separator)^0), P(1) + local any = P(1) splitter = other * (separator * C(any^0) + "") -- ? splitters_s[separator] = splitter else - local other = C((1 - separator)^0) splitter = other * (separator * other)^0 splitters_m[separator] = splitter end @@ -118,16 +143,16 @@ function lpeg.split(separator,str) return match(c,str) end -function string:split(separator) +function string.split(str,separator) local c = cache[separator] if not c then c = Ct(splitat(separator)) cache[separator] = c end - return match(c,self) + return match(c,str) end -lpeg.splitters = cache +--~ lpeg.splitters = cache -- no longer public local cache = { } @@ -135,22 +160,22 @@ function lpeg.checkedsplit(separator,str) local c = cache[separator] if not c then separator = P(separator) - local other = C((1 - separator)^0) + local other = C((1 - separator)^1) c = Ct(separator^0 * other * (separator^1 * other)^0) cache[separator] = c end return match(c,str) end -function string:checkedsplit(separator) +function string.checkedsplit(str,separator) local c = cache[separator] if not c then separator = P(separator) - local other = C((1 - separator)^0) + local other = C((1 - separator)^1) c = Ct(separator^0 * other * (separator^1 * other)^0) cache[separator] = c end - return match(c,self) + return match(c,str) end --~ function lpeg.append(list,pp) @@ -173,7 +198,9 @@ local function f2(s) local c1, c2 = f1(s,1,2) return c1 * 64 + c2 local function f3(s) local c1, c2, c3 = f1(s,1,3) return (c1 * 64 + c2) * 64 + c3 - 925824 end local function f4(s) local c1, c2, c3, c4 = f1(s,1,4) return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168 end -patterns.utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 +local utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 + +patterns.utf8byte = utf8byte --~ local str = " a b c d " @@ -212,22 +239,25 @@ function lpeg.keeper(str) end end +-- Just for fun I looked at the used bytecode and +-- p = (p and p + pp) or pp gets one more (testset). + function lpeg.replacer(t) if #t > 0 then local p for i=1,#t do local ti= t[i] local pp = P(ti[1]) / ti[2] - p = (p and p + pp ) or pp + if p then + p = p + pp + else + p = pp + end end return Cs((p + 1)^0) end end ---~ print(utf.check("")) ---~ print(utf.check("abcde")) ---~ print(utf.check("abcde\255\123")) - local splitters_f, splitters_s = { }, { } function lpeg.firstofsplit(separator) -- always return value @@ -251,6 +281,7 @@ function lpeg.secondofsplit(separator) -- nil if not split end function lpeg.balancer(left,right) + left, right = P(left), P(right) return P { left * ((1 - left - right) + V(1))^0 * right } end @@ -262,3 +293,176 @@ end --~ print(6,match(lpeg.secondofsplit(":",""),"bc")) --~ print(7,match(lpeg.secondofsplit(":"),"bc")) --~ print(9,match(lpeg.secondofsplit(":","123"),"bc")) + +--~ -- slower: +--~ +--~ function lpeg.counter(pattern) +--~ local n, pattern = 0, (lpeg.P(pattern)/function() n = n + 1 end + lpeg.P(1))^0 +--~ return function(str) n = 0 ; lpegmatch(pattern,str) ; return n end +--~ end + +local nany = utf8char/"" + +function lpeg.counter(pattern) + pattern = Cs((P(pattern)/" " + nany)^0) + return function(str) + return #match(pattern,str) + end +end + +if utfgmatch then + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local n = 0 + for _ in utfgmatch(str,what) do + n = n + 1 + end + return n + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +else + + local cache = { } + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local p = cache[what] + if not p then + p = Cs((P(what)/" " + nany)^0) + cache[p] = p + end + return #match(p,str) + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +end + +local patterns_escapes = { -- also defines in l-string + ["%"] = "%%", + ["."] = "%.", + ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", + ["["] = "%[", ["]"] = "%]", + ["("] = "%)", [")"] = "%)", + -- ["{"] = "%{", ["}"] = "%}" + -- ["^"] = "%^", ["$"] = "%$", +} + +local simple_escapes = { -- also defines in l-string + ["-"] = "%-", + ["."] = "%.", + ["?"] = ".", + ["*"] = ".*", +} + +local p = Cs((S("-.+*%()[]") / patterns_escapes + P(1))^0) +local s = Cs((S("-.+*%()[]") / simple_escapes + P(1))^0) + +function string.escapedpattern(str,simple) + if simple then + return match(s,str) + else + return match(p,str) + end +end + +-- utf extensies + +lpeg.UP = lpeg.P + +if utfcharacters then + + function lpeg.US(str) + local p + for uc in utfcharacters(str) do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + + +elseif utfgmatch then + + function lpeg.US(str) + local p + for uc in utfgmatch(str,".") do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + +else + + function lpeg.US(str) + local p + local f = function(uc) + if p then + p = p + P(uc) + else + p = P(uc) + end + end + match((utf8char/f)^0,str) + return p + end + +end + +local range = Cs(utf8byte) * (Cs(utf8byte) + Cc(false)) + +local utfchar = unicode and unicode.utf8 and unicode.utf8.char + +function lpeg.UR(str,more) + local first, last + if type(str) == "number" then + first = str + last = more or first + else + first, last = match(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 -- a somewhat arbitrary criterium + local p + for i=first,last do + if p then + p = p + P(utfchar(i)) + else + p = P(utfchar(i)) + end + end + return p -- nil when invalid range + else + local f = function(b) + return b >= first and b <= last + end + return utf8byte / f -- nil when invalid range + end +end + +--~ lpeg.print(lpeg.R("ab","cd","gh")) +--~ lpeg.print(lpeg.P("a","b","c")) +--~ lpeg.print(lpeg.S("a","b","c")) + +--~ print(lpeg.count("äáàa",lpeg.P("á") + lpeg.P("à"))) +--~ print(lpeg.count("äáàa",lpeg.UP("áà"))) +--~ print(lpeg.count("äáàa",lpeg.US("àá"))) +--~ print(lpeg.count("äáàa",lpeg.UR("aá"))) +--~ print(lpeg.count("äáàa",lpeg.UR("àá"))) +--~ print(lpeg.count("äáàa",lpeg.UR(0x0000,0xFFFF))) diff --git a/tex/context/base/l-number.lua b/tex/context/base/l-number.lua index 08acb6040..e4eae3d72 100644 --- a/tex/context/base/l-number.lua +++ b/tex/context/base/l-number.lua @@ -6,6 +6,8 @@ if not modules then modules = { } end modules ['l-number'] = { license = "see context related readme files" } +-- this module will be replaced when we have the bit library + local tostring = tostring local format, floor, insert, match = string.format, math.floor, string.match local concat, insert = table.concat, table.insert diff --git a/tex/context/base/l-os.lua b/tex/context/base/l-os.lua index c58983cfc..c2e229834 100644 --- a/tex/context/base/l-os.lua +++ b/tex/context/base/l-os.lua @@ -160,7 +160,7 @@ end -- no need for function anymore as we have more clever code and helpers now -- this metatable trickery might as well disappear -os.resolvers = os.resolvers or { } +os.resolvers = os.resolvers or { } -- will become private local resolvers = os.resolvers diff --git a/tex/context/base/l-set.lua b/tex/context/base/l-set.lua index e78b67876..2370f0139 100644 --- a/tex/context/base/l-set.lua +++ b/tex/context/base/l-set.lua @@ -51,10 +51,11 @@ function set.tolist(n) if n == 0 or not tabs[n] then return "" else - local t = { } + local t, n = { }, 0 for k, v in next, tabs[n] do if v then - t[#t+1] = k + n = n + 1 + t[n] = k end end return concat(t," ") diff --git a/tex/context/base/l-string.lua b/tex/context/base/l-string.lua index d0f58b37d..1e55f6bac 100644 --- a/tex/context/base/l-string.lua +++ b/tex/context/base/l-string.lua @@ -8,7 +8,7 @@ if not modules then modules = { } end modules ['l-string'] = { local string = string local sub, gsub, find, match, gmatch, format, char, byte, rep, lower = string.sub, string.gsub, string.find, string.match, string.gmatch, string.format, string.char, string.byte, string.rep, string.lower -local lpegmatch = lpeg.match +local lpegmatch, S, C, Ct = lpeg.match, lpeg.S, lpeg.C, lpeg.Ct -- some functions may disappear as they are not used anywhere @@ -16,157 +16,75 @@ if not string.split then -- this will be overloaded by a faster lpeg variant - function string:split(pattern) - if #self > 0 then - local t = { } - for s in gmatch(self..pattern,"(.-)"..pattern) do - t[#t+1] = s + function string.split(str,pattern) + local t = { } + if #str > 0 then + local n = 1 + for s in gmatch(str..pattern,"(.-)"..pattern) do + t[n] = s + n = n + 1 end - return t - else - return { } end + return t end end -string.patterns = { } - -local escapes = { - ["%"] = "%%", - ["."] = "%.", - ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", - ["^"] = "%^", ["$"] = "%$", - ["["] = "%[", ["]"] = "%]", - ["("] = "%(", [")"] = "%)", - ["{"] = "%{", ["}"] = "%}" -} - -string.patterns.escapes = escapes - -function string:esc() -- variant 2 - return (gsub(self,"(.)",escapes)) -end - -function string:unquote() - return (gsub(self,"^([\"\'])(.*)%1$","%2")) +function string.unquoted(str) + return (gsub(str,"^([\"\'])(.*)%1$","%2")) end ---~ function string:unquote() ---~ if find(self,"^[\'\"]") then ---~ return sub(self,2,-2) +--~ function stringunquoted(str) +--~ if find(str,"^[\'\"]") then +--~ return sub(str,2,-2) --~ else ---~ return self +--~ return str --~ end --~ end -function string:quote() -- we could use format("%q") - return format("%q",self) +function string.quoted(str) + return format("%q",str) -- always " end -function string:count(pattern) -- variant 3 +function string.count(str,pattern) -- variant 3 local n = 0 - for _ in gmatch(self,pattern) do + for _ in gmatch(str,pattern) do -- not for utf n = n + 1 end return n end -function string:limit(n,sentinel) - if #self > n then +function string.limit(str,n,sentinel) + if #str > n then sentinel = sentinel or " ..." - return sub(self,1,(n-#sentinel)) .. sentinel + return sub(str,1,(n-#sentinel)) .. sentinel else - return self - end -end - ---~ function string:strip() -- the .- is quite efficient ---~ -- return match(self,"^%s*(.-)%s*$") or "" ---~ -- return match(self,'^%s*(.*%S)') or '' -- posted on lua list ---~ return find(s,'^%s*$') and '' or match(s,'^%s*(.*%S)') ---~ end - -do -- roberto's variant: - local space = lpeg.S(" \t\v\n") - local nospace = 1 - space - local stripper = space^0 * lpeg.C((space^0 * nospace^1)^0) - function string.strip(str) - return lpegmatch(stripper,str) or "" - end -end - -function string:is_empty() - return not find(self,"%S") -end - -function string:enhance(pattern,action) - local ok, n = true, 0 - while ok do - ok = false - self = gsub(self,pattern, function(...) - ok, n = true, n + 1 - return action(...) - end) + return str end - return self, n end -if not string.characters then - - local function nextchar(str, index) - index = index + 1 - return (index <= #str) and index or nil, sub(str,index,index) - end - function string:characters() - return nextchar, self, 0 - end - local function nextbyte(str, index) - index = index + 1 - return (index <= #str) and index or nil, byte(sub(str,index,index)) - end - function string:bytes() - return nextbyte, self, 0 - end - -end +local space = S(" \t\v\n") +local nospace = 1 - space +local stripper = space^0 * C((space^0 * nospace^1)^0) -- roberto's code -function string:rpadd(n,chr) - local m = n-#self - if m > 0 then - return self .. rep(chr or " ",m) - else - return self - end +function string.strip(str) + return lpegmatch(stripper,str) or "" end -function string:lpadd(n,chr) - local m = n-#self - if m > 0 then - return rep(chr or " ",m) .. self - else - return self - end +function string.is_empty(str) + return not find(str,"%S") end -string.padd = string.rpadd - local patterns_escapes = { - ["-"] = "%-", - ["."] = "%.", - ["+"] = "%+", - ["*"] = "%*", ["%"] = "%%", - ["("] = "%)", - [")"] = "%)", - ["["] = "%[", - ["]"] = "%]", + ["."] = "%.", + ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", + ["["] = "%[", ["]"] = "%]", + ["("] = "%)", [")"] = "%)", + -- ["{"] = "%{", ["}"] = "%}" + -- ["^"] = "%^", ["$"] = "%$", } -function string:escapedpattern() - return (gsub(self,".",patterns_escapes)) -end - local simple_escapes = { ["-"] = "%-", ["."] = "%.", @@ -174,22 +92,28 @@ local simple_escapes = { ["*"] = ".*", } -function string:partialescapedpattern() - return (gsub(self,".",simple_escapes)) -end - -function string:tohash() - local t = { } - for s in gmatch(self,"([^, ]+)") do -- lpeg - t[s] = true +function string.escapedpattern(str,simple) + if simple then + return (gsub(str,".",simple_escapes)) + else + return (gsub(str,".",patterns_escapes)) end - return t end -local pattern = lpeg.Ct(lpeg.C(1)^0) - -function string:totable() - return lpegmatch(pattern,self) +function string.topattern(str,lowercase,strict) + if str == "" then + return ".*" + else + str = gsub(str,".",simple_escapes) + if lowercase then + str = lower(str) + end + if strict then + return "^" .. str .. "$" + else + return str + end + end end --~ local t = { @@ -205,6 +129,8 @@ end --~ print(string.tabtospace(t[k])) --~ end +-- The following functions might end up in another namespace. + function string.tabtospace(str,tab) -- we don't handle embedded newlines while true do @@ -224,27 +150,19 @@ function string.tabtospace(str,tab) return str end -function string:compactlong() -- strips newlines and leading spaces - self = gsub(self,"[\n\r]+ *","") - self = gsub(self,"^ *","") - return self -end +--~ local template = string.striplong([[ +--~ aaaa +--~ bb +--~ cccccc +--~ ]]) -function string:striplong() -- strips newlines and leading spaces - self = gsub(self,"^%s*","") - self = gsub(self,"[\n\r]+ *","\n") - return self +function string.striplong(str) -- strips all leading spaces + str = gsub(str,"^%s*","") + str = gsub(str,"[\n\r]+ *","\n") + return str end -function string:topattern(lowercase,strict) - if lowercase then - self = lower(self) - end - self = gsub(self,".",simple_escapes) - if self == "" then - self = ".*" - elseif strict then - self = "^" .. self .. "$" - end - return self -end +-- obsolete names: + +string.quote = string.quoted +string.unquote = string.unquoted diff --git a/tex/context/base/l-table.lua b/tex/context/base/l-table.lua index 70c04e69c..abaaa62ba 100644 --- a/tex/context/base/l-table.lua +++ b/tex/context/base/l-table.lua @@ -61,24 +61,26 @@ end -- extra functions, some might go (when not used) function table.strip(tab) - local lst = { } + local lst, l = { }, 0 for i=1,#tab do local s = gsub(tab[i],"^%s*(.-)%s*$","%1") if s == "" then -- skip this one else - lst[#lst+1] = s + l = l + 1 + lst[l] = s end end return lst end function table.keys(t) - local k = { } + local keys, k = { }, 0 for key, _ in next, t do - k[#k+1] = key + k = k + 1 + keys[k] = key end - return k + return keys end local function compare(a,b) @@ -91,9 +93,10 @@ local function compare(a,b) end local function sortedkeys(tab) - local srt, kind = { }, 0 -- 0=unknown 1=string, 2=number 3=mixed + local srt, kind, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed for key,_ in next, tab do - srt[#srt+1] = key + s = s + 1 + srt[s] = key if kind == 3 then -- no further check else @@ -116,10 +119,11 @@ local function sortedkeys(tab) end local function sortedhashkeys(tab) -- fast one - local srt = { } + local srt, s = { }, 0 for key,_ in next, tab do if key then - srt[#srt+1] = key + s= s + 1 + srt[s] = key end end sort(srt) @@ -133,8 +137,7 @@ local function nothing() end local function sortedhash(t) if t then - local s = sortedhashkeys(t) -- maybe just sortedkeys - local n = 0 + local n, s = 0, sortedkeys(t) -- the robust one local function kv(s) n = n + 1 local k = s[n] @@ -150,20 +153,30 @@ table.sortedhash = sortedhash table.sortedpairs = sortedhash function table.append(t, list) - for _,v in next, list do - insert(t,v) + local n = #t + for i=1,#list do + n = n + 1 + t[n] = list[i] end + return t end function table.prepend(t, list) - for k,v in next, list do - insert(t,k,v) + 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, ...) -- first one is target t = t or { } - local lst = {...} + local lst = { ... } for i=1,#lst do for k, v in next, lst[i] do t[k] = v @@ -173,7 +186,7 @@ function table.merge(t, ...) -- first one is target end function table.merged(...) - local tmp, lst = { }, {...} + local tmp, lst = { }, { ... } for i=1,#lst do for k, v in next, lst[i] do tmp[k] = v @@ -183,22 +196,24 @@ function table.merged(...) end function table.imerge(t, ...) - local lst = {...} + local lst, nt = { ... }, #t for i=1,#lst do local nst = lst[i] for j=1,#nst do - t[#t+1] = nst[j] + nt = nt + 1 + t[nt] = nst[j] end end return t end function table.imerged(...) - local tmp, lst = { }, {...} + local tmp, ntmp, lst = { }, 0, {...} for i=1,#lst do local nst = lst[i] for j=1,#nst do - tmp[#tmp+1] = nst[j] + ntmp = ntmp + 1 + tmp[ntmp] = nst[j] end end return tmp @@ -274,11 +289,14 @@ function table.tohash(t,value) end function table.fromhash(t) - local h = { } + local hsh, h = { }, 0 for k, v in next, t do -- no ipairs here - if v then h[#h+1] = k end + if v then + h = h + 1 + hsh[h] = k + end end - return h + return hsh end table.serialize_functions = true @@ -299,20 +317,23 @@ local function simple_table(t) n = n + 1 end if n == #t then - local tt = { } + local tt, nt = { }, 0 for i=1,#t do local v = t[i] local tv = type(v) if tv == "number" then + nt = nt + 1 if hexify then - tt[#tt+1] = format("0x%04X",v) + tt[nt] = format("0x%04X",v) else - tt[#tt+1] = tostring(v) -- tostring not needed + tt[nt] = tostring(v) -- tostring not needed end elseif tv == "boolean" then - tt[#tt+1] = tostring(v) + nt = nt + 1 + tt[nt] = tostring(v) elseif tv == "string" then - tt[#tt+1] = format("%q",v) + nt = nt + 1 + tt[nt] = format("%q",v) else tt = nil break @@ -618,10 +639,11 @@ end --~ 'return' : return { } --~ number : [number] = { } -function table.serialize(root,name,reduce,noquotes,hexify) - local t = { } +function table.serialize(root,name,reduce,noquotes,hexify) -- can be faster if flush == false and t as argument + local t, n = { }, 0 local function flush(s) - t[#t+1] = s + n = n + 1 + t[n] = s end serialize(root,name,flush,reduce,noquotes,hexify) return concat(t,"\n") @@ -647,12 +669,13 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) if f then local maxtab = table.tofile_maxtab if maxtab > 1 then - local t = { } + local t, n = { }, 0 local function flush(s) - t[#t+1] = s - if #t > maxtab then + n = n + 1 + t[n] = s + if n > maxtab then f:write(concat(t,"\n"),"\n") -- hm, write(sometable) should be nice - t = { } + t, n = { }, 0 -- we could recycle t if needed end end serialize(root,name,flush,reduce,noquotes,hexify) @@ -668,52 +691,72 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) end end -local function flatten(t,f,complete) -- is this used? meybe a variant with next, ... - for i=1,#t do - local v = t[i] - if type(v) == "table" then - if complete or type(v[1]) == "table" then - flatten(v,f,complete) +local function flattened(t,f,depth) + if f == nil then + f = { } + depth = 0xFFFF + elseif tonumber(f) then + -- assume then only two arguments are given + 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 + f[k] = v end + end + end + local n = #f + for k=1,#t do + local v = t[k] + if depth > 0 and type(v) == "table" then + flattened(v,f,depth-1) + n = #f else - f[#f+1] = v + n = n + 1 + f[n] = v end end -end - -function table.flatten(t) - local f = { } - flatten(t,f,true) - return f -end - -function table.unnest(t) -- bad name - local f = { } - flatten(t,f,false) return f end -table.flattenonelevel = table.unnest - --- a better one: +table.flattened = flattened -local function flattened(t,f) - if not f then +local function unnest(t,f) -- only used in mk, for old times sake + if not f then -- and only relevant for token lists f = { } end - for k, v in next, t do + for i=1,#t do + local v = t[i] if type(v) == "table" then - flattened(v,f) + if type(v[1]) == "table" then + unnest(v,f) + else + f[#f+1] = v + end else - f[k] = v + f[#f+1] = v end end return f end -table.flattened = flattened +function table.unnest(t) -- bad name + return unnest(t) +end + +--~ function table.unnest(t) -- for old times sake, undocumented (only in mk) +--~ return flattened(t,1) +--~ end + +--~ function table.are_equal(a,b) +--~ return table.serialize(a) == table.serialize(b) +--~ end local function are_equal(a,b,n,m) -- indexed if a and b and #a == #b then @@ -739,7 +782,7 @@ end local function identical(a,b) -- assumes same structure for ka, va in next, a do - local vb = b[k] + local vb = b[ka] if va == vb then -- same elseif type(va) == "table" and type(vb) == "table" then @@ -753,8 +796,8 @@ local function identical(a,b) -- assumes same structure return true end -table.are_equal = are_equal table.identical = identical +table.are_equal = are_equal -- maybe also make a combined one @@ -780,14 +823,14 @@ function table.contains(t, v) end function table.count(t) - local n, e = 0, next(t) - while e do - n, e = n + 1, next(t,e) + local n = 0 + for k, v in next, t do + n = n + 1 end return n end -function table.swapped(t,s) +function table.swapped(t,s) -- hash local n = { } if s then --~ for i=1,#s do @@ -809,55 +852,34 @@ function table.swapped(t,s) return n end ---~ function table.are_equal(a,b) ---~ return table.serialize(a) == table.serialize(b) ---~ end - -function table.clone(t,p) -- t is optional or nil or table - if not p then - t, p = { }, t or { } - elseif not t then - t = { } - end - setmetatable(t, { __index = function(_,key) return p[key] end }) -- why not __index = p ? - return t -end - -function table.hexed(t,seperator) - local tt = { } - for i=1,#t do tt[i] = format("0x%04X",t[i]) end - return concat(tt,seperator or " ") -end - -function table.swaphash(h) -- needs another name - local r = { } - for k,v in next, h do - r[v] = lower(gsub(k," ","")) - end - return r -end - -function table.reverse(t) - local tt = { } - if #t > 0 then - for i=#t,1,-1 do - tt[#tt+1] = t[i] +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 - return tt end function table.sequenced(t,sep,simple) -- hash only - local s = { } + local s, n = { }, 0 for k, v in sortedhash(t) do if simple then if v == true then - s[#s+1] = k + n = n + 1 + s[n] = k elseif v and v~= "" then - s[#s+1] = k .. "=" .. tostring(v) + n = n + 1 + s[n] = k .. "=" .. tostring(v) end else - s[#s+1] = k .. "=" .. tostring(v) + n = n + 1 + s[n] = k .. "=" .. tostring(v) end end return concat(s, sep or " | ") @@ -867,7 +889,7 @@ function table.print(...) table.tohandle(print,...) end --- -- -- obsolete but we keep them for a while and will comment them later -- -- -- +-- -- -- obsolete but we keep them for a while and might comment them later -- -- -- -- roughly: copy-loop : unpack : sub == 0.9 : 0.4 : 0.45 (so in critical apps, use unpack) @@ -882,12 +904,5 @@ function table.is_empty(t) end function table.has_one_entry(t) - local n = next(t) - return n and not next(t,n) -end - -function table.replace(a,b) - for k,v in next, b do - a[k] = v - end + return t and not next(t,next(t)) end diff --git a/tex/context/base/l-unicode.lua b/tex/context/base/l-unicode.lua index 10f21e325..a97f01d1e 100644 --- a/tex/context/base/l-unicode.lua +++ b/tex/context/base/l-unicode.lua @@ -81,30 +81,37 @@ function unicode.utftype(f) end function unicode.utf16_to_utf8(str, endian) -- maybe a gsub is faster or an lpeg - local result, tmp, n, m, p = { }, { }, 0, 0, 0 + local result, tmp, n, m, p, r, t = { }, { }, 0, 0, 0, 0, 0 -- we reuse tmp -- lf | cr | crlf / (cr:13, lf:10) local function doit() if n == 10 then if p ~= 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = 0 end elseif n == 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = n else - tmp[#tmp+1] = utfchar(n) + t = t + 1 + tmp[t] = utfchar(n) p = 0 end end for l,r in bytepairs(str) do if r then if endian then - n = l*256 + r + n = 256*l + r else - n = r*256 + l + n = 256*r + l end if m > 0 then n = (m-0xD800)*0x400 + (n-0xDC00) + 0x10000 @@ -117,29 +124,36 @@ function unicode.utf16_to_utf8(str, endian) -- maybe a gsub is faster or an lpeg end end end - if #tmp > 0 then - result[#result+1] = concat(tmp) + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) end return result end function unicode.utf32_to_utf8(str, endian) - local result = { } - local tmp, n, m, p = { }, 0, -1, 0 + local result, tmp, n, m, p, r, t = { }, { }, 0, -1, 0, 0, 0 -- lf | cr | crlf / (cr:13, lf:10) local function doit() if n == 10 then if p ~= 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = 0 end elseif n == 13 then - result[#result+1] = concat(tmp) - tmp = { } + if t > 0 then + r = r + 1 + result[r] = concat(tmp,"",1,t) + t = 0 + end p = n else - tmp[#tmp+1] = utfchar(n) + t = t + 1 + tmp[t] = utfchar(n) p = 0 end end @@ -147,15 +161,15 @@ function unicode.utf32_to_utf8(str, endian) if a and b then if m < 0 then if endian then - m = a*256*256*256 + b*256*256 + m = 256*256*256*a + 256*256*b else - m = b*256 + a + m = 256*b + a end else if endian then - n = m + a*256 + b + n = m + 256*a + b else - n = m + b*256*256*256 + a*256*256 + n = m + 256*256*256*b + 256*256*a end m = -1 doit() @@ -165,13 +179,14 @@ function unicode.utf32_to_utf8(str, endian) end end if #tmp > 0 then - result[#result+1] = concat(tmp) + r = r + 1 + result[r] = concat(tmp,"",1,t) end return result end local function little(c) - local b = byte(c) -- b = c:byte() + local b = byte(c) if b < 0x10000 then return char(b%256,b/256) else @@ -201,9 +216,10 @@ function unicode.utf8_to_utf16(str,littleendian) end function unicode.utfcodes(str) - local t = { } - for k,v in utfvalues(str) do - t[#t+1] = format("0x%04X",k) + local t, n = { }, 0 + for u in utfvalues(str) do + n = n + 1 + t[n] = format("0x%04X",u) end return concat(t,separator or " ") end diff --git a/tex/context/base/lang-ini.lua b/tex/context/base/lang-ini.lua index 7bbf42aa7..507d59a9a 100644 --- a/tex/context/base/lang-ini.lua +++ b/tex/context/base/lang-ini.lua @@ -292,16 +292,17 @@ end languages.logger = languages.logger or { } function languages.logger.report() - local result = { } + local result, r = { }, 0 local sorted = table.sortedkeys(registered) for i=1,#sorted do local tag = sorted[i] local l = registered[tag] if l.loaded then - result[#result+1] = format("%s:%s:%s", tag, l.parent, l.number) + r = r + 1 + result[r] = format("%s:%s:%s", tag, l.parent, l.number) end end - return (#result > 0 and concat(result," ")) or "none" + return (r > 0 and concat(result," ")) or "none" end -- must happen at the tex end diff --git a/tex/context/base/lang-wrd.lua b/tex/context/base/lang-wrd.lua index 7f7099c6d..d008ed341 100644 --- a/tex/context/base/lang-wrd.lua +++ b/tex/context/base/lang-wrd.lua @@ -8,6 +8,7 @@ if not modules then modules = { } end modules ['lang-ini'] = { local utf = unicode.utf8 local lower, utfchar = string.lower, utf.char +local concat = table.concat local lpegmatch = lpeg.match local P, S, Cs = lpeg.P, lpeg.S, lpeg.Cs @@ -59,6 +60,7 @@ local loaded = { } -- we share lists function words.load(tag,filename) local fullname = resolvers.findfile(filename,'other text file') or "" if fullname ~= "" then + report_languages("loading word file '%s'",fullname) statistics.starttiming(languages) local list = loaded[fullname] if not list then @@ -78,9 +80,13 @@ function words.found(id, str) local tag = languages.numbers[id] if tag then local data = wordsdata[tag] - return data and (data[str] or data[lower(str)]) - else - return false + if data then + if data[str] then + return 1 + elseif data[lower(str)] then + return 2 + end + end end end @@ -89,20 +95,86 @@ end -- there is an n=1 problem somewhere in nested boxes +--~ local function mark_words(head,whenfound) -- can be optimized and shared +--~ local current, start, str, language, n, done = head, nil, "", nil, 0, false +--~ local function action() +--~ if #str > 0 then +--~ local f = whenfound(language,str) +--~ if f then +--~ done = true +--~ for i=1,n do +--~ f(start) +--~ start = start.next +--~ end +--~ end +--~ end +--~ str, start, n = "", nil, 0 +--~ end +--~ while current do +--~ local id = current.id +--~ if id == glyph_code then +--~ local a = current.lang +--~ if a then +--~ if a ~= language then +--~ if start then +--~ action() +--~ end +--~ language = a +--~ end +--~ elseif start then +--~ action() +--~ language = a +--~ end +--~ local components = current.components +--~ if components then +--~ start = start or current +--~ n = n + 1 +--~ for g in traverse_nodes(components) do +--~ str = str .. utfchar(g.char) +--~ end +--~ else +--~ local code = current.char +--~ local data = chardata[code] +--~ if is_letter[data.category] then +--~ start = start or current +--~ n = n + 1 +--~ str = str .. utfchar(code) -- slow, maybe str should be a table (and given max) +--~ elseif start then +--~ action() +--~ end +--~ end +--~ elseif id == disc_code then +--~ if n > 0 then +--~ n = n + 1 +--~ end +--~ elseif id == kern_code and current.subtype == kerning_code and start then +--~ -- ok +--~ elseif start then +--~ action() +--~ end +--~ current = current.next +--~ end +--~ if start then +--~ action() +--~ end +--~ return head, done +--~ end + local function mark_words(head,whenfound) -- can be optimized and shared - local current, start, str, language, n, done = head, nil, "", nil, 0, false + local current, language, done = head, nil, nil, 0, false + local str, s, nds, n = { }, 0, { }, 0 -- n could also be a table, saves calls local function action() - if #str > 0 then - local f = whenfound(language,str) - if f then + if s > 0 then + local word = concat(str,"",1,s) + local mark = whenfound(language,word) + if mark then done = true for i=1,n do - f(start) - start = start.next + mark(nds[i]) end end end - str, start, n = "", nil, 0 + n, s = 0, 0 end while current do local id = current.id @@ -110,46 +182,48 @@ local function mark_words(head,whenfound) -- can be optimized and shared local a = current.lang if a then if a ~= language then - if start then + if s > 0 then action() end language = a end - elseif start then + elseif s > 0 then action() language = a end local components = current.components if components then - start = start or current n = n + 1 + nds[n] = current for g in traverse_nodes(components) do - str = str .. utfchar(g.char) + s = s + 1 + str[s] = utfchar(g.char) end else local code = current.char local data = chardata[code] - if data.uccode or data.lccode then ---~ if is_letter[code] then -- why does this fail - start = start or current + if is_letter[data.category] then n = n + 1 - str = str .. utfchar(code) -- slow, maybe str should be a table - elseif start then + nds[n] = current + s = s + 1 + str[s] = utfchar(code) + elseif s > 0 then action() end end - elseif id == disc_code then + elseif id == disc_code then -- take the replace if n > 0 then n = n + 1 + nds[n] = current end - elseif id == kern_code and current.subtype == kerning_code and start then + elseif id == kern_code and current.subtype == kerning_code and s > 0 then -- ok - elseif start then + elseif s > 0 then action() end current = current.next end - if start then + if s > 0 then action() end return head, done @@ -192,7 +266,9 @@ local cache = { } -- can also be done with method 1 -- frozen colors once used setmetatable(cache, { __index = function(t,k) -- k == language, numbers[k] == tag local c - if k < 0 then + if type(k) == "string" then + c = colist[k] + elseif k < 0 then c = colist["word:unset"] else c = colist["word:" .. (numbers[k] or "unset")] or colist["word:unknown"] diff --git a/tex/context/base/lang-wrd.mkiv b/tex/context/base/lang-wrd.mkiv index ad353905f..22c234d0c 100644 --- a/tex/context/base/lang-wrd.mkiv +++ b/tex/context/base/lang-wrd.mkiv @@ -60,10 +60,10 @@ \c!method=1, \c!list=] -\definecolor[word:yes] [darkgreen] -\definecolor[word:no] [darkred] +\definecolor[word:yes] [g=.75] +\definecolor[word:no] [r=.75] -%definecolor[word:unset] [darkgray] +%definecolor[word:unset] [s=.75] \definecolor[word:en] [b=.75] \definecolor[word:de] [r=.75] \definecolor[word:nl] [g=.75] diff --git a/tex/context/base/lpdf-col.lua b/tex/context/base/lpdf-col.lua index f5612a32b..a8e35dc74 100644 --- a/tex/context/base/lpdf-col.lua +++ b/tex/context/base/lpdf-col.lua @@ -25,7 +25,7 @@ local forcedmodel = colors.forcedmodel -- This module assumes that some functions are defined in the colors namespace -- which most likely will be loaded later. -function lpdf.color(model,ca,default) -- todo: use gray when no color +local function lpdfcolor(model,ca,default) -- todo: use gray when no color if colors.supported then local cv = colorsvalue(ca) if cv then @@ -57,6 +57,12 @@ function lpdf.color(model,ca,default) -- todo: use gray when no color end end +lpdf.color = lpdfcolor + +function lpdf.pdfcolor(attribute) -- bonus, for pgf and friends + context(lpdfcolor(1,attribute)) +end + function lpdf.transparency(ct,default) -- kind of overlaps with transparencycode -- beware, we need this hack because normally transparencies are not -- yet registered and therefore the number is not not known ... we diff --git a/tex/context/base/lpdf-epa.lua b/tex/context/base/lpdf-epa.lua index 5709ced3f..58bbcf821 100644 --- a/tex/context/base/lpdf-epa.lua +++ b/tex/context/base/lpdf-epa.lua @@ -21,26 +21,36 @@ local backends, lpdf = backends, lpdf local variables = interfaces.variables local codeinjections = backends.pdf.codeinjections +local layerspec = { -- predefining saves time + "epdflinks" +} + +local locationspec = { -- predefining saves time + x = "", + y = "", + preset = "leftbottom", +} + +local buttonspec = { -- predefining saves time + width = "", + height = "", + offset = variables.overlay, + frame = trace_links and variables.on or variables.off, +} + local function add_link(x,y,w,h,destination,what) if trace_links then report_link("dx: %04i, dy: %04i, wd: %04i, ht: %04i, destination: %s, type: %s",x,y,w,h,destination,what) end + locationspec.x = x .. "bp" + locationspec.y = y .. "bp" + buttonspec.width = w .. "bp" + buttonspec.height = h .. "bp" context.setlayer ( - { "epdflinks" }, - { x = x .. "bp", y = y .. "bp", preset = "leftbottom" }, - function() - -- context.blackrule { width = w.."bp", height = h.."bp", depth = "0bp" } - context.button ( - { - width = w.."bp", - height = h.."bp", - offset = variables.overlay, - frame = trace_links and variables.on or variables.off, - }, - "", - { destination } - ) - end + layerspec, + locationspec, + function() context.button ( buttonspec, "", { destination } ) end + -- context.nested.button(buttonspec, "", { destination }) -- time this ) end @@ -136,7 +146,8 @@ function codeinjections.mergereferences(specification) end end context.flushlayer { "epdflinks" } - context("\\gdef\\figurereference{%s}",reference) -- global + -- context("\\gdef\\figurereference{%s}",reference) -- global + context.setgvalue("figurereference",reference) -- global specification.reference = reference return namespace end diff --git a/tex/context/base/lpdf-epd.lua b/tex/context/base/lpdf-epd.lua index 8a17aeb6b..ae84a29c7 100644 --- a/tex/context/base/lpdf-epd.lua +++ b/tex/context/base/lpdf-epd.lua @@ -61,9 +61,8 @@ local dictionary_access = { checked_access = function(tab,key,v) local n = v:getTypeName() ---~ print("!!!!!!!!!!!!!!",n) if n == "array" then - local t = { __data__ = v, size = v:arrayGetLength() } + local t = { __data__ = v, size = v:arrayGetLength() or 0 } setmetatable(t,array_access) if cache_lookups then rawset(tab,key,t) end return t @@ -130,7 +129,7 @@ local page_access = { local annots = d:getAnnots() local a = { __data__ = annots, - size = annots:arrayGetLength() + size = annots:arrayGetLength() or 0 } setmetatable(a,basic_annots_access) rawset(t,k,a) diff --git a/tex/context/base/lpdf-ini.lua b/tex/context/base/lpdf-ini.lua index a39040d7f..4b7972abe 100644 --- a/tex/context/base/lpdf-ini.lua +++ b/tex/context/base/lpdf-ini.lua @@ -45,15 +45,17 @@ local function tosixteen(str) if not str or str == "" then return "()" else - local r = { "<feff" } + local r, n = { "<feff" }, 1 for b in utfvalues(str) do + n = n + 1 if b < 0x10000 then - r[#r+1] = format("%04x",b) + r[n] = format("%04x",b) else - r[#r+1] = format("%04x%04x",b/1024+0xD800,b%1024+0xDC00) + r[n] = format("%04x%04x",b/1024+0xD800,b%1024+0xDC00) end end - r[#r+1] = ">" + n = n + 1 + r[n] = ">" return concat(r) end end @@ -115,24 +117,25 @@ tostring_d = function(t,contentonly,key) return "<< >>" end else - local r = { } + local r, rn = { }, 0 for k, v in next, t do + rn = rn + 1 local tv = type(v) if tv == "string" then - r[#r+1] = format("/%s %s",k,toeight(v)) + r[rn] = format("/%s %s",k,toeight(v)) elseif tv == "unicode" then - r[#r+1] = format("/%s %s",k,tosixteen(v)) + r[rn] = format("/%s %s",k,tosixteen(v)) elseif tv == "table" then local mv = getmetatable(v) if mv and mv.__lpdftype then - r[#r+1] = format("/%s %s",k,tostring(v)) + r[rn] = format("/%s %s",k,tostring(v)) elseif v[1] then - r[#r+1] = format("/%s %s",k,tostring_a(v)) + r[rn] = format("/%s %s",k,tostring_a(v)) else - r[#r+1] = format("/%s %s",k,tostring_d(v)) + r[rn] = format("/%s %s",k,tostring_d(v)) end else - r[#r+1] = format("/%s %s",k,tostring(v)) + r[rn] = format("/%s %s",k,tostring(v)) end end if contentonly then @@ -146,7 +149,8 @@ tostring_d = function(t,contentonly,key) end tostring_a = function(t,contentonly,key) - if #t == 0 then + local tn = #t + if tn == 0 then if contentonly then return "" else @@ -154,24 +158,25 @@ tostring_a = function(t,contentonly,key) end else local r = { } - for k, v in next, t do + for k=1,tn do + local v = t[k] local tv = type(v) if tv == "string" then - r[#r+1] = toeight(v) + r[k] = toeight(v) elseif tv == "unicode" then - r[#r+1] = tosixteen(v) + r[k] = tosixteen(v) elseif tv == "table" then local mv = getmetatable(v) local mt = mv and mv.__lpdftype if mt then - r[#r+1] = tostring(v) + r[k] = tostring(v) elseif v[1] then - r[#r+1] = tostring_a(v) + r[k] = tostring_a(v) else - r[#r+1] = tostring_d(v) + r[k] = tostring_d(v) end else - r[#r+1] = tostring(v) + r[k] = tostring(v) end end if contentonly then diff --git a/tex/context/base/lpdf-tag.lua b/tex/context/base/lpdf-tag.lua index d05e3e22f..00ddef025 100644 --- a/tex/context/base/lpdf-tag.lua +++ b/tex/context/base/lpdf-tag.lua @@ -74,10 +74,10 @@ local add_ids = false -- true local function finishstructure() if #structure_kids > 0 then - local nums = pdfarray() + local nums, n = pdfarray(), 0 for i=1,#tree do - nums[#nums+1] = i-1 - nums[#nums+1] = pdfreference(pdfflushobject(tree[i])) + n = n + 1 ; nums[n] = i-1 + n = n + 1 ; nums[n] = pdfreference(pdfflushobject(tree[i])) end local parenttree = pdfdictionary { Nums = nums diff --git a/tex/context/base/luat-cbk.lua b/tex/context/base/luat-cbk.lua index a21976372..93538c6bb 100644 --- a/tex/context/base/luat-cbk.lua +++ b/tex/context/base/luat-cbk.lua @@ -124,7 +124,7 @@ end function callbacks.freeze(name,freeze) freeze = type(freeze) == "string" and freeze if find(name,"%*") then - local pattern = name -- string.partialescapedpattern(name) + local pattern = name for name, _ in next, list do if find(name,pattern) then frozen[name] = freeze or frozen[name] or "frozen" diff --git a/tex/context/base/luat-cnf.lua b/tex/context/base/luat-cnf.lua index 8b52a269c..2ab97e71d 100644 --- a/tex/context/base/luat-cnf.lua +++ b/tex/context/base/luat-cnf.lua @@ -28,9 +28,6 @@ local variablenames = allocate { -- most of this becomes obsolete 'max_in_open', -- 15 'max_print_line', -- 79 'max_strings', -- 15000 - 'ocp_stack_size', -- 1000 - 'ocp_list_size', -- 1000 - 'ocp_buf_size', -- 1000 'param_size', -- 60 'pk_dpi', -- 72 'save_size', -- 4000 diff --git a/tex/context/base/luat-env.lua b/tex/context/base/luat-env.lua index ed0884992..b8245afba 100644 --- a/tex/context/base/luat-env.lua +++ b/tex/context/base/luat-env.lua @@ -19,7 +19,7 @@ local report_resolvers = logs.new("resolvers") local allocate, mark = utilities.storage.allocate, utilities.storage.mark local format, sub, match, gsub, find = string.format, string.sub, string.match, string.gsub, string.find -local unquote, quote = string.unquote, string.quote +local unquoted, quoted = string.unquoted, string.quoted local concat = table.concat -- precautions @@ -87,7 +87,7 @@ function environment.initializearguments(arg) if index > 0 then local flag, value = match(argument,"^%-+(.-)=(.-)$") if flag then - arguments[flag] = unquote(value or "") + arguments[flag] = unquoted(value or "") else flag = match(argument,"^%-+(.+)") if flag then @@ -152,19 +152,21 @@ end function environment.reconstructcommandline(arg,noquote) arg = arg or environment.originalarguments if noquote and #arg == 1 then + -- we could just do: return unquoted(resolvers.resolve(arg[i])) local a = arg[1] a = resolvers.resolve(a) - a = unquote(a) + a = unquoted(a) return a elseif #arg > 0 then local result = { } for i=1,#arg do + -- we could just do: result[#result+1] = format("%q",unquoted(resolvers.resolve(arg[i]))) local a = arg[i] a = resolvers.resolve(a) - a = unquote(a) + a = unquoted(a) a = gsub(a,'"','\\"') -- tricky if find(a," ") then - result[#result+1] = quote(a) + result[#result+1] = quoted(a) else result[#result+1] = a end @@ -175,6 +177,23 @@ function environment.reconstructcommandline(arg,noquote) end end +--~ -- to be tested: +--~ +--~ function environment.reconstructcommandline(arg,noquote) +--~ arg = arg or environment.originalarguments +--~ if noquote and #arg == 1 then +--~ return unquoted(resolvers.resolve(arg[1])) +--~ elseif #arg > 0 then +--~ local result = { } +--~ for i=1,#arg do +--~ result[#result+1] = format("%q",unquoted(resolvers.resolve(arg[i]))) -- always quote +--~ end +--~ return concat(result," ") +--~ else +--~ return "" +--~ end +--~ end + if arg then -- new, reconstruct quoted snippets (maybe better just remove the " then and add them later) diff --git a/tex/context/base/luat-fio.lua b/tex/context/base/luat-fio.lua index db27e480d..5f65ead48 100644 --- a/tex/context/base/luat-fio.lua +++ b/tex/context/base/luat-fio.lua @@ -43,7 +43,6 @@ if not resolvers.instance then register('find_format_file' , function(name) return findbinfile(name,"fmt") end, true) register('find_image_file' , function(name) return findbinfile(name,"tex") end, true) register('find_map_file' , function(name) return findbinfile(name,"map") end, true) - register('find_ocp_file' , function(name) return findbinfile(name,"ocp") end, true) register('find_opentype_file' , function(name) return findbinfile(name,"otf") end, true) register('find_output_file' , function(name) return name end, true) register('find_pk_file' , function(name) return findbinfile(name,"pk") end, true) @@ -58,7 +57,6 @@ if not resolvers.instance then -- format -- image register('read_map_file' , function(file) return loadbinfile(file,"map") end, true) - register('read_ocp_file' , function(file) return loadbinfile(file,"ocp") end, true) -- output register('read_pk_file' , function(file) return loadbinfile(file,"pk") end, true) -- 600dpi/manfnt.720pk register('read_sfd_file' , function(file) return loadbinfile(file,"sfd") end, true) diff --git a/tex/context/base/luat-fmt.lua b/tex/context/base/luat-fmt.lua index 42f75a1ba..2be205d8c 100644 --- a/tex/context/base/luat-fmt.lua +++ b/tex/context/base/luat-fmt.lua @@ -11,17 +11,17 @@ local format = string.format -- helper for mtxrun -local quote = string.quote +local quoted = string.quoted local function primaryflags() local trackers = environment.argument("trackers") local directives = environment.argument("directives") local flags = "" if trackers and trackers ~= "" then - flags = flags .. "--trackers=" .. quote(trackers) + flags = flags .. "--trackers=" .. quoted(trackers) end if directives and directives ~= "" then - flags = flags .. "--directives=" .. quote(directives) + flags = flags .. "--directives=" .. quoted(directives) end return flags end @@ -86,7 +86,7 @@ function environment.make_format(name) return end -- generate format - local command = format("luatex --ini %s --lua=%s %s %sdump",primaryflags(),quote(usedluastub),quote(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\") + local command = format("luatex --ini %s --lua=%s %s %sdump",primaryflags(),quoted(usedluastub),quoted(fulltexsourcename),os.platform == "unix" and "\\\\" or "\\") logs.simple("running command: %s\n",command) os.spawn(command) -- remove related mem files @@ -124,8 +124,7 @@ function environment.run_format(name,data,more) logs.simple("using format name: %s",fmtname) logs.simple("no luc/lua with name: %s",barename) else - local q = string.quote - local command = format("luatex %s --fmt=%s --lua=%s %s %s",primaryflags(),quote(barename),quote(luaname),quote(data),more ~= "" and quote(more) or "") + local command = format("luatex %s --fmt=%s --lua=%s %s %s",primaryflags(),quoted(barename),quoted(luaname),quoted(data),more ~= "" and quoted(more) or "") logs.simple("running command: %s",command) os.spawn(command) end diff --git a/tex/context/base/luat-ini.lua b/tex/context/base/luat-ini.lua index b57962c2d..40a49defa 100644 --- a/tex/context/base/luat-ini.lua +++ b/tex/context/base/luat-ini.lua @@ -95,6 +95,8 @@ local protected = { userdata, thirddata = nil, nil +-- we could have a metatable that automaticaly creates a top level namespace + if not setfenv then texio.write_nl("warning: we need to fix setfenv by using 'load in' or '_ENV'") end diff --git a/tex/context/base/luat-lib.mkiv b/tex/context/base/luat-lib.mkiv index 02bafa4cc..81d371cc4 100644 --- a/tex/context/base/luat-lib.mkiv +++ b/tex/context/base/luat-lib.mkiv @@ -13,12 +13,12 @@ \writestatus{loading}{ConTeXt Lua Macros / Libraries} +\registerctxluafile{util-tab}{1.001} \registerctxluafile{util-pck}{1.001} \registerctxluafile{util-seq}{1.001} %registerctxluafile{util-mrg}{1.001} % not needed in context itself, only mtxrun \registerctxluafile{util-lua}{1.001} \registerctxluafile{util-prs}{1.001} -\registerctxluafile{util-tab}{1.001} \registerctxluafile{util-fmt}{1.001} \registerctxluafile{util-deb}{1.001} % could also be done in trac-deb.mkiv \registerctxluafile{util-sto}{1.001} % could also be done in trac-deb.mkiv diff --git a/tex/context/base/luat-run.lua b/tex/context/base/luat-run.lua index f22470f5f..580137d8a 100644 --- a/tex/context/base/luat-run.lua +++ b/tex/context/base/luat-run.lua @@ -6,7 +6,7 @@ if not modules then modules = { } end modules ['luat-run'] = { license = "see context related readme files" } -local format, rpadd = string.format, string.rpadd +local format = string.format local insert = table.insert local trace_lua_dump = false trackers.register("system.dump", function(v) trace_lua_dump = v end) diff --git a/tex/context/base/lxml-aux.lua b/tex/context/base/lxml-aux.lua index 7392afabb..088d82881 100644 --- a/tex/context/base/lxml-aux.lua +++ b/tex/context/base/lxml-aux.lua @@ -128,16 +128,17 @@ end function xml.collect_tags(root, pattern, nonamespace) local collected = xmlapplylpath(root,pattern) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collected[c] local ns, tg = e.ns, e.tg + n = n + 1 if nonamespace then - t[#t+1] = tg + t[n] = tg elseif ns == "" then - t[#t+1] = tg + t[n] = tg else - t[#t+1] = ns .. ":" .. tg + t[n] = ns .. ":" .. tg end end return t @@ -250,8 +251,10 @@ local function inject_element(root,pattern,whatever,prepend) else be, af = edt, cp end + local bn = #be for i=1,#af do - be[#be+1] = af[i] + bn = bn + 1 + be[bn] = af[i] end if rri then r.dt[rri].dt = be @@ -339,11 +342,12 @@ local function stripelement(e,nolines,anywhere) local edt = e.dt if edt then if anywhere then - local t = { } + local t, n = { }, 0 for e=1,#edt do local str = edt[e] if type(str) ~= "string" then - t[#t+1] = str + n = n + 1 + t[n] = str elseif str ~= "" then -- todo: lpeg for each case if nolines then @@ -351,7 +355,8 @@ local function stripelement(e,nolines,anywhere) end str = gsub(str,"^%s*(.-)%s*$","%1") if str ~= "" then - t[#t+1] = str + n = n + 1 + t[n] = str end end end @@ -378,9 +383,10 @@ local function stripelement(e,nolines,anywhere) end end end - if #edt > 0 then + local nedt = #nedt + if nedt > 0 then -- strip end - local str = edt[#edt] + local str = edt[nedt] if type(str) ~= "string" then -- nothing elseif str == "" then @@ -393,7 +399,7 @@ local function stripelement(e,nolines,anywhere) if str == "" then remove(edt) else - edt[#edt] = str + edt[nedt] = str end end end diff --git a/tex/context/base/lxml-lpt.lua b/tex/context/base/lxml-lpt.lua index ef9a1175b..274601d83 100644 --- a/tex/context/base/lxml-lpt.lua +++ b/tex/context/base/lxml-lpt.lua @@ -134,7 +134,7 @@ apply_axis['root'] = function(list) rt = ll end end - collected[#collected+1] = rt + collected[l] = rt end return collected end @@ -142,14 +142,14 @@ end apply_axis['self'] = function(list) --~ local collected = { } --~ for l=1,#list do ---~ collected[#collected+1] = list[l] +--~ collected[l] = list[l] --~ end --~ return collected return list end apply_axis['child'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local dt = ll.dt @@ -158,7 +158,8 @@ apply_axis['child'] = function(list) for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en @@ -170,68 +171,74 @@ apply_axis['child'] = function(list) return collected end -local function collect(list,collected) +local function collect(list,collected,c) local dt = list.dt if dt then local en = 0 for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en - collect(dk,collected) + c = collect(dk,collected,c) end end list.en = en end + return c end apply_axis['descendant'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do - collect(list[l],collected) + c = collect(list[l],collected,c) end return collected end -local function collect(list,collected) +local function collect(list,collected,c) local dt = list.dt if dt then local en = 0 for k=1,#dt do local dk = dt[k] if dk.tg then - collected[#collected+1] = dk + c = c + 1 + collected[c] = dk dk.ni = k -- refresh en = en + 1 dk.ei = en - collect(dk,collected) + c = collect(dk,collected,c) end end list.en = en end + return c end apply_axis['descendant-or-self'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] if ll.special ~= true then -- catch double root - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end - collect(ll,collected) + c = collect(ll,collected,c) end return collected end apply_axis['ancestor'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] while ll do ll = ll.__p__ if ll then - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end end end @@ -239,14 +246,16 @@ apply_axis['ancestor'] = function(list) end apply_axis['ancestor-or-self'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll while ll do ll = ll.__p__ if ll then - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end end end @@ -254,11 +263,12 @@ apply_axis['ancestor-or-self'] = function(list) end apply_axis['parent'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local pl = list[l].__p__ if pl then - collected[#collected+1] = pl + c = c + 1 + collected[c] = pl end end return collected @@ -273,7 +283,7 @@ apply_axis['namespace'] = function(list) end apply_axis['following'] = function(list) -- incomplete ---~ local collected = { } +--~ local collected, c = { }, 0 --~ for l=1,#list do --~ local ll = list[l] --~ local p = ll.__p__ @@ -281,7 +291,8 @@ apply_axis['following'] = function(list) -- incomplete --~ for i=ll.ni+1,#d do --~ local di = d[i] --~ if type(di) == "table" then ---~ collected[#collected+1] = di +--~ c = c + 1 +--~ collected[c] = di --~ break --~ end --~ end @@ -291,7 +302,7 @@ apply_axis['following'] = function(list) -- incomplete end apply_axis['preceding'] = function(list) -- incomplete ---~ local collected = { } +--~ local collected, c = { }, 0 --~ for l=1,#list do --~ local ll = list[l] --~ local p = ll.__p__ @@ -299,7 +310,8 @@ apply_axis['preceding'] = function(list) -- incomplete --~ for i=ll.ni-1,1,-1 do --~ local di = d[i] --~ if type(di) == "table" then ---~ collected[#collected+1] = di +--~ c = c + 1 +--~ collected[c] = di --~ break --~ end --~ end @@ -309,7 +321,7 @@ apply_axis['preceding'] = function(list) -- incomplete end apply_axis['following-sibling'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -317,7 +329,8 @@ apply_axis['following-sibling'] = function(list) for i=ll.ni+1,#d do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -325,7 +338,7 @@ apply_axis['following-sibling'] = function(list) end apply_axis['preceding-sibling'] = function(list) - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -333,7 +346,8 @@ apply_axis['preceding-sibling'] = function(list) for i=1,ll.ni-1 do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -341,7 +355,7 @@ apply_axis['preceding-sibling'] = function(list) end apply_axis['reverse-sibling'] = function(list) -- reverse preceding - local collected = { } + local collected, c = { }, 0 for l=1,#list do local ll = list[l] local p = ll.__p__ @@ -349,7 +363,8 @@ apply_axis['reverse-sibling'] = function(list) -- reverse preceding for i=ll.ni-1,1,-1 do local di = d[i] if type(di) == "table" then - collected[#collected+1] = di + c = c + 1 + collected[c] = di end end end @@ -375,7 +390,7 @@ local function apply_nodes(list,directive,nodes) return { } end else - local collected, m, p = { }, 0, nil + local collected, c, m, p = { }, 0, 0, nil if not nns then -- only check tag for l=1,#list do local ll = list[l] @@ -384,11 +399,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ntg == ltg then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif ntg ~= ltg then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -400,11 +417,13 @@ local function apply_nodes(list,directive,nodes) if directive then if lns == nns then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif lns ~= nns then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -418,11 +437,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif not ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -430,7 +451,7 @@ local function apply_nodes(list,directive,nodes) return collected end else - local collected, m, p = { }, 0, nil + local collected, c, m, p = { }, 0, 0, nil for l=1,#list do local ll = list[l] local ltg = ll.tg @@ -447,11 +468,13 @@ local function apply_nodes(list,directive,nodes) if directive then if ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end elseif not ok then local llp = ll.__p__ ; if llp ~= p then p, m = llp, 1 else m = m + 1 end - collected[#collected+1], ll.mi = ll, m + c = c + 1 + collected[c], ll.mi = ll, m end end end @@ -462,12 +485,13 @@ end local quit_expression = false local function apply_expression(list,expression,order) - local collected = { } + local collected, c = { }, 0 quit_expression = false for l=1,#list do local ll = list[l] if expression(list,ll,l,order) then -- nasty, order alleen valid als n=1 - collected[#collected+1] = ll + c = c + 1 + collected[c] = ll end if quit_expression then break @@ -765,7 +789,7 @@ local function nodesettostring(set,nodetest) if not ns or ns == "" then ns = "*" end if not tg or tg == "" then tg = "*" end tg = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) - t[#t+1] = (directive and tg) or format("not(%s)",tg) + t[i] = (directive and tg) or format("not(%s)",tg) end if nodetest == false then return format("not(%s)",concat(t,"|")) @@ -784,7 +808,7 @@ local function tagstostring(list) local ns, tg = li.ns, li.tg if not ns or ns == "" then ns = "*" end if not tg or tg == "" then tg = "*" end - t[#t+1] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) + t[i] = (tg == "@rt@" and "[root]") or format("%s:%s",ns,tg) end return concat(t," ") end diff --git a/tex/context/base/lxml-tex.lua b/tex/context/base/lxml-tex.lua index 0bf488342..bfa64fe65 100644 --- a/tex/context/base/lxml-tex.lua +++ b/tex/context/base/lxml-tex.lua @@ -31,6 +31,8 @@ local xmlapplylpath = xml.applylpath local variables = (interfaces and interfaces.variables) or { } +local insertbeforevalue, insertaftervalue = utilities.tables.insertbeforevalue, utilities.tables.insertaftervalue + local starttiming, stoptiming = statistics.starttiming, statistics.stoptiming local trace_setups = false trackers.register("lxml.setups", function(v) trace_setups = v end) @@ -696,12 +698,12 @@ function lxml.installsetup(what,document,setup,where) if trace_loading then commands.writestatus("lxml","inserting setup %s for %s before %s",setup,document,where) end - table.insertbeforevalue(sd,setup,where) + insertbeforevalue(sd,setup,where) elseif what == 4 then if trace_loading then commands.writestatus("lxml","inserting setup %s for %s after %s",setup,document,where) end - table.insertaftervalue(sd,setup,where) + insertaftervalue(sd,setup,where) end end @@ -1450,9 +1452,10 @@ function lxml.toparameters(id) if e then local a = e.at if a and next(a) then - local setups = { } + local setups, s = { }, 0 for k, v in next, a do - setups[#setups+1] = k .. "=" .. v + s = s + 1 + setups[s] = k .. "=" .. v end setups = concat(setups,",") -- tracing diff --git a/tex/context/base/lxml-xml.lua b/tex/context/base/lxml-xml.lua index 2a6d56800..2053d2353 100644 --- a/tex/context/base/lxml-xml.lua +++ b/tex/context/base/lxml-xml.lua @@ -26,15 +26,18 @@ local function all(collected) return collected end -local function reverse(collected) - if collected then - local reversed = { } - for c=#collected,1,-1 do - reversed[#reversed+1] = collected[c] - end - return reversed - end -end +--~ local function reverse(collected) +--~ if collected then +--~ local reversed, r = { }, 0 +--~ for c=#collected,1,-1 do +--~ r = r + 1 +--~ reversed[r] = collected[c] +--~ end +--~ return reversed +--~ end +--~ end + +local reverse = table.reversed local function attribute(collected,name) if collected and #collected > 0 then @@ -125,11 +128,12 @@ end local function texts(collected) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collection[c] if e and e.dt then - t[#t+1] = e.dt + n = n + 1 + t[n] = e.dt end end return t @@ -172,14 +176,15 @@ end local function tags(collected,nonamespace) if collected then - local t = { } + local t, n = { }, 0 for c=1,#collected do local e = collected[c] local ns, tg = e.ns, e.tg + n = n + 1 if nonamespace or ns == "" then - t[#t+1] = tg + t[n] = tg else - t[#t+1] = ns .. ":" .. tg + t[n] = ns .. ":" .. tg end end return t diff --git a/tex/context/base/m-database.lua b/tex/context/base/m-database.lua index 810c8ba39..ffaafa2f3 100644 --- a/tex/context/base/m-database.lua +++ b/tex/context/base/m-database.lua @@ -65,7 +65,7 @@ end for i=1,#data do local line = data[i] if line ~= "" and (not checker or not lpegmatch(checker,line)) then - local result = { } -- we collect as this is nicer in tracing + local result, r = { }, 0 -- we collect as this is nicer in tracing local list = lpegmatch(splitter,line) if not found then local setups = settings.setups or "" @@ -77,20 +77,20 @@ end sprint(ctxcatcodes,before) found = true end - result[#result+1] = first + r = r + 1 ; result[r] = first for j=1,#list do result[#result+1] = left if command == "" then - result[#result+1] = list[j] + r = r + 1 ; result[r] = list[j] else - result[#result+1] = command - result[#result+1] = "{" - result[#result+1] = list[j] - result[#result+1] = "}" + r = r + 1 ; result[r] = command + r = r + 1 ; result[r] = "{" + r = r + 1 ; result[r] = list[j] + r = r + 1 ; result[r] = "}" end - result[#result+1] = right + r = r + 1 ; result[r] = right end - result[#result+1] = last + r = r + 1 ; result[r] = last sprint(ctxcatcodes,result) end end diff --git a/tex/context/base/meta-pdf.lua b/tex/context/base/meta-pdf.lua index 0c77469a8..ad4bcca81 100644 --- a/tex/context/base/meta-pdf.lua +++ b/tex/context/base/meta-pdf.lua @@ -94,11 +94,11 @@ local function flushpath(cmd) local px, py = v[3], v[4] ; v[3], v[4] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[3],v[4]) local px, py = v[5], v[6] ; v[5], v[6] = (sy*(px-tx)-ry*(py-ty))/d, (sx*(py-ty)-rx*(px-tx))/d -- mpconcat(v[5],v[6]) end - path[#path+1] = concat(v," ") + path[k] = concat(v," ") end else for k=1,#m_stack_path do - path[#path+1] = concat(m_stack_path[k]," ") + path[k] = concat(m_stack_path[k]," ") end end flushconcat() @@ -146,10 +146,13 @@ end function mps.rlineto(x,y) local dx, dy = 0, 0 - if #m_stack_path > 0 then - dx, dy = m_stack_path[#m_stack_path][1], m_stack_path[#m_stack_path][2] + local topofstack = #m_stack_path + if topofstack > 0 then + local msp = m_stack_path[topofstack] + dx = msp[1] + dy = msp[2] end - m_stack_path[#m_stack_path+1] = {dx,dy,"l"} + m_stack_path[topofstack+1] = {dx,dy,"l"} end function mps.translate(tx,ty) diff --git a/tex/context/base/mlib-pdf.lua b/tex/context/base/mlib-pdf.lua index 47c93513a..30db3f52c 100644 --- a/tex/context/base/mlib-pdf.lua +++ b/tex/context/base/mlib-pdf.lua @@ -162,64 +162,81 @@ local function curved(ith,pth) end local function flushnormalpath(path, t, open) - t = t or { } - local pth, ith + local pth, ith, nt + if t then + nt = #t + else + t = { } + nt = 0 + end for i=1,#path do + nt = nt + 1 pth = path[i] if not ith then - t[#t+1] = format("%f %f m",pth.x_coord,pth.y_coord) + t[nt] = format("%f %f m",pth.x_coord,pth.y_coord) elseif curved(ith,pth) then - t[#t+1] = format("%f %f %f %f %f %f c",ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord) + t[nt] = format("%f %f %f %f %f %f c",ith.right_x,ith.right_y,pth.left_x,pth.left_y,pth.x_coord,pth.y_coord) else - t[#t+1] = format("%f %f l",pth.x_coord,pth.y_coord) + t[nt] = format("%f %f l",pth.x_coord,pth.y_coord) end ith = pth end if not open then + nt = nt + 1 local one = path[1] if curved(pth,one) then - t[#t+1] = format("%f %f %f %f %f %f c",pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord ) + t[nt] = format("%f %f %f %f %f %f c",pth.right_x,pth.right_y,one.left_x,one.left_y,one.x_coord,one.y_coord ) else - t[#t+1] = format("%f %f l",one.x_coord,one.y_coord) + t[nt] = format("%f %f l",one.x_coord,one.y_coord) end elseif #path == 1 then -- special case .. draw point local one = path[1] - t[#t+1] = format("%f %f l",one.x_coord,one.y_coord) + nt = nt + 1 + t[nt] = format("%f %f l",one.x_coord,one.y_coord) end return t end local function flushconcatpath(path, t, open) - t = t or { } - t[#t+1] = format("%f %f %f %f %f %f cm", sx, rx, ry, sy, tx ,ty) - local pth, ith + local pth, ith, nt + if t then + nt = #t + else + t = { } + nt = 0 + end + nt = nt + 1 + t[nt] = format("%f %f %f %f %f %f cm", sx, rx, ry, sy, tx ,ty) for i=1,#path do + nt = nt + 1 pth = path[i] if not ith then - t[#t+1] = format("%f %f m",mpconcat(pth.x_coord,pth.y_coord)) + t[nt] = format("%f %f m",mpconcat(pth.x_coord,pth.y_coord)) elseif curved(ith,pth) then local a, b = mpconcat(ith.right_x,ith.right_y) local c, d = mpconcat(pth.left_x,pth.left_y) - t[#t+1] = format("%f %f %f %f %f %f c",a,b,c,d,mpconcat(pth.x_coord,pth.y_coord)) + t[nt] = format("%f %f %f %f %f %f c",a,b,c,d,mpconcat(pth.x_coord,pth.y_coord)) else - t[#t+1] = format("%f %f l",mpconcat(pth.x_coord, pth.y_coord)) + t[nt] = format("%f %f l",mpconcat(pth.x_coord, pth.y_coord)) end ith = pth end if not open then + nt = nt + 1 local one = path[1] if curved(pth,one) then local a, b = mpconcat(pth.right_x,pth.right_y) local c, d = mpconcat(one.left_x,one.left_y) - t[#t+1] = format("%f %f %f %f %f %f c",a,b,c,d,mpconcat(one.x_coord, one.y_coord)) + t[nt] = format("%f %f %f %f %f %f c",a,b,c,d,mpconcat(one.x_coord, one.y_coord)) else - t[#t+1] = format("%f %f l",mpconcat(one.x_coord,one.y_coord)) + t[nt] = format("%f %f l",mpconcat(one.x_coord,one.y_coord)) end elseif #path == 1 then -- special case .. draw point + nt = nt + 1 local one = path[1] - t[#t+1] = format("%f %f l",mpconcat(one.x_coord,one.y_coord)) + t[nt] = format("%f %f l",mpconcat(one.x_coord,one.y_coord)) end return t end @@ -475,8 +492,10 @@ local flusher = { context.startnointerference() end, flushfigure = function(literals) + local n = #t for i=1, #literals do - t[#t+1] = literals[i] + n = n + 1 + t[n] = literals[i] end end, stopfigure = function() @@ -504,7 +523,7 @@ function metapost.totable(result) local field = fields[f] tt[field] = object[field] end - t[#t+1] = tt + t[o] = tt end local b = figure:boundingbox() return { diff --git a/tex/context/base/mlib-pps.lua b/tex/context/base/mlib-pps.lua index e0592ca02..6fae400fc 100644 --- a/tex/context/base/mlib-pps.lua +++ b/tex/context/base/mlib-pps.lua @@ -109,9 +109,10 @@ function specials.register(str) -- only colors local size, content, n, class = match(str,"^%%%%MetaPostSpecial: (%d+) (.*) (%d+) (%d+)$") if class then -- use lpeg splitter - local data = { } + local data , d = { }, 0 for s in gmatch(content,"[^ ]+") do - data[#data+1] = s + d = d + 1 + data[d] = s end class, n = tonumber(class), tonumber(n) if class == 3 or class == 4 or class == 5 then @@ -682,14 +683,15 @@ local do_end_fig = "; endfig ;" local do_safeguard = ";" function metapost.texttextsdata() - local t, n = { }, 0 + local t, nt, n = { }, 0, 0 for n, box in next, textexts do if box then local wd, ht, dp = box.width/factor, box.height/factor, box.depth/factor if trace_textexts then report_mplib("passed textext data %s: (%0.4f,%0.4f,%0.4f)",n,wd,ht,dp) end - t[#t+1] = format(text_data_template,n,wd,n,ht,n,dp) + nt = nt + 1 + t[nt] = format(text_data_template,n,wd,n,ht,n,dp) else break end @@ -782,11 +784,12 @@ function makempy.processgraphics(graphics) if io.exists(pdffile) then command = format("pstoedit -ssp -dt -f mpost %s %s", pdffile, mpyfile) os.execute(command) - local result = { } + local result, r = { }, 0 if io.exists(mpyfile) then local data = io.loaddata(mpyfile) for figure in gmatch(data,"beginfig(.-)endfig") do - result[#result+1] = format("begingraphictextfig%sendgraphictextfig ;\n", figure) + r = r + 1 + result[r] = format("begingraphictextfig%sendgraphictextfig ;\n", figure) end io.savedata(mpyfile,concat(result,"")) end diff --git a/tex/context/base/mlib-run.lua b/tex/context/base/mlib-run.lua index 7d670b77d..3d87c817e 100644 --- a/tex/context/base/mlib-run.lua +++ b/tex/context/base/mlib-run.lua @@ -323,7 +323,7 @@ function metapost.process(mpx, data, trialrun, flusher, multipass, isextrapass, if not metapost.reporterror(result) then if metapost.showlog then local str = (result.term ~= "" and result.term) or "no terminal output" - if not str:is_empty() then + if not string.is_empty(str) then metapost.lastlog = metapost.lastlog .. "\n" .. str report_mplib("mp log: %s",str) end diff --git a/tex/context/base/mult-cld.lua b/tex/context/base/mult-cld.lua index f4c501423..a200625bb 100644 --- a/tex/context/base/mult-cld.lua +++ b/tex/context/base/mult-cld.lua @@ -268,9 +268,10 @@ end) local tracedwriter = function(...) nofwriters = nofwriters + 1 - local t, f = { "w : " }, flush + local t, f, n = { "w : " }, flush, 0 flush = function(...) - t[#t+1] = concat({...},"",2) + n = n + 1 + t[n] = concat({...},"",2) normalflush(...) end normalwriter(...) @@ -411,9 +412,10 @@ local nested = { } context.nested = nested local function indexer(t,k) local f = function(...) - local t, savedflush = { }, flush + local t, savedflush, n = { }, flush, 0 flush = function(c,f,s,...) -- catcodes are ignored - t[#t+1] = s and concat{f,s,...} or f -- optimized for #args == 1 + n = n + 1 + t[n] = s and concat{f,s,...} or f -- optimized for #args == 1 end context[k](...) flush = savedflush @@ -424,9 +426,10 @@ local function indexer(t,k) end local function caller(t,...) - local t, savedflush = { }, flush + local t, savedflush, n = { }, flush, 0 flush = function(c,f,s,...) -- catcodes are ignored - t[#t+1] = s and concat{f,s,...} or f -- optimized for #args == 1 + n = n + 1 + t[n] = s and concat{f,s,...} or f -- optimized for #args == 1 end context(...) flush = savedflush diff --git a/tex/context/base/mult-clm.lua b/tex/context/base/mult-clm.lua index 044e31d2b..e2fff18dc 100644 --- a/tex/context/base/mult-clm.lua +++ b/tex/context/base/mult-clm.lua @@ -126,9 +126,11 @@ function mkiv.tolist(t) for i=1,#t do r[i] = t[i] end + local n = #r for k,v in table.sortedhash(t) do if type(k) ~= "number" then - r[#r+1] = k .. "=" .. v + n = n + 1 + r[n] = k .. "=" .. v end end return concat(r,", ") diff --git a/tex/context/base/node-fin.lua b/tex/context/base/node-fin.lua index af2ba0421..f7668415d 100644 --- a/tex/context/base/node-fin.lua +++ b/tex/context/base/node-fin.lua @@ -178,10 +178,15 @@ local nsdata, nsnone, nslistwise, nsforced, nsselector, nstrigger local current, current_selector, done = 0, 0, false -- nb, stack has a local current ! function states.initialize(namespace,attribute,head) - nsdata, nsnone = namespace.data, namespace.none - nsforced, nsselector, nslistwise = namespace.forced, namespace.selector, namespace.listwise - nstrigger = triggering and namespace.triggering and trigger - current, current_selector, done = 0, 0, false -- todo: done cleanup + nsdata = namespace.data + nsnone = namespace.none + nsforced = namespace.forced + nsselector = namespace.selector + nslistwise = namespace.listwise + nstrigger = triggering and namespace.triggering and trigger + current = 0 + current_selector = 0 + done = false -- todo: done cleanup end function states.finalize(namespace,attribute,head) -- is this one ok? @@ -213,11 +218,13 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr if default and c == inheritance then if current ~= default then head = insert_node_before(head,stack,copy_node(nsdata[default])) - current, done = default, true + current = default + done = true end elseif current ~= c then head = insert_node_before(head,stack,copy_node(nsdata[c])) - current, done = c, true + current = c + done = true end -- here ? compare selective if id == glue_code then --leader @@ -250,11 +257,13 @@ local function process(namespace,attribute,head,inheritance,default) -- one attr elseif default and inheritance then if current ~= default then head = insert_node_before(head,stack,copy_node(nsdata[default])) - current, done = default, true + current = default + done = true end elseif current > 0 then head = insert_node_before(head,stack,copy_node(nsnone)) - current, done = 0, true + current = 0 + done = true end elseif id == hlist_code or id == vlist_code then local content = stack.list @@ -300,21 +309,25 @@ local function selective(namespace,attribute,head,inheritance,default) -- two at if current ~= default then local data = nsdata[default] head = insert_node_before(head,stack,copy_node(data[nsforced or has_attribute(stack,nsselector) or nsselector])) - current, done = default, true + current = default + done = true end else local s = has_attribute(stack,nsselector) if current ~= c or current_selector ~= s then local data = nsdata[c] head = insert_node_before(head,stack,copy_node(data[nsforced or has_attribute(stack,nsselector) or nsselector])) - current, current_selector, done = c, s, true + current = c + current_selector = s + done = true end end elseif default and inheritance then if current ~= default then local data = nsdata[default] head = insert_node_before(head,stack,copy_node(data[nsforced or has_attribute(stack,nsselector) or nsselector])) - current, done = default, true + current = default + done = true end elseif current > 0 then head = insert_node_before(head,stack,copy_node(nsnone)) diff --git a/tex/context/base/node-fnt.lua b/tex/context/base/node-fnt.lua index 7e8fa3694..399cd8642 100644 --- a/tex/context/base/node-fnt.lua +++ b/tex/context/base/node-fnt.lua @@ -107,7 +107,8 @@ function handlers.characters(head) end end end - prevfont, prevattr = font, attr + prevfont = font + prevattr = attr end end if trace_fontrun then @@ -122,11 +123,13 @@ function handlers.characters(head) local n = #processors if n > 0 then local h, d = processors[1](head,font,0) - head, done = h or head, done or d + head = h or head + done = done or d if n > 1 then for i=2,n do local h, d = processors[i](head,font,0) - head, done = h or head, done or d + head = h or head + done = done or d end end end @@ -134,11 +137,13 @@ function handlers.characters(head) for font, processors in next, usedfonts do local n = #processors local h, d = processors[1](head,font,0) - head, done = h or head, done or d + head = h or head + done = done or d if n > 1 then for i=2,n do local h, d = processors[i](head,font,0) - head, done = h or head, done or d + head = h or head + done = done or d end end end @@ -148,11 +153,13 @@ function handlers.characters(head) for attribute, processors in next, dynamics do -- attr can switch in between local n = #processors local h, d = processors[1](head,font,attribute) - head, done = h or head, done or d + head = h or head + done = done or d if n > 1 then for i=2,n do local h, d = processors[i](head,font,attribute) - head, done = h or head, done or d + head = h or head + done = done or d end end end @@ -161,11 +168,13 @@ function handlers.characters(head) for attribute, processors in next, dynamics do -- attr can switch in between local n = #processors local h, d = processors[1](head,font,attribute) - head, done = h or head, done or d + head = h or head + done = done or d if n > 1 then for i=2,n do local h, d = processors[i](head,font,attribute) - head, done = h or head, done or d + head = h or head + done = done or d end end end diff --git a/tex/context/base/node-ini.lua b/tex/context/base/node-ini.lua index 6c48ba21d..f0b9b83fa 100644 --- a/tex/context/base/node-ini.lua +++ b/tex/context/base/node-ini.lua @@ -232,7 +232,8 @@ local function remove(head, current, free_too) free_node(t) t = nil else - t.next, t.prev = nil, nil + t.next = nil + t.prev = nil end end return head, current, t @@ -295,10 +296,12 @@ function nodes.replace(head,current,new) -- no head returned if false end local prev, next = current.prev, current.next if next then - new.next, next.prev = next, new + new.next = next + next.prev = new end if prev then - new.prev, prev.next = prev, new + new.prev = prev + prev.next = new end if head then if head == current then diff --git a/tex/context/base/node-inj.lua b/tex/context/base/node-inj.lua index f576c7ee2..e4380a4ea 100644 --- a/tex/context/base/node-inj.lua +++ b/tex/context/base/node-inj.lua @@ -188,12 +188,13 @@ function injections.handler(head,where,keep) trace(head) end -- in the future variant we will not copy items but refs to tables - local done, ky, rl, valid, cx, wx, mk = false, { }, { }, { }, { }, { }, { } + local done, ky, rl, valid, cx, wx, mk, nofvalid = false, { }, { }, { }, { }, { }, { }, 0 if has_kerns then -- move outside loop local nf, tm = nil, nil for n in traverse_id(glyph_code,head) do if n.subtype < 256 then - valid[#valid+1] = n + nofvalid = nofvalid + 1 + valid[nofvalid] = n if n.font ~= nf then nf = n.font tm = fontdata[nf].marks @@ -221,7 +222,8 @@ function injections.handler(head,where,keep) local nf, tm = nil, nil for n in traverse_id(glyph_code,head) do if n.subtype < 256 then - valid[#valid+1] = n + nofvalid = nofvalid + 1 + valid[nofvalid] = n if n.font ~= nf then nf = n.font tm = fontdata[nf].marks @@ -230,7 +232,7 @@ function injections.handler(head,where,keep) end end end - if #valid > 0 then + if nofvalid > 0 then -- we can assume done == true because we have cursives and marks local cx = { } if has_kerns and next(ky) then @@ -243,7 +245,7 @@ function injections.handler(head,where,keep) local p_cursbase, p = nil, nil -- since we need valid[n+1] we can also use a "while true do" local t, d, maxt = { }, { }, 0 - for i=1,#valid do -- valid == glyphs + for i=1,nofvalid do -- valid == glyphs local n = valid[i] if not mk[n] then local n_cursbase = has_attribute(n,cursbase) @@ -307,7 +309,7 @@ function injections.handler(head,where,keep) end end if has_marks then - for i=1,#valid do + for i=1,nofvalid do local p = valid[i] local p_markbase = has_attribute(p,markbase) if p_markbase then diff --git a/tex/context/base/node-pro.lua b/tex/context/base/node-pro.lua index 01dfc3352..9eb431d6b 100644 --- a/tex/context/base/node-pro.lua +++ b/tex/context/base/node-pro.lua @@ -33,15 +33,15 @@ local actions = tasks.actions("processors",4) local n = 0 -local function reconstruct(head) - local t = { } - local h = head +local function reconstruct(head) -- we probably have a better one + local t, n, h = { }, 0, head while h do + n = n + 1 local id = h.id - if id == glyph_code then - t[#t+1] = utfchar(h.char) + if id == glyph_code then -- todo: disc etc + t[n] = utfchar(h.char) else - t[#t+1] = "[]" + t[n] = "[]" end h = h.next end diff --git a/tex/context/base/node-res.lua b/tex/context/base/node-res.lua index 1487faa66..0a0cd0dc1 100644 --- a/tex/context/base/node-res.lua +++ b/tex/context/base/node-res.lua @@ -31,10 +31,11 @@ local nodecodes = nodes.nodecodes local glyph_code = nodecodes.glyph -local reserved = { } +local reserved, nofreserved = { }, 0 local function register_node(n) - reserved[#reserved+1] = n + nofreserved = nofreserved + 1 + reserved[nofreserved] = n return n end @@ -42,8 +43,8 @@ pool.register = register_node function pool.cleanup(nofboxes) -- todo nodes.tracers.steppers.reset() -- todo: make a registration subsystem - local nr, nl = #reserved, 0 - for i=1,nr do + local nl, nr = 0, nofreserved + for i=1,nofreserved do local ri = reserved[i] -- if not (ri.id == glue_spec and not ri.is_writable) then free_node(reserved[i]) @@ -60,6 +61,7 @@ function pool.cleanup(nofboxes) -- todo end end reserved = { } + nofreserved = 0 return nr, nl, nofboxes -- can be nil end diff --git a/tex/context/base/node-rul.lua b/tex/context/base/node-rul.lua index a0a18590b..51a7b4d3a 100644 --- a/tex/context/base/node-rul.lua +++ b/tex/context/base/node-rul.lua @@ -121,6 +121,8 @@ local checkdir = true -- omkeren class en level -> scheelt functie call in analyze +-- todo: switching inside math + local function processwords(attribute,data,flush,head,parent) -- we have hlistdir and local dir local n = head if n then diff --git a/tex/context/base/node-ser.lua b/tex/context/base/node-ser.lua index fd211b2c8..f90741aac 100644 --- a/tex/context/base/node-ser.lua +++ b/tex/context/base/node-ser.lua @@ -125,9 +125,10 @@ local function totable(n,flat,verbose) end if n then if flat then - local t = { } + local t, tn = { }, 0 while n do - t[#t+1] = to_table(n,flat,verbise) + tn = tn + 1 + t[tn] = to_table(n,flat,verbose) n = n.next end return t @@ -219,9 +220,10 @@ local function serialize(root,name,handle,depth,m) end function nodes.serialize(root,name) - local t = { } + local t, n = { }, 0 local function flush(s) - t[#t+1] = s + n = n + 1 + t[n] = s end serialize(root, name, flush, nil, 0) return concat(t,"\n") diff --git a/tex/context/base/node-spl.lua b/tex/context/base/node-spl.lua index 8d8c297bd..a942d3cdb 100644 --- a/tex/context/base/node-spl.lua +++ b/tex/context/base/node-spl.lua @@ -114,7 +114,7 @@ end local contextsetups = fonts.definers.specifiers.contextsetups local function convert(featuresets,name,set,what) - local list, numbers = set[what], { } + local list, numbers, nofnumbers = set[what], { }, 0 if list then for i=1,#list do local feature = list[i] @@ -126,7 +126,8 @@ local function convert(featuresets,name,set,what) fn = fs and fs.number end if fn then - numbers[#numbers+1] = fn + nofnumbers = nofnumbers + 1 + numbers[nofnumbers] = fn if trace_goodies or trace_optimize then report_fonts("solution %s of '%s' uses feature '%s' with number %s",i,name,feature,fn) end @@ -134,7 +135,7 @@ local function convert(featuresets,name,set,what) report_fonts("solution %s has an invalid feature reference '%s'",i,name,tostring(feature)) end end - return #numbers > 0 and numbers + return nofnumbers > 0 and numbers end end @@ -179,18 +180,22 @@ function splitters.define(name,parameters) end else if l then + local n = #less_set for i=1,#l do local ss = contextsetups[l[i]] if ss then - less_set[#less_set+1] = ss.number + n = n + 1 + less_set[n] = ss.number end end end if m then + local n = #more_set for i=1,#m do local ss = contextsetups[m[i]] if ss then - more_set[#more_set+1] = ss.number + n = n + 1 + more_set[n] = ss.number end end end @@ -198,14 +203,14 @@ function splitters.define(name,parameters) if trace_optimize then report_fonts("defining solutions '%s', less: '%s', more: '%s'",name,concat(less_set or {}," "),concat(more_set or {}," ")) end - solutions[#solutions+1] = { + local nofsolutions = #solutions + 1 + solutions[nofsolutions] = { solution = solution, less = less_set or { }, more = more_set or { }, settings = settings, -- for tracing } ---~ print(table.serialize(solutions[#solutions])) - tex.write(#solutions) + tex.write(nofsolutions) end local nofwords, noftries, nofadapted, nofkept, nofparagraphs = 0, 0, 0, 0, 0 @@ -287,13 +292,14 @@ function splitters.split(head) end local function collect_words(list) - local words, word = { }, nil + local words, w, word = { }, 0, nil for current in traverse_ids(whatsit_code,list) do if current.subtype == userdefined_code then local user_id = current.user_id if user_id == 1 then word = { current.value, current, current } - words[#words+1] = word + w = w + 1 + words[w] = word elseif user_id == 2 then word[3] = current end diff --git a/tex/context/base/node-tra.lua b/tex/context/base/node-tra.lua index 78042cf44..d41089e6c 100644 --- a/tex/context/base/node-tra.lua +++ b/tex/context/base/node-tra.lua @@ -515,7 +515,7 @@ end --~ else --~ t[#t+1] = format("[%s]",node_type(last_id) or "?") --~ end ---~ return table.concat(table.reverse(t)," ") +--~ return table.concat(table.reversed(t)," ") --~ end @@ -631,7 +631,7 @@ end -- might move elsewhere local factor = number.dimenfactors.pt -local stripper = lpeg.patterns.strip_zeros +local stripper = lpeg.patterns.stripzeros local points = function(n) return lpegmatch(stripper,format("%.5fpt",n*factor)) diff --git a/tex/context/base/page-lin.lua b/tex/context/base/page-lin.lua index 186989839..d31785329 100644 --- a/tex/context/base/page-lin.lua +++ b/tex/context/base/page-lin.lua @@ -257,9 +257,10 @@ end function boxed.stage_two(n,m) if #current_list > 0 then m = m or lines.scratchbox - local t, i = { }, 0 + local t, tn = { }, 0 for l in traverse_id(hlist_code,texbox[m].list) do - t[#t+1] = copy_node(l) + tn = tn + 1 + t[tn] = copy_node(l) end for i=1,#current_list do local li = current_list[i] diff --git a/tex/context/base/s-fnt-10.tex b/tex/context/base/s-fnt-10.tex index 8214946c6..56f2decb4 100644 --- a/tex/context/base/s-fnt-10.tex +++ b/tex/context/base/s-fnt-10.tex @@ -148,7 +148,7 @@ end \page \egroup} -\endinput +% \endinput \starttext @@ -171,6 +171,6 @@ end % \ShowCompleteFont{name:palatinosansinformalcombold}{20pt}{2} % \ShowCompleteFont{name:palatinonovaregular}{11pt}{2} -% \ShowCompleteFont{name:optimanovaltregular}{11pt}{2} +\ShowCompleteFont{pirat.ttf}{12pt}{1} \stoptext diff --git a/tex/context/base/scrn-int.mkiv b/tex/context/base/scrn-int.mkiv index 83263d1df..dc919a8aa 100644 --- a/tex/context/base/scrn-int.mkiv +++ b/tex/context/base/scrn-int.mkiv @@ -247,6 +247,7 @@ \def\doinsertcomment#1% {\begingroup + \doifelse\@@ccoption\v!max{\let\@@ccopen\s!true}{\let\@@ccopen\s!false}% \ctxlua{backends.codeinjections.presetsymbollist("\@@ccsymbol")}% % in between predefined symbols are dealt with \ctxlua{backends.codeinjections.registercomment { @@ -273,7 +274,6 @@ \def\dostartcomment[#1][#2][#3]% {\bgroup \doifassignmentelse{#1}{\getparameters[\??cc][#1]}{\getparameters[\??cc][\c!title=#1,#2]}% - \doifelse\@@ccoption\v!max{\let\@@ccopen\s!true}{\let\@@ccopen\s!false}% \setcurrentbuffer{\v!comment\v!buffer}% \dostartbuffer[\v!comment\v!buffer][\v!comment\v!buffer][\e!start\v!comment][\e!stop\v!comment]} diff --git a/tex/context/base/sort-ini.lua b/tex/context/base/sort-ini.lua index 6511cd427..fb40c81a5 100644 --- a/tex/context/base/sort-ini.lua +++ b/tex/context/base/sort-ini.lua @@ -43,7 +43,7 @@ with any demand so nothign here is frozen.</p> local utf = unicode.utf8 local gsub, rep, sub, sort, concat = string.gsub, string.rep, string.sub, table.sort, table.concat local utfbyte, utfchar = utf.byte, utf.char -local utfcharacters, utfvalues, strcharacters = string.utfcharacters, string.utfvalues, string.characters +local utfcharacters = string.utfcharacters local next, type, tonumber, rawget, rawset = next, type, tonumber, rawget, rawset local allocate = utilities.storage.allocate @@ -140,7 +140,7 @@ local function preparetables(data) end local mtm = { __index = function(t,k) - local n + local n, nn if k then if trace_tests then report_sorters("simplifing character 0x%04x %s",utfbyte(k),k) @@ -153,8 +153,10 @@ local function preparetables(data) local ml = rawget(t,l) if ml then n = { } + nn = 0 for i=1,#ml do - n[#n+1] = ml[i] + (t.__delta or 0) + nn = nn + 1 + n[nn] = ml[i] + (t.__delta or 0) end if trace_tests then report_sorters(" 2 order: %s",concat(n," ")) @@ -168,6 +170,7 @@ local function preparetables(data) report_sorters(" 3 shape: %s",s) end n = { } + nn = 0 for l in utfcharacters(s) do local ml = rawget(t,l) if ml then @@ -176,7 +179,8 @@ local function preparetables(data) end if ml then for i=1,#ml do - n[#n+1] = ml[i] + nn = nn + 1 + n[nn] = ml[i] end end else @@ -188,7 +192,8 @@ local function preparetables(data) local ml = rawget(t,l) if ml then for i=1,#ml do - n[#n+1] = ml[i] + (t.__delta or 0) + nn = nn + 1 + n[nn] = ml[i] + (t.__delta or 0) end end end @@ -200,6 +205,7 @@ local function preparetables(data) end if not n then n = { 0 } + -- nn = 1 if trace_tests then report_sorters(" 7 order: 0") end @@ -207,6 +213,7 @@ local function preparetables(data) end else n = { 0 } + -- nn = 1 if trace_tests then report_sorters(" 8 order: 0") end @@ -266,10 +273,12 @@ local function setlanguage(l,m,d) -- local seq = utilities.parsers.settings_to_array(method or "") -- check the list sequence = { } + local nofsequence = 0 for i=1,#seq do local s = seq[i] if validmethods[s] then - sequence[#sequence+1] = s + nofsequence = nofsequence + 1 + sequence[nofsequence] = s else report_sorters("invalid sorter method '%s' in '%s'",s,method) end @@ -434,6 +443,7 @@ function splitters.utf(str) -- we could append m and u but this is cleaner, s is end local m_case, z_case, p_case, m_mapping, z_mapping, p_mapping, char, byte, n = { }, { }, { }, { }, { }, { }, { }, { }, 0 + local nm, nz, np = 0, 0, 0 for sc in utfcharacters(str) do local b = utfbyte(sc) if b >= digitsoffset then @@ -456,9 +466,12 @@ function splitters.utf(str) -- we could append m and u but this is cleaner, s is p_case[n] = b char[n] = sc byte[n] = b - m_mapping[#m_mapping+1] = b - z_mapping[#z_mapping+1] = b - p_mapping[#p_mapping+1] = b + nm = nm + 1 + nz = nz + 1 + np = np + 1 + m_mapping[nm] = b + z_mapping[nz] = b + p_mapping[np] = b else local l = lower[sc] n = n + 1 @@ -474,15 +487,18 @@ function splitters.utf(str) -- we could append m and u but this is cleaner, s is char[n], byte[n] = sc, b local msc = m_mappings[sc] for i=1,#msc do - m_mapping[#m_mapping+1] = msc[i] + nm = nm + 1 + m_mapping[nm] = msc[i] end local zsc = z_mappings[sc] for i=1,#zsc do - z_mapping[#z_mapping+1] = zsc[i] + nz = nz + 1 + z_mapping[nz] = zsc[i] end local psc = p_mappings[sc] for i=1,#psc do - p_mapping[#p_mapping+1] = psc[i] + np = np + 1 + p_mapping[np] = psc[i] end end end @@ -503,15 +519,6 @@ function splitters.utf(str) -- we could append m and u but this is cleaner, s is return t end - -function table.remap(t) - local tt = { } - for k,v in next, t do - tt[v] = k - end - return tt -end - local function pack(entry) local t = { } local split = entry.split diff --git a/tex/context/base/spac-ver.lua b/tex/context/base/spac-ver.lua index d58eaa329..9e29398cc 100644 --- a/tex/context/base/spac-ver.lua +++ b/tex/context/base/spac-ver.lua @@ -624,18 +624,20 @@ end local trace_list, tracing_info, before, after = { }, false, "", "" +local stripzeros, topoints = utilities.formatters.stripzeros, number.topoints + local function glue_to_string(glue) local spec = glue.spec local t = { points(spec.width) } if spec.stretch_order and spec.stretch_order ~= 0 then t[#t+1] = format("plus %s%s",spec.stretch/65536,fillcodes[spec.stretch_order]) elseif spec.stretch and spec.stretch ~= 0 then - t[#t+1] = format("plus %s",utilities.formatters.strip_zeros(number.topoints(spec.stretch))) + t[#t+1] = format("plus %s",stripzeros(topoints(spec.stretch))) end if spec.shrink_order and spec.shrink_order ~= 0 then t[#t+1] = format("minus %s%s",spec.shrink/65536,fillcodes[spec.shrink_order]) elseif spec.shrink and spec.shrink ~= 0 then - t[#t+1] = format("minus %s",utilities.formatters.strip_zeros(number.topoints(spec.shrink))) + t[#t+1] = format("minus %s",stripzeros(topoints(spec.shrink))) end return concat(t," ") end @@ -648,9 +650,9 @@ local function nodes_to_string(head) if id == penalty_code then t[#t+1] = format("%s:%s",ty,current.penalty) elseif id == glue_code then - t[#t+1] = format("%s:%s",ty,utilities.formatters.strip_zeros(number.topoints(current.spec.width))) + t[#t+1] = format("%s:%s",ty,stripzeros(topoints(current.spec.width))) elseif id == kern_code then - t[#t+1] = format("%s:%s",ty,utilities.formatters.strip_zeros(number.topoints(current.kern))) + t[#t+1] = format("%s:%s",ty,stripzeros(topoints(current.kern))) else t[#t+1] = ty end diff --git a/tex/context/base/strc-bkm.lua b/tex/context/base/strc-bkm.lua index 87327618c..08f26c88f 100644 --- a/tex/context/base/strc-bkm.lua +++ b/tex/context/base/strc-bkm.lua @@ -105,7 +105,7 @@ function bookmarks.place() if next(names) then local list = lists.filtercollected(names,"all",nil,lists.collected,forced) if #list > 0 then - local levels, lastlevel = { }, 1 + local levels, noflevels, lastlevel = { }, 0, 1 for i=1,#list do local li = list[i] local metadata = li.metadata @@ -133,7 +133,8 @@ function bookmarks.place() title = concat(sections.typesetnumber(sectiondata,"direct",numberspec,sectiondata)) .. " " .. title end end - levels[#levels+1] = { + noflevels = noflevels + 1 + levels[noflevels] = { lastlevel, stripped(title), -- can be replaced by converter li.references, -- has internal and realpage @@ -142,7 +143,6 @@ function bookmarks.place() end end end ---~ print(table.serialize(levels)) bookmarks.finalize(levels) end function bookmarks.place() end -- prevent second run diff --git a/tex/context/base/strc-des.mkiv b/tex/context/base/strc-des.mkiv index 06e4c2534..68e3ba155 100644 --- a/tex/context/base/strc-des.mkiv +++ b/tex/context/base/strc-des.mkiv @@ -676,18 +676,32 @@ \def\@@doenumerationtext {\ifconditional\enumerationnumberenabled \iftrialtypesetting - \doenumerationfullnumber\showdntext + \doenumerationfullnumberyes \doenumerationcouplingsymbol \else \doenumerationregistercoupling - \doenumerationfullnumber\showdntext + \doenumerationfullnumberyes \doenumerationcouplingsymbol \fi \else - \doenumerationfullnumber\showdnpuretext + \doenumerationfullnumbernop \fi} -\def\doenumerationfullnumber#1% text, title +\def\doenumerationfullnumberyes % text, title + {\begingroup + \dosetdescriptionattributes\c!headstyle\c!headcolor + \the\everyenumeration + \descriptionparameter\c!command{\strut\showdntext\doenumerationnumber\doenumerationextratext}% + \endgroup} + +\def\doenumerationfullnumbernop % text, title + {\begingroup + \dosetdescriptionattributes\c!headstyle\c!headcolor + \the\everyenumeration + \descriptionparameter\c!command{\strut\showdnpuretext\doenumerationextratext}% + \endgroup} + +\def\doenumerationfullnumber#1% text, title (used in notes) .. todo {\begingroup \dosetdescriptionattributes\c!headstyle\c!headcolor \the\everyenumeration @@ -696,14 +710,15 @@ \def\doenumerationextratext {\doif{\descriptionparameter\c!title}\v!yes - {\begingroup - \dosetdescriptionattributes\c!titlestyle\c!titlecolor - \hskip\descriptionparameter\c!titledistance - \descriptionparameter\c!titlecommand - {\descriptionparameter\c!titleleft - \begstrut\@@dodoenumerationtext\endstrut - \descriptionparameter\c!titleright}% - \endgroup}} + {\doifsomething\@@dodoenumerationtext + {\begingroup + \dosetdescriptionattributes\c!titlestyle\c!titlecolor + \hskip\descriptionparameter\c!titledistance + \descriptionparameter\c!titlecommand + {\descriptionparameter\c!titleleft + \begstrut\@@dodoenumerationtext\endstrut + \descriptionparameter\c!titleright}% + \endgroup}}} \def\doenumerationsavecounter {\savestructurecounter[\currentdescriptionnumber]} \def\doenumerationrestorecounter {\restorestructurecounter[\currentdescriptionnumber]} diff --git a/tex/context/base/strc-doc.lua b/tex/context/base/strc-doc.lua index 322322e2a..1b8dfeb6f 100644 --- a/tex/context/base/strc-doc.lua +++ b/tex/context/base/strc-doc.lua @@ -725,57 +725,7 @@ function sections.fullnumber(depth,what) end end ---~ function sections.directnumber(depth,what) ---~ local sectiondata = sections.findnumber(depth,what) ---~ return sectiondata and sections.typesetnumber(sectiondata,'direct',sectiondata) or "" ---~ end - function sections.getnumber(depth,what) -- redefined here local sectiondata = sections.findnumber(depth,what) texwrite((sectiondata and sectiondata.numbers[depth]) or 0) end - ---~ local done, preceding = false, false ---~ local function process(index,result) -- move to outer ---~ -- todo: too much (100 steps) ---~ local number = numbers and (numbers[index] or 0) ---~ local ownnumber = ownnumbers and ownnumbers[index] or "" ---~ if number > criterium or (ownnumber ~= "") then ---~ local block = (entry.block ~= "" and entry.block) or sections.currentblock() -- added ---~ if preceding then ---~ local separator = sets.get("structure:separators",block,separatorset,preceding,".") ---~ if separator then ---~ if result then ---~ result[#result+1] = ignoreprocessor(separator) ---~ else ---~ sprintprocessor(ctxcatcodes,separator) ---~ end ---~ end ---~ preceding = false ---~ end ---~ if result then ---~ if ownnumber ~= "" then ---~ result[#result+1] = ownnumber ---~ elseif conversion and conversion ~= "" then -- traditional (e.g. used in itemgroups) ---~ result[#result+1] = converters.convert(conversion,number,true) ---~ else ---~ local theconversion = sets.get("structure:conversions",block,conversionset,index,"numbers") ---~ result[#result+1] = converters.convert(theconversion,number,true) ---~ end ---~ else ---~ if ownnumber ~= "" then ---~ sprintprocessor(ctxcatcodes,ownnumber) ---~ elseif conversion and conversion ~= "" then -- traditional (e.g. used in itemgroups) ---~ context.convertnumber(conversion,number) ---~ else ---~ local theconversion = sets.get("structure:conversions",block,conversionset,index,"numbers") ---~ sprintprocessor(ctxcatcodes,theconversion,function(str) ---~ return format("\\convertnumber{%s}{%s}",str or "numbers",number) ---~ end) ---~ end ---~ end ---~ preceding, done = index, true ---~ else ---~ preceding = preceding or false ---~ end ---~ end diff --git a/tex/context/base/strc-lst.lua b/tex/context/base/strc-lst.lua index 9709e7180..7b6448aca 100644 --- a/tex/context/base/strc-lst.lua +++ b/tex/context/base/strc-lst.lua @@ -55,7 +55,7 @@ local matchingtilldepth, numberatdepth = sections.matchingtilldepth, sections.nu -- -- -- -- -- -- -function table.zerostrippedconcat(t,separator) +local function zerostrippedconcat(t,separator) -- for the moment not public local f, l = 1, #t for i=f,l do if t[i] == 0 then @@ -198,7 +198,7 @@ local splitter = lpeg.splitat(":") local function filtercollected(names, criterium, number, collected, forced, nested) -- names is hash or string local numbers, depth = documents.data.numbers, documents.data.depth - local result, detail = { }, nil + local result, nofresult, detail = { }, 0, nil local block = false -- all criterium = gsub(criterium or ""," ","") -- not needed -- new, will be applied stepwise @@ -230,7 +230,8 @@ local function filtercollected(names, criterium, number, collected, forced, nest local v = collected[i] local r = v.references if r and r.section == 0 then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v end end elseif all or criterium == variables.all or criterium == variables.text then @@ -243,7 +244,8 @@ local function filtercollected(names, criterium, number, collected, forced, nest local name = metadata.name or false local sectionnumber = (r.section == 0) or sections.collected[r.section] if forced[name] or (sectionnumber and not metadata.nolist and (all or names[name])) then -- and not sectionnumber.hidenumber then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v end end end @@ -271,7 +273,8 @@ local function filtercollected(names, criterium, number, collected, forced, nest end end if ok then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v end end end @@ -304,7 +307,8 @@ local function filtercollected(names, criterium, number, collected, forced, nest end end if ok then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v end end end @@ -335,7 +339,8 @@ local function filtercollected(names, criterium, number, collected, forced, nest end end if ok then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v end end end @@ -372,7 +377,8 @@ local function filtercollected(names, criterium, number, collected, forced, nest local cnumbers = sectionnumber.numbers if cnumbers then if (all or names[metadata.name or false]) and #cnumbers >= depth and matchingtilldepth(depth,cnumbers,parent) then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v end end end @@ -405,7 +411,7 @@ function lists.process(specification) for i=1,#lists.result do local r = lists.result[i] local m = r.metadata - local s = specials and r.numberdata and specials[table.zerostrippedconcat(r.numberdata.numbers,".")] or "" + local s = specials and r.numberdata and specials[zerostrippedconcat(r.numberdata.numbers,".")] or "" context.processlistofstructure(m.name,m.kind,i,s) end end @@ -441,17 +447,6 @@ function lists.location(n) texsprint(l.references.internal or n) end ---~ function lists.stamp(n) ---~ local l = lists.result[n] ---~ local numberdata = l.numberdata ---~ if numberdata then ---~ local numbers = numberdata.numbers ---~ if numbers then ---~ tex.sprint(table.zerostrippedconcat(numbers,".")) ---~ end ---~ end ---~ end - function lists.sectionnumber(name,n,spec) local data = lists.result[n] local sectiondata = sections.collected[data.references.section] diff --git a/tex/context/base/strc-lst.mkiv b/tex/context/base/strc-lst.mkiv index 4a88da083..f752051d7 100644 --- a/tex/context/base/strc-lst.mkiv +++ b/tex/context/base/strc-lst.mkiv @@ -264,7 +264,8 @@ \setuplistalternative[b][\c!distance=5em,\c!width=2em,\c!stretch=10em,\c!command=\hfill] \setuplistalternative[c][\c!distance=5em,\c!width=0pt,\c!stretch=10em,\c!command=\hskip.5em\listdots\hskip.5em\relax] -\def\listdots{\leaders\hbox to .5em{\hss.\hss}\hfill} +%def\listdots{\leaders \hbox to .5em{\hss.\hss}\hfill} +\def\listdots{\gleaders\hbox to .5em{\hss.\hss}\hfill} % \setvalue{\??li\c!alternative}{\getvalue{\??li\c!alternative b}} % ? % \getvalue{\??li\c!alternative} % ? diff --git a/tex/context/base/strc-not.mkiv b/tex/context/base/strc-not.mkiv index 97cf2c7d1..183ef65a1 100644 --- a/tex/context/base/strc-not.mkiv +++ b/tex/context/base/strc-not.mkiv @@ -183,7 +183,7 @@ \c!width=\defaultnotewidth, \c!height=\textheight, \c!numbercommand=\high, - \c!command=\noteparameter\c!numbercommand, % downward compatible + \c!command=, % \noteparameter\c!numbercommand, % (command in enumeration) too messy, called twice \c!separator=,% \@@koseparator, \c!textcommand=\high, \c!textstyle=\tx, diff --git a/tex/context/base/strc-ref.lua b/tex/context/base/strc-ref.lua index 459cbb9bb..3dddc6bc5 100644 --- a/tex/context/base/strc-ref.lua +++ b/tex/context/base/strc-ref.lua @@ -552,11 +552,12 @@ function exporters.lists.generic(data) end if numberdata then local numbers = numberdata.numbers - local t = { } + local t, tn = { }, 0 for i=1,#numbers do local n = numbers[i] if n ~= 0 then - t[#t+1] = n + tn = tn + 1 + t[tn] = n end end useddata.number = concat(t,".") @@ -1162,21 +1163,23 @@ function references.setinternalreference(prefix,tag,internal,view) if innermethod == "page" then return unsetvalue else - local t = { } -- maybe add to current + local t, tn = { }, 0 -- maybe add to current if tag then if prefix and prefix ~= "" then prefix = prefix .. ":" for ref in gmatch(tag,"[^,]+") do - t[#t+1] = prefix .. ref + tn = tn + 1 + t[tn] = prefix .. ref end else for ref in gmatch(tag,"[^,]+") do - t[#t+1] = ref + tn = tn + 1 + t[tn] = ref end end end if internal and innermethod == "names" then -- mixed or page - t[#t+1] = "aut:" .. internal + t[tn] = "aut:" .. internal end local destination = references.mark(t,nil,nil,view) -- returns an attribute texcount.lastdestinationattribute = destination diff --git a/tex/context/base/strc-reg.lua b/tex/context/base/strc-reg.lua index abab0959a..7e392ba72 100644 --- a/tex/context/base/strc-reg.lua +++ b/tex/context/base/strc-reg.lua @@ -8,7 +8,8 @@ if not modules then modules = { } end modules ['strc-reg'] = { local next, type = next, type local texwrite, texcount = tex.write, tex.count -local format, gmatch, concat, remove = string.format, string.gmatch, table.concat, table.remove +local format, gmatch = string.format, string.gmatch +local equal, concat, remove = table.are_equal, table.concat, table.remove local utfchar = utf.char local lpegmatch = lpeg.match local allocate, mark = utilities.storage.allocate, utilities.storage.mark @@ -44,7 +45,7 @@ local function filtercollected(names,criterium,number,collected,prevmode) if not criterium or criterium == "" then criterium = variables.all end local data = documents.data local numbers, depth = data.numbers, data.depth - local hash, result, all, detail = { }, { }, not names or names == "" or names == variables.all, nil + local hash, result, nofresult, all, detail = { }, { }, 0, not names or names == "" or names == variables.all, nil if not all then for s in gmatch(names,"[^, ]+") do hash[s] = true @@ -54,11 +55,13 @@ local function filtercollected(names,criterium,number,collected,prevmode) for i=1,#collected do local v = collected[i] if all then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v else local vmn = v.metadata and v.metadata.name if hash[vmn] then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v end end end @@ -78,7 +81,8 @@ local function filtercollected(names,criterium,number,collected,prevmode) end end if ok then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v end end else @@ -92,7 +96,8 @@ local function filtercollected(names,criterium,number,collected,prevmode) end end if ok then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v end end end @@ -123,7 +128,8 @@ local function filtercollected(names,criterium,number,collected,prevmode) end end if ok then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v end end end @@ -152,7 +158,8 @@ local function filtercollected(names,criterium,number,collected,prevmode) local cnumbers = sectionnumber.numbers if cnumbers then if (all or hash[metadata.name or false]) and #cnumbers >= depth and matchingtilldepth(depth,cnumbers) then - result[#result+1] = v + nofresult = nofresult + 1 + result[nofresult] = v end end end @@ -394,13 +401,15 @@ local function crosslinkseewords(result) -- all words local seeparent = seeparents[text] if seeparent then local seeindex = seewords[text] - local s, d, w, l = { }, data.split, seeparent.split, data.list + local s, ns, d, w, l = { }, 0, data.split, seeparent.split, data.list -- trick: we influence sorting by adding fake subentries for i=1,#d do - s[#s+1] = d[i] -- parent + ns = ns + 1 + s[ns] = d[i] -- parent end for i=1,#w do - s[#s+1] = w[i] -- see + ns = ns + 1 + s[ns] = w[i] -- see end data.split = s -- we also register a fake extra list entry so that the @@ -416,7 +425,6 @@ local function crosslinkseewords(result) -- all words end end - local function removeemptyentries(result) local i, n, m = 1, #result, 0 while i <= n do @@ -465,53 +473,61 @@ function registers.sort(data,options) end function registers.unique(data,options) - local result, prev, equal = { }, nil, table.are_equal + local result, nofresult, prev = { }, 0, nil local dataresult = data.result for k=1,#dataresult do local v = dataresult[k] - if not prev then - result[#result+1], prev = v, v - else + if prev then local pr, vr = prev.references, v.references if not equal(prev.list,v.list) then - result[#result+1], prev = v, v + -- ok elseif pr.realpage ~= vr.realpage then - result[#result+1], prev = v, v + -- ok else local pl, vl = pr.lastrealpage, vr.lastrealpage if pl or vl then if not vl then - result[#result+1], prev = v, v + -- ok elseif not pl then - result[#result+1], prev = v, v + -- ok elseif pl ~= vl then - result[#result+1], prev = v, v + -- ok + else + v = nil end + else + v = nil end end end + if v then + nofresult = nofresult + 1 + result[nofresult] = v + prev = v + end end data.result = result end -function registers.finalize(data,options) +function registers.finalize(data,options) -- maps character to index (order) local result = data.result data.metadata.nofsorted = #result - local split, lasttag, s, d = { }, nil, nil, nil - -- maps character to index (order) + local split, nofsplit, lasttag, done, nofdone = { }, 0, nil, nil, 0 + local firstofsplit = sorters.firstofsplit for k=1,#result do local v = result[k] - local entry, tag = sorters.firstofsplit(v) + local entry, tag = firstofsplit(v) if tag ~= lasttag then if trace_registers then report_registers("splitting at %s",tag) end - d = { } - s = { tag = tag, data = d } - split[#split+1] = s + done, nofdone = { }, 0 + nofsplit = nofsplit + 1 + split[nofsplit] = { tag = tag, data = done } lasttag = tag end - d[#d+1] = v + nofdone = nofdone + 1 + done[nofdone] = v end data.result = split end @@ -576,19 +592,6 @@ local function pagenumber(entry,prefixspec,pagespec) ) end --- local usedtags = { } --- for i=1,#result do --- usedtags[#usedtags+1] = result[i].tag --- end --- context.setvalue("usedregistertags",concat(usedtags,",")) -- todo: { } and escape special chars - ---~ local function remove(pages,i) -- todo: use table.remove(pages,i) ---~ for j=i,#pages-1 do ---~ pages[j] = pages[j+1] ---~ end ---~ pages[#pages] = nil ---~ end - local function collapsedpage(pages) for i=2,#pages do local first, second = pages[i-1], pages[i] @@ -649,10 +652,10 @@ end function collapsepages(pages) while collapsedpage(pages) do end + return #pages end function registers.flush(data,options,prefixspec,pagespec) - local equal = table.are_equal local collapse_singles = options.compress == variables.yes local collapse_ranges = options.compress == variables.all local result = data.result @@ -716,7 +719,7 @@ function registers.flush(data,options,prefixspec,pagespec) if collapse_singles or collapse_ranges then -- we collapse ranges and keep existing ranges as they are -- so we get prebuilt as well as built ranges - local first, last, prev, pages, dd = entry, nil, entry, { }, d + local first, last, prev, pages, dd, nofpages = entry, nil, entry, { }, d, 0 while dd < #data do dd = dd + 1 local next = data[dd] @@ -730,28 +733,32 @@ function registers.flush(data,options,prefixspec,pagespec) --~ first = nil break elseif next.references.lastrealpage then - pages[#pages+1] = first and { first, last or first } or { entry, entry } - pages[#pages+1] = { next, next } + nofpages = nofpages + 1 + pages[nofpages] = first and { first, last or first } or { entry, entry } + nofpages = nofpages + 1 + pages[nofpages] = { next, next } first, last, prev = nil, nil, nil elseif not first then first, prev = next, next elseif next.references.realpage - prev.references.realpage == 1 then -- 1 ? last, prev = next, next else - pages[#pages+1] = { first, last or first } + nofpages = nofpages + 1 + pages[nofpages] = { first, last or first } first, last, prev = next, nil, next end end end if first then - pages[#pages+1] = { first, last or first } + nofpages = nofpages + 1 + pages[nofpages] = { first, last or first } end - if collapse_ranges and #pages > 1 then - collapsepages(pages) + if collapse_ranges and nofpages > 1 then + nofpages = collapsepages(pages) end - if #pages > 0 then -- or 0 + if nofpages > 0 then -- or 0 d = dd - for p=1,#pages do + for p=1,nofpages do local first, last = pages[p][1], pages[p][2] if first == last then if first.references.lastrealpage then @@ -793,9 +800,10 @@ function registers.flush(data,options,prefixspec,pagespec) end context.stopregisterpages() elseif kind == 'see' then - local t = { } + local t, nt = { }, 0 while true do - t[#t+1] = entry + nt = nt + 1 + t[nt] = entry if d == #data then break else @@ -810,8 +818,7 @@ function registers.flush(data,options,prefixspec,pagespec) end end context.startregisterseewords() - local n = #t - for i=1,n do + for i=1,nt do local entry = t[i] local processor = entry.processors and entry.processors[1] or "" local seeindex = entry.references.seeindex or "" diff --git a/tex/context/base/supp-fil.lua b/tex/context/base/supp-fil.lua index 62252bf06..b95d72d7f 100644 --- a/tex/context/base/supp-fil.lua +++ b/tex/context/base/supp-fil.lua @@ -264,18 +264,20 @@ end statistics.register("loaded tex modules", function() if next(modstatus) then - local t, f = { }, { } + local t, f, nt, nf = { }, { }, 0, 0 for k, v in table.sortedhash(modstatus) do k = file.basename(k) if v == 0 then - f[#f+1] = k + nf = nf + 1 + f[nf] = k else - t[#t+1] = k + nt = nt + 1 + t[nt] = k end end - local ts = (#t>0 and format(" (%s)",concat(t," "))) or "" - local fs = (#f>0 and format(" (%s)",concat(f," "))) or "" - return format("%s requested, %s found%s, %s missing%s",#t+#f,#t,ts,#f,fs) + local ts = (nt>0 and format(" (%s)",concat(t," "))) or "" + local fs = (nf>0 and format(" (%s)",concat(f," "))) or "" + return format("%s requested, %s found%s, %s missing%s",nt+nf,nt,ts,nf,fs) else return nil end diff --git a/tex/context/base/supp-fil.mkiv b/tex/context/base/supp-fil.mkiv index 55b8528ae..aa2891689 100644 --- a/tex/context/base/supp-fil.mkiv +++ b/tex/context/base/supp-fil.mkiv @@ -39,11 +39,8 @@ \def\f!parentpath {..} \fi -% \def\openinputfile #1#2{\immediate\openin #1="#2"\relax} \def\closeinputfile #1{\immediate\closein #1} -% \def\openoutputfile#1#2{\immediate\openout#1="#2"\relax} \def\closeoutputfile#1{\immediate\closeout#1} - -\def\openinputfile #1#2{\immediate\openin #1=#2\relax} \def\closeinputfile #1{\immediate\closein #1} -\def\openoutputfile#1#2{\immediate\openout#1=#2\relax} \def\closeoutputfile#1{\immediate\closeout#1} +\def\openinputfile #1#2{\immediate\openin #1={#2}} \def\closeinputfile #1{\immediate\closein #1} +\def\openoutputfile#1#2{\immediate\openout#1={#2}} \def\closeoutputfile#1{\immediate\closeout#1} %D \macros %D {pushendofline,popendofline} @@ -191,7 +188,7 @@ \unexpanded\def\input{\normalinput} -\def\inputgivenfile#1{\normalinput"#1"\relax} +\def\inputgivenfile#1{\normalinput{#1}} %D \macros %D {readfile,ReadFile} diff --git a/tex/context/base/toks-ini.lua b/tex/context/base/toks-ini.lua index 7a4c221a4..f8b5b9439 100644 --- a/tex/context/base/toks-ini.lua +++ b/tex/context/base/toks-ini.lua @@ -57,9 +57,10 @@ tokens.letter = function(chr) return createtoken(utfbyte(chr), 11) end tokens.other = function(chr) return createtoken(utfbyte(chr), 12) end tokens.letters = function(str) - local t = { } + local t, n = { }, 0 for chr in string.utfvalues(str) do - t[#t+1] = createtoken(chr, 11) + n = n + 1 + t[n] = createtoken(chr, 11) end return t end @@ -99,7 +100,7 @@ local letter = command_id("letter") local other = command_id("other_char") function collectors.install(tag,end_cs) - local data = { } + local data, d = { }, 0 collectordata[tag] = data local endcs = csname_id(end_cs) while true do @@ -111,7 +112,8 @@ function collectors.install(tag,end_cs) elseif a == call and registered[b] then expand() else - data[#data+1] = t + d = d + 1 + data[d] = t end end end @@ -135,31 +137,38 @@ function collectors.show(tag, method) end function collectors.defaultwords(t,str) - t[#t+1] = tokens.bgroup - t[#t+1] = createtoken("red") + local n = #t + n = n + 1 + t[n] = tokens.bgroup + n = n + 1 + t[n] = createtoken("red") for i=1,#str do - t[#t+1] = tokens.other('*') + n = n + 1 + t[n] = tokens.other('*') end - t[#t+1] = tokens.egroup + n = n + 1 + t[n] = tokens.egroup end function collectors.dowithwords(tag,handle) - local t, w = { }, { } + local t, w, tn, wn = { }, { }, 0, 0 handle = handle or collectors.defaultwords local tagdata = collectordata[tag] for k=1,#tagdata do local v = tagdata[k] if v[1] == letter then - w[#w+1] = v[2] + wn = wn + 1 + w[wn] = v[2] else - if #w > 0 then + if wn > 0 then handle(t,w) - w = { } + wn = 0 end - t[#t+1] = v + tn = tn + 1 + t[tn] = v end end - if #w > 0 then + if wn > 0 then handle(t,w) end collectordata[tag] = t @@ -297,35 +306,35 @@ function remapper.convert(tag,toks) local skipping = 0 -- todo: math if data then - local t = { } + local t, n = { }, 0 for s=1,#toks do local tok = toks[s] local one, two = tok[1], tok[2] if one == 11 or one == 12 then if two == leftbracket then skipping = skipping + 1 - t[#t+1] = tok + n = n + 1 ; t[n] = tok elseif two == rightbracket then skipping = skipping - 1 - t[#t+1] = tok + n = n + 1 ; t[n] = tok elseif skipping == 0 then local new = data[two] if new then if #new > 1 then for n=1,#new do - t[#t+1] = new[n] + n = n + 1 ; t[n] = new[n] end else - t[#t+1] = new[1] + n = n + 1 ; t[n] = new[1] end else - t[#t+1] = tok + n = n + 1 ; t[n] = tok end else - t[#t+1] = tok + n = n + 1 ; t[n] = tok end else - t[#t+1] = tok + n = n + 1 ; t[n] = tok end end return t diff --git a/tex/context/base/trac-inf.lua b/tex/context/base/trac-inf.lua index 7bd7a7ca4..aa224cf38 100644 --- a/tex/context/base/trac-inf.lua +++ b/tex/context/base/trac-inf.lua @@ -13,6 +13,7 @@ if not modules then modules = { } end modules ['trac-inf'] = { local format = string.format local clock = os.gettimeofday or os.clock -- should go in environment +local write_nl = texio.write_nl statistics = statistics or { } local statistics = statistics @@ -108,7 +109,7 @@ end function statistics.show(reporter) if statistics.enable then - if not reporter then reporter = function(tag,data,n) texio.write_nl(tag .. " " .. data) end end + if not reporter then reporter = function(tag,data,n) write_nl(tag .. " " .. data) end end -- this code will move local register = statistics.register register("luatex banner", function() @@ -131,18 +132,24 @@ function statistics.show(reporter) reporter(s[1],r,n) end end - texio.write_nl("") -- final newline + write_nl("") -- final newline statistics.enable = false end end + +local template, nn = nil, 0 -- we only calcute it once + function statistics.showjobstat(tag,data,n) if type(data) == "table" then for i=1,#data do statistics.showjobstat(tag,data[i],n) end else - texio.write_nl(format("%-15s: %s - %s","mkiv lua stats",tag:rpadd(n," "),data)) + if not template or n > nn then + template, n = format("%%-%ss: %%-%ss - %%s",15,n), nn + end + write_nl(format(template,"mkiv lua stats",tag,data)) end end diff --git a/tex/context/base/trac-set.lua b/tex/context/base/trac-set.lua index 9ae215817..f4e152b14 100644 --- a/tex/context/base/trac-set.lua +++ b/tex/context/base/trac-set.lua @@ -8,7 +8,7 @@ if not modules then modules = { } end modules ['trac-set'] = { -- might become u local type, next, tostring = type, next, tostring local concat = table.concat -local format, find, lower, gsub, partialescapedpattern = string.format, string.find, string.lower, string.gsub, string.partialescapedpattern +local format, find, lower, gsub, escapedpattern = string.format, string.find, string.lower, string.gsub, string.escapedpattern local is_boolean = string.is_boolean local settings_to_hash = utilities.parsers.settings_to_hash local allocate = utilities.storage.allocate @@ -88,7 +88,7 @@ local function set(t,what,newvalue) for name, functions in next, data do if done[name] then -- prevent recursion due to wildcards - elseif find(name,partialescapedpattern(w)) then + elseif find(name,escapedpattern(w,true)) then done[name] = true for i=1,#functions do functions[i](value) diff --git a/tex/context/base/typo-krn.lua b/tex/context/base/typo-krn.lua index 83f2e9102..91849db85 100644 --- a/tex/context/base/typo-krn.lua +++ b/tex/context/base/typo-krn.lua @@ -230,7 +230,7 @@ function kerns.set(factor) tasks.enableaction("processors","typesetters.kerns.handler") enabled = true end - if factor > 0 then + if factor ~= 0 then local a = factors[factor] if not a then a = #mapping + 1 diff --git a/tex/context/base/typo-spa.lua b/tex/context/base/typo-spa.lua index c375b9fb5..e351cc9f7 100644 --- a/tex/context/base/typo-spa.lua +++ b/tex/context/base/typo-spa.lua @@ -65,7 +65,9 @@ local function process(namespace,attribute,head) map = map[start.char] unset_attribute(start,attribute) -- needed? if map then - local left, right, alternative = map.left, map.right, map.alternative + local left = map.left + local right = map.right + local alternative = map.alternative local quad = quaddata[start.font] local prev = start.prev if left and left ~= 0 and prev then diff --git a/tex/context/base/util-fmt.lua b/tex/context/base/util-fmt.lua index e94782c32..e049d0b94 100644 --- a/tex/context/base/util-fmt.lua +++ b/tex/context/base/util-fmt.lua @@ -35,9 +35,9 @@ local stripper = Cs((number + 1)^0) --~ lpegmatch(stripper,str) --~ print(#str, os.clock()-ts, lpegmatch(stripper,sample)) -lpeg.patterns.strip_zeros = stripper +lpeg.patterns.stripzeros = stripper -function formatters.strip_zeros(str) +function formatters.stripzeros(str) return lpegmatch(stripper,str) end diff --git a/tex/context/base/util-lua.lua b/tex/context/base/util-lua.lua index 3aecbfa26..6ad325fcf 100644 --- a/tex/context/base/util-lua.lua +++ b/tex/context/base/util-lua.lua @@ -13,7 +13,7 @@ utilities.report = utilities.report or print function utilities.lua.compile(luafile,lucfile,cleanup,strip) -- defaults: cleanup=false strip=true utilities.report("lua: compiling %s into %s",luafile,lucfile) os.remove(lucfile) - local command = "-o " .. string.quote(lucfile) .. " " .. string.quote(luafile) + local command = "-o " .. string.quoted(lucfile) .. " " .. string.quoted(luafile) if strip ~= false then command = "-s " .. command end @@ -24,3 +24,25 @@ function utilities.lua.compile(luafile,lucfile,cleanup,strip) -- defaults: clean end return done end + +--~ local getmetatable, type = getmetatable, type + +--~ local types = { } + +--~ function utilities.lua.registerdatatype(d,name) +--~ types[getmetatable(d)] = name +--~ end + +--~ function utilities.lua.datatype(d) +--~ local t = type(d) +--~ if t == "userdata" then +--~ local m = getmetatable(d) +--~ return m and types[m] or "userdata" +--~ else +--~ return t +--~ end +--~ end + +--~ utilities.lua.registerdatatype(lpeg.P("!"),"lpeg") + +--~ print(utilities.lua.datatype(lpeg.P("oeps"))) diff --git a/tex/context/base/util-pck.lua b/tex/context/base/util-pck.lua index cd1fc510e..c802a09ed 100644 --- a/tex/context/base/util-pck.lua +++ b/tex/context/base/util-pck.lua @@ -17,12 +17,13 @@ local packers = utilities.packers packers.version = 1.00 local function hashed(t) - local s = { } + local s, ns = { }, 0 for k, v in next, t do + ns = ns + 1 if type(v) == "table" then - s[#s+1] = k .. "={" .. hashed(v) .. "}" + s[ns] = k .. "={" .. hashed(v) .. "}" else - s[#s+1] = k .. "=" .. tostring(v) + s[ns] = k .. "=" .. tostring(v) end end sort(s) @@ -30,9 +31,10 @@ local function hashed(t) end local function simplehashed(t) - local s = { } + local s, ns = { }, 0 for k, v in next, t do - s[#s+1] = k.. "=" .. v + ns = ns + 1 + s[ns] = k .. "=" .. v end sort(s) return concat(s,",") diff --git a/tex/context/base/util-prs.lua b/tex/context/base/util-prs.lua index e1a2ebbed..08fe2f686 100644 --- a/tex/context/base/util-prs.lua +++ b/tex/context/base/util-prs.lua @@ -14,7 +14,7 @@ parsers.patterns = parsers.patterns or { } local P, R, V, C, Ct, Carg = lpeg.P, lpeg.R, lpeg.V, lpeg.C, lpeg.Ct, lpeg.Carg local lpegmatch = lpeg.match local concat, format, gmatch = table.concat, string.format, string.gmatch -local tostring, type, next = tostring, type, next +local tostring, type, next, setmetatable = tostring, type, next, setmetatable local sortedhash = table.sortedhash local escape, left, right = P("\\"), P('{'), P('}') @@ -133,7 +133,7 @@ end function parsers.hash_to_string(h,separator,yes,no,strict,omit) if h then - local t, s = { }, table.sortedkeys(h) + local t, tn, s = { }, 0, table.sortedkeys(h) omit = omit and table.tohash(omit) for i=1,#s do local key = s[i] @@ -142,15 +142,19 @@ function parsers.hash_to_string(h,separator,yes,no,strict,omit) if type(value) == "boolean" then if yes and no then if value then - t[#t+1] = key .. '=' .. yes + tn = tn + 1 + t[tn] = key .. '=' .. yes elseif not strict then - t[#t+1] = key .. '=' .. no + tn = tn + 1 + t[tn] = key .. '=' .. no end elseif value or not strict then - t[#t+1] = key .. '=' .. tostring(value) + tn = tn + 1 + t[tn] = key .. '=' .. tostring(value) end else - t[#t+1] = key .. '=' .. value + tn = tn + 1 + t[tn] = key .. '=' .. value end end end @@ -177,10 +181,11 @@ function parsers.settings_to_set(str,t) -- tohash? -- todo: lpeg -- duplicate an end function parsers.simple_hash_to_string(h, separator) - local t = { } + local t, tn = { }, 0 for k, v in sortedhash(h) do if v then - t[#t+1] = k + tn = tn + 1 + t[tn] = k end end return concat(t,separator or ",") @@ -198,8 +203,16 @@ end function parsers.getparameters(self,class,parentclass,settings) local sc = self[class] if not sc then - sc = table.clone(self[parent]) + sc = { } self[class] = sc + if parentclass then + local sp = self[parentclass] + if not sp then + sp = { } + self[parentclass] = sp + end + setmetatable(sc, { __index = sp }) + end end parsers.settings_to_hash(settings,sc) end diff --git a/tex/context/base/util-seq.lua b/tex/context/base/util-seq.lua index 415918a44..14c425dfc 100644 --- a/tex/context/base/util-seq.lua +++ b/tex/context/base/util-seq.lua @@ -19,8 +19,12 @@ local format, gsub, concat, gmatch = string.format, string.gsub, table.concat, s local type, loadstring = type, loadstring utilities = utilities or { } -utilities.sequencers = utilities.sequencers or { } -local sequencers = utilities.sequencers +local tables = utilities.tables + +local sequencers = { } +utilities.sequencers = sequencers + +local removevalue, insertaftervalue, insertbeforevalue = tables.removevalue, tables.insertaftervalue, tables.insertbeforevalue local function validaction(action) local g = _G @@ -45,23 +49,23 @@ end function sequencers.prependgroup(t,group,where) local list, order = t.list, t.order - table.removevalue(order,group) - table.insertbeforevalue(order,where,group) + removevalue(order,group) + insertbeforevalue(order,where,group) list[group] = { } end function sequencers.appendgroup(t,group,where) local list, order = t.list, t.order - table.removevalue(order,group) - table.insertaftervalue(order,where,group) + removevalue(order,group) + insertaftervalue(order,where,group) list[group] = { } end function sequencers.prependaction(t,group,action,where,kind,force) local g = t.list[group] if g and (force or validaction(action)) then - table.removevalue(g,action) - table.insertbeforevalue(g,where,action) + removevalue(g,action) + insertbeforevalue(g,where,action) t.kind[action] = kind end end @@ -69,8 +73,8 @@ end function sequencers.appendaction(t,group,action,where,kind,force) local g = t.list[group] if g and (force or validaction(action)) then - table.removevalue(g,action) - table.insertaftervalue(g,where,action) + removevalue(g,action) + insertaftervalue(g,where,action) t.kind[action] = kind end end @@ -87,7 +91,7 @@ end function sequencers.removeaction(t,group,action,force) local g = t.list[group] if g and (force or validaction(action)) then - table.removevalue(g,action) + removevalue(g,action) end end @@ -114,7 +118,7 @@ end]] function sequencers.tostring(t) local list, order, kind, gskip, askip = t.list, t.order, t.kind, t.gskip, t.askip - local vars, calls, args = { }, { }, nil + local vars, calls, args, n = { }, { }, nil, 0 for i=1,#order do local group = order[i] if not gskip[group] then @@ -123,8 +127,9 @@ function sequencers.tostring(t) local action = actions[i] if not askip[action] then local localized = localize(action) - vars [#vars +1] = format("local %s = %s", localized, action) - calls[#calls+1] = format(" %s(...) -- %s %i", localized, group, i) + n = n + 1 + vars [n] = format("local %s = %s", localized, action) + calls[n] = format(" %s(...) -- %s %i", localized, group, i) end end end @@ -148,7 +153,7 @@ end]] function sequencers.nodeprocessor(t,n) local list, order, kind, gskip, askip = t.list, t.order, t.kind, t.gskip, t.askip - local vars, calls, args = { }, { }, nil + local vars, calls, args, n = { }, { }, nil, 0 if n == 0 then args = "" elseif n == 1 then @@ -172,11 +177,12 @@ function sequencers.nodeprocessor(t,n) local action = actions[i] if not askip[action] then local localized = localize(action) - vars[#vars+1] = format("local %s = %s",localized,action) + n = n + 1 + vars[n] = format("local %s = %s",localized,action) if kind[action] == "nohead" then - calls[#calls+1] = format(" ok = %s(head%s) done = done or ok -- %s %i",localized,args,group,i) + calls[n] = format(" ok = %s(head%s) done = done or ok -- %s %i",localized,args,group,i) else - calls[#calls+1] = format(" head, ok = %s(head%s) done = done or ok -- %s %i",localized,args,group,i) + calls[n] = format(" head, ok = %s(head%s) done = done or ok -- %s %i",localized,args,group,i) end end end diff --git a/tex/context/base/util-sto.lua b/tex/context/base/util-sto.lua index 34787be9b..19b8093c9 100644 --- a/tex/context/base/util-sto.lua +++ b/tex/context/base/util-sto.lua @@ -98,3 +98,14 @@ function storage.setinitializer(data,initialize) 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 diff --git a/tex/context/base/util-tab.lua b/tex/context/base/util-tab.lua index 6c0ae8970..ea440e736 100644 --- a/tex/context/base/util-tab.lua +++ b/tex/context/base/util-tab.lua @@ -15,14 +15,15 @@ local concat, insert, remove = table.concat, table.insert, table.remove local setmetatable = setmetatable function tables.definetable(target) -- defines undefined tables - local composed, t = nil, { } + local composed, t, n = nil, { }, 0 for name in gmatch(target,"([^%.]+)") do + n = n + 1 if composed then composed = composed .. "." .. name else composed = name end - t[#t+1] = format("%s = %s or { }",composed,composed) + t[n] = format("%s = %s or { }",composed,composed) end return concat(t,"\n") end @@ -35,7 +36,7 @@ function tables.accesstable(target) return t end -function table.removevalue(t,value) -- todo: n +function tables.removevalue(t,value) -- todo: n if value then for i=1,#t do if t[i] == value then @@ -46,7 +47,7 @@ function table.removevalue(t,value) -- todo: n end end -function table.insertbeforevalue(t,value,extra) +function tables.insertbeforevalue(t,value,extra) for i=1,#t do if t[i] == extra then remove(t,i) @@ -61,7 +62,7 @@ function table.insertbeforevalue(t,value,extra) insert(t,1,extra) end -function table.insertaftervalue(t,value,extra) +function tables.insertaftervalue(t,value,extra) for i=1,#t do if t[i] == extra then remove(t,i) diff --git a/tex/context/base/x-mathml.lua b/tex/context/base/x-mathml.lua index 9ea128ef3..209f575e1 100644 --- a/tex/context/base/x-mathml.lua +++ b/tex/context/base/x-mathml.lua @@ -450,17 +450,6 @@ function lxml.mml.stripped(str) tex.sprint(ctxcatcodes,str:strip()) end -function table.keys_as_string(t) - local k = { } - for k,_ in next, t do - k[#k+1] = k - end - return concat(k,"") -end - ---~ local leftdelimiters = "[" .. table.keys_as_string(l_replacements) .. "]" ---~ local rightdelimiters = "[" .. table.keys_as_string(r_replacements) .. "]" - function characters.remapentity(chr,slot) texsprint(format("{\\catcode%s=13\\xdef%s{\\string%s}}",slot,utfchar(slot),chr)) end diff --git a/tex/generic/context/luatex-fonts-merged.lua b/tex/generic/context/luatex-fonts-merged.lua index fdf07d31c..0cdeaeba6 100644 --- a/tex/generic/context/luatex-fonts-merged.lua +++ b/tex/generic/context/luatex-fonts-merged.lua @@ -1,6 +1,6 @@ -- merged file : luatex-fonts-merged.lua -- parent file : luatex-fonts.lua --- merge date : 10/22/10 16:46:39 +-- merge date : 10/29/10 11:35:59 do -- begin closure to overcome local limits and interference @@ -14,7 +14,7 @@ if not modules then modules = { } end modules ['l-string'] = { local string = string local sub, gsub, find, match, gmatch, format, char, byte, rep, lower = string.sub, string.gsub, string.find, string.match, string.gmatch, string.format, string.char, string.byte, string.rep, string.lower -local lpegmatch = lpeg.match +local lpegmatch, S, C, Ct = lpeg.match, lpeg.S, lpeg.C, lpeg.Ct -- some functions may disappear as they are not used anywhere @@ -22,157 +22,75 @@ if not string.split then -- this will be overloaded by a faster lpeg variant - function string:split(pattern) - if #self > 0 then - local t = { } - for s in gmatch(self..pattern,"(.-)"..pattern) do - t[#t+1] = s + function string.split(str,pattern) + local t = { } + if #str > 0 then + local n = 1 + for s in gmatch(str..pattern,"(.-)"..pattern) do + t[n] = s + n = n + 1 end - return t - else - return { } end + return t end end -string.patterns = { } - -local escapes = { - ["%"] = "%%", - ["."] = "%.", - ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", - ["^"] = "%^", ["$"] = "%$", - ["["] = "%[", ["]"] = "%]", - ["("] = "%(", [")"] = "%)", - ["{"] = "%{", ["}"] = "%}" -} - -string.patterns.escapes = escapes - -function string:esc() -- variant 2 - return (gsub(self,"(.)",escapes)) -end - -function string:unquote() - return (gsub(self,"^([\"\'])(.*)%1$","%2")) +function string.unquoted(str) + return (gsub(str,"^([\"\'])(.*)%1$","%2")) end ---~ function string:unquote() ---~ if find(self,"^[\'\"]") then ---~ return sub(self,2,-2) +--~ function stringunquoted(str) +--~ if find(str,"^[\'\"]") then +--~ return sub(str,2,-2) --~ else ---~ return self +--~ return str --~ end --~ end -function string:quote() -- we could use format("%q") - return format("%q",self) +function string.quoted(str) + return format("%q",str) -- always " end -function string:count(pattern) -- variant 3 +function string.count(str,pattern) -- variant 3 local n = 0 - for _ in gmatch(self,pattern) do + for _ in gmatch(str,pattern) do -- not for utf n = n + 1 end return n end -function string:limit(n,sentinel) - if #self > n then +function string.limit(str,n,sentinel) + if #str > n then sentinel = sentinel or " ..." - return sub(self,1,(n-#sentinel)) .. sentinel + return sub(str,1,(n-#sentinel)) .. sentinel else - return self - end -end - ---~ function string:strip() -- the .- is quite efficient ---~ -- return match(self,"^%s*(.-)%s*$") or "" ---~ -- return match(self,'^%s*(.*%S)') or '' -- posted on lua list ---~ return find(s,'^%s*$') and '' or match(s,'^%s*(.*%S)') ---~ end - -do -- roberto's variant: - local space = lpeg.S(" \t\v\n") - local nospace = 1 - space - local stripper = space^0 * lpeg.C((space^0 * nospace^1)^0) - function string.strip(str) - return lpegmatch(stripper,str) or "" - end -end - -function string:is_empty() - return not find(self,"%S") -end - -function string:enhance(pattern,action) - local ok, n = true, 0 - while ok do - ok = false - self = gsub(self,pattern, function(...) - ok, n = true, n + 1 - return action(...) - end) + return str end - return self, n end -if not string.characters then - - local function nextchar(str, index) - index = index + 1 - return (index <= #str) and index or nil, sub(str,index,index) - end - function string:characters() - return nextchar, self, 0 - end - local function nextbyte(str, index) - index = index + 1 - return (index <= #str) and index or nil, byte(sub(str,index,index)) - end - function string:bytes() - return nextbyte, self, 0 - end - -end +local space = S(" \t\v\n") +local nospace = 1 - space +local stripper = space^0 * C((space^0 * nospace^1)^0) -- roberto's code -function string:rpadd(n,chr) - local m = n-#self - if m > 0 then - return self .. rep(chr or " ",m) - else - return self - end +function string.strip(str) + return lpegmatch(stripper,str) or "" end -function string:lpadd(n,chr) - local m = n-#self - if m > 0 then - return rep(chr or " ",m) .. self - else - return self - end +function string.is_empty(str) + return not find(str,"%S") end -string.padd = string.rpadd - local patterns_escapes = { - ["-"] = "%-", - ["."] = "%.", - ["+"] = "%+", - ["*"] = "%*", ["%"] = "%%", - ["("] = "%)", - [")"] = "%)", - ["["] = "%[", - ["]"] = "%]", + ["."] = "%.", + ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", + ["["] = "%[", ["]"] = "%]", + ["("] = "%)", [")"] = "%)", + -- ["{"] = "%{", ["}"] = "%}" + -- ["^"] = "%^", ["$"] = "%$", } -function string:escapedpattern() - return (gsub(self,".",patterns_escapes)) -end - local simple_escapes = { ["-"] = "%-", ["."] = "%.", @@ -180,22 +98,28 @@ local simple_escapes = { ["*"] = ".*", } -function string:partialescapedpattern() - return (gsub(self,".",simple_escapes)) -end - -function string:tohash() - local t = { } - for s in gmatch(self,"([^, ]+)") do -- lpeg - t[s] = true +function string.escapedpattern(str,simple) + if simple then + return (gsub(str,".",simple_escapes)) + else + return (gsub(str,".",patterns_escapes)) end - return t end -local pattern = lpeg.Ct(lpeg.C(1)^0) - -function string:totable() - return lpegmatch(pattern,self) +function string.topattern(str,lowercase,strict) + if str == "" then + return ".*" + else + str = gsub(str,".",simple_escapes) + if lowercase then + str = lower(str) + end + if strict then + return "^" .. str .. "$" + else + return str + end + end end --~ local t = { @@ -211,6 +135,8 @@ end --~ print(string.tabtospace(t[k])) --~ end +-- The following functions might end up in another namespace. + function string.tabtospace(str,tab) -- we don't handle embedded newlines while true do @@ -230,30 +156,22 @@ function string.tabtospace(str,tab) return str end -function string:compactlong() -- strips newlines and leading spaces - self = gsub(self,"[\n\r]+ *","") - self = gsub(self,"^ *","") - return self -end +--~ local template = string.striplong([[ +--~ aaaa +--~ bb +--~ cccccc +--~ ]]) -function string:striplong() -- strips newlines and leading spaces - self = gsub(self,"^%s*","") - self = gsub(self,"[\n\r]+ *","\n") - return self +function string.striplong(str) -- strips all leading spaces + str = gsub(str,"^%s*","") + str = gsub(str,"[\n\r]+ *","\n") + return str end -function string:topattern(lowercase,strict) - if lowercase then - self = lower(self) - end - self = gsub(self,".",simple_escapes) - if self == "" then - self = ".*" - elseif strict then - self = "^" .. self .. "$" - end - return self -end +-- obsolete names: + +string.quote = string.quoted +string.unquote = string.unquoted end -- closure @@ -275,15 +193,29 @@ local patterns = lpeg.patterns local P, R, S, Ct, C, Cs, Cc, V = lpeg.P, lpeg.R, lpeg.S, lpeg.Ct, lpeg.C, lpeg.Cs, lpeg.Cc, lpeg.V local match = lpeg.match +local utfcharacters = string.utfcharacters +local utfgmatch = unicode and unicode.utf8.gmatch + local digit, sign = R('09'), S('+-') local cr, lf, crlf = P("\r"), P("\n"), P("\r\n") -local utf8byte = R("\128\191") +local utf8next = R("\128\191") +local escaped = P("\\") * P(1) +local squote = P("'") +local dquote = P('"') -patterns.utf8byte = utf8byte patterns.utf8one = R("\000\127") -patterns.utf8two = R("\194\223") * utf8byte -patterns.utf8three = R("\224\239") * utf8byte * utf8byte -patterns.utf8four = R("\240\244") * utf8byte * utf8byte * utf8byte +patterns.utf8two = R("\194\223") * utf8next +patterns.utf8three = R("\224\239") * utf8next * utf8next +patterns.utf8four = R("\240\244") * utf8next * utf8next * utf8next +patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') + +local utf8char = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four +local validutf8char = utf8char^0 * P(-1) * Cc(true) + Cc(false) + +patterns.utf8 = utf8char +patterns.utf8char = utf8char +patterns.validutf8 = validutf8char +patterns.validutf8char = validutf8char patterns.digit = digit patterns.sign = sign @@ -310,17 +242,28 @@ patterns.nonspace = 1 - patterns.space patterns.nonspacer = 1 - patterns.spacer patterns.whitespace = patterns.eol + patterns.spacer patterns.nonwhitespace = 1 - patterns.whitespace -patterns.utf8 = patterns.utf8one + patterns.utf8two + patterns.utf8three + patterns.utf8four -patterns.utfbom = P('\000\000\254\255') + P('\255\254\000\000') + P('\255\254') + P('\254\255') + P('\239\187\191') -patterns.validutf8 = patterns.utf8^0 * P(-1) * Cc(true) + Cc(false) patterns.comma = P(",") patterns.commaspacer = P(",") * patterns.spacer^0 patterns.period = P(".") - -patterns.undouble = P('"')/"" * (1-P('"'))^0 * P('"')/"" -patterns.unsingle = P("'")/"" * (1-P("'"))^0 * P("'")/"" +patterns.escaped = escaped +patterns.squote = squote +patterns.dquote = dquote +patterns.undouble = (dquote/"") * ((escaped + (1-dquote))^0) * (dquote/"") +patterns.unsingle = (squote/"") * ((escaped + (1-squote))^0) * (squote/"") +patterns.unquoted = patterns.undouble + patterns.unsingle -- more often undouble patterns.unspacer = ((patterns.spacer^1)/"")^0 +local unquoted = Cs(patterns.unquoted * P(-1)) -- not C + +function string.unquoted(str) + return match(unquoted,str) or str +end + +--~ print(string.unquoted("test")) +--~ print(string.unquoted([["t\"est"]])) +--~ print(string.unquoted([["t\"est"x]])) +--~ print(string.unquoted("\'test\'")) + function lpeg.anywhere(pattern) --slightly adapted from website return P { P(pattern) + 1 * V(1) } -- why so complex? end @@ -336,8 +279,8 @@ local content = (empty + nonempty)^1 local capture = Ct(content^0) -function string:splitlines() - return match(capture,self) +function string.splitlines(str) + return match(capture,str) end patterns.textline = content @@ -353,12 +296,12 @@ 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 other, any = C((1 - separator)^0), P(1) + local any = P(1) splitter = other * (separator * C(any^0) + "") -- ? splitters_s[separator] = splitter else - local other = C((1 - separator)^0) splitter = other * (separator * other)^0 splitters_m[separator] = splitter end @@ -379,16 +322,16 @@ function lpeg.split(separator,str) return match(c,str) end -function string:split(separator) +function string.split(str,separator) local c = cache[separator] if not c then c = Ct(splitat(separator)) cache[separator] = c end - return match(c,self) + return match(c,str) end -lpeg.splitters = cache +--~ lpeg.splitters = cache -- no longer public local cache = { } @@ -396,22 +339,22 @@ function lpeg.checkedsplit(separator,str) local c = cache[separator] if not c then separator = P(separator) - local other = C((1 - separator)^0) + local other = C((1 - separator)^1) c = Ct(separator^0 * other * (separator^1 * other)^0) cache[separator] = c end return match(c,str) end -function string:checkedsplit(separator) +function string.checkedsplit(str,separator) local c = cache[separator] if not c then separator = P(separator) - local other = C((1 - separator)^0) + local other = C((1 - separator)^1) c = Ct(separator^0 * other * (separator^1 * other)^0) cache[separator] = c end - return match(c,self) + return match(c,str) end --~ function lpeg.append(list,pp) @@ -434,7 +377,9 @@ local function f2(s) local c1, c2 = f1(s,1,2) return c1 * 64 + c2 local function f3(s) local c1, c2, c3 = f1(s,1,3) return (c1 * 64 + c2) * 64 + c3 - 925824 end local function f4(s) local c1, c2, c3, c4 = f1(s,1,4) return ((c1 * 64 + c2) * 64 + c3) * 64 + c4 - 63447168 end -patterns.utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 +local utf8byte = patterns.utf8one/f1 + patterns.utf8two/f2 + patterns.utf8three/f3 + patterns.utf8four/f4 + +patterns.utf8byte = utf8byte --~ local str = " a b c d " @@ -473,22 +418,25 @@ function lpeg.keeper(str) end end +-- Just for fun I looked at the used bytecode and +-- p = (p and p + pp) or pp gets one more (testset). + function lpeg.replacer(t) if #t > 0 then local p for i=1,#t do local ti= t[i] local pp = P(ti[1]) / ti[2] - p = (p and p + pp ) or pp + if p then + p = p + pp + else + p = pp + end end return Cs((p + 1)^0) end end ---~ print(utf.check("")) ---~ print(utf.check("abcde")) ---~ print(utf.check("abcde\255\123")) - local splitters_f, splitters_s = { }, { } function lpeg.firstofsplit(separator) -- always return value @@ -512,6 +460,7 @@ function lpeg.secondofsplit(separator) -- nil if not split end function lpeg.balancer(left,right) + left, right = P(left), P(right) return P { left * ((1 - left - right) + V(1))^0 * right } end @@ -524,6 +473,179 @@ end --~ print(7,match(lpeg.secondofsplit(":"),"bc")) --~ print(9,match(lpeg.secondofsplit(":","123"),"bc")) +--~ -- slower: +--~ +--~ function lpeg.counter(pattern) +--~ local n, pattern = 0, (lpeg.P(pattern)/function() n = n + 1 end + lpeg.P(1))^0 +--~ return function(str) n = 0 ; lpegmatch(pattern,str) ; return n end +--~ end + +local nany = utf8char/"" + +function lpeg.counter(pattern) + pattern = Cs((P(pattern)/" " + nany)^0) + return function(str) + return #match(pattern,str) + end +end + +if utfgmatch then + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local n = 0 + for _ in utfgmatch(str,what) do + n = n + 1 + end + return n + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +else + + local cache = { } + + function lpeg.count(str,what) -- replaces string.count + if type(what) == "string" then + local p = cache[what] + if not p then + p = Cs((P(what)/" " + nany)^0) + cache[p] = p + end + return #match(p,str) + else -- 4 times slower but still faster than / function + return #match(Cs((P(what)/" " + nany)^0),str) + end + end + +end + +local patterns_escapes = { -- also defines in l-string + ["%"] = "%%", + ["."] = "%.", + ["+"] = "%+", ["-"] = "%-", ["*"] = "%*", + ["["] = "%[", ["]"] = "%]", + ["("] = "%)", [")"] = "%)", + -- ["{"] = "%{", ["}"] = "%}" + -- ["^"] = "%^", ["$"] = "%$", +} + +local simple_escapes = { -- also defines in l-string + ["-"] = "%-", + ["."] = "%.", + ["?"] = ".", + ["*"] = ".*", +} + +local p = Cs((S("-.+*%()[]") / patterns_escapes + P(1))^0) +local s = Cs((S("-.+*%()[]") / simple_escapes + P(1))^0) + +function string.escapedpattern(str,simple) + if simple then + return match(s,str) + else + return match(p,str) + end +end + +-- utf extensies + +lpeg.UP = lpeg.P + +if utfcharacters then + + function lpeg.US(str) + local p + for uc in utfcharacters(str) do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + + +elseif utfgmatch then + + function lpeg.US(str) + local p + for uc in utfgmatch(str,".") do + if p then + p = p + P(uc) + else + p = P(uc) + end + end + return p + end + +else + + function lpeg.US(str) + local p + local f = function(uc) + if p then + p = p + P(uc) + else + p = P(uc) + end + end + match((utf8char/f)^0,str) + return p + end + +end + +local range = Cs(utf8byte) * (Cs(utf8byte) + Cc(false)) + +local utfchar = unicode and unicode.utf8 and unicode.utf8.char + +function lpeg.UR(str,more) + local first, last + if type(str) == "number" then + first = str + last = more or first + else + first, last = match(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 -- a somewhat arbitrary criterium + local p + for i=first,last do + if p then + p = p + P(utfchar(i)) + else + p = P(utfchar(i)) + end + end + return p -- nil when invalid range + else + local f = function(b) + return b >= first and b <= last + end + return utf8byte / f -- nil when invalid range + end +end + +--~ lpeg.print(lpeg.R("ab","cd","gh")) +--~ lpeg.print(lpeg.P("a","b","c")) +--~ lpeg.print(lpeg.S("a","b","c")) + +--~ print(lpeg.count("äáàa",lpeg.P("á") + lpeg.P("à"))) +--~ print(lpeg.count("äáàa",lpeg.UP("áà"))) +--~ print(lpeg.count("äáàa",lpeg.US("àá"))) +--~ print(lpeg.count("äáàa",lpeg.UR("aá"))) +--~ print(lpeg.count("äáàa",lpeg.UR("àá"))) +--~ print(lpeg.count("äáàa",lpeg.UR(0x0000,0xFFFF))) + end -- closure do -- begin closure to overcome local limits and interference @@ -541,8 +663,12 @@ local type, tonumber = type, tonumber boolean = boolean or { } local boolean = boolean +-- function boolean.tonumber(b) +-- return b and 1 or 0 -- test and test and return or return +-- end + function boolean.tonumber(b) - if b then return 1 else return 0 end + if b then return 1 else return 0 end -- test and return or return end function toboolean(str,tolerant) @@ -566,6 +692,8 @@ function toboolean(str,tolerant) end end +string.toboolean = toboolean + function string.is_boolean(str,default) if type(str) == "string" then if str == "true" or str == "yes" or str == "on" or str == "t" then @@ -577,14 +705,6 @@ function string.is_boolean(str,default) return default end -function boolean.alwaystrue() - return true -end - -function boolean.falsetrue() - return false -end - end -- closure do -- begin closure to overcome local limits and interference @@ -698,24 +818,26 @@ end -- extra functions, some might go (when not used) function table.strip(tab) - local lst = { } + local lst, l = { }, 0 for i=1,#tab do local s = gsub(tab[i],"^%s*(.-)%s*$","%1") if s == "" then -- skip this one else - lst[#lst+1] = s + l = l + 1 + lst[l] = s end end return lst end function table.keys(t) - local k = { } + local keys, k = { }, 0 for key, _ in next, t do - k[#k+1] = key + k = k + 1 + keys[k] = key end - return k + return keys end local function compare(a,b) @@ -728,9 +850,10 @@ local function compare(a,b) end local function sortedkeys(tab) - local srt, kind = { }, 0 -- 0=unknown 1=string, 2=number 3=mixed + local srt, kind, s = { }, 0, 0 -- 0=unknown 1=string, 2=number 3=mixed for key,_ in next, tab do - srt[#srt+1] = key + s = s + 1 + srt[s] = key if kind == 3 then -- no further check else @@ -753,10 +876,11 @@ local function sortedkeys(tab) end local function sortedhashkeys(tab) -- fast one - local srt = { } + local srt, s = { }, 0 for key,_ in next, tab do if key then - srt[#srt+1] = key + s= s + 1 + srt[s] = key end end sort(srt) @@ -770,8 +894,7 @@ local function nothing() end local function sortedhash(t) if t then - local s = sortedhashkeys(t) -- maybe just sortedkeys - local n = 0 + local n, s = 0, sortedkeys(t) -- the robust one local function kv(s) n = n + 1 local k = s[n] @@ -787,20 +910,30 @@ table.sortedhash = sortedhash table.sortedpairs = sortedhash function table.append(t, list) - for _,v in next, list do - insert(t,v) + local n = #t + for i=1,#list do + n = n + 1 + t[n] = list[i] end + return t end function table.prepend(t, list) - for k,v in next, list do - insert(t,k,v) + 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, ...) -- first one is target t = t or { } - local lst = {...} + local lst = { ... } for i=1,#lst do for k, v in next, lst[i] do t[k] = v @@ -810,7 +943,7 @@ function table.merge(t, ...) -- first one is target end function table.merged(...) - local tmp, lst = { }, {...} + local tmp, lst = { }, { ... } for i=1,#lst do for k, v in next, lst[i] do tmp[k] = v @@ -820,22 +953,24 @@ function table.merged(...) end function table.imerge(t, ...) - local lst = {...} + local lst, nt = { ... }, #t for i=1,#lst do local nst = lst[i] for j=1,#nst do - t[#t+1] = nst[j] + nt = nt + 1 + t[nt] = nst[j] end end return t end function table.imerged(...) - local tmp, lst = { }, {...} + local tmp, ntmp, lst = { }, 0, {...} for i=1,#lst do local nst = lst[i] for j=1,#nst do - tmp[#tmp+1] = nst[j] + ntmp = ntmp + 1 + tmp[ntmp] = nst[j] end end return tmp @@ -911,11 +1046,14 @@ function table.tohash(t,value) end function table.fromhash(t) - local h = { } + local hsh, h = { }, 0 for k, v in next, t do -- no ipairs here - if v then h[#h+1] = k end + if v then + h = h + 1 + hsh[h] = k + end end - return h + return hsh end table.serialize_functions = true @@ -936,20 +1074,23 @@ local function simple_table(t) n = n + 1 end if n == #t then - local tt = { } + local tt, nt = { }, 0 for i=1,#t do local v = t[i] local tv = type(v) if tv == "number" then + nt = nt + 1 if hexify then - tt[#tt+1] = format("0x%04X",v) + tt[nt] = format("0x%04X",v) else - tt[#tt+1] = tostring(v) -- tostring not needed + tt[nt] = tostring(v) -- tostring not needed end elseif tv == "boolean" then - tt[#tt+1] = tostring(v) + nt = nt + 1 + tt[nt] = tostring(v) elseif tv == "string" then - tt[#tt+1] = format("%q",v) + nt = nt + 1 + tt[nt] = format("%q",v) else tt = nil break @@ -1255,10 +1396,11 @@ end --~ 'return' : return { } --~ number : [number] = { } -function table.serialize(root,name,reduce,noquotes,hexify) - local t = { } +function table.serialize(root,name,reduce,noquotes,hexify) -- can be faster if flush == false and t as argument + local t, n = { }, 0 local function flush(s) - t[#t+1] = s + n = n + 1 + t[n] = s end serialize(root,name,flush,reduce,noquotes,hexify) return concat(t,"\n") @@ -1284,12 +1426,13 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) if f then local maxtab = table.tofile_maxtab if maxtab > 1 then - local t = { } + local t, n = { }, 0 local function flush(s) - t[#t+1] = s - if #t > maxtab then + n = n + 1 + t[n] = s + if n > maxtab then f:write(concat(t,"\n"),"\n") -- hm, write(sometable) should be nice - t = { } + t, n = { }, 0 -- we could recycle t if needed end end serialize(root,name,flush,reduce,noquotes,hexify) @@ -1305,52 +1448,72 @@ function table.tofile(filename,root,name,reduce,noquotes,hexify) end end -local function flatten(t,f,complete) -- is this used? meybe a variant with next, ... - for i=1,#t do - local v = t[i] - if type(v) == "table" then - if complete or type(v[1]) == "table" then - flatten(v,f,complete) +local function flattened(t,f,depth) + if f == nil then + f = { } + depth = 0xFFFF + elseif tonumber(f) then + -- assume then only two arguments are given + 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 + f[k] = v end + end + end + local n = #f + for k=1,#t do + local v = t[k] + if depth > 0 and type(v) == "table" then + flattened(v,f,depth-1) + n = #f else - f[#f+1] = v + n = n + 1 + f[n] = v end end -end - -function table.flatten(t) - local f = { } - flatten(t,f,true) return f end -function table.unnest(t) -- bad name - local f = { } - flatten(t,f,false) - return f -end - -table.flattenonelevel = table.unnest - --- a better one: +table.flattened = flattened -local function flattened(t,f) - if not f then +local function unnest(t,f) -- only used in mk, for old times sake + if not f then -- and only relevant for token lists f = { } end - for k, v in next, t do + for i=1,#t do + local v = t[i] if type(v) == "table" then - flattened(v,f) + if type(v[1]) == "table" then + unnest(v,f) + else + f[#f+1] = v + end else - f[k] = v + f[#f+1] = v end end return f end -table.flattened = flattened +function table.unnest(t) -- bad name + return unnest(t) +end + +--~ function table.unnest(t) -- for old times sake, undocumented (only in mk) +--~ return flattened(t,1) +--~ end + +--~ function table.are_equal(a,b) +--~ return table.serialize(a) == table.serialize(b) +--~ end local function are_equal(a,b,n,m) -- indexed if a and b and #a == #b then @@ -1376,7 +1539,7 @@ end local function identical(a,b) -- assumes same structure for ka, va in next, a do - local vb = b[k] + local vb = b[ka] if va == vb then -- same elseif type(va) == "table" and type(vb) == "table" then @@ -1390,8 +1553,8 @@ local function identical(a,b) -- assumes same structure return true end -table.are_equal = are_equal table.identical = identical +table.are_equal = are_equal -- maybe also make a combined one @@ -1417,14 +1580,14 @@ function table.contains(t, v) end function table.count(t) - local n, e = 0, next(t) - while e do - n, e = n + 1, next(t,e) + local n = 0 + for k, v in next, t do + n = n + 1 end return n end -function table.swapped(t,s) +function table.swapped(t,s) -- hash local n = { } if s then --~ for i=1,#s do @@ -1446,55 +1609,34 @@ function table.swapped(t,s) return n end ---~ function table.are_equal(a,b) ---~ return table.serialize(a) == table.serialize(b) ---~ end - -function table.clone(t,p) -- t is optional or nil or table - if not p then - t, p = { }, t or { } - elseif not t then - t = { } - end - setmetatable(t, { __index = function(_,key) return p[key] end }) -- why not __index = p ? - return t -end - -function table.hexed(t,seperator) - local tt = { } - for i=1,#t do tt[i] = format("0x%04X",t[i]) end - return concat(tt,seperator or " ") -end - -function table.swaphash(h) -- needs another name - local r = { } - for k,v in next, h do - r[v] = lower(gsub(k," ","")) - end - return r -end - -function table.reverse(t) - local tt = { } - if #t > 0 then - for i=#t,1,-1 do - tt[#tt+1] = t[i] +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 - return tt end function table.sequenced(t,sep,simple) -- hash only - local s = { } + local s, n = { }, 0 for k, v in sortedhash(t) do if simple then if v == true then - s[#s+1] = k + n = n + 1 + s[n] = k elseif v and v~= "" then - s[#s+1] = k .. "=" .. tostring(v) + n = n + 1 + s[n] = k .. "=" .. tostring(v) end else - s[#s+1] = k .. "=" .. tostring(v) + n = n + 1 + s[n] = k .. "=" .. tostring(v) end end return concat(s, sep or " | ") @@ -1504,7 +1646,7 @@ function table.print(...) table.tohandle(print,...) end --- -- -- obsolete but we keep them for a while and will comment them later -- -- -- +-- -- -- obsolete but we keep them for a while and might comment them later -- -- -- -- roughly: copy-loop : unpack : sub == 0.9 : 0.4 : 0.45 (so in critical apps, use unpack) @@ -1519,14 +1661,7 @@ function table.is_empty(t) end function table.has_one_entry(t) - local n = next(t) - return n and not next(t,n) -end - -function table.replace(a,b) - for k,v in next, b do - a[k] = v - end + return t and not next(t,next(t)) end end -- closure @@ -1694,7 +1829,7 @@ end file.isreadable = file.is_readable -- depricated file.iswritable = file.is_writable -- depricated --- todo: lpeg +-- todo: lpeg \\ / .. does not save much local checkedsplit = string.checkedsplit @@ -1709,7 +1844,7 @@ end -- we can hash them weakly ---~ function file.old_collapse_path(str) -- fails on b.c/.. +--~ function file.collapsepath(str) -- fails on b.c/.. --~ str = gsub(str,"\\","/") --~ if find(str,"/") then --~ str = gsub(str,"^%./",(gsub(getcurrentdir(),"\\","/")) .. "/") -- ./xx in qualified @@ -1733,7 +1868,7 @@ end --~ Of course there are some optimizations too. Finally we had to deal with --~ windows drive prefixes and thinsg like sys://. -function file.collapse_path(str,anchor) +function file.collapsepath(str,anchor) if anchor and not find(str,"^/") and not find(str,"^%a:") then str = getcurrentdir() .. "/" .. str end @@ -1788,8 +1923,10 @@ function file.collapse_path(str,anchor) end end +file.collapse_path = file.collapsepath + --~ local function test(str) ---~ print(string.format("%-20s %-15s %-15s",str,file.collapse_path(str),file.collapse_path(str,true))) +--~ print(string.format("%-20s %-15s %-15s",str,file.collapsepath(str),file.collapsepath(str,true))) --~ end --~ test("a/b.c/d") test("b.c/d") test("b.c/..") --~ test("/") test("c:/..") test("sys://..") @@ -2847,12 +2984,13 @@ function injections.handler(head,where,keep) trace(head) end -- in the future variant we will not copy items but refs to tables - local done, ky, rl, valid, cx, wx, mk = false, { }, { }, { }, { }, { }, { } + local done, ky, rl, valid, cx, wx, mk, nofvalid = false, { }, { }, { }, { }, { }, { }, 0 if has_kerns then -- move outside loop local nf, tm = nil, nil for n in traverse_id(glyph_code,head) do if n.subtype < 256 then - valid[#valid+1] = n + nofvalid = nofvalid + 1 + valid[nofvalid] = n if n.font ~= nf then nf = n.font tm = fontdata[nf].marks @@ -2880,7 +3018,8 @@ function injections.handler(head,where,keep) local nf, tm = nil, nil for n in traverse_id(glyph_code,head) do if n.subtype < 256 then - valid[#valid+1] = n + nofvalid = nofvalid + 1 + valid[nofvalid] = n if n.font ~= nf then nf = n.font tm = fontdata[nf].marks @@ -2889,7 +3028,7 @@ function injections.handler(head,where,keep) end end end - if #valid > 0 then + if nofvalid > 0 then -- we can assume done == true because we have cursives and marks local cx = { } if has_kerns and next(ky) then @@ -2902,7 +3041,7 @@ function injections.handler(head,where,keep) local p_cursbase, p = nil, nil -- since we need valid[n+1] we can also use a "while true do" local t, d, maxt = { }, { }, 0 - for i=1,#valid do -- valid == glyphs + for i=1,nofvalid do -- valid == glyphs local n = valid[i] if not mk[n] then local n_cursbase = has_attribute(n,cursbase) @@ -2966,7 +3105,7 @@ function injections.handler(head,where,keep) end end if has_marks then - for i=1,#valid do + for i=1,nofvalid do local p = valid[i] local p_markbase = has_attribute(p,markbase) if p_markbase then @@ -3795,13 +3934,13 @@ function tfm.scale(tfmtable, scaledpoints, relativeid) local ivc = vc[i] local key = ivc[1] if key == "right" then - tt[#tt+1] = { key, ivc[2]*hdelta } + tt[i] = { key, ivc[2]*hdelta } elseif key == "down" then - tt[#tt+1] = { key, ivc[2]*vdelta } + tt[i] = { key, ivc[2]*vdelta } elseif key == "rule" then - tt[#tt+1] = { key, ivc[2]*vdelta, ivc[3]*hdelta } + tt[i] = { key, ivc[2]*vdelta, ivc[3]*hdelta } else -- not comment - tt[#tt+1] = ivc -- shared since in cache and untouched + tt[i] = ivc -- shared since in cache and untouched end end chr.commands = tt @@ -4783,9 +4922,18 @@ local baselines = allocate { ['romn'] = 'Roman baseline' } -local verbosescripts = allocate(table.swaphash(scripts )) -local verboselanguages = allocate(table.swaphash(languages)) -local verbosefeatures = allocate(table.swaphash(features )) + +local function swap(h) -- can be a tables.swap when we get a better name + local r = { } + for k, v in next, h do + r[v] = lower(gsub(k," ","")) + end + return r +end + +local verbosescripts = allocate(swap(scripts )) +local verboselanguages = allocate(swap(languages)) +local verbosefeatures = allocate(swap(features )) tables.scripts = scripts tables.languages = languages @@ -4806,12 +4954,6 @@ for k, v in next, verbosefeatures do verbosefeatures[lower(k)] = v end --- can be sped up by local tables - -function tables.totag(id) -- not used - return format("%4s",lower(id)) -end - local function resolve(tab,id) if tab and id then id = lower(id) @@ -5325,21 +5467,23 @@ fonts.map.addtounicode = function(data,filename) originals[index], tounicode[index], ns = unicode, tounicode16(unicode), ns + 1 end else - local t = { } + local t, n = { }, 0 for l=1,nplit do local base = split[l] local u = unicodes[base] or (aglmap and aglmap[base]) if not u then break elseif type(u) == "table" then - t[#t+1] = u[1] + n = n + 1 + t[n] = u[1] else - t[#t+1] = u + n = n + 1 + t[n] = u end end - if #t == 0 then -- done then + if n == 0 then -- done then -- nothing - elseif #t == 1 then + elseif n == 1 then originals[index], tounicode[index], nl, unicode = t[1], tounicode16(t[1]), nl + 1, true else originals[index], tounicode[index], nl, unicode = t, tounicode16sequence(t), nl + 1, true @@ -5499,13 +5643,13 @@ if not modules then modules = { } end modules ['font-otf'] = { local utf = unicode.utf8 -local concat, utfbyte = table.concat, utf.byte +local utfbyte = utf.byte local format, gmatch, gsub, find, match, lower, strip = string.format, string.gmatch, string.gsub, string.find, string.match, string.lower, string.strip local type, next, tonumber, tostring = type, next, tonumber, tostring local abs = math.abs local getn = table.getn local lpegmatch = lpeg.match -local reverse = table.reverse +local reversed, concat = table.reversed, table.concat local ioflush = io.flush local allocate = utilities.storage.allocate @@ -5543,7 +5687,7 @@ local definers = fonts.definers otf.glists = { "gsub", "gpos" } -otf.version = 2.705 -- beware: also sync font-mis.lua +otf.version = 2.706 -- beware: also sync font-mis.lua otf.cache = containers.define("fonts", "otf", otf.version, true) local loadmethod = "table" -- table, mixed, sparse @@ -6454,6 +6598,7 @@ actions["prepare unicodes"] = function(data,filename,raw) local luatex = data.luatex local indices, unicodes, multiples, internals = { }, { }, { }, { } local mapmap = data.map or raw.map + local mapenc = nil -- will go away if not mapmap then report_otf("no map in %s",filename) mapmap = { } @@ -6463,6 +6608,7 @@ actions["prepare unicodes"] = function(data,filename,raw) mapmap = { } data.map.map = mapmap else + mapenc = mapmap.enc -- will go away mapmap = mapmap.map end local criterium = fonts.privateoffset @@ -6486,41 +6632,53 @@ actions["prepare unicodes"] = function(data,filename,raw) indices[unicode] = index unicodes[name] = unicode end + -- maybe deal with altuni here in the future but first we need + -- to encounter a proper font that sets them else -- message that something is wrong end end end -- beware: the indices table is used to initialize the tfm table - for unicode, index in next, mapmap do - if not internals[index] then - local name = glyphs[index].name - if name then - local un = unicodes[name] - if not un then - unicodes[name] = unicode -- or 0 - elseif type(un) == "number" then -- tonumber(un) - if un ~= unicode then - multiples[#multiples+1] = name - unicodes[name] = { un, unicode } - indices[unicode] = index - end - else - local ok = false - for u=1,#un do - if un[u] == unicode then - ok = true - break + local encname = lower(data.enc_name or (mapenc and mapenc[1] and mapenc[1].enc_name) or "") -- mapenc will go away + -- will become: local encname = lower(data.enc_name or "") + if encname == "" or encname == "unicodebmp" or encname == "unicodefull" then -- maybe find(encname,"unicode") + if trace_loading then + report_otf("using extra unicode map") + end + -- ok -- we can also consider using the altuni + for unicode, index in next, mapmap do + if not internals[index] then + local name = glyphs[index].name + if name then + local un = unicodes[name] + if not un then + unicodes[name] = unicode -- or 0 + elseif type(un) == "number" then -- tonumber(un) + if un ~= unicode then + multiples[#multiples+1] = name + unicodes[name] = { un, unicode } + indices[unicode] = index + end + else + local ok = false + for u=1,#un do + if un[u] == unicode then + ok = true + break + end + end + if not ok then + multiples[#multiples+1] = name + un[#un+1] = unicode + indices[unicode] = index end - end - if not ok then - multiples[#multiples+1] = name - un[#un+1] = unicode - indices[unicode] = index end end end end + else + report_otf("warning: non unicode map '%s', only using glyph unicode data",encname or "whatever") end if trace_loading then if #multiples > 0 then @@ -6549,7 +6707,7 @@ actions["reorganize lookups"] = function(data,filename,raw) for _, vv in next, v.rules do local c = vv.coverage if c and c.before then - c.before = reverse(c.before) + c.before = reversed(c.before) end end end @@ -7068,8 +7226,8 @@ local function copytotfm(data,cache_id) -- we can save a copy when we reorder th local glyphs, pfminfo, metadata = data.glyphs or { }, data.pfminfo or { }, data.metadata or { } local luatex = data.luatex local unicodes = luatex.unicodes -- names to unicodes - local indices = luatex.indices local mode = data.mode or "base" - + local indices = luatex.indices + local mode = data.mode or "base" local characters, parameters, math_parameters, descriptions = { }, { }, { }, { } local designsize = metadata.designsize or metadata.design_size or 100 if designsize == 0 then @@ -8099,7 +8257,7 @@ local zwj = 0x200D local wildcard = "*" local default = "dflt" -local split_at_space = lpeg.splitters[" "] or lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway +local split_at_space = lpeg.Ct(lpeg.splitat(" ")) -- no trailing or multiple spaces anyway local nodecodes = nodes.nodecodes local whatcodes = nodes.whatcodes @@ -8205,9 +8363,9 @@ local function gref(n) local num, nam = { }, { } for i=1,#n do local ni = n[i] - num[#num+1] = format("U+%04X",ni) - local dni = descriptions[ni] - nam[#num] = (dni and dni.name) or "?" + local di = descriptions[ni] + num[i] = format("U+%04X",ni) + nam[i] = di and di.name or "?" end return format("%s (%s)",concat(num," "), concat(nam," ")) end @@ -10177,13 +10335,14 @@ otf.features.prepare = { } local function split(replacement,original,cache,unicodes) -- we can cache this too, but not the same (although unicode is a unique enough hash) - local o, t, n = { }, { }, 0 + local o, t, n, no = { }, { }, 0, 0 for s in gmatch(original,"[^ ]+") do local us = unicodes[s] + no = no + 1 if type(us) == "number" then -- tonumber(us) - o[#o+1] = us + o[no] = us else - o[#o+1] = us[1] + o[no] = us[1] end end for s in gmatch(replacement,"[^ ]+") do @@ -10200,9 +10359,11 @@ end local function uncover(covers,result,cache,unicodes) -- lpeg hardly faster (.005 sec on mk) + local nofresults = #result for n=1,#covers do local c = covers[n] local cc = cache[c] + nofresults = nofresults + 1 if not cc then local t = { } for s in gmatch(c,"[^ ]+") do @@ -10216,9 +10377,9 @@ local function uncover(covers,result,cache,unicodes) end end cache[c] = t - result[#result+1] = t + result[nofresults] = t else - result[#result+1] = cc + result[nofresults] = cc end end end @@ -10260,16 +10421,17 @@ local function prepare_lookups(tfmdata) --~ end end, multiple = function (p,lookup,glyph,unicode) - local old, new = unicode, { } + local old, new, nnew = unicode, { }, 0 local m = multiple[lookup] if not m then m = { } multiple[lookup] = m end m[old] = new for pc in gmatch(p[2],"[^ ]+") do local upc = unicodes[pc] + nnew = nnew + 1 if type(upc) == "number" then - new[#new+1] = upc + new[nnew] = upc else - new[#new+1] = upc[1] + new[nnew] = upc[1] end end --~ if trace_lookups then @@ -10277,16 +10439,17 @@ local function prepare_lookups(tfmdata) --~ end end, alternate = function(p,lookup,glyph,unicode) - local old, new = unicode, { } + local old, new, nnew = unicode, { }, 0 local a = alternate[lookup] if not a then a = { } alternate[lookup] = a end a[old] = new for pc in gmatch(p[2],"[^ ]+") do local upc = unicodes[pc] + nnew = nnew + 1 if type(upc) == "number" then - new[#new+1] = upc + new[nnew] = upc else - new[#new+1] = upc[1] + new[nnew] = upc[1] end end --~ if trace_lookups then @@ -10480,7 +10643,7 @@ local function prepare_contextchains(tfmdata) contexts = { } contextchain[lookupname] = contexts end - local t = { } + local t, nt = { }, 0 for nofrules=1,#rules do -- does #rules>1 happen often? local rule = rules[nofrules] local coverage = rule.coverage @@ -10496,7 +10659,8 @@ local function prepare_contextchains(tfmdata) uncover(after,sequence,cache,unicodes) end if sequence[1] then - t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } for unic, _ in next, sequence[start] do local cu = contexts[unic] if not cu then @@ -10516,7 +10680,7 @@ local function prepare_contextchains(tfmdata) contexts = { } reversecontextchain[lookupname] = contexts end - local t = { } + local t, nt = { }, 0 for nofrules=1,#rules do local rule = rules[nofrules] local reversecoverage = rule.reversecoverage @@ -10536,7 +10700,8 @@ local function prepare_contextchains(tfmdata) end if sequence[1] then -- this is different from normal coverage, we assume only replacements - t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups, replacements } for unic, _ in next, sequence[start] do local cu = contexts[unic] if not cu then @@ -10556,7 +10721,7 @@ local function prepare_contextchains(tfmdata) contexts = { } contextchain[lookupname] = contexts end - local t = { } + local t, nt = { }, 0 for nofrules=1,#rules do -- nearly the same as coverage so we could as well rename it local rule = rules[nofrules] @@ -10576,7 +10741,8 @@ local function prepare_contextchains(tfmdata) uncover(back,sequence,cache,unicodes) end if sequence[1] then - t[#t+1] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } + nt = nt + 1 + t[nt] = { nofrules, lookuptype, sequence, start, stop, rule.lookups } for unic, _ in next, sequence[start] do local cu = contexts[unic] if not cu then @@ -15035,14 +15201,15 @@ local sortedhashkeys = table.sortedhashkeys function tfm.hashfeatures(specification) local features = specification.features if features then - local t = { } + local t, tn = { }, 0 local normal = features.normal if normal and next(normal) then local f = sortedhashkeys(normal) for i=1,#f do local v = f[i] if v ~= "number" and v ~= "features" then -- i need to figure this out, features - t[#t+1] = v .. '=' .. tostring(normal[v]) + tn = tn + 1 + t[tn] = v .. '=' .. tostring(normal[v]) end end end @@ -15051,13 +15218,15 @@ function tfm.hashfeatures(specification) local f = sortedhashkeys(vtf) for i=1,#f do local v = f[i] - t[#t+1] = v .. '=' .. tostring(vtf[v]) + tn = tn + 1 + t[tn] = v .. '=' .. tostring(vtf[v]) end end ---~ if specification.mathsize then ---~ t[#t+1] = "mathsize=" .. specification.mathsize ---~ end - if #t > 0 then + --~ if specification.mathsize then + --~ tn = tn + 1 + --~ t[tn] = "mathsize=" .. specification.mathsize + --~ end + if tn > 0 then return concat(t,"+") end end @@ -15642,7 +15811,7 @@ local function colonized(specification) -- xetex mode list = { } lpegmatch(pattern,specification.specification) -- for k, v in next, list do - -- list[k] = v:is_boolean() + -- list[k] = is_boolean(v) -- if type(list[a]) == "nil" then -- list[k] = v -- end |