diff options
Diffstat (limited to 'tex/context/base/cldf-ini.lua')
-rw-r--r-- | tex/context/base/cldf-ini.lua | 2132 |
1 files changed, 1066 insertions, 1066 deletions
diff --git a/tex/context/base/cldf-ini.lua b/tex/context/base/cldf-ini.lua index 4a7d9f025..c61a5b523 100644 --- a/tex/context/base/cldf-ini.lua +++ b/tex/context/base/cldf-ini.lua @@ -1,1066 +1,1066 @@ -if not modules then modules = { } end modules ['cldf-ini'] = { - version = 1.001, - comment = "companion to cldf-ini.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - --- 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. --- --- 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 - --- todo: context("%bold{total: }%s",total) --- todo: context.documentvariable("title") - -local tex = tex - -context = context or { } -local context = context - -local format, gsub, validstring = string.format, string.gsub, string.valid -local next, type, tostring, tonumber, setmetatable, unpack, select = next, type, tostring, tonumber, setmetatable, unpack, select -local insert, remove, concat = table.insert, table.remove, table.concat -local lpegmatch, lpegC, lpegS, lpegP, lpegCc, patterns = lpeg.match, lpeg.C, lpeg.S, lpeg.P, lpeg.Cc, lpeg.patterns -local formatters = string.formatters -- using formatteds is slower in this case - -local texsprint = tex.sprint -local textprint = tex.tprint -local texprint = tex.print -local texwrite = tex.write -local texcount = tex.count - -local isnode = node.is_node -- after 0.65 just node.type -local writenode = node.write -local copynodelist = node.copy_list - -local catcodenumbers = catcodes.numbers - -local ctxcatcodes = catcodenumbers.ctxcatcodes -local prtcatcodes = catcodenumbers.prtcatcodes -local texcatcodes = catcodenumbers.texcatcodes -local txtcatcodes = catcodenumbers.txtcatcodes -local vrbcatcodes = catcodenumbers.vrbcatcodes -local xmlcatcodes = catcodenumbers.xmlcatcodes - -local flush = texsprint -local flushdirect = texprint -local flushraw = texwrite - -local report_context = logs.reporter("cld","tex") -local report_cld = logs.reporter("cld","stack") - -local processlines = true -- experiments.register("context.processlines", function(v) processlines = v end) - --- for tracing it's easier to have two stacks - -local _stack_f_, _n_f_ = { }, 0 -local _stack_n_, _n_n_ = { }, 0 - -local function _store_f_(ti) - _n_f_ = _n_f_ + 1 - _stack_f_[_n_f_] = ti - return _n_f_ -end - -local function _store_n_(ti) - _n_n_ = _n_n_ + 1 - _stack_n_[_n_n_] = ti - return _n_n_ -end - -local function _flush_f_(n) - local sn = _stack_f_[n] - if not sn then - report_cld("data with id %a cannot be found on stack",n) - else - local tn = type(sn) - if tn == "function" then - if not sn() and texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private! - _stack_f_[n] = nil - else - -- keep, beware, that way the stack can grow - end - else - if texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private! - writenode(sn) - _stack_f_[n] = nil - else - writenode(copynodelist(sn)) - -- keep, beware, that way the stack can grow - end - end - end -end - -local function _flush_n_(n) - local sn = _stack_n_[n] - if not sn then - report_cld("data with id %a cannot be found on stack",n) - elseif texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private! - writenode(sn) - _stack_n_[n] = nil - else - writenode(copynodelist(sn)) - -- keep, beware, that way the stack can grow - end -end - -function context.restart() - _stack_f_, _n_f_ = { }, 0 - _stack_n_, _n_n_ = { }, 0 -end - -context._stack_f_ = _stack_f_ -context._store_f_ = _store_f_ -context._flush_f_ = _flush_f_ _cldf_ = _flush_f_ - -context._stack_n_ = _stack_n_ -context._store_n_ = _store_n_ -context._flush_n_ = _flush_n_ _cldn_ = _flush_n_ - --- Should we keep the catcodes with the function? - -local catcodestack = { } -local currentcatcodes = ctxcatcodes -local contentcatcodes = ctxcatcodes - -local catcodes = { - ctx = ctxcatcodes, ctxcatcodes = ctxcatcodes, context = ctxcatcodes, - prt = prtcatcodes, prtcatcodes = prtcatcodes, protect = prtcatcodes, - tex = texcatcodes, texcatcodes = texcatcodes, plain = texcatcodes, - txt = txtcatcodes, txtcatcodes = txtcatcodes, text = txtcatcodes, - vrb = vrbcatcodes, vrbcatcodes = vrbcatcodes, verbatim = vrbcatcodes, - xml = xmlcatcodes, xmlcatcodes = xmlcatcodes, -} - -local function pushcatcodes(c) - insert(catcodestack,currentcatcodes) - currentcatcodes = (c and catcodes[c] or tonumber(c)) or currentcatcodes - contentcatcodes = currentcatcodes -end - -local function popcatcodes() - currentcatcodes = remove(catcodestack) or currentcatcodes - contentcatcodes = currentcatcodes -end - -context.pushcatcodes = pushcatcodes -context.popcatcodes = popcatcodes - --- -- -- - -local newline = patterns.newline -local space = 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() - flush(currentcatcodes," \r") -end - -local function n_emptyline() - flushdirect(currentcatcodes,"\r") -end - -local function n_simpleline() - 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 - -local flushlines = context.newtexthandler { - content = n_content, - endofline = n_endofline, - emptyline = n_emptyline, - simpleline = n_simpleline, -} - -context.__flushlines = flushlines -- maybe context.helpers.flushtexlines -context.__flush = flush -context.__flushdirect = flushdirect - --- The next variant is only used in rare cases (buffer to mp): - -local printlines_ctx = ( - (newline) / function() texprint("") end + - (1-newline)^1 / function(s) texprint(ctxcatcodes,s) end * newline^-1 -)^0 - -local printlines_raw = ( - (newline) / function() texprint("") end + - (1-newline)^1 / function(s) texprint(s) end * newline^-1 -)^0 - -function context.printlines(str,raw) -- todo: see if via file is useable - if raw then - lpegmatch(printlines_raw,str) - else - lpegmatch(printlines_ctx,str) - 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,tag) - if data and data ~= "" then - local filename = resolvers.savers.byscheme("virtual",validstring(tag,"viafile"),data) - -- context.startregime { "utf" } - context.input(filename) - -- context.stopregime() - end -end - --- -- -- "{" .. ti .. "}" is somewhat slower in a cld-mkiv run than "{",ti,"}" - -local containseol = patterns.containseol - -local function writer(parent,command,first,...) -- already optimized before call - local t = { first, ... } - flush(currentcatcodes,command) -- todo: ctx|prt|texcatcodes - local direct = false - for i=1,#t do - local ti = t[i] - local typ = type(ti) - if direct then - if typ == "string" or typ == "number" then - flush(currentcatcodes,ti) - else -- node.write - report_context("error: invalid use of direct in %a, only strings and numbers can be flushed directly, not %a",command,typ) - end - direct = false - elseif ti == nil then - -- nothing - elseif ti == "" then - flush(currentcatcodes,"{}") - elseif typ == "string" then - -- is processelines seen ? - if processlines and lpegmatch(containseol,ti) then - flush(currentcatcodes,"{") - local flushlines = parent.__flushlines or flushlines - flushlines(ti) - flush(currentcatcodes,"}") - elseif currentcatcodes == contentcatcodes then - flush(currentcatcodes,"{",ti,"}") - else - flush(currentcatcodes,"{") - flush(contentcatcodes,ti) - flush(currentcatcodes,"}") - end - elseif typ == "number" then - -- numbers never have funny catcodes - flush(currentcatcodes,"{",ti,"}") - elseif typ == "table" then - local tn = #ti - if tn == 0 then - local done = false - for k, v in next, ti do - if done then - if v == "" then - flush(currentcatcodes,",",k,'=') - else - flush(currentcatcodes,",",k,"={",v,"}") - end - else - if v == "" then - flush(currentcatcodes,"[",k,"=") - else - flush(currentcatcodes,"[",k,"={",v,"}") - end - done = true - end - end - if done then - flush(currentcatcodes,"]") - else - flush(currentcatcodes,"[]") - end - elseif tn == 1 then -- some 20% faster than the next loop - local tj = ti[1] - if type(tj) == "function" then - flush(currentcatcodes,"[\\cldf{",_store_f_(tj),"}]") - else - flush(currentcatcodes,"[",tj,"]") - end - else -- is concat really faster than flushes here? probably needed anyway (print artifacts) - for j=1,tn do - local tj = ti[j] - if type(tj) == "function" then - ti[j] = "\\cldf{" .. _store_f_(tj) .. "}" - end - end - flush(currentcatcodes,"[",concat(ti,","),"]") - end - elseif typ == "function" then - flush(currentcatcodes,"{\\cldf{",_store_f_(ti),"}}") -- todo: ctx|prt|texcatcodes - elseif typ == "boolean" then - if ti then - flushdirect(currentcatcodes,"\r") - else - direct = true - end - elseif typ == "thread" then - report_context("coroutines not supported as we cannot yield across boundaries") - elseif isnode(ti) then -- slow - flush(currentcatcodes,"{\\cldn{",_store_n_(ti),"}}") - else - report_context("error: %a gets a weird argument %a",command,ti) - end - end -end - -local generics = { } context.generics = generics - -local function indexer(parent,k) - if type(k) == "string" then - local c = "\\" .. tostring(generics[k] or k) - local f = function(first,...) - if first == nil then - flush(currentcatcodes,c) - else - return writer(parent,c,first,...) - end - end - parent[k] = f - return f - else - return context -- catch - end -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) - elseif f ~= nil then - local typ = type(f) - if typ == "string" then - if a then - flush(contentcatcodes,formatters[f](a,...)) -- was currentcatcodes - elseif processlines and lpegmatch(containseol,f) then - local flushlines = parent.__flushlines or flushlines - flushlines(f) - else - flush(contentcatcodes,f) - end - elseif typ == "number" then - if a then - flush(currentcatcodes,f,a,...) - else - flush(currentcatcodes,f) - end - elseif typ == "function" then - -- ignored: a ... - flush(currentcatcodes,"{\\cldf{",_store_f_(f),"}}") -- todo: ctx|prt|texcatcodes - elseif typ == "boolean" then - if f then - if a ~= nil then - local flushlines = parent.__flushlines or flushlines - flushlines(a) - else - flushdirect(currentcatcodes,"\n") -- no \r, else issues with \startlines ... use context.par() otherwise - end - else - if a ~= nil then - -- no command, same as context(a,...) - writer(parent,"",a,...) - else - -- ignored - end - end - elseif typ == "thread" then - report_context("coroutines not supported as we cannot yield across boundaries") - elseif isnode(f) then -- slow - -- writenode(f) - flush(currentcatcodes,"\\cldn{",_store_n_(f),"}") - else - report_context("error: %a gets a weird argument %a","context",f) - end - end -end - -local defaultcaller = caller - -setmetatable(context, { __index = indexer, __call = caller } ) - --- now we tweak unprotect and protect - -function context.unprotect() - -- at the lua end - insert(catcodestack,currentcatcodes) - currentcatcodes = prtcatcodes - contentcatcodes = currentcatcodes - -- at the tex end - flush("\\unprotect") -end - -function context.protect() - -- at the tex end - flush("\\protect") - -- at the lua end - currentcatcodes = remove(catcodestack) or currentcatcodes - 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,formatters[fmt](first,...)) - else - flush(catcodes,fmt) - end - else - if fmt then - flush(formatters[catcodes](fmt,first,...)) - else - flush(catcodes) - end - end -end - -function tex.fprint(fmt,first,...) -- goodie - if first then - flush(currentcatcodes,formatters[fmt](first,...)) - else - flush(currentcatcodes,fmt) - end -end - --- logging - -local trace_stack = { } - -local normalflush = flush -local normalflushdirect = flushdirect -local normalflushraw = flushraw -local normalwriter = writer -local currenttrace = nil -local nofwriters = 0 -local nofflushes = 0 - -local visualizer = lpeg.replacer { - { "\n","<<newline>>" }, - { "\r","<<par>>" }, -} - -statistics.register("traced context", function() - if nofwriters > 0 or nofflushes > 0 then - return format("writers: %s, flushes: %s, maxstack: %s",nofwriters,nofflushes,_n_f_) - end -end) - -local tracedwriter = function(parent,...) -- also catcodes ? - nofwriters = nofwriters + 1 - 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 = lpegmatch(visualizer,s) - n = n + 1 - t[n] = s - normal(catcodes,...) - end - flush = function(...) traced(normalflush, ...) end - flushdirect = function(...) traced(normalflushdirect,...) end - normalwriter(parent,...) - flush = savedflush - flushdirect = savedflushdirect - currenttrace(concat(t)) -end - --- we could reuse collapsed - -local traced = function(normal,one,two,...) - nofflushes = nofflushes + 1 - 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 = { formatters["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] = lpegmatch(visualizer,argument) - elseif argtype == "number" then - collapsed[c] = argument - else - collapsed[c] = formatters["<<%S>>"](argument) - end - end - currenttrace(concat(collapsed)) - else - -- no catcodes - normal(one) - local argtype = type(one) - if argtype == "string" then - currenttrace(formatters["f : - : %s"](lpegmatch(visualizer,one))) - elseif argtype == "number" then - currenttrace(formatters["f : - : %s"](one)) - else - currenttrace(formatters["f : - : <<%S>>"](one)) - end - end -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 = 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 = normalflush - flushdirect = normalflushdirect - writer = normalwriter - -- - context.__flush = flush - context.__flushdirect = flushdirect - end - return flush, writer, flushdirect -end - -local function settracing(v) - if v then - return pushlogger(report_context) - else - return poplogger() - end -end - --- todo: share flushers so that we can define in other files - -trackers.register("context.trace",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) - -function context.runfile(filename) - local foundname = resolvers.findtexfile(file.addsuffix(filename,"cld")) or "" - if foundname ~= "" then - local ok = dofile(foundname) - if type(ok) == "function" then - if trace_cld then - report_context("begin of file %a (function call)",foundname) - end - ok() - if trace_cld then - report_context("end of file %a (function call)",foundname) - end - elseif ok then - report_context("file %a is processed and returns true",foundname) - else - report_context("file %a is processed and returns nothing",foundname) - end - else - report_context("unknown file %a",filename) - end -end - --- some functions - -function context.direct(first,...) - if first ~= nil then - return writer(context,"",first,...) - end -end - --- context.delayed (todo: lines) - -local delayed = { } context.delayed = delayed -- maybe also store them - -local function indexer(parent,k) - local f = function(...) - local a = { ... } - return function() - return context[k](unpack(a)) - end - end - parent[k] = f - return f -end - -local function caller(parent,...) -- todo: nodes - local a = { ... } - return function() - return context(unpack(a)) - end -end - --- local function indexer(parent,k) --- local f = function(a,...) --- if not a then --- return function() --- return context[k]() --- end --- elseif select("#",...) == 0 then --- return function() --- return context[k](a) --- end --- elseif a then --- local t = { ... } --- return function() --- return context[k](a,unpack(t)) --- end --- end --- end --- parent[k] = f --- return f --- end --- --- local function caller(parent,a,...) -- todo: nodes --- if not a then --- return function() --- return context() --- end --- elseif select("#",...) == 0 then --- return function() --- return context(a) --- end --- elseif a then --- local t = { ... } --- return function() --- return context(a,unpack(t)) --- end --- end --- end - -setmetatable(delayed, { __index = indexer, __call = caller } ) - --- context.nested (todo: lines) - -local nested = { } context.nested = nested - -local function indexer(parent,k) - local f = function(...) - local t, savedflush, n = { }, flush, 0 - flush = function(c,f,s,...) -- catcodes are ignored - n = n + 1 - t[n] = s and concat{f,s,...} or f -- optimized for #args == 1 - end - context[k](...) - flush = savedflush - return concat(t) - end - parent[k] = f - return f -end - -local function caller(parent,...) - local t, savedflush, n = { }, flush, 0 - flush = function(c,f,s,...) -- catcodes are ignored - n = n + 1 - t[n] = s and concat{f,s,...} or f -- optimized for #args == 1 - end - context(...) - flush = savedflush - return concat(t) -end - -setmetatable(nested, { __index = indexer, __call = caller } ) - --- verbatim - -local verbatim = { } context.verbatim = verbatim - -local function indexer(parent,k) - local command = context[k] - local f = function(...) - local savedcatcodes = contentcatcodes - contentcatcodes = vrbcatcodes - command(...) - contentcatcodes = savedcatcodes - end - parent[k] = f - return f -end - -local function caller(parent,...) - local savedcatcodes = contentcatcodes - contentcatcodes = vrbcatcodes - defaultcaller(parent,...) - contentcatcodes = savedcatcodes -end - -setmetatable(verbatim, { __index = indexer, __call = caller } ) - --- formatted - -local formatted = { } context.formatted = formatted - --- local function indexer(parent,k) --- local command = context[k] --- local f = function(fmt,...) --- command(formatters[fmt](...)) --- end --- parent[k] = f --- return f --- end - -local function indexer(parent,k) - if type(k) == "string" then - local c = "\\" .. tostring(generics[k] or k) - local f = function(first,second,...) - if first == nil then - flush(currentcatcodes,c) - elseif second then - return writer(parent,c,formatters[first](second,...)) - else - return writer(parent,c,first) - end - end - parent[k] = f - return f - else - return context -- catch - end -end - --- local function caller(parent,...) --- context.fprint(...) --- end - -local function caller(parent,catcodes,fmt,first,...) - if type(catcodes) == "number" then - if first then - flush(catcodes,formatters[fmt](first,...)) - else - flush(catcodes,fmt) - end - else - if fmt then - flush(formatters[catcodes](fmt,first,...)) - else - flush(catcodes) - end - end -end - -setmetatable(formatted, { __index = indexer, __call = caller } ) - --- metafun (this will move to another file) - -local metafun = { } context.metafun = metafun - -local mpdrawing = "\\MPdrawing" - -local function caller(parent,f,a,...) - if not parent then - -- skip - elseif f then - local typ = type(f) - if typ == "string" then - if a then - flush(currentcatcodes,mpdrawing,"{",formatters[f](a,...),"}") - else - flush(currentcatcodes,mpdrawing,"{",f,"}") - end - elseif typ == "number" then - if a then - flush(currentcatcodes,mpdrawing,"{",f,a,...,"}") - else - flush(currentcatcodes,mpdrawing,"{",f,"}") - end - elseif typ == "function" then - -- ignored: a ... - flush(currentcatcodes,mpdrawing,"{\\cldf{",store_(f),"}}") - elseif typ == "boolean" then - -- ignored: a ... - if f then - flush(currentcatcodes,mpdrawing,"{^^M}") - else - report_context("warning: %a gets argument 'false' which is currently unsupported","metafun") - end - else - report_context("error: %a gets a weird argument %a","metafun",tostring(f)) - end - end -end - -setmetatable(metafun, { __call = caller } ) - -function metafun.start() - context.resetMPdrawing() -end - -function metafun.stop() - context.MPdrawingdonetrue() - context.getMPdrawing() -end - -function metafun.color(name) - return formatters[ [[\MPcolor{%s}]] ](name) -end - --- metafun.delayed - -local delayed = { } metafun.delayed = delayed - -local function indexer(parent,k) - local f = function(...) - local a = { ... } - return function() - return metafun[k](unpack(a)) - end - end - parent[k] = f - return f -end - - -local function caller(parent,...) - local a = { ... } - return function() - return metafun(unpack(a)) - end -end - -setmetatable(delayed, { __index = indexer, __call = caller } ) - --- helpers: - -function context.concat(...) - context(concat(...)) -end +if not modules then modules = { } end modules ['cldf-ini'] = {
+ version = 1.001,
+ comment = "companion to cldf-ini.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- 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.
+--
+-- 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
+
+-- todo: context("%bold{total: }%s",total)
+-- todo: context.documentvariable("title")
+
+local tex = tex
+
+context = context or { }
+local context = context
+
+local format, gsub, validstring = string.format, string.gsub, string.valid
+local next, type, tostring, tonumber, setmetatable, unpack, select = next, type, tostring, tonumber, setmetatable, unpack, select
+local insert, remove, concat = table.insert, table.remove, table.concat
+local lpegmatch, lpegC, lpegS, lpegP, lpegCc, patterns = lpeg.match, lpeg.C, lpeg.S, lpeg.P, lpeg.Cc, lpeg.patterns
+local formatters = string.formatters -- using formatteds is slower in this case
+
+local texsprint = tex.sprint
+local textprint = tex.tprint
+local texprint = tex.print
+local texwrite = tex.write
+local texcount = tex.count
+
+local isnode = node.is_node -- after 0.65 just node.type
+local writenode = node.write
+local copynodelist = node.copy_list
+
+local catcodenumbers = catcodes.numbers
+
+local ctxcatcodes = catcodenumbers.ctxcatcodes
+local prtcatcodes = catcodenumbers.prtcatcodes
+local texcatcodes = catcodenumbers.texcatcodes
+local txtcatcodes = catcodenumbers.txtcatcodes
+local vrbcatcodes = catcodenumbers.vrbcatcodes
+local xmlcatcodes = catcodenumbers.xmlcatcodes
+
+local flush = texsprint
+local flushdirect = texprint
+local flushraw = texwrite
+
+local report_context = logs.reporter("cld","tex")
+local report_cld = logs.reporter("cld","stack")
+
+local processlines = true -- experiments.register("context.processlines", function(v) processlines = v end)
+
+-- for tracing it's easier to have two stacks
+
+local _stack_f_, _n_f_ = { }, 0
+local _stack_n_, _n_n_ = { }, 0
+
+local function _store_f_(ti)
+ _n_f_ = _n_f_ + 1
+ _stack_f_[_n_f_] = ti
+ return _n_f_
+end
+
+local function _store_n_(ti)
+ _n_n_ = _n_n_ + 1
+ _stack_n_[_n_n_] = ti
+ return _n_n_
+end
+
+local function _flush_f_(n)
+ local sn = _stack_f_[n]
+ if not sn then
+ report_cld("data with id %a cannot be found on stack",n)
+ else
+ local tn = type(sn)
+ if tn == "function" then
+ if not sn() and texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private!
+ _stack_f_[n] = nil
+ else
+ -- keep, beware, that way the stack can grow
+ end
+ else
+ if texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private!
+ writenode(sn)
+ _stack_f_[n] = nil
+ else
+ writenode(copynodelist(sn))
+ -- keep, beware, that way the stack can grow
+ end
+ end
+ end
+end
+
+local function _flush_n_(n)
+ local sn = _stack_n_[n]
+ if not sn then
+ report_cld("data with id %a cannot be found on stack",n)
+ elseif texcount["@@trialtypesetting"] == 0 then -- @@trialtypesetting is private!
+ writenode(sn)
+ _stack_n_[n] = nil
+ else
+ writenode(copynodelist(sn))
+ -- keep, beware, that way the stack can grow
+ end
+end
+
+function context.restart()
+ _stack_f_, _n_f_ = { }, 0
+ _stack_n_, _n_n_ = { }, 0
+end
+
+context._stack_f_ = _stack_f_
+context._store_f_ = _store_f_
+context._flush_f_ = _flush_f_ _cldf_ = _flush_f_
+
+context._stack_n_ = _stack_n_
+context._store_n_ = _store_n_
+context._flush_n_ = _flush_n_ _cldn_ = _flush_n_
+
+-- Should we keep the catcodes with the function?
+
+local catcodestack = { }
+local currentcatcodes = ctxcatcodes
+local contentcatcodes = ctxcatcodes
+
+local catcodes = {
+ ctx = ctxcatcodes, ctxcatcodes = ctxcatcodes, context = ctxcatcodes,
+ prt = prtcatcodes, prtcatcodes = prtcatcodes, protect = prtcatcodes,
+ tex = texcatcodes, texcatcodes = texcatcodes, plain = texcatcodes,
+ txt = txtcatcodes, txtcatcodes = txtcatcodes, text = txtcatcodes,
+ vrb = vrbcatcodes, vrbcatcodes = vrbcatcodes, verbatim = vrbcatcodes,
+ xml = xmlcatcodes, xmlcatcodes = xmlcatcodes,
+}
+
+local function pushcatcodes(c)
+ insert(catcodestack,currentcatcodes)
+ currentcatcodes = (c and catcodes[c] or tonumber(c)) or currentcatcodes
+ contentcatcodes = currentcatcodes
+end
+
+local function popcatcodes()
+ currentcatcodes = remove(catcodestack) or currentcatcodes
+ contentcatcodes = currentcatcodes
+end
+
+context.pushcatcodes = pushcatcodes
+context.popcatcodes = popcatcodes
+
+-- -- --
+
+local newline = patterns.newline
+local space = 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()
+ flush(currentcatcodes," \r")
+end
+
+local function n_emptyline()
+ flushdirect(currentcatcodes,"\r")
+end
+
+local function n_simpleline()
+ 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
+
+local flushlines = context.newtexthandler {
+ content = n_content,
+ endofline = n_endofline,
+ emptyline = n_emptyline,
+ simpleline = n_simpleline,
+}
+
+context.__flushlines = flushlines -- maybe context.helpers.flushtexlines
+context.__flush = flush
+context.__flushdirect = flushdirect
+
+-- The next variant is only used in rare cases (buffer to mp):
+
+local printlines_ctx = (
+ (newline) / function() texprint("") end +
+ (1-newline)^1 / function(s) texprint(ctxcatcodes,s) end * newline^-1
+)^0
+
+local printlines_raw = (
+ (newline) / function() texprint("") end +
+ (1-newline)^1 / function(s) texprint(s) end * newline^-1
+)^0
+
+function context.printlines(str,raw) -- todo: see if via file is useable
+ if raw then
+ lpegmatch(printlines_raw,str)
+ else
+ lpegmatch(printlines_ctx,str)
+ 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,tag)
+ if data and data ~= "" then
+ local filename = resolvers.savers.byscheme("virtual",validstring(tag,"viafile"),data)
+ -- context.startregime { "utf" }
+ context.input(filename)
+ -- context.stopregime()
+ end
+end
+
+-- -- -- "{" .. ti .. "}" is somewhat slower in a cld-mkiv run than "{",ti,"}"
+
+local containseol = patterns.containseol
+
+local function writer(parent,command,first,...) -- already optimized before call
+ local t = { first, ... }
+ flush(currentcatcodes,command) -- todo: ctx|prt|texcatcodes
+ local direct = false
+ for i=1,#t do
+ local ti = t[i]
+ local typ = type(ti)
+ if direct then
+ if typ == "string" or typ == "number" then
+ flush(currentcatcodes,ti)
+ else -- node.write
+ report_context("error: invalid use of direct in %a, only strings and numbers can be flushed directly, not %a",command,typ)
+ end
+ direct = false
+ elseif ti == nil then
+ -- nothing
+ elseif ti == "" then
+ flush(currentcatcodes,"{}")
+ elseif typ == "string" then
+ -- is processelines seen ?
+ if processlines and lpegmatch(containseol,ti) then
+ flush(currentcatcodes,"{")
+ local flushlines = parent.__flushlines or flushlines
+ flushlines(ti)
+ flush(currentcatcodes,"}")
+ elseif currentcatcodes == contentcatcodes then
+ flush(currentcatcodes,"{",ti,"}")
+ else
+ flush(currentcatcodes,"{")
+ flush(contentcatcodes,ti)
+ flush(currentcatcodes,"}")
+ end
+ elseif typ == "number" then
+ -- numbers never have funny catcodes
+ flush(currentcatcodes,"{",ti,"}")
+ elseif typ == "table" then
+ local tn = #ti
+ if tn == 0 then
+ local done = false
+ for k, v in next, ti do
+ if done then
+ if v == "" then
+ flush(currentcatcodes,",",k,'=')
+ else
+ flush(currentcatcodes,",",k,"={",v,"}")
+ end
+ else
+ if v == "" then
+ flush(currentcatcodes,"[",k,"=")
+ else
+ flush(currentcatcodes,"[",k,"={",v,"}")
+ end
+ done = true
+ end
+ end
+ if done then
+ flush(currentcatcodes,"]")
+ else
+ flush(currentcatcodes,"[]")
+ end
+ elseif tn == 1 then -- some 20% faster than the next loop
+ local tj = ti[1]
+ if type(tj) == "function" then
+ flush(currentcatcodes,"[\\cldf{",_store_f_(tj),"}]")
+ else
+ flush(currentcatcodes,"[",tj,"]")
+ end
+ else -- is concat really faster than flushes here? probably needed anyway (print artifacts)
+ for j=1,tn do
+ local tj = ti[j]
+ if type(tj) == "function" then
+ ti[j] = "\\cldf{" .. _store_f_(tj) .. "}"
+ end
+ end
+ flush(currentcatcodes,"[",concat(ti,","),"]")
+ end
+ elseif typ == "function" then
+ flush(currentcatcodes,"{\\cldf{",_store_f_(ti),"}}") -- todo: ctx|prt|texcatcodes
+ elseif typ == "boolean" then
+ if ti then
+ flushdirect(currentcatcodes,"\r")
+ else
+ direct = true
+ end
+ elseif typ == "thread" then
+ report_context("coroutines not supported as we cannot yield across boundaries")
+ elseif isnode(ti) then -- slow
+ flush(currentcatcodes,"{\\cldn{",_store_n_(ti),"}}")
+ else
+ report_context("error: %a gets a weird argument %a",command,ti)
+ end
+ end
+end
+
+local generics = { } context.generics = generics
+
+local function indexer(parent,k)
+ if type(k) == "string" then
+ local c = "\\" .. tostring(generics[k] or k)
+ local f = function(first,...)
+ if first == nil then
+ flush(currentcatcodes,c)
+ else
+ return writer(parent,c,first,...)
+ end
+ end
+ parent[k] = f
+ return f
+ else
+ return context -- catch
+ end
+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)
+ elseif f ~= nil then
+ local typ = type(f)
+ if typ == "string" then
+ if a then
+ flush(contentcatcodes,formatters[f](a,...)) -- was currentcatcodes
+ elseif processlines and lpegmatch(containseol,f) then
+ local flushlines = parent.__flushlines or flushlines
+ flushlines(f)
+ else
+ flush(contentcatcodes,f)
+ end
+ elseif typ == "number" then
+ if a then
+ flush(currentcatcodes,f,a,...)
+ else
+ flush(currentcatcodes,f)
+ end
+ elseif typ == "function" then
+ -- ignored: a ...
+ flush(currentcatcodes,"{\\cldf{",_store_f_(f),"}}") -- todo: ctx|prt|texcatcodes
+ elseif typ == "boolean" then
+ if f then
+ if a ~= nil then
+ local flushlines = parent.__flushlines or flushlines
+ flushlines(a)
+ else
+ flushdirect(currentcatcodes,"\n") -- no \r, else issues with \startlines ... use context.par() otherwise
+ end
+ else
+ if a ~= nil then
+ -- no command, same as context(a,...)
+ writer(parent,"",a,...)
+ else
+ -- ignored
+ end
+ end
+ elseif typ == "thread" then
+ report_context("coroutines not supported as we cannot yield across boundaries")
+ elseif isnode(f) then -- slow
+ -- writenode(f)
+ flush(currentcatcodes,"\\cldn{",_store_n_(f),"}")
+ else
+ report_context("error: %a gets a weird argument %a","context",f)
+ end
+ end
+end
+
+local defaultcaller = caller
+
+setmetatable(context, { __index = indexer, __call = caller } )
+
+-- now we tweak unprotect and protect
+
+function context.unprotect()
+ -- at the lua end
+ insert(catcodestack,currentcatcodes)
+ currentcatcodes = prtcatcodes
+ contentcatcodes = currentcatcodes
+ -- at the tex end
+ flush("\\unprotect")
+end
+
+function context.protect()
+ -- at the tex end
+ flush("\\protect")
+ -- at the lua end
+ currentcatcodes = remove(catcodestack) or currentcatcodes
+ 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,formatters[fmt](first,...))
+ else
+ flush(catcodes,fmt)
+ end
+ else
+ if fmt then
+ flush(formatters[catcodes](fmt,first,...))
+ else
+ flush(catcodes)
+ end
+ end
+end
+
+function tex.fprint(fmt,first,...) -- goodie
+ if first then
+ flush(currentcatcodes,formatters[fmt](first,...))
+ else
+ flush(currentcatcodes,fmt)
+ end
+end
+
+-- logging
+
+local trace_stack = { }
+
+local normalflush = flush
+local normalflushdirect = flushdirect
+local normalflushraw = flushraw
+local normalwriter = writer
+local currenttrace = nil
+local nofwriters = 0
+local nofflushes = 0
+
+local visualizer = lpeg.replacer {
+ { "\n","<<newline>>" },
+ { "\r","<<par>>" },
+}
+
+statistics.register("traced context", function()
+ if nofwriters > 0 or nofflushes > 0 then
+ return format("writers: %s, flushes: %s, maxstack: %s",nofwriters,nofflushes,_n_f_)
+ end
+end)
+
+local tracedwriter = function(parent,...) -- also catcodes ?
+ nofwriters = nofwriters + 1
+ 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 = lpegmatch(visualizer,s)
+ n = n + 1
+ t[n] = s
+ normal(catcodes,...)
+ end
+ flush = function(...) traced(normalflush, ...) end
+ flushdirect = function(...) traced(normalflushdirect,...) end
+ normalwriter(parent,...)
+ flush = savedflush
+ flushdirect = savedflushdirect
+ currenttrace(concat(t))
+end
+
+-- we could reuse collapsed
+
+local traced = function(normal,one,two,...)
+ nofflushes = nofflushes + 1
+ 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 = { formatters["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] = lpegmatch(visualizer,argument)
+ elseif argtype == "number" then
+ collapsed[c] = argument
+ else
+ collapsed[c] = formatters["<<%S>>"](argument)
+ end
+ end
+ currenttrace(concat(collapsed))
+ else
+ -- no catcodes
+ normal(one)
+ local argtype = type(one)
+ if argtype == "string" then
+ currenttrace(formatters["f : - : %s"](lpegmatch(visualizer,one)))
+ elseif argtype == "number" then
+ currenttrace(formatters["f : - : %s"](one))
+ else
+ currenttrace(formatters["f : - : <<%S>>"](one))
+ end
+ end
+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 = 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 = normalflush
+ flushdirect = normalflushdirect
+ writer = normalwriter
+ --
+ context.__flush = flush
+ context.__flushdirect = flushdirect
+ end
+ return flush, writer, flushdirect
+end
+
+local function settracing(v)
+ if v then
+ return pushlogger(report_context)
+ else
+ return poplogger()
+ end
+end
+
+-- todo: share flushers so that we can define in other files
+
+trackers.register("context.trace",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)
+
+function context.runfile(filename)
+ local foundname = resolvers.findtexfile(file.addsuffix(filename,"cld")) or ""
+ if foundname ~= "" then
+ local ok = dofile(foundname)
+ if type(ok) == "function" then
+ if trace_cld then
+ report_context("begin of file %a (function call)",foundname)
+ end
+ ok()
+ if trace_cld then
+ report_context("end of file %a (function call)",foundname)
+ end
+ elseif ok then
+ report_context("file %a is processed and returns true",foundname)
+ else
+ report_context("file %a is processed and returns nothing",foundname)
+ end
+ else
+ report_context("unknown file %a",filename)
+ end
+end
+
+-- some functions
+
+function context.direct(first,...)
+ if first ~= nil then
+ return writer(context,"",first,...)
+ end
+end
+
+-- context.delayed (todo: lines)
+
+local delayed = { } context.delayed = delayed -- maybe also store them
+
+local function indexer(parent,k)
+ local f = function(...)
+ local a = { ... }
+ return function()
+ return context[k](unpack(a))
+ end
+ end
+ parent[k] = f
+ return f
+end
+
+local function caller(parent,...) -- todo: nodes
+ local a = { ... }
+ return function()
+ return context(unpack(a))
+ end
+end
+
+-- local function indexer(parent,k)
+-- local f = function(a,...)
+-- if not a then
+-- return function()
+-- return context[k]()
+-- end
+-- elseif select("#",...) == 0 then
+-- return function()
+-- return context[k](a)
+-- end
+-- elseif a then
+-- local t = { ... }
+-- return function()
+-- return context[k](a,unpack(t))
+-- end
+-- end
+-- end
+-- parent[k] = f
+-- return f
+-- end
+--
+-- local function caller(parent,a,...) -- todo: nodes
+-- if not a then
+-- return function()
+-- return context()
+-- end
+-- elseif select("#",...) == 0 then
+-- return function()
+-- return context(a)
+-- end
+-- elseif a then
+-- local t = { ... }
+-- return function()
+-- return context(a,unpack(t))
+-- end
+-- end
+-- end
+
+setmetatable(delayed, { __index = indexer, __call = caller } )
+
+-- context.nested (todo: lines)
+
+local nested = { } context.nested = nested
+
+local function indexer(parent,k)
+ local f = function(...)
+ local t, savedflush, n = { }, flush, 0
+ flush = function(c,f,s,...) -- catcodes are ignored
+ n = n + 1
+ t[n] = s and concat{f,s,...} or f -- optimized for #args == 1
+ end
+ context[k](...)
+ flush = savedflush
+ return concat(t)
+ end
+ parent[k] = f
+ return f
+end
+
+local function caller(parent,...)
+ local t, savedflush, n = { }, flush, 0
+ flush = function(c,f,s,...) -- catcodes are ignored
+ n = n + 1
+ t[n] = s and concat{f,s,...} or f -- optimized for #args == 1
+ end
+ context(...)
+ flush = savedflush
+ return concat(t)
+end
+
+setmetatable(nested, { __index = indexer, __call = caller } )
+
+-- verbatim
+
+local verbatim = { } context.verbatim = verbatim
+
+local function indexer(parent,k)
+ local command = context[k]
+ local f = function(...)
+ local savedcatcodes = contentcatcodes
+ contentcatcodes = vrbcatcodes
+ command(...)
+ contentcatcodes = savedcatcodes
+ end
+ parent[k] = f
+ return f
+end
+
+local function caller(parent,...)
+ local savedcatcodes = contentcatcodes
+ contentcatcodes = vrbcatcodes
+ defaultcaller(parent,...)
+ contentcatcodes = savedcatcodes
+end
+
+setmetatable(verbatim, { __index = indexer, __call = caller } )
+
+-- formatted
+
+local formatted = { } context.formatted = formatted
+
+-- local function indexer(parent,k)
+-- local command = context[k]
+-- local f = function(fmt,...)
+-- command(formatters[fmt](...))
+-- end
+-- parent[k] = f
+-- return f
+-- end
+
+local function indexer(parent,k)
+ if type(k) == "string" then
+ local c = "\\" .. tostring(generics[k] or k)
+ local f = function(first,second,...)
+ if first == nil then
+ flush(currentcatcodes,c)
+ elseif second then
+ return writer(parent,c,formatters[first](second,...))
+ else
+ return writer(parent,c,first)
+ end
+ end
+ parent[k] = f
+ return f
+ else
+ return context -- catch
+ end
+end
+
+-- local function caller(parent,...)
+-- context.fprint(...)
+-- end
+
+local function caller(parent,catcodes,fmt,first,...)
+ if type(catcodes) == "number" then
+ if first then
+ flush(catcodes,formatters[fmt](first,...))
+ else
+ flush(catcodes,fmt)
+ end
+ else
+ if fmt then
+ flush(formatters[catcodes](fmt,first,...))
+ else
+ flush(catcodes)
+ end
+ end
+end
+
+setmetatable(formatted, { __index = indexer, __call = caller } )
+
+-- metafun (this will move to another file)
+
+local metafun = { } context.metafun = metafun
+
+local mpdrawing = "\\MPdrawing"
+
+local function caller(parent,f,a,...)
+ if not parent then
+ -- skip
+ elseif f then
+ local typ = type(f)
+ if typ == "string" then
+ if a then
+ flush(currentcatcodes,mpdrawing,"{",formatters[f](a,...),"}")
+ else
+ flush(currentcatcodes,mpdrawing,"{",f,"}")
+ end
+ elseif typ == "number" then
+ if a then
+ flush(currentcatcodes,mpdrawing,"{",f,a,...,"}")
+ else
+ flush(currentcatcodes,mpdrawing,"{",f,"}")
+ end
+ elseif typ == "function" then
+ -- ignored: a ...
+ flush(currentcatcodes,mpdrawing,"{\\cldf{",store_(f),"}}")
+ elseif typ == "boolean" then
+ -- ignored: a ...
+ if f then
+ flush(currentcatcodes,mpdrawing,"{^^M}")
+ else
+ report_context("warning: %a gets argument 'false' which is currently unsupported","metafun")
+ end
+ else
+ report_context("error: %a gets a weird argument %a","metafun",tostring(f))
+ end
+ end
+end
+
+setmetatable(metafun, { __call = caller } )
+
+function metafun.start()
+ context.resetMPdrawing()
+end
+
+function metafun.stop()
+ context.MPdrawingdonetrue()
+ context.getMPdrawing()
+end
+
+function metafun.color(name)
+ return formatters[ [[\MPcolor{%s}]] ](name)
+end
+
+-- metafun.delayed
+
+local delayed = { } metafun.delayed = delayed
+
+local function indexer(parent,k)
+ local f = function(...)
+ local a = { ... }
+ return function()
+ return metafun[k](unpack(a))
+ end
+ end
+ parent[k] = f
+ return f
+end
+
+
+local function caller(parent,...)
+ local a = { ... }
+ return function()
+ return metafun(unpack(a))
+ end
+end
+
+setmetatable(delayed, { __index = indexer, __call = caller } )
+
+-- helpers:
+
+function context.concat(...)
+ context(concat(...))
+end
|