diff options
author | Philipp Gesang <phg42.2a@gmail.com> | 2014-07-26 12:58:22 +0200 |
---|---|---|
committer | Philipp Gesang <phg42.2a@gmail.com> | 2014-07-26 12:58:22 +0200 |
commit | 8f36023f13e4c443ca697a491d1919a6c11a3ead (patch) | |
tree | c19a1fef71d8265f41ee73b0ebe551dadbe645d1 | |
parent | cc7cbd37b22131aa81a58be88da43ad15ea18436 (diff) | |
parent | 48944f8f4c0ca1f3f7d59ce911a689a7777197ee (diff) | |
download | lualibs-8f36023f13e4c443ca697a491d1919a6c11a3ead.tar.gz |
Merge pull request #25 from phi-gamma/master
prepare release
-rw-r--r-- | Makefile | 7 | ||||
-rw-r--r-- | lualibs-dir.lua | 308 | ||||
-rw-r--r-- | lualibs-file.lua | 17 | ||||
-rw-r--r-- | lualibs-lpeg.lua | 114 | ||||
-rw-r--r-- | lualibs-table.lua | 105 | ||||
-rw-r--r-- | lualibs-unicode.lua | 32 | ||||
-rw-r--r-- | lualibs-util-dim.lua | 95 | ||||
-rw-r--r-- | lualibs-util-prs.lua | 30 | ||||
-rw-r--r-- | lualibs-util-str.lua | 41 | ||||
-rw-r--r-- | whatsnew.lua | 1 |
10 files changed, 529 insertions, 221 deletions
@@ -34,6 +34,7 @@ DISTDIR = ./lualibs TEXMFROOT = ./texmf CTAN_ZIP = $(NAME).zip +CTAN_ZIPSIG = $(CTAN_ZIP).asc TDS_ZIP = $(NAME).tds.zip ZIPS = $(CTAN_ZIP) $(TDS_ZIP) @@ -46,6 +47,7 @@ all: $(GENERATED) $(DOC_TEX) doc: $(COMPILED) unpack: $(UNPACKED) ctan: check $(CTAN_ZIP) +sign: $(CTAN_ZIPSIG) tds: $(TDS_ZIP) world: all ctan @@ -83,6 +85,11 @@ $(CTAN_ZIP): $(ALL_FILES) $(TDS_ZIP) $(make-ctandir) @zip -r -9 $@ $(DISTDIR) $(TDS_ZIP) >/dev/null +$(CTAN_ZIPSIG): $(CTAN_ZIP) + @echo "Signing package $(CTAN_ZIP)" + @$(RM) -- $@ + @gpg --batch --armor --detach-sign "$(CTAN_ZIP)" + define run-install @mkdir -p $(RUNDIR) && cp $(RUNFILES) $(RUNDIR) @mkdir -p $(DOCDIR) && cp $(DOCFILES) $(DOCDIR) diff --git a/lualibs-dir.lua b/lualibs-dir.lua index 2572120..bcf28d0 100644 --- a/lualibs-dir.lua +++ b/lualibs-dir.lua @@ -9,7 +9,7 @@ if not modules then modules = { } end modules ['l-dir'] = { -- dir.expandname will be merged with cleanpath and collapsepath local type, select = type, select -local find, gmatch, match, gsub = string.find, string.gmatch, string.match, string.gsub +local find, gmatch, match, gsub, sub = string.find, string.gmatch, string.match, string.gsub, string.sub local concat, insert, remove, unpack = table.concat, table.insert, table.remove, table.unpack local lpegmatch = lpeg.match @@ -21,8 +21,8 @@ local lfs = lfs local attributes = lfs.attributes local walkdir = lfs.dir -local isdir = lfs.isdir -local isfile = lfs.isfile +local isdir = lfs.isdir -- not robust, will be overloaded anyway +local isfile = lfs.isfile -- not robust, will be overloaded anyway local currentdir = lfs.currentdir local chdir = lfs.chdir local mkdir = lfs.mkdir @@ -31,20 +31,41 @@ local onwindows = os.type == "windows" or find(os.getenv("PATH"),";",1,true) -- in case we load outside luatex -if not isdir then - function isdir(name) - local a = attributes(name) - return a and a.mode == "directory" +if onwindows then + + -- lfs.isdir does not like trailing / + -- lfs.dir accepts trailing / + + local tricky = S("/\\") * P(-1) + + isdir = function(name) + if lpegmatch(tricky,name) then + return attributes(name,"mode") == "directory" + else + return attributes(name.."/.","mode") == "directory" + end end - lfs.isdir = isdir -end -if not isfile then - function isfile(name) - local a = attributes(name) - return a and a.mode == "file" + isfile = function(name) + return attributes(name,"mode") == "file" end + + lfs.isdir = isdir lfs.isfile = isfile + +else + + isdir = function(name) + return attributes(name,"mode") == "directory" + end + + isfile = function(name) + return attributes(name,"mode") == "file" + end + + lfs.isdir = isdir + lfs.isfile = isfile + end -- handy @@ -53,63 +74,104 @@ function dir.current() return (gsub(currentdir(),"\\","/")) end --- optimizing for no find (*) does not save time - ---~ local function globpattern(path,patt,recurse,action) -- fails in recent luatex due to some change in lfs ---~ local ok, scanner ---~ if path == "/" then ---~ ok, scanner = xpcall(function() return walkdir(path..".") end, function() end) -- kepler safe ---~ else ---~ ok, scanner = xpcall(function() return walkdir(path) end, function() end) -- kepler safe ---~ end ---~ if ok and type(scanner) == "function" then ---~ if not find(path,"/$") then path = path .. '/' end ---~ for name in scanner do ---~ local full = path .. name ---~ local mode = attributes(full,'mode') ---~ if mode == 'file' then ---~ if find(full,patt) then ---~ action(full) ---~ end ---~ elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then ---~ globpattern(full,patt,recurse,action) ---~ end ---~ end ---~ end ---~ end - -local lfsisdir = isdir - -local function isdir(path) - path = gsub(path,"[/\\]+$","") - return lfsisdir(path) -end +-- somewhat optimized -lfs.isdir = isdir +local function glob_pattern_function(path,patt,recurse,action) + if isdir(path) then + local usedpath + if path == "/" then + usedpath = "/." + elseif not find(path,"/$") then + usedpath = path .. "/." + path = path .. "/" + else + usedpath = path + end + local dirs + for name in walkdir(usedpath) do + if name ~= "." and name ~= ".." then + local full = path .. name + local mode = attributes(full,'mode') + if mode == 'file' then + if not patt or find(full,patt) then + action(full) + end + elseif recurse and mode == "directory" then + if not dirs then + dirs = { full } + else + dirs[#dirs+1] = full + end + end + end + end + if dirs then + for i=1,#dirs do + glob_pattern_function(dirs[i],patt,recurse,action) + end + end + end +end -local function globpattern(path,patt,recurse,action) - if path == "/" then - path = path .. "." - elseif not find(path,"/$") then - path = path .. '/' +local function glob_pattern_table(path,patt,recurse,result) + if not result then + result = { } end - if isdir(path) then -- lfs.isdir does not like trailing / - for name in walkdir(path) do -- lfs.dir accepts trailing / - local full = path .. name - local mode = attributes(full,'mode') - if mode == 'file' then - if find(full,patt) then - action(full) + if isdir(path) then + local usedpath + if path == "/" then + usedpath = "/." + elseif not find(path,"/$") then + usedpath = path .. "/." + path = path .. "/" + else + usedpath = path + end + local dirs + for name in walkdir(usedpath) do + if name ~= "." and name ~= ".." then + local full = path .. name + local mode = attributes(full,'mode') + if mode == 'file' then + if not patt or find(full,patt) then + result[#result+1] = full + end + elseif recurse and mode == "directory" then + if not dirs then + dirs = { full } + else + dirs[#dirs+1] = full + end end - elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then - globpattern(full,patt,recurse,action) end end + if dirs then + for i=1,#dirs do + glob_pattern_table(dirs[i],patt,recurse,result) + end + end + end + return result +end + +local function globpattern(path,patt,recurse,method) + local kind = type(method) + if pattern and sub(patt,1,-3) == path then + patt = false + end + if kind == "function" then + return glob_pattern_function(path,patt,recurse,method) + elseif kind == "table" then + return glob_pattern_table(path,patt,recurse,method) + else + return glob_pattern_table(path,patt,recurse,{ }) end end dir.globpattern = globpattern +-- never or seldom used so far: + local function collectpattern(path,patt,recurse,result) local ok, scanner result = result or { } @@ -119,18 +181,26 @@ local function collectpattern(path,patt,recurse,result) ok, scanner, first = xpcall(function() return walkdir(path) end, function() end) -- kepler safe end if ok and type(scanner) == "function" then - if not find(path,"/$") then path = path .. '/' end + if not find(path,"/$") then + path = path .. '/' + end for name in scanner, first do - local full = path .. name - local attr = attributes(full) - local mode = attr.mode - if mode == 'file' then - if find(full,patt) then + if name == "." then + -- skip + elseif name == ".." then + -- skip + else + local full = path .. name + local attr = attributes(full) + local mode = attr.mode + if mode == 'file' then + if find(full,patt) then + result[name] = attr + end + elseif recurse and mode == "directory" then + attr.list = collectpattern(full,patt,recurse) result[name] = attr end - elseif recurse and (mode == "directory") and (name ~= '.') and (name ~= "..") then - attr.list = collectpattern(full,patt,recurse) - result[name] = attr end end end @@ -143,15 +213,10 @@ local separator if onwindows then -- we could sanitize here --- pattern = Ct { --- [1] = (C(P(".") + S("/\\")^1) + C(R("az","AZ") * P(":") * S("/\\")^0) + Cc("./")) * V(2) * V(3), --- [2] = C(((1-S("*?/\\"))^0 * S("/\\"))^0), --- [3] = C(P(1)^0) --- } - local slash = S("/\\") / "/" - pattern = Ct { +-- pattern = Ct { + pattern = { [1] = (Cs(P(".") + slash^1) + Cs(R("az","AZ") * P(":") * slash^0) + Cc("./")) * V(2) * V(3), [2] = Cs(((1-S("*?/\\"))^0 * slash)^0), [3] = Cs(P(1)^0) @@ -159,7 +224,8 @@ if onwindows then -- we could sanitize here else -- assume unix - pattern = Ct { +-- pattern = Ct { + pattern = { [1] = (C(P(".") + P("/")^1) + Cc("./")) * V(2) * V(3), [2] = C(((1-S("*?/"))^0 * P("/"))^0), [3] = C(P(1)^0) @@ -186,12 +252,11 @@ local function glob(str,t) elseif isfile(str) then t(str) else - local split = lpegmatch(pattern,str) -- we could use the file splitter - if split then - local root, path, base = split[1], split[2], split[3] + local root, path, base = lpegmatch(pattern,str) -- we could use the file splitter + if root and path and base then local recurse = find(base,"**",1,true) -- find(base,"%*%*") - local start = root .. path - local result = lpegmatch(filter,start .. base) + local start = root .. path + local result = lpegmatch(filter,start .. base) globpattern(start,result,recurse,t) end end @@ -210,16 +275,12 @@ local function glob(str,t) return { str } end else - local split = lpegmatch(pattern,str) -- we could use the file splitter - if split then - local t = t or { } - local action = action or function(name) t[#t+1] = name end - local root, path, base = split[1], split[2], split[3] - local recurse = find(base,"**",1,true) -- find(base,"%*%*") - local start = root .. path - local result = lpegmatch(filter,start .. base) - globpattern(start,result,recurse,action) - return t + local root, path, base = lpegmatch(pattern,str) -- we could use the file splitter + if root and path and base then + local recurse = find(base,"**",1,true) -- find(base,"%*%*") + local start = root .. path + local result = lpegmatch(filter,start .. base) + return globpattern(start,result,recurse,t) else return { } end @@ -229,11 +290,20 @@ end dir.glob = glob ---~ list = dir.glob("**/*.tif") ---~ list = dir.glob("/**/*.tif") ---~ list = dir.glob("./**/*.tif") ---~ list = dir.glob("oeps/**/*.tif") ---~ list = dir.glob("/oeps/**/*.tif") +-- local c = os.clock() +-- local t = dir.glob("e:/**") +-- local t = dir.glob("t:/sources/**") +-- local t = dir.glob("t:/**") +-- print(os.clock()-c,#t) + +-- for i=1,3000 do print(t[i]) end +-- for i=1,10 do print(t[i]) end + +-- list = dir.glob("**/*.tif") +-- list = dir.glob("/**/*.tif") +-- list = dir.glob("./**/*.tif") +-- list = dir.glob("oeps/**/*.tif") +-- list = dir.glob("/oeps/**/*.tif") local function globfiles(path,recurse,func,files) -- func == pattern or function if type(func) == "string" then @@ -275,10 +345,10 @@ function dir.ls(pattern) return concat(glob(pattern),"\n") end ---~ mkdirs("temp") ---~ mkdirs("a/b/c") ---~ mkdirs(".","/a/b/c") ---~ mkdirs("a","b","c") +-- mkdirs("temp") +-- mkdirs("a/b/c") +-- mkdirs(".","/a/b/c") +-- mkdirs("a","b","c") local make_indeed = true -- false @@ -347,17 +417,17 @@ if onwindows then return pth, (isdir(pth) == true) end - --~ print(dir.mkdirs("","","a","c")) - --~ print(dir.mkdirs("a")) - --~ print(dir.mkdirs("a:")) - --~ print(dir.mkdirs("a:/b/c")) - --~ print(dir.mkdirs("a:b/c")) - --~ print(dir.mkdirs("a:/bbb/c")) - --~ print(dir.mkdirs("/a/b/c")) - --~ print(dir.mkdirs("/aaa/b/c")) - --~ print(dir.mkdirs("//a/b/c")) - --~ print(dir.mkdirs("///a/b/c")) - --~ print(dir.mkdirs("a/bbb//ccc/")) + -- print(dir.mkdirs("","","a","c")) + -- print(dir.mkdirs("a")) + -- print(dir.mkdirs("a:")) + -- print(dir.mkdirs("a:/b/c")) + -- print(dir.mkdirs("a:b/c")) + -- print(dir.mkdirs("a:/bbb/c")) + -- print(dir.mkdirs("/a/b/c")) + -- print(dir.mkdirs("/aaa/b/c")) + -- print(dir.mkdirs("//a/b/c")) + -- print(dir.mkdirs("///a/b/c")) + -- print(dir.mkdirs("a/bbb//ccc/")) else @@ -408,13 +478,13 @@ else return pth, (isdir(pth) == true) end - --~ print(dir.mkdirs("","","a","c")) - --~ print(dir.mkdirs("a")) - --~ print(dir.mkdirs("/a/b/c")) - --~ print(dir.mkdirs("/aaa/b/c")) - --~ print(dir.mkdirs("//a/b/c")) - --~ print(dir.mkdirs("///a/b/c")) - --~ print(dir.mkdirs("a/bbb//ccc/")) + -- print(dir.mkdirs("","","a","c")) + -- print(dir.mkdirs("a")) + -- print(dir.mkdirs("/a/b/c")) + -- print(dir.mkdirs("/aaa/b/c")) + -- print(dir.mkdirs("//a/b/c")) + -- print(dir.mkdirs("///a/b/c")) + -- print(dir.mkdirs("a/bbb//ccc/")) end @@ -424,7 +494,7 @@ dir.makedirs = dir.mkdirs if onwindows then - function dir.expandname(str) -- will be merged with cleanpath and collapsepath + function dir.expandname(str) -- will be merged with cleanpath and collapsepath\ local first, nothing, last = match(str,"^(//)(//*)(.*)$") if first then first = dir.current() .. "/" -- dir.current sanitizes diff --git a/lualibs-file.lua b/lualibs-file.lua index ebb2b39..c05372a 100644 --- a/lualibs-file.lua +++ b/lualibs-file.lua @@ -495,6 +495,23 @@ function file.collapsepath(str,anchor) -- anchor: false|nil, true, "." end end +-- better this way: + +local tricky = S("/\\") * P(-1) +local attributes = lfs.attributes + +function lfs.isdir(name) + if lpegmatch(tricky,name) then + return attributes(name,"mode") == "directory" + else + return attributes(name.."/.","mode") == "directory" + end +end + +function lfs.isfile(name) + return attributes(name,"mode") == "file" +end + -- local function test(str,...) -- print(string.format("%-20s %-15s %-30s %-20s",str,file.collapsepath(str),file.collapsepath(str,true),file.collapsepath(str,"."))) -- end diff --git a/lualibs-lpeg.lua b/lualibs-lpeg.lua index 6feb708..f3fd28b 100644 --- a/lualibs-lpeg.lua +++ b/lualibs-lpeg.lua @@ -107,7 +107,8 @@ local uppercase = R("AZ") local underscore = P("_") local hexdigit = digit + lowercase + uppercase local cr, lf, crlf = P("\r"), P("\n"), P("\r\n") -local newline = crlf + S("\r\n") -- cr + lf +----- newline = crlf + S("\r\n") -- cr + lf +local newline = P("\r") * (P("\n") + P(true)) + P("\n") local escaped = P("\\") * anything local squote = P("'") local dquote = P('"') @@ -141,8 +142,8 @@ patterns.utfbom_16_be = utfbom_16_be patterns.utfbom_16_le = utfbom_16_le patterns.utfbom_8 = utfbom_8 -patterns.utf_16_be_nl = P("\000\r\000\n") + P("\000\r") + P("\000\n") -patterns.utf_16_le_nl = P("\r\000\n\000") + P("\r\000") + P("\n\000") +patterns.utf_16_be_nl = P("\000\r\000\n") + P("\000\r") + P("\000\n") -- P("\000\r") * (P("\000\n") + P(true)) + P("\000\n") +patterns.utf_16_le_nl = P("\r\000\n\000") + P("\r\000") + P("\n\000") -- P("\r\000") * (P("\n\000") + P(true)) + P("\n\000") patterns.utf8one = R("\000\127") patterns.utf8two = R("\194\223") * utf8next @@ -224,9 +225,12 @@ patterns.integer = sign^-1 * digit^1 patterns.unsigned = digit^0 * period * digit^1 patterns.float = sign^-1 * patterns.unsigned patterns.cunsigned = digit^0 * comma * digit^1 +patterns.cpunsigned = digit^0 * (period + comma) * digit^1 patterns.cfloat = sign^-1 * patterns.cunsigned +patterns.cpfloat = sign^-1 * patterns.cpunsigned patterns.number = patterns.float + patterns.integer patterns.cnumber = patterns.cfloat + patterns.integer +patterns.cpnumber = patterns.cpfloat + patterns.integer patterns.oct = zero * octdigit^1 patterns.octal = patterns.oct patterns.HEX = zero * P("X") * (digit+uppercase)^1 @@ -495,7 +499,6 @@ function lpeg.finder(lst,makefunction,isutf) -- beware: slower than find with 'p pattern = P(lst) end if isutf then --- pattern = ((utf8char or 1)-pattern)^0 * pattern pattern = ((utf8char or 1)-pattern)^0 * pattern else pattern = (1-pattern)^0 * pattern @@ -812,21 +815,76 @@ end -- experiment: -local function make(t) - local p +-- local function make(t) +-- local p +-- local keys = sortedkeys(t) +-- for i=1,#keys do +-- local k = keys[i] +-- local v = t[k] +-- if not p then +-- if next(v) then +-- p = P(k) * make(v) +-- else +-- p = P(k) +-- end +-- else +-- if next(v) then +-- p = p + P(k) * make(v) +-- else +-- p = p + P(k) +-- end +-- end +-- end +-- return p +-- end + +-- local function make(t) +-- local p = P(false) +-- local keys = sortedkeys(t) +-- for i=1,#keys do +-- local k = keys[i] +-- local v = t[k] +-- if next(v) then +-- p = p + P(k) * make(v) +-- else +-- p = p + P(k) +-- end +-- end +-- return p +-- end + +-- function lpeg.utfchartabletopattern(list) -- goes to util-lpg +-- local tree = { } +-- for i=1,#list do +-- local t = tree +-- for c in gmatch(list[i],".") do +-- local tc = t[c] +-- if not tc then +-- tc = { } +-- t[c] = tc +-- end +-- t = tc +-- end +-- end +-- return make(tree) +-- end + +local function make(t,hash) + local p = P(false) local keys = sortedkeys(t) for i=1,#keys do local k = keys[i] local v = t[k] - if not p then + local h = hash[v] + if h then if next(v) then - p = P(k) * make(v) + p = p + P(k) * (make(v,hash) + P(true)) else - p = P(k) + p = p + P(k) * P(true) end else if next(v) then - p = p + P(k) * make(v) + p = p + P(k) * make(v,hash) else p = p + P(k) end @@ -837,16 +895,38 @@ end function lpeg.utfchartabletopattern(list) -- goes to util-lpg local tree = { } - for i=1,#list do - local t = tree - for c in gmatch(list[i],".") do - if not t[c] then - t[c] = { } + local hash = { } + local n = #list + if n == 0 then + -- we could always use this branch + for s in next, list do + local t = tree + for c in gmatch(s,".") do + local tc = t[c] + if not tc then + tc = { } + t[c] = tc + end + t = tc + end + hash[t] = s + end + else + for i=1,n do + local t = tree + local s = list[i] + for c in gmatch(s,".") do + local tc = t[c] + if not tc then + tc = { } + t[c] = tc + end + t = tc end - t = t[c] + hash[t] = s end end - return make(tree) + return make(tree,hash) end -- inspect ( lpeg.utfchartabletopattern { diff --git a/lualibs-table.lua b/lualibs-table.lua index d231830..e642106 100644 --- a/lualibs-table.lua +++ b/lualibs-table.lua @@ -164,14 +164,14 @@ local function sortedhash(t,cmp) end local n = 0 local m = #s - local function kv(s) + local function kv() -- (s) if n < m then n = n + 1 local k = s[n] return k, t[k] end end - return kv, s + return kv -- , s else return nothing end @@ -400,7 +400,7 @@ local function simple_table(t) if tv == "number" then nt = nt + 1 if hexify then - tt[nt] = format("0x%04X",v) + tt[nt] = format("0x%X",v) else tt[nt] = tostring(v) -- tostring not needed end @@ -451,7 +451,7 @@ local function do_serialize(root,name,depth,level,indexed) local tn = type(name) if tn == "number" then if hexify then - handle(format("%s[0x%04X]={",depth,name)) + handle(format("%s[0x%X]={",depth,name)) else handle(format("%s[%s]={",depth,name)) end @@ -502,7 +502,7 @@ local function do_serialize(root,name,depth,level,indexed) if compact and first and tk == "number" and k >= first and k <= last then if tv == "number" then if hexify then - handle(format("%s 0x%04X,",depth,v)) + handle(format("%s 0x%X,",depth,v)) else handle(format("%s %s,",depth,v)) -- %.99g end @@ -543,25 +543,25 @@ local function do_serialize(root,name,depth,level,indexed) elseif tv == "number" then if tk == "number" then if hexify then - handle(format("%s [0x%04X]=0x%04X,",depth,k,v)) + handle(format("%s [0x%X]=0x%X,",depth,k,v)) else handle(format("%s [%s]=%s,",depth,k,v)) -- %.99g end elseif tk == "boolean" then if hexify then - handle(format("%s [%s]=0x%04X,",depth,k and "true" or "false",v)) + handle(format("%s [%s]=0x%X,",depth,k and "true" or "false",v)) else handle(format("%s [%s]=%s,",depth,k and "true" or "false",v)) -- %.99g end elseif noquotes and not reserved[k] and lpegmatch(propername,k) then if hexify then - handle(format("%s %s=0x%04X,",depth,k,v)) + handle(format("%s %s=0x%X,",depth,k,v)) else handle(format("%s %s=%s,",depth,k,v)) -- %.99g end else if hexify then - handle(format("%s [%q]=0x%04X,",depth,k,v)) + handle(format("%s [%q]=0x%X,",depth,k,v)) else handle(format("%s [%q]=%s,",depth,k,v)) -- %.99g end @@ -570,7 +570,7 @@ local function do_serialize(root,name,depth,level,indexed) if reduce and tonumber(v) then if tk == "number" then if hexify then - handle(format("%s [0x%04X]=%s,",depth,k,v)) + handle(format("%s [0x%X]=%s,",depth,k,v)) else handle(format("%s [%s]=%s,",depth,k,v)) end @@ -584,7 +584,7 @@ local function do_serialize(root,name,depth,level,indexed) else if tk == "number" then if hexify then - handle(format("%s [0x%04X]=%q,",depth,k,v)) + handle(format("%s [0x%X]=%q,",depth,k,v)) else handle(format("%s [%s]=%q,",depth,k,v)) end @@ -600,7 +600,7 @@ local function do_serialize(root,name,depth,level,indexed) if not next(v) then if tk == "number" then if hexify then - handle(format("%s [0x%04X]={},",depth,k)) + handle(format("%s [0x%X]={},",depth,k)) else handle(format("%s [%s]={},",depth,k)) end @@ -616,7 +616,7 @@ local function do_serialize(root,name,depth,level,indexed) if st then if tk == "number" then if hexify then - handle(format("%s [0x%04X]={ %s },",depth,k,concat(st,", "))) + handle(format("%s [0x%X]={ %s },",depth,k,concat(st,", "))) else handle(format("%s [%s]={ %s },",depth,k,concat(st,", "))) end @@ -636,7 +636,7 @@ local function do_serialize(root,name,depth,level,indexed) elseif tv == "boolean" then if tk == "number" then if hexify then - handle(format("%s [0x%04X]=%s,",depth,k,v and "true" or "false")) + handle(format("%s [0x%X]=%s,",depth,k,v and "true" or "false")) else handle(format("%s [%s]=%s,",depth,k,v and "true" or "false")) end @@ -653,7 +653,7 @@ local function do_serialize(root,name,depth,level,indexed) -- local f = getinfo(v).what == "C" and dump(function(...) return v(...) end) or dump(v) -- maybe strip if tk == "number" then if hexify then - handle(format("%s [0x%04X]=load(%q),",depth,k,f)) + handle(format("%s [0x%X]=load(%q),",depth,k,f)) else handle(format("%s [%s]=load(%q),",depth,k,f)) end @@ -668,7 +668,7 @@ local function do_serialize(root,name,depth,level,indexed) else if tk == "number" then if hexify then - handle(format("%s [0x%04X]=%q,",depth,k,tostring(v))) + handle(format("%s [0x%X]=%q,",depth,k,tostring(v))) else handle(format("%s [%s]=%q,",depth,k,tostring(v))) end @@ -727,7 +727,7 @@ local function serialize(_handle,root,name,specification) -- handle wins end elseif tname == "number" then if hexify then - handle(format("[0x%04X]={",name)) + handle(format("[0x%X]={",name)) else handle("[" .. name .. "]={") end @@ -922,16 +922,27 @@ end table.identical = identical table.are_equal = are_equal --- maybe also make a combined one - -function table.compact(t) -- remove empty tables, assumes subtables - if t then - for k, v in next, t do - if not next(v) then -- no type checking - t[k] = nil +local function sparse(old,nest,keeptables) + local new = { } + for k, v in next, old do + if not (v == "" or v == false) then + if nest and type(v) == "table" then + v = sparse(v,nest) + if keeptables or next(v) then + new[k] = v + end + else + new[k] = v end end end + return new +end + +table.sparse = sparse + +function table.compact(t) + return sparse(t,true,true) end function table.contains(t, v) @@ -1114,3 +1125,49 @@ function table.values(t,s) -- optional sort flag return { } end end + +-- maybe this will move to util-tab.lua + +-- for k, v in table.filtered(t,pattern) do ... end +-- for k, v in table.filtered(t,pattern,true) do ... end +-- for k, v in table.filtered(t,pattern,true,cmp) do ... end + +function table.filtered(t,pattern,sort,cmp) + if t and type(pattern) == "string" then + if sort then + local s + if cmp then + -- it would be nice if the sort function would accept a third argument (or nicer, an optional first) + s = sortedhashkeys(t,function(a,b) return cmp(t,a,b) end) + else + s = sortedkeys(t) -- the robust one + end + local n = 0 + local m = #s + local function kv(s) + while n < m do + n = n + 1 + local k = s[n] + if find(k,pattern) then + return k, t[k] + end + end + end + return kv, s + else + local n = next(t) + local function iterator() + while n do + local k = n + n = next(t,k) + if find(k,pattern) then + return k, t[k] + end + end + end + return iterator, t + end + else + return nothing + end +end diff --git a/lualibs-unicode.lua b/lualibs-unicode.lua index be61f3d..fb4ea37 100644 --- a/lualibs-unicode.lua +++ b/lualibs-unicode.lua @@ -34,25 +34,29 @@ local type = type local char, byte, format, sub, gmatch = string.char, string.byte, string.format, string.sub, string.gmatch local concat = table.concat local P, C, R, Cs, Ct, Cmt, Cc, Carg, Cp = lpeg.P, lpeg.C, lpeg.R, lpeg.Cs, lpeg.Ct, lpeg.Cmt, lpeg.Cc, lpeg.Carg, lpeg.Cp -local lpegmatch, patterns = lpeg.match, lpeg.patterns -local bytepairs = string.bytepairs +local lpegmatch = lpeg.match +local patterns = lpeg.patterns +local tabletopattern = lpeg.utfchartabletopattern -local finder = lpeg.finder -local replacer = lpeg.replacer +local bytepairs = string.bytepairs -local utfvalues = utf.values -local utfgmatch = utf.gmatch -- not always present +local finder = lpeg.finder +local replacer = lpeg.replacer + +local utfvalues = utf.values +local utfgmatch = utf.gmatch -- not always present local p_utftype = patterns.utftype local p_utfstricttype = patterns.utfstricttype local p_utfoffset = patterns.utfoffset -local p_utf8char = patterns.utf8char +local p_utf8char = patterns.utf8character local p_utf8byte = patterns.utf8byte local p_utfbom = patterns.utfbom local p_newline = patterns.newline local p_whitespace = patterns.whitespace + if not unicode then unicode = { utf = utf } -- for a while @@ -510,8 +514,20 @@ end -- a replacement for simple gsubs: +-- function utf.remapper(mapping) +-- local pattern = Cs((p_utf8char/mapping)^0) +-- return function(str) +-- if not str or str == "" then +-- return "" +-- else +-- return lpegmatch(pattern,str) +-- end +-- end, pattern +-- end + function utf.remapper(mapping) - local pattern = Cs((p_utf8char/mapping)^0) + local pattern = type(mapping) == "table" and tabletopattern(mapping) or p_utf8char + local pattern = Cs((pattern/mapping + p_utf8char)^0) return function(str) if not str or str == "" then return "" diff --git a/lualibs-util-dim.lua b/lualibs-util-dim.lua index 6906149..bfffb10 100644 --- a/lualibs-util-dim.lua +++ b/lualibs-util-dim.lua @@ -24,13 +24,15 @@ local formatters = string.formatters local texget = tex and tex.get or function() return 65536*10*100 end +local p_stripzeros = lpeg.patterns.stripzeros + --this might become another namespace number = number or { } local number = number -number.tonumberf = function(n) return match(format("%.20f",n),"(.-0?)0*$") end -- one zero too much but alas -number.tonumberg = function(n) return format("%.20g",n) end +number.tonumberf = function(n) return lpegmatch(p_stripzeros,format("%.20f",n)) end +number.tonumberg = function(n) return format("%.20g",n) end local dimenfactors = allocate { ["pt"] = 1/65536, @@ -46,66 +48,65 @@ local dimenfactors = allocate { ["nc"] = ( 5080/65043)/65536 } ---~ print(table.serialize(dimenfactors)) ---~ ---~ %.99g: ---~ ---~ t={ ---~ ["bp"]=1.5201782378580324e-005, ---~ ["cc"]=1.1883696112892098e-006, ---~ ["cm"]=5.3628510057769479e-007, ---~ ["dd"]=1.4260435335470516e-005, ---~ ["em"]=0.000152587890625, ---~ ["ex"]=6.103515625e-005, ---~ ["in"]=2.1113586636917117e-007, ---~ ["mm"]=5.3628510057769473e-008, ---~ ["nc"]=1.1917446679504327e-006, ---~ ["nd"]=1.4300936015405194e-005, ---~ ["pc"]=1.2715657552083333e-006, ---~ ["pt"]=1.52587890625e-005, ---~ ["sp"]=1, ---~ } ---~ ---~ patched %s and tonumber ---~ ---~ t={ ---~ ["bp"]=0.00001520178238, ---~ ["cc"]=0.00000118836961, ---~ ["cm"]=0.0000005362851, ---~ ["dd"]=0.00001426043534, ---~ ["em"]=0.00015258789063, ---~ ["ex"]=0.00006103515625, ---~ ["in"]=0.00000021113587, ---~ ["mm"]=0.00000005362851, ---~ ["nc"]=0.00000119174467, ---~ ["nd"]=0.00001430093602, ---~ ["pc"]=0.00000127156576, ---~ ["pt"]=0.00001525878906, ---~ ["sp"]=1, ---~ } +-- print(table.serialize(dimenfactors)) +-- +-- %.99g: +-- +-- t={ +-- ["bp"]=1.5201782378580324e-005, +-- ["cc"]=1.1883696112892098e-006, +-- ["cm"]=5.3628510057769479e-007, +-- ["dd"]=1.4260435335470516e-005, +-- ["em"]=0.000152587890625, +-- ["ex"]=6.103515625e-005, +-- ["in"]=2.1113586636917117e-007, +-- ["mm"]=5.3628510057769473e-008, +-- ["nc"]=1.1917446679504327e-006, +-- ["nd"]=1.4300936015405194e-005, +-- ["pc"]=1.2715657552083333e-006, +-- ["pt"]=1.52587890625e-005, +-- ["sp"]=1, +-- } +-- +-- patched %s and tonumber +-- +-- t={ +-- ["bp"]=0.00001520178238, +-- ["cc"]=0.00000118836961, +-- ["cm"]=0.0000005362851, +-- ["dd"]=0.00001426043534, +-- ["em"]=0.00015258789063, +-- ["ex"]=0.00006103515625, +-- ["in"]=0.00000021113587, +-- ["mm"]=0.00000005362851, +-- ["nc"]=0.00000119174467, +-- ["nd"]=0.00001430093602, +-- ["pc"]=0.00000127156576, +-- ["pt"]=0.00001525878906, +-- ["sp"]=1, +-- } --[[ldx-- <p>A conversion function that takes a number, unit (string) and optional format (string) is implemented using this table.</p> --ldx]]-- +local f_none = formatters["%s%s"] +local f_true = formatters["%0.5f%s"] local function numbertodimen(n,unit,fmt) if type(n) == 'string' then return n else unit = unit or 'pt' + n = n * dimenfactors[unit] if not fmt then - fmt = "%s%s" + fmt = f_none(n,unit) elseif fmt == true then - fmt = "%0.5f%s" + fmt = f_true(n,unit) + else + return formatters[fmt](n,unit) end - return format(fmt,n*dimenfactors[unit],unit) - -- if fmt then - -- return format(fmt,n*dimenfactors[unit],unit) - -- else - -- return match(format("%.20f",n*dimenfactors[unit]),"(.-0?)0*$") .. unit - -- end end end diff --git a/lualibs-util-prs.lua b/lualibs-util-prs.lua index 2cede91..f51f6fc 100644 --- a/lualibs-util-prs.lua +++ b/lualibs-util-prs.lua @@ -542,8 +542,8 @@ end -- -local pattern_math = Cs((P("%")/"\\percent " + P("^") * Cc("{") * lpegpatterns.integer * Cc("}") + P(1))^0) -local pattern_text = Cs((P("%")/"\\percent " + (P("^")/"\\high") * Cc("{") * lpegpatterns.integer * Cc("}") + P(1))^0) +local pattern_math = Cs((P("%")/"\\percent " + P("^") * Cc("{") * lpegpatterns.integer * Cc("}") + anything)^0) +local pattern_text = Cs((P("%")/"\\percent " + (P("^")/"\\high") * Cc("{") * lpegpatterns.integer * Cc("}") + anything)^0) patterns.unittotex = pattern @@ -551,7 +551,7 @@ function parsers.unittotex(str,textmode) return lpegmatch(textmode and pattern_text or pattern_math,str) end -local pattern = Cs((P("^") / "<sup>" * lpegpatterns.integer * Cc("</sup>") + P(1))^0) +local pattern = Cs((P("^") / "<sup>" * lpegpatterns.integer * Cc("</sup>") + anything)^0) function parsers.unittoxml(str) return lpegmatch(pattern,str) @@ -648,3 +648,27 @@ function utilities.parsers.runtime(time) local seconds = mod(time,60) return days, hours, minutes, seconds end + +-- + +local spacing = whitespace^0 +local apply = P("->") +local method = C((1-apply)^1) +local token = lbrace * C((1-rbrace)^1) * rbrace + C(anything^1) + +local pattern = spacing * (method * spacing * apply + Carg(1)) * spacing * token + +function utilities.parsers.splitmethod(str,default) + if str then + return lpegmatch(pattern,str,1,default or false) + else + return default or false, "" + end +end + +-- print(utilities.parsers.splitmethod(" foo -> {bar} ")) +-- print(utilities.parsers.splitmethod("foo->{bar}")) +-- print(utilities.parsers.splitmethod("foo->bar")) +-- print(utilities.parsers.splitmethod("foo")) +-- print(utilities.parsers.splitmethod("{foo}")) +-- print(utilities.parsers.splitmethod()) diff --git a/lualibs-util-str.lua b/lualibs-util-str.lua index 52c48ba..2739a20 100644 --- a/lualibs-util-str.lua +++ b/lualibs-util-str.lua @@ -216,6 +216,7 @@ local striplinepatterns = { ["retain"] = p_retain_normal, ["retain and collapse"] = p_retain_collapse, ["retain and no empty"] = p_retain_noempty, + ["collapse"] = patterns.collapser, -- how about: stripper fullstripper } strings.striplinepatterns = striplinepatterns @@ -224,6 +225,8 @@ function strings.striplines(str,how) return str and lpegmatch(how and striplinepatterns[how] or p_prune_collapse,str) or str end +-- also see: string.collapsespaces + strings.striplong = strings.striplines -- for old times sake -- local str = table.concat( { @@ -284,6 +287,7 @@ end -- octal %...o number -- string %...s string number -- float %...f number +-- checked float %...F number -- exponential %...e number -- exponential %...E number -- autofloat %...g number @@ -524,9 +528,22 @@ local format_f = function(f) return format("format('%%%sf',a%s)",f,n) end -local format_F = function(f) +-- The next one formats an integer as integer and very small values as zero. This is needed +-- for pdf backend code. +-- +-- 1.23 % 1 : 0.23 +-- - 1.23 % 1 : 0.77 +-- +-- We could probably use just %s with integers but who knows what Lua 5.3 will do? So let's +-- for the moment use %i. + +local format_F = function() -- beware, no cast to number n = n + 1 - return format("((a%s == 0 and '0') or (a%s == 1 and '1') or format('%%%sf',a%s))",n,n,f,n) + if not f or f == "" then + return format("(((a%s > -0.0000000005 and a%s < 0.0000000005) and '0') or format((a%s %% 1 == 0) and '%%i' or '%%.9f',a%s))",n,n,n,n) + else + return format("format((a%s %% 1 == 0) and '%%i' or '%%%sf',a%s)",n,f,n) + end end local format_g = function(f) @@ -1077,3 +1094,23 @@ end -- string.formatteds = formatteds -- -- setmetatable(formatteds, { __index = make, __call = use }) + +-- This is a somewhat silly one used in commandline reconstruction but the older +-- method, using a combination of fine, gsub, quoted and unquoted was not that +-- reliable. +-- +-- '"foo"bar \"and " whatever"' => "foo\"bar \"and \" whatever" +-- 'foo"bar \"and " whatever' => "foo\"bar \"and \" whatever" + +local dquote = patterns.dquote -- P('"') +local equote = patterns.escaped + dquote / '\\"' + 1 +local space = patterns.space +local cquote = Cc('"') + +local pattern = + Cs(dquote * (equote - P(-2))^0 * dquote) -- we keep the outer but escape unescaped ones + + Cs(cquote * (equote - space)^0 * space * equote^0 * cquote) -- we escape unescaped ones + +function string.optionalquoted(str) + return lpegmatch(pattern,str) or str +end diff --git a/whatsnew.lua b/whatsnew.lua index 0c0ca9b..e9cc378 100644 --- a/whatsnew.lua +++ b/whatsnew.lua @@ -67,7 +67,6 @@ local filenames = { ["util"] = { "deb", "dim", - "env", "jsn", "lua", "prs", |