diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/cldf-ini.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/cldf-ini.lua | 517 |
1 files changed, 367 insertions, 150 deletions
diff --git a/Master/texmf-dist/tex/context/base/cldf-ini.lua b/Master/texmf-dist/tex/context/base/cldf-ini.lua index 8edc0502321..ed86c29234f 100644 --- a/Master/texmf-dist/tex/context/base/cldf-ini.lua +++ b/Master/texmf-dist/tex/context/base/cldf-ini.lua @@ -6,35 +6,34 @@ if not modules then modules = { } end modules ['cldf-ini'] = { license = "see context related readme files" } --- This is an experiment: generating context code at the lua end. After all --- it is surprisingly simple to implement due to metatables. I was wondering --- if there was a more natural way to deal with commands at the lua end. --- Of course it's a bit slower but often more readable when mixed with lua --- code. It can also be handy when generating documents from databases or --- when constructing large tables or so. +-- This started as an experiment: generating context code at the lua end. After all +-- it is surprisingly simple to implement due to metatables. I was wondering if +-- there was a more natural way to deal with commands at the lua end. Of course it's +-- a bit slower but often more readable when mixed with lua code. It can also be handy +-- when generating documents from databases or when constructing large tables or so. -- --- Todo: optional checking against interface --- Todo: coroutine trickery --- Todo: maybe use txtcatcodes - +-- maybe optional checking against interface +-- currently no coroutine trickery +-- we could always use prtcatcodes (context.a_b_c) but then we loose protection -- tflush needs checking ... sort of weird that it's not a table - -- __flushlines is an experiment and rather ugly so it will go away +-- +-- tex.print == line with endlinechar appended local tex = tex context = context or { } local context = context -local format, find, gmatch, splitlines = string.format, string.find, string.gmatch, string.splitlines -local next, type, tostring, setmetatable = next, type, tostring, setmetatable +local format, find, gmatch, gsub = string.format, string.find, string.gmatch, string.gsub +local next, type, tostring, tonumber, setmetatable = next, type, tostring, tonumber, setmetatable local insert, remove, concat = table.insert, table.remove, table.concat -local lpegmatch = lpeg.match +local lpegmatch, lpegC, lpegS, lpegP, lpegCc = lpeg.match, lpeg.C, lpeg.S, lpeg.P, lpeg.Cc local texsprint = tex.sprint local textprint = tex.tprint local texprint = tex.print -local texiowrite = texio.write +local texwrite = tex.write local texcount = tex.count local isnode = node.is_node -- after 0.65 just node.type @@ -49,6 +48,8 @@ local vrbcatcodes = tex.vrbcatcodes local xmlcatcodes = tex.xmlcatcodes local flush = texsprint +local flushdirect = texprint +local flushraw = texwrite local report_context = logs.reporter("cld","tex") local report_cld = logs.reporter("cld","stack") @@ -137,62 +138,188 @@ local catcodes = { xml = xmlcatcodes, xmlcatcodes = xmlcatcodes, } -function context.pushcatcodes(c) +local function pushcatcodes(c) insert(catcodestack,currentcatcodes) currentcatcodes = (c and catcodes[c] or tonumber(c)) or currentcatcodes contentcatcodes = currentcatcodes end -function context.popcatcodes() +local function popcatcodes() currentcatcodes = remove(catcodestack) or currentcatcodes contentcatcodes = currentcatcodes end -function tex.fprint(...) -- goodie - texsprint(currentcatcodes,format(...)) -end +context.pushcatcodes = pushcatcodes +context.popcatcodes = popcatcodes --- -- -- todo: tracing +-- -- -- -local newline = lpeg.patterns.newline -local space = lpeg.patterns.spacer -local spacing = newline * space^0 -local content = lpeg.C((1-spacing)^1) -local emptyline = space^0 * newline^2 -local endofline = space^0 * newline * space^0 -local simpleline = endofline * lpeg.P(-1) +--~ local capture = ( +--~ space^0 * newline^2 * lpeg.Cc("") / texprint + +--~ space^0 * newline * space^0 * lpeg.Cc(" ") / texsprint + +--~ content / texsprint +--~ )^0 + +local newline = lpeg.patterns.newline +local space = lpeg.patterns.spacer +local spacing = newline * space^0 +local content = lpegC((1-spacing)^1) -- texsprint +local emptyline = space^0 * newline^2 -- texprint("") +local endofline = space^0 * newline * space^0 -- texsprint(" ") +local simpleline = endofline * lpegP(-1) -- + +local verbose = lpegC((1-space-newline)^1) +local beginstripper = (lpegS(" \t")^1 * newline^1) / "" +local endstripper = beginstripper * lpegP(-1) + +local justaspace = space * lpegCc("") +local justanewline = newline * lpegCc("") local function n_content(s) flush(contentcatcodes,s) end +local function n_verbose(s) + flush(vrbcatcodes,s) +end + local function n_endofline() - texsprint(" \r") + flush(currentcatcodes," \r") end local function n_emptyline() - texprint("\r") + flushdirect(currentcatcodes,"\r") end local function n_simpleline() - texprint("\r") + flush(currentcatcodes," \r") +end + +local n_exception = "" + +-- better a table specification + +function context.newtexthandler(specification) -- can also be used for verbose + specification = specification or { } + -- + local s_catcodes = specification.catcodes + -- + local f_before = specification.before + local f_after = specification.after + -- + local f_endofline = specification.endofline or n_endofline + local f_emptyline = specification.emptyline or n_emptyline + local f_simpleline = specification.simpleline or n_simpleline + local f_content = specification.content or n_content + local f_space = specification.space + -- + local p_exception = specification.exception + -- + if s_catcodes then + f_content = function(s) + flush(s_catcodes,s) + end + end + -- + local pattern + if f_space then + if p_exception then + local content = lpegC((1-spacing-p_exception)^1) + pattern = + ( + justaspace / f_space + + justanewline / f_endofline + + p_exception + + content / f_content + )^0 + else + local content = lpegC((1-space-endofline)^1) + pattern = + ( + justaspace / f_space + + justanewline / f_endofline + + content / f_content + )^0 + end + else + if p_exception then + local content = lpegC((1-spacing-p_exception)^1) + pattern = + simpleline / f_simpleline + + + ( + emptyline / f_emptyline + + endofline / f_endofline + + p_exception + + content / f_content + )^0 + else + local content = lpegC((1-spacing)^1) + pattern = + simpleline / f_simpleline + + + ( + emptyline / f_emptyline + + endofline / f_endofline + + content / f_content + )^0 + end + end + -- + if f_before then + pattern = (P(true) / f_before) * pattern + end + -- + if f_after then + pattern = pattern * (P(true) / f_after) + end + -- + return function(str) return lpegmatch(pattern,str) end, pattern +end + +function context.newverbosehandler(specification) -- a special variant for e.g. cdata in lxml-tex + specification = specification or { } + -- + local f_line = specification.line or function() flushdirect("\r") end + local f_space = specification.space or function() flush(" ") end + local f_content = specification.content or n_verbose + local f_before = specification.before + local f_after = specification.after + -- + local pattern = + justanewline / f_line -- so we get call{} + + verbose / f_content + + justaspace / f_space -- so we get call{} + -- + if specification.strip then + pattern = beginstripper^0 * (endstripper + pattern)^0 + else + pattern = pattern^0 + end + -- + if f_before then + pattern = (lpegP(true) / f_before) * pattern + end + -- + if f_after then + pattern = pattern * (lpegP(true) / f_after) + end + -- + return function(str) return lpegmatch(pattern,str) end, pattern end -function lpeg.texlinesplitter(f_content,f_endofline,f_emptyline,f_simpleline) - local splitlines = - simpleline / (f_simpleline or n_simpleline) - + ( - emptyline / (f_emptyline or n_emptyline) - + endofline / (f_endofline or n_emptyline) - + content / (f_content or n_content) - )^0 - return function(str) return lpegmatch(splitlines,str) end -end +local flushlines = context.newtexthandler { + content = n_content, + endofline = n_endofline, + emptyline = n_emptyline, + simpleline = n_simpleline, +} -local flushlines = lpeg.texlinesplitter(n_content,n_endofline,n_emptyline,n_simpleline) +context.__flushlines = flushlines -- maybe context.helpers.flushtexlines +context.__flush = flush +context.__flushdirect = flushdirect -context.__flushlines = flushlines -- maybe context.helpers.flushtexlines -context.__flush = flush +-- The next variant is only used in rare cases (buffer to mp): local printlines_ctx = ( (newline) / function() texprint("") end + @@ -204,7 +331,7 @@ local printlines_raw = ( (1-newline)^1 / function(s) texprint(s) end * newline^-1 )^0 -function context.printlines(str,raw) +function context.printlines(str,raw) -- todo: see if via file is useable if raw then lpegmatch(printlines_raw,str) else @@ -212,22 +339,23 @@ function context.printlines(str,raw) end end --- -- -- +-- This is the most reliable way to deal with nested buffers and other +-- catcode sensitive data. local methodhandler = resolvers.methodhandler function context.viafile(data) - -- this is the only way to deal with nested buffers - -- and other catcode sensitive data if data and data ~= "" then local filename = resolvers.savers.byscheme("virtual","viafile",data) + -- context.startregime { "utf" } context.input(filename) + -- context.stopregime() end end -- -- -- -local function writer(parent,command,first,...) +local function writer(parent,command,first,...) -- already optimized before call local t = { first, ... } flush(currentcatcodes,command) -- todo: ctx|prt|texcatcodes local direct = false @@ -246,6 +374,7 @@ local function writer(parent,command,first,...) elseif ti == "" then flush(currentcatcodes,"{}") elseif typ == "string" then + -- is processelines seen ? if processlines and find(ti,"[\n\r]") then -- we can check for ti == "\n" flush(currentcatcodes,"{") local flushlines = parent.__flushlines or flushlines @@ -270,13 +399,13 @@ local function writer(parent,command,first,...) if v == "" then flush(currentcatcodes,",",k,'=') else - flush(currentcatcodes,",",k,'=',v) + flush(currentcatcodes,",",k,"={",v,"}") end else if v == "" then - flush(currentcatcodes,"[",k,'=') + flush(currentcatcodes,"[",k,"=") else - flush(currentcatcodes,"[",k,'=',v) + flush(currentcatcodes,"[",k,"={",v,"}") end done = true end @@ -302,8 +431,7 @@ local function writer(parent,command,first,...) flush(currentcatcodes,"{\\cldf{",_store_f_(ti),"}}") -- todo: ctx|prt|texcatcodes elseif typ == "boolean" then if ti then - -- flush(currentcatcodes,"^^M") - texprint("") + flushdirect(currentcatcodes,"\r") else direct = true end @@ -332,9 +460,60 @@ local function indexer(parent,k) return f end +-- Potential optimization: after the first call we know if there will be an +-- argument. Of course there is the side effect that for instance abuse like +-- context.NC(str) fails as well as optional arguments. So, we don't do this +-- in practice. We just keep the next trick commented. The gain on some +-- 100000 calls is not that large: 0.100 => 0.95 which is neglectable. +-- +-- local function constructor(parent,k,c,first,...) +-- if first == nil then +-- local f = function() +-- flush(currentcatcodes,c) +-- end +-- parent[k] = f +-- return f() +-- else +-- local f = function(...) +-- return writer(parent,c,...) +-- end +-- parent[k] = f +-- return f(first,...) +-- end +-- end +-- +-- local function indexer(parent,k) +-- local c = "\\" .. tostring(generics[k] or k) +-- local f = function(...) +-- return constructor(parent,k,c,...) +-- end +-- parent[k] = f +-- return f +-- end + +-- only for internal usage: + +function context.constructcsonly(k) -- not much faster than the next but more mem efficient + local c = "\\" .. tostring(generics[k] or k) + rawset(context, k, function() + flush(prtcatcodes,c) + end) +end + +function context.constructcs(k) + local c = "\\" .. tostring(generics[k] or k) + rawset(context, k, function(first,...) + if first == nil then + flush(prtcatcodes,c) + else + return writer(context,c,first,...) + end + end) +end + local function caller(parent,f,a,...) if not parent then - -- so we don't need to test in the calling (slower but often no issue) (will go) + -- so we don't need to test in the calling (slower but often no issue) elseif f ~= nil then local typ = type(f) if typ == "string" then @@ -362,8 +541,7 @@ local function caller(parent,f,a,...) flushlines(f) -- ignore ... maybe some day else - -- flush(currentcatcodes,"^^M") - texprint("") + flushdirect(currentcatcodes,"\r") end else if a ~= nil then @@ -407,15 +585,45 @@ function context.protect() contentcatcodes = currentcatcodes end +function context.sprint(...) -- takes catcodes as first argument + flush(...) +end + +function context.fprint(catcodes,fmt,first,...) + if type(catcodes) == "number" then + if first then + flush(catcodes,format(fmt,first,...)) + else + flush(catcodes,fmt) + end + else + if fmt then + flush(format(catodes,fmt,first,...)) + else + flush(catcodes) + end + end +end + +function tex.fprint(fmt,first,...) -- goodie + if first then + flush(currentcatcodes,format(fmt,first,...)) + else + flush(currentcatcodes,fmt) + end +end + -- logging local trace_stack = { } -local normalflush = flush -local normalwriter = writer -local currenttrace = nil -local nofwriters = 0 -local nofflushes = 0 +local normalflush = flush +local normalflushdirect = flushdirect +local normalflushraw = flushraw +local normalwriter = writer +local currenttrace = nil +local nofwriters = 0 +local nofflushes = 0 statistics.register("traced context", function() if nofwriters > 0 or nofflushes > 0 then @@ -423,59 +631,99 @@ statistics.register("traced context", function() end end) -local tracedwriter = function(parent,...) +local tracedwriter = function(parent,...) -- also catcodes ? nofwriters = nofwriters + 1 - local t, f, n = { "w : " }, flush, 0 - flush = function(...) + local savedflush = flush + local savedflushdirect = flushdirect -- unlikely to be used here + local t, n = { "w : - : " }, 1 + local traced = function(normal,catcodes,...) -- todo: check for catcodes + local s = concat({...}) + s = gsub(s,"\r","<<newline>>") -- unlikely n = n + 1 - t[n] = concat({...},"",2) - normalflush(...) + t[n] = s + normal(catcodes,...) end + flush = function(...) traced(normalflush, ...) end + flushdirect = function(...) traced(normalflushdirect,...) end normalwriter(parent,...) - flush = f + flush = savedflush + flushdirect = savedflushdirect currenttrace(concat(t)) end -local tracedflush = function(...) +-- we could reuse collapsed + +local traced = function(normal,one,two,...) nofflushes = nofflushes + 1 - normalflush(...) - local t = { ... } - t[1] = "f : " -- replaces the catcode - for i=2,#t do - local ti = t[i] - local tt = type(ti) - if tt == "string" then - -- ok - elseif tt == "number" then - -- ok + if two then + -- only catcodes if 'one' is number + normal(one,two,...) + local catcodes = type(one) == "number" and one + local arguments = catcodes and { two, ... } or { one, two, ... } + local collapsed, c = { format("f : %s : ", catcodes or '-') }, 1 + for i=1,#arguments do + local argument = arguments[i] + local argtype = type(argument) + c = c + 1 + if argtype == "string" then + collapsed[c] = gsub(argument,"\r","<<newline>>") + elseif argtype == "number" then + collapsed[c] = argument + else + collapsed[c] = format("<<%s>>",tostring(argument)) + end + end + currenttrace(concat(collapsed)) + else + -- no catcodes + normal(one) + local argtype = type(one) + if argtype == "string" then + currenttrace(format("f : - : %s",gsub(one,"\r","<<newline>>"))) + elseif argtype == "number" then + currenttrace(format("f : - : %s",one)) else - t[i] = format("<%s>",tostring(ti)) + currenttrace(format("f : - : <<%s>>",tostring(one))) end - -- currenttrace(format("%02i: %s",i-1,tostring(t[i]))) end - currenttrace(concat(t)) end +local tracedflush = function(...) traced(normalflush, ...) end +local tracedflushdirect = function(...) traced(normalflushdirect,...) end + local function pushlogger(trace) + trace = trace or report_context insert(trace_stack,currenttrace) currenttrace = trace - flush, writer = tracedflush, tracedwriter - context.__flush = flush + -- + flush = tracedflush + flushdirect = tracedflushdirect + writer = tracedwriter + -- + context.__flush = flush + context.__flushdirect = flushdirect + -- + return flush, writer, flushdirect end local function poplogger() currenttrace = remove(trace_stack) if not currenttrace then - flush, writer = normalflush, normalwriter - context.__flush = flush + flush = normalflush + flushdirect = normalflushdirect + writer = normalwriter + -- + context.__flush = flush + context.__flushdirect = flushdirect end + return flush, writer, flushdirect end local function settracing(v) if v then - pushlogger(report_context) + return pushlogger(report_context) else - poplogger() + return poplogger() end end @@ -483,9 +731,35 @@ end trackers.register("context.trace",settracing) -context.pushlogger = pushlogger -context.poplogger = poplogger -context.settracing = settracing +context.pushlogger = pushlogger +context.poplogger = poplogger +context.settracing = settracing + +-- -- untested, no time now: +-- +-- local tracestack, tracestacktop = { }, false +-- +-- function context.pushtracing(v) +-- insert(tracestack,tracestacktop) +-- if type(v) == "function" then +-- pushlogger(v) +-- v = true +-- else +-- pushlogger() +-- end +-- tracestacktop = v +-- settracing(v) +-- end +-- +-- function context.poptracing() +-- poplogger() +-- tracestacktop = remove(tracestack) or false +-- settracing(tracestacktop) +-- end + +function context.getlogger() + return flush, writer, flush_direct +end local trace_cld = false trackers.register("context.files", function(v) trace_cld = v end) @@ -679,67 +953,10 @@ end setmetatable(delayed, { __index = indexer, __call = caller } ) ---~ Not that useful yet. Maybe something like this when the main loop ---~ is a coroutine. It also does not help taking care of nested calls. ---~ Even worse, it interferes with other mechanisms using context calls. ---~ ---~ local create, yield, resume = coroutine.create, coroutine.yield, coroutine.resume ---~ local getflush, setflush = context.getflush, context.setflush ---~ local texsprint, ctxcatcodes = tex.sprint, tex.ctxcatcodes ---~ ---~ function context.getflush() ---~ return flush ---~ end ---~ ---~ function context.setflush(newflush) ---~ local oldflush = flush ---~ flush = newflush or flush ---~ return oldflush ---~ end ---~ ---~ function context.direct(f) ---~ local routine = create(f) ---~ local oldflush = getflush() ---~ function newflush(...) ---~ oldflush(...) ---~ yield(true) ---~ end ---~ setflush(newflush) ---~ ---~ -- local function resumecontext() ---~ -- local done = resume(routine) ---~ -- if not done then ---~ -- return ---~ -- end ---~ -- resumecontext() -- stack overflow ... no tail recursion ---~ -- end ---~ -- context.resume = resumecontext ---~ -- texsprint(ctxcatcodes,"\\ctxlua{context.resume()}") ---~ ---~ local function resumecontext() ---~ local done = resume(routine) ---~ if not done then ---~ return ---~ end ---~ -- texsprint(ctxcatcodes,"\\exitloop") ---~ texsprint(ctxcatcodes,"\\ctxlua{context.resume()}") -- can be simple macro call ---~ end ---~ context.resume = resumecontext ---~ -- texsprint(ctxcatcodes,"\\doloop{\\ctxlua{context.resume()}}") -- can be fast loop at the tex end ---~ texsprint(ctxcatcodes,"\\ctxlua{context.resume()}") ---~ ---~ end ---~ ---~ function something() ---~ context("\\setbox0") ---~ context("\\hbox{hans hagen xx}") ---~ context("\\the\\wd0/\\box0") ---~ end ---~ ---~ context.direct(something) - -- helpers: +-- we could have faster calls here + function context.concat(t,separator) local done = false for i=1,#t do |