diff options
author | Marius <mariausol@gmail.com> | 2013-05-19 20:40:34 +0300 |
---|---|---|
committer | Marius <mariausol@gmail.com> | 2013-05-19 20:40:34 +0300 |
commit | 13ec4b540e0d46c97fd7b089e0b7413da81e0a9f (patch) | |
tree | bebfa563a17c06b3bd3bf8f6f4ba6d025e00d107 /tex/context/base/buff-ver.lua | |
parent | 69ad13650cda027526271179e95b5294694143a1 (diff) | |
download | context-13ec4b540e0d46c97fd7b089e0b7413da81e0a9f.tar.gz |
beta 2013.05.19 19:27
Diffstat (limited to 'tex/context/base/buff-ver.lua')
-rw-r--r-- | tex/context/base/buff-ver.lua | 1536 |
1 files changed, 768 insertions, 768 deletions
diff --git a/tex/context/base/buff-ver.lua b/tex/context/base/buff-ver.lua index e327a59dd..30525b456 100644 --- a/tex/context/base/buff-ver.lua +++ b/tex/context/base/buff-ver.lua @@ -1,768 +1,768 @@ -if not modules then modules = { } end modules ['buff-ver'] = { - version = 1.001, - comment = "companion to buff-ver.mkiv", - author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", - copyright = "PRAGMA ADE / ConTeXt Development Team", - license = "see context related readme files" -} - --- The default visualizers have reserved names starting with buff-imp-*. Users are --- supposed to use different names for their own variants. --- --- todo: skip=auto - -local type, next, rawset, rawget, setmetatable, getmetatable = type, next, rawset, rawget, setmetatable, getmetatable -local format, lower, upper,match, find, sub = string.format, string.lower, string.upper, string.match, string.find, string.sub -local splitlines = string.splitlines -local concat = table.concat -local C, P, R, S, V, Carg, Cc, Cs = lpeg.C, lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.Carg, lpeg.Cc, lpeg.Cs -local patterns, lpegmatch, is_lpeg = lpeg.patterns, lpeg.match, lpeg.is_lpeg - -local context, commands = context, commands - -local trace_visualize = false trackers.register("buffers.visualize", function(v) trace_visualize = v end) -local report_visualizers = logs.reporter("buffers","visualizers") - -local allocate = utilities.storage.allocate - -visualizers = visualizers or { } -local specifications = allocate() -visualizers.specifications = specifications - -local tabtospace = utilities.strings.tabtospace -local variables = interfaces.variables -local settings_to_array = utilities.parsers.settings_to_array -local variables = interfaces.variables -local findfile = resolvers.findfile -local addsuffix = file.addsuffix - -local v_auto = variables.auto -local v_yes = variables.yes - --- beware, all macros have an argument: - -local doinlineverbatimnewline = context.doinlineverbatimnewline -local doinlineverbatimbeginline = context.doinlineverbatimbeginline -local doinlineverbatimemptyline = context.doinlineverbatimemptyline -local doinlineverbatimstart = context.doinlineverbatimstart -local doinlineverbatimstop = context.doinlineverbatimstop - -local dodisplayverbatimnewline = context.dodisplayverbatimnewline -local dodisplayverbatimbeginline = context.dodisplayverbatimbeginline -local dodisplayverbatimemptyline = context.dodisplayverbatimemptyline -local dodisplayverbatimstart = context.dodisplayverbatimstart -local dodisplayverbatimstop = context.dodisplayverbatimstop - -local verbatim = context.verbatim -local doverbatimspace = context.doverbatimspace - -local CargOne = Carg(1) - -local function f_emptyline(s,settings) - if settings and settings.nature == "inline" then - doinlineverbatimemptyline() - else - dodisplayverbatimemptyline() - end -end - -local function f_beginline(s,settings) - if settings and settings.nature == "inline" then - doinlineverbatimbeginline() - else - dodisplayverbatimbeginline() - end -end - -local function f_newline(s,settings) - if settings and settings.nature == "inline" then - doinlineverbatimnewline() - else - dodisplayverbatimnewline() - end -end - -local function f_start(s,settings) - if settings and settings.nature == "inline" then - doinlineverbatimstart() - else - dodisplayverbatimstart() - end -end - -local function f_stop(s,settings) - if settings and settings.nature == "inline" then - doinlineverbatimstop() - else - dodisplayverbatimstop() - end -end - -local function f_default(s) -- (s,settings) - verbatim(s) -end - -local function f_space() -- (s,settings) - doverbatimspace() -end - -local function f_signal() -- (s,settings) - -- we use these for special purposes -end - -local signal = "\000" - -visualizers.signal = signal -visualizers.signalpattern = P(signal) - -local functions = { __index = { - emptyline = f_emptyline, - newline = f_newline, - default = f_default, - beginline = f_beginline, - space = f_space, - start = f_start, - stop = f_stop, - signal = f_signal, - } -} - -local handlers = { } - -function visualizers.newhandler(name,data) - local tname, tdata = type(name), type(data) - if tname == "table" then -- (data) - setmetatable(name,getmetatable(name) or functions) - return name - elseif tname == "string" then - if tdata == "string" then -- ("name","parent") - local result = { } - setmetatable(result,getmetatable(handlers[data]) or functions) - handlers[name] = result - return result - elseif tdata == "table" then -- ("name",data) - setmetatable(data,getmetatable(data) or functions) - handlers[name] = data - return data - else -- ("name") - local result = { } - setmetatable(result,functions) - handlers[name] = result - return result - end - else -- () - local result = { } - setmetatable(result,functions) - return result - end -end - -function visualizers.newgrammar(name,t) - name = lower(name) - t = t or { } - local g = visualizers.specifications[name] - g = g and g.grammar - if g then - if trace_visualize then - report_visualizers("cloning grammar %a",name) - end - for k,v in next, g do - if not t[k] then - t[k] = v - end - if is_lpeg(v) then - t[name..":"..k] = v - end - end - end - return t -end - -local function getvisualizer(method,nature) - method = lower(method) - local m = specifications[method] or specifications.default - if nature then - if trace_visualize then - report_visualizers("getting visualizer %a with nature %a",method,nature) - end - return m and (m[nature] or m.parser) or nil - else - if trace_visualize then - report_visualizers("getting visualizer %a",method) - end - return m and m.parser or nil - end -end - -local fallback = context.verbatim - -local function makepattern(visualizer,replacement,pattern) - if not pattern then - report_visualizers("error in visualizer %a",replacement) - return patterns.alwaystrue - else - if type(visualizer) == "table" and type(replacement) == "string" then - replacement = visualizer[replacement] or fallback - else - replacement = fallback - end - return (C(pattern) * CargOne) / replacement - end -end - -local function makenested(handler,how,start,stop) - local b, e, f = P(start), P(stop), how - if type(how) == "string" then - f = function(s) getvisualizer(how,"direct")(s) end - end - return makepattern(handler,"name",b) - * ((1-e)^1/f) - * makepattern(handler,"name",e) -end - -visualizers.pattern = makepattern -visualizers.makepattern = makepattern -visualizers.makenested = makenested - -function visualizers.load(name) - name = lower(name) - if rawget(specifications,name) == nil then - name = lower(name) - local texname = findfile(format("buff-imp-%s.mkiv",name)) - local luaname = findfile(format("buff-imp-%s.lua" ,name)) - if texname == "" or luaname == "" then - -- assume a user specific file - luaname = findfile(addsuffix(name,"mkiv")) - texname = findfile(addsuffix(name,"lua" )) - end - if texname == "" or luaname == "" then - if trace_visualize then - report_visualizers("unknown visualizer %a",name) - end - else - if trace_visualize then - report_visualizers("loading visualizer %a",name) - end - lua.registercode(luaname) - context.input(texname) - end - if rawget(specifications,name) == nil then - rawset(specifications,name,false) - end - end -end - -function visualizers.register(name,specification) - name = lower(name) - if trace_visualize then - report_visualizers("registering visualizer %a",name) - end - specifications[name] = specification - local parser, handler = specification.parser, specification.handler - local displayparser = specification.display or parser - local inlineparser = specification.inline or parser - local isparser = is_lpeg(parser) - local start, stop - if isparser then - start = makepattern(handler,"start",patterns.alwaysmatched) - stop = makepattern(handler,"stop",patterns.alwaysmatched) - end - if handler then - if isparser then - specification.display = function(content,settings) - if handler.startdisplay then handler.startdisplay(settings) end - lpegmatch(start * displayparser * stop,content,1,settings) - if handler.stopdisplay then handler.stopdisplay(settings) end - end - specification.inline = function(content,settings) - if handler.startinline then handler.startinline(settings) end - lpegmatch(start * inlineparser * stop,content,1,settings) - if handler.stopinline then handler.stopinline(settings) end - end - specification.direct = function(content,settings) - lpegmatch(parser,content,1,settings) - end - elseif parser then - specification.display = function(content,settings) - if handler.startdisplay then handler.startdisplay(settings) end - parser(content,settings) - if handler.stopdisplay then handler.stopdisplay(settings) end - end - specification.inline = function(content,settings) - if handler.startinline then handler.startinline(settings) end - parser(content,settings) - if handler.stopinline then handler.stopinline(settings) end - end - specification.direct = parser - end - elseif isparser then - specification.display = function(content,settings) - lpegmatch(start * displayparser * stop,content,1,settings) - end - specification.inline = function(content,settings) - lpegmatch(start * inlineparser * stop,content,1,settings) - end - specification.direct = function(content,settings) - lpegmatch(parser,content,1,settings) - end - elseif parser then - specification.display = parser - specification.inline = parser - specification.direct = parser - end - return specification -end - -local escapepatterns = allocate() -visualizers.escapepatterns = escapepatterns - -local function texmethod(s) - context.bgroup() - context(s) - context.egroup() -end - -local function texcommand(s) - context[s]() -end - -local function defaultmethod(s,settings) - lpegmatch(getvisualizer("default"),lower(s),1,settings) -end - --- we can consider using a nested instead - -local space_pattern = patterns.space^0 -local name_pattern = R("az","AZ")^1 - --- the hack is needed in order to retain newlines when an escape happens at the --- at the begin of a line; it also ensures proper line numbering; a bit messy - -local function hack(pattern) - return Cs(pattern * Cc(signal)) -end - -local split_processor = typesetters.processors.split -local apply_processor = typesetters.processors.apply - --- todo: { before = b, after = a, processor = p }, ... - -function visualizers.registerescapepattern(name,befores,afters,normalmethod,escapemethod,processors) - local escapepattern = escapepatterns[name] - if not escapepattern then - if type(befores) ~= "table" then befores = { befores } end - if type(afters) ~= "table" then afters = { afters } end - if type(processors) ~= "table" then processors = { processors } end - for i=1,#befores do - local before = befores[i] - local after = afters[i] - local processor = processors[i] - if trace_visualize then - report_visualizers("registering escape pattern, name %a, index %a, before %a, after %a, processor %a", - name,i,before,after,processor or "default") - end - before = P(before) * space_pattern - after = space_pattern * P(after) - local action - if processor then - action = function(s) apply_processor(processor,s) end - else - action = escapemethod or texmethod - end - local ep = (before / "") * ((1 - after)^0 / action) * (after / "") - if escapepattern then - escapepattern = escapepattern + ep - else - escapepattern = ep - end - end - escapepattern = ( - escapepattern - + hack((1 - escapepattern)^1) / (normalmethod or defaultmethod) - )^0 - escapepatterns[name] = escapepattern - end - return escapepattern -end - -function visualizers.registerescapeline(name,befores,normalmethod,escapemethod,processors) - local escapepattern = escapepatterns[name] - if not escapepattern then - if type(befores) ~= "table" then befores = { befores } end - if type(processors) ~= "table" then processors = { processors } end - for i=1,#befores do - local before = befores[i] - local processor = processors[i] - if trace_visualize then - report_visualizers("registering escape line pattern, name %a, before %a, after <<newline>>",name,before) - end - before = P(before) * space_pattern - after = space_pattern * P("\n") - local action - if processor then - action = function(s) apply_processor(processor,s) end - else - action = escapemethod or texmethod - end - local ep = (before / "") * ((1 - after)^0 / action) * (space_pattern / "") - if escapepattern then - escapepattern = escapepattern + ep - else - escapepattern = ep - end - end - escapepattern = ( - escapepattern - + hack((1 - escapepattern)^1) / (normalmethod or defaultmethod) - )^0 - escapepatterns[name] = escapepattern - end - return escapepattern -end - -function visualizers.registerescapecommand(name,token,normalmethod,escapecommand,processor) - local escapepattern = escapepatterns[name] - if not escapepattern then - if trace_visualize then - report_visualizers("registering escape token, name %a, token %a",name,token) - end - token = P(token) - local notoken = hack((1 - token)^1) - local cstoken = name_pattern * space_pattern - escapepattern = ( - (token / "") - * (cstoken / (escapecommand or texcommand)) - + (notoken / (normalmethod or defaultmethod)) - )^0 - escapepatterns[name] = escapepattern - end - return escapepattern -end - -local escapedvisualizers = { } - -local function visualize(content,settings) -- maybe also method in settings - if content and content ~= "" then - local method = lower(settings.method or "default") - local m - local e = settings.escape - if e and e ~= "" then - local newname = format("%s : %s",method,e) - local newspec = specifications[newname] - if newspec then - m = newspec - else - local starts, stops, processors = { }, { }, { } - if e == v_yes then - starts[1] = "/BTEX" - stops [1] = "/ETEX" - else - local s = settings_to_array(e,true) - for i=1,#s do - local si = s[i] - local processor, pattern = split_processor(si) - si = processor and pattern or si - local start, stop = match(si,"^(.-),(.-)$") - if start then - local n = #starts + 1 - starts[n] = start - stops [n] = stop or "" - processors[n] = processor - end - end - end - local oldvisualizer = specifications[method] or specifications.default - local oldparser = oldvisualizer.direct - local newparser - if starts[1] and stops[1] ~= "" then - newparser = visualizers.registerescapepattern(newname,starts,stops,oldparser,nil,processors) - elseif starts[1] then - newparser = visualizers.registerescapeline(newname,starts,oldparser,nil,processors) - else -- for old times sake: /em - newparser = visualizers.registerescapecommand(newname,e,oldparser,nil,processors) - end - m = visualizers.register(newname, { - parser = newparser, - handler = oldvisualizer.handler, - }) - end - else - m = specifications[method] or specifications.default - end - local nature = settings.nature or "display" - local n = m and m[nature] - if n then - if trace_visualize then - report_visualizers("visualize using method %a and nature %a",method,nature) - end - n(content,settings) - else - if trace_visualize then - report_visualizers("visualize using method %a",method) - end - fallback(content,1,settings) - end - end -end - -visualizers.visualize = visualize -visualizers.getvisualizer = getvisualizer - -local fallbacks = { } table.setmetatableindex(fallbacks,function(t,k) local v = { nature = k } t[k] = v return v end) - -local function checkedsettings(settings,nature) - if not settings then - -- let's avoid dummy tables as much as possible - return fallbacks[nature] - else - if not settings.nature then - settings.nature = nature - end - return settings - end -end - -function visualizers.visualizestring(content,settings) - visualize(content,checkedsettings(settings,"inline")) -end - -function visualizers.visualizefile(name,settings) - visualize(resolvers.loadtexfile(name),checkedsettings(settings,"display")) -end - -function visualizers.visualizebuffer(name,settings) - visualize(buffers.getcontent(name),checkedsettings(settings,"display")) -end - --- -- - -local space = C(patterns.space) * CargOne / f_space -local newline = C(patterns.newline) * CargOne / f_newline -local emptyline = C(patterns.emptyline) * CargOne / f_emptyline -local beginline = C(patterns.beginline) * CargOne / f_beginline -local anything = C(patterns.somecontent) * CargOne / f_default - ------ verbosed = (space + newline * (emptyline^0) * beginline + anything)^0 -local verbosed = (space + newline * (emptyline^0) * beginline + emptyline + newline + anything)^0 - -local function write(s,settings) -- bad name - lpegmatch(verbosed,s,1,settings or false) -end - -visualizers.write = write -visualizers.writenewline = f_newline -visualizers.writeemptyline = f_emptyline -visualizers.writespace = f_space -visualizers.writedefault = f_default - -function visualizers.writeargument(...) - context("{") -- If we didn't have tracing then we could - write(...) -- use a faster print to tex variant for the - context("}") -- { } tokens as they always have ctxcatcodes. -end - --- helpers - -local function realign(lines,strip) -- "yes", <number> - local n - if strip == v_yes then - n = math.huge - for i=1, #lines do - local spaces = find(lines[i],"%S") - if not spaces then - -- empty line - elseif spaces == 0 then - n = 0 - break - elseif spaces < n then - n = spaces - end - end - n = n - 1 - else - n = tonumber(strip) - end - if n and n > 0 then - local copy = { } - for i=1,#lines do - copy[i] = sub(lines[i],n+1) - end - return copy - end - return lines -end - -local function getstrip(lines,first,last) - local first, last = first or 1, last or #lines - for i=first,last do - local li = lines[i] - if #li == 0 or find(li,"^%s*$") then - first = first + 1 - else - break - end - end - for i=last,first,-1 do - local li = lines[i] - if #li == 0 or find(li,"^%s*$") then - last = last - 1 - else - break - end - end - return first, last, last - first + 1 -end - -local function getrange(lines,first,last,range) -- 1,3 1,+3 fromhere,tothere - local noflines = #lines - local first, last = first or 1, last or noflines - if last < 0 then - last = noflines + last - end - local range = settings.range - local what = settings_to_array(range) - local r_first, r_last = what[1], what[2] - local f, l = tonumber(r_first), tonumber(r_last) - if r_first then - if f then - if f > first then - first = f - end - else - for i=first,last do - if find(lines[i],r_first) then - first = i + 1 - break - end - end - end - end - if r_last then - if l then - if l < 0 then - l = noflines + l - end - if find(r_last,"^[%+]") then -- 1,+3 - l = first + l - end - if l < last then - last = l - end - else - for i=first,last do - if find(lines[i],r_last) then - last = i - 1 - break - end - end - end - end - return first, last -end - -local tablength = 7 - -local function dotabs(content,settings) - local tab = settings.tab - tab = tab and (tab == v_yes and tablength or tonumber(tab)) - if tab then - return tabtospace(content,tab) - else - return content - end -end - -local function filter(lines,settings) -- todo: inline or display in settings - local strip = settings.strip - if strip and strip ~= "" then - lines = realign(lines,strip) - end - local line, n = 0, 0 - local first, last, m = getstrip(lines) - if range then - first, last = getrange(lines,first,last,range) - first, last = getstrip(lines,first,last) - end - -- \r is \endlinechar but \n would is more generic so this choice is debatable - local content = concat(lines,(settings.nature == "inline" and " ") or "\n",first,last) - return content, m -end - -local getlines = buffers.getlines - --- interface - -function commands.doifelsevisualizer(name) - commands.doifelse(specifications[lower(name)]) -end - -commands.loadvisualizer = visualizers.load - --- local decodecomment = resolvers.macros.decodecomment -- experiment - -function commands.typebuffer(settings) - local lines = getlines(settings.name) - if lines then - local content, m = filter(lines,settings) - if content and content ~= "" then - -- content = decodecomment(content) - content = dotabs(content,settings) - visualize(content,checkedsettings(settings,"display")) - end - end -end - -function commands.processbuffer(settings) - local lines = getlines(settings.name) - if lines then - local content, m = filter(lines,settings) - if content and content ~= "" then - content = dotabs(content,settings) - visualize(content,checkedsettings(settings,"direct")) - end - end -end - --- not really buffers but it's closely related - --- A string.gsub(str,"(\\.-) +$","%1") is faster than an lpeg when there is a --- match but slower when there is no match. But anyway, we need a more clever --- parser so we use lpeg. --- --- [[\text ]] [[\text{}]] [[\text \text ]] [[\text \\ \text ]] - ------ strip = Cs((P(" ")^1 * P(-1)/"" + 1)^0) -local strip = Cs((P("\\") * ((1-S("\\ "))^1) * (P(" ")/"") + 1)^0) -- - -function commands.typestring(settings) - local content = settings.data - if content and content ~= "" then - content = #content > 1 and lpegmatch(strip,content) or content -- can be an option, but needed in e.g. tabulate - -- content = decodecomment(content) - -- content = dotabs(content,settings) - visualize(content,checkedsettings(settings,"inline")) - end -end - -function commands.typefile(settings) - local filename = settings.name - local foundname = resolvers.findtexfile(filename) - if foundname and foundname ~= "" then - local str = resolvers.loadtexfile(foundname) - if str and str ~= "" then - local regime = settings.regime - if regime and regime ~= "" then - str = regimes.translate(str,regime) - end - if str and str~= "" then - -- content = decodecomment(content) - local lines = splitlines(str) - local content, m = filter(lines,settings) - if content and content ~= "" then - content = dotabs(content,settings) - visualize(content,checkedsettings(settings,"display")) - end - end - end - end -end +if not modules then modules = { } end modules ['buff-ver'] = {
+ version = 1.001,
+ comment = "companion to buff-ver.mkiv",
+ author = "Hans Hagen, PRAGMA-ADE, Hasselt NL",
+ copyright = "PRAGMA ADE / ConTeXt Development Team",
+ license = "see context related readme files"
+}
+
+-- The default visualizers have reserved names starting with buff-imp-*. Users are
+-- supposed to use different names for their own variants.
+--
+-- todo: skip=auto
+
+local type, next, rawset, rawget, setmetatable, getmetatable = type, next, rawset, rawget, setmetatable, getmetatable
+local format, lower, upper,match, find, sub = string.format, string.lower, string.upper, string.match, string.find, string.sub
+local splitlines = string.splitlines
+local concat = table.concat
+local C, P, R, S, V, Carg, Cc, Cs = lpeg.C, lpeg.P, lpeg.R, lpeg.S, lpeg.V, lpeg.Carg, lpeg.Cc, lpeg.Cs
+local patterns, lpegmatch, is_lpeg = lpeg.patterns, lpeg.match, lpeg.is_lpeg
+
+local context, commands = context, commands
+
+local trace_visualize = false trackers.register("buffers.visualize", function(v) trace_visualize = v end)
+local report_visualizers = logs.reporter("buffers","visualizers")
+
+local allocate = utilities.storage.allocate
+
+visualizers = visualizers or { }
+local specifications = allocate()
+visualizers.specifications = specifications
+
+local tabtospace = utilities.strings.tabtospace
+local variables = interfaces.variables
+local settings_to_array = utilities.parsers.settings_to_array
+local variables = interfaces.variables
+local findfile = resolvers.findfile
+local addsuffix = file.addsuffix
+
+local v_auto = variables.auto
+local v_yes = variables.yes
+
+-- beware, all macros have an argument:
+
+local doinlineverbatimnewline = context.doinlineverbatimnewline
+local doinlineverbatimbeginline = context.doinlineverbatimbeginline
+local doinlineverbatimemptyline = context.doinlineverbatimemptyline
+local doinlineverbatimstart = context.doinlineverbatimstart
+local doinlineverbatimstop = context.doinlineverbatimstop
+
+local dodisplayverbatimnewline = context.dodisplayverbatimnewline
+local dodisplayverbatimbeginline = context.dodisplayverbatimbeginline
+local dodisplayverbatimemptyline = context.dodisplayverbatimemptyline
+local dodisplayverbatimstart = context.dodisplayverbatimstart
+local dodisplayverbatimstop = context.dodisplayverbatimstop
+
+local verbatim = context.verbatim
+local doverbatimspace = context.doverbatimspace
+
+local CargOne = Carg(1)
+
+local function f_emptyline(s,settings)
+ if settings and settings.nature == "inline" then
+ doinlineverbatimemptyline()
+ else
+ dodisplayverbatimemptyline()
+ end
+end
+
+local function f_beginline(s,settings)
+ if settings and settings.nature == "inline" then
+ doinlineverbatimbeginline()
+ else
+ dodisplayverbatimbeginline()
+ end
+end
+
+local function f_newline(s,settings)
+ if settings and settings.nature == "inline" then
+ doinlineverbatimnewline()
+ else
+ dodisplayverbatimnewline()
+ end
+end
+
+local function f_start(s,settings)
+ if settings and settings.nature == "inline" then
+ doinlineverbatimstart()
+ else
+ dodisplayverbatimstart()
+ end
+end
+
+local function f_stop(s,settings)
+ if settings and settings.nature == "inline" then
+ doinlineverbatimstop()
+ else
+ dodisplayverbatimstop()
+ end
+end
+
+local function f_default(s) -- (s,settings)
+ verbatim(s)
+end
+
+local function f_space() -- (s,settings)
+ doverbatimspace()
+end
+
+local function f_signal() -- (s,settings)
+ -- we use these for special purposes
+end
+
+local signal = "\000"
+
+visualizers.signal = signal
+visualizers.signalpattern = P(signal)
+
+local functions = { __index = {
+ emptyline = f_emptyline,
+ newline = f_newline,
+ default = f_default,
+ beginline = f_beginline,
+ space = f_space,
+ start = f_start,
+ stop = f_stop,
+ signal = f_signal,
+ }
+}
+
+local handlers = { }
+
+function visualizers.newhandler(name,data)
+ local tname, tdata = type(name), type(data)
+ if tname == "table" then -- (data)
+ setmetatable(name,getmetatable(name) or functions)
+ return name
+ elseif tname == "string" then
+ if tdata == "string" then -- ("name","parent")
+ local result = { }
+ setmetatable(result,getmetatable(handlers[data]) or functions)
+ handlers[name] = result
+ return result
+ elseif tdata == "table" then -- ("name",data)
+ setmetatable(data,getmetatable(data) or functions)
+ handlers[name] = data
+ return data
+ else -- ("name")
+ local result = { }
+ setmetatable(result,functions)
+ handlers[name] = result
+ return result
+ end
+ else -- ()
+ local result = { }
+ setmetatable(result,functions)
+ return result
+ end
+end
+
+function visualizers.newgrammar(name,t)
+ name = lower(name)
+ t = t or { }
+ local g = visualizers.specifications[name]
+ g = g and g.grammar
+ if g then
+ if trace_visualize then
+ report_visualizers("cloning grammar %a",name)
+ end
+ for k,v in next, g do
+ if not t[k] then
+ t[k] = v
+ end
+ if is_lpeg(v) then
+ t[name..":"..k] = v
+ end
+ end
+ end
+ return t
+end
+
+local function getvisualizer(method,nature)
+ method = lower(method)
+ local m = specifications[method] or specifications.default
+ if nature then
+ if trace_visualize then
+ report_visualizers("getting visualizer %a with nature %a",method,nature)
+ end
+ return m and (m[nature] or m.parser) or nil
+ else
+ if trace_visualize then
+ report_visualizers("getting visualizer %a",method)
+ end
+ return m and m.parser or nil
+ end
+end
+
+local fallback = context.verbatim
+
+local function makepattern(visualizer,replacement,pattern)
+ if not pattern then
+ report_visualizers("error in visualizer %a",replacement)
+ return patterns.alwaystrue
+ else
+ if type(visualizer) == "table" and type(replacement) == "string" then
+ replacement = visualizer[replacement] or fallback
+ else
+ replacement = fallback
+ end
+ return (C(pattern) * CargOne) / replacement
+ end
+end
+
+local function makenested(handler,how,start,stop)
+ local b, e, f = P(start), P(stop), how
+ if type(how) == "string" then
+ f = function(s) getvisualizer(how,"direct")(s) end
+ end
+ return makepattern(handler,"name",b)
+ * ((1-e)^1/f)
+ * makepattern(handler,"name",e)
+end
+
+visualizers.pattern = makepattern
+visualizers.makepattern = makepattern
+visualizers.makenested = makenested
+
+function visualizers.load(name)
+ name = lower(name)
+ if rawget(specifications,name) == nil then
+ name = lower(name)
+ local texname = findfile(format("buff-imp-%s.mkiv",name))
+ local luaname = findfile(format("buff-imp-%s.lua" ,name))
+ if texname == "" or luaname == "" then
+ -- assume a user specific file
+ luaname = findfile(addsuffix(name,"mkiv"))
+ texname = findfile(addsuffix(name,"lua" ))
+ end
+ if texname == "" or luaname == "" then
+ if trace_visualize then
+ report_visualizers("unknown visualizer %a",name)
+ end
+ else
+ if trace_visualize then
+ report_visualizers("loading visualizer %a",name)
+ end
+ lua.registercode(luaname)
+ context.input(texname)
+ end
+ if rawget(specifications,name) == nil then
+ rawset(specifications,name,false)
+ end
+ end
+end
+
+function visualizers.register(name,specification)
+ name = lower(name)
+ if trace_visualize then
+ report_visualizers("registering visualizer %a",name)
+ end
+ specifications[name] = specification
+ local parser, handler = specification.parser, specification.handler
+ local displayparser = specification.display or parser
+ local inlineparser = specification.inline or parser
+ local isparser = is_lpeg(parser)
+ local start, stop
+ if isparser then
+ start = makepattern(handler,"start",patterns.alwaysmatched)
+ stop = makepattern(handler,"stop",patterns.alwaysmatched)
+ end
+ if handler then
+ if isparser then
+ specification.display = function(content,settings)
+ if handler.startdisplay then handler.startdisplay(settings) end
+ lpegmatch(start * displayparser * stop,content,1,settings)
+ if handler.stopdisplay then handler.stopdisplay(settings) end
+ end
+ specification.inline = function(content,settings)
+ if handler.startinline then handler.startinline(settings) end
+ lpegmatch(start * inlineparser * stop,content,1,settings)
+ if handler.stopinline then handler.stopinline(settings) end
+ end
+ specification.direct = function(content,settings)
+ lpegmatch(parser,content,1,settings)
+ end
+ elseif parser then
+ specification.display = function(content,settings)
+ if handler.startdisplay then handler.startdisplay(settings) end
+ parser(content,settings)
+ if handler.stopdisplay then handler.stopdisplay(settings) end
+ end
+ specification.inline = function(content,settings)
+ if handler.startinline then handler.startinline(settings) end
+ parser(content,settings)
+ if handler.stopinline then handler.stopinline(settings) end
+ end
+ specification.direct = parser
+ end
+ elseif isparser then
+ specification.display = function(content,settings)
+ lpegmatch(start * displayparser * stop,content,1,settings)
+ end
+ specification.inline = function(content,settings)
+ lpegmatch(start * inlineparser * stop,content,1,settings)
+ end
+ specification.direct = function(content,settings)
+ lpegmatch(parser,content,1,settings)
+ end
+ elseif parser then
+ specification.display = parser
+ specification.inline = parser
+ specification.direct = parser
+ end
+ return specification
+end
+
+local escapepatterns = allocate()
+visualizers.escapepatterns = escapepatterns
+
+local function texmethod(s)
+ context.bgroup()
+ context(s)
+ context.egroup()
+end
+
+local function texcommand(s)
+ context[s]()
+end
+
+local function defaultmethod(s,settings)
+ lpegmatch(getvisualizer("default"),lower(s),1,settings)
+end
+
+-- we can consider using a nested instead
+
+local space_pattern = patterns.space^0
+local name_pattern = R("az","AZ")^1
+
+-- the hack is needed in order to retain newlines when an escape happens at the
+-- at the begin of a line; it also ensures proper line numbering; a bit messy
+
+local function hack(pattern)
+ return Cs(pattern * Cc(signal))
+end
+
+local split_processor = typesetters.processors.split
+local apply_processor = typesetters.processors.apply
+
+-- todo: { before = b, after = a, processor = p }, ...
+
+function visualizers.registerescapepattern(name,befores,afters,normalmethod,escapemethod,processors)
+ local escapepattern = escapepatterns[name]
+ if not escapepattern then
+ if type(befores) ~= "table" then befores = { befores } end
+ if type(afters) ~= "table" then afters = { afters } end
+ if type(processors) ~= "table" then processors = { processors } end
+ for i=1,#befores do
+ local before = befores[i]
+ local after = afters[i]
+ local processor = processors[i]
+ if trace_visualize then
+ report_visualizers("registering escape pattern, name %a, index %a, before %a, after %a, processor %a",
+ name,i,before,after,processor or "default")
+ end
+ before = P(before) * space_pattern
+ after = space_pattern * P(after)
+ local action
+ if processor then
+ action = function(s) apply_processor(processor,s) end
+ else
+ action = escapemethod or texmethod
+ end
+ local ep = (before / "") * ((1 - after)^0 / action) * (after / "")
+ if escapepattern then
+ escapepattern = escapepattern + ep
+ else
+ escapepattern = ep
+ end
+ end
+ escapepattern = (
+ escapepattern
+ + hack((1 - escapepattern)^1) / (normalmethod or defaultmethod)
+ )^0
+ escapepatterns[name] = escapepattern
+ end
+ return escapepattern
+end
+
+function visualizers.registerescapeline(name,befores,normalmethod,escapemethod,processors)
+ local escapepattern = escapepatterns[name]
+ if not escapepattern then
+ if type(befores) ~= "table" then befores = { befores } end
+ if type(processors) ~= "table" then processors = { processors } end
+ for i=1,#befores do
+ local before = befores[i]
+ local processor = processors[i]
+ if trace_visualize then
+ report_visualizers("registering escape line pattern, name %a, before %a, after <<newline>>",name,before)
+ end
+ before = P(before) * space_pattern
+ after = space_pattern * P("\n")
+ local action
+ if processor then
+ action = function(s) apply_processor(processor,s) end
+ else
+ action = escapemethod or texmethod
+ end
+ local ep = (before / "") * ((1 - after)^0 / action) * (space_pattern / "")
+ if escapepattern then
+ escapepattern = escapepattern + ep
+ else
+ escapepattern = ep
+ end
+ end
+ escapepattern = (
+ escapepattern
+ + hack((1 - escapepattern)^1) / (normalmethod or defaultmethod)
+ )^0
+ escapepatterns[name] = escapepattern
+ end
+ return escapepattern
+end
+
+function visualizers.registerescapecommand(name,token,normalmethod,escapecommand,processor)
+ local escapepattern = escapepatterns[name]
+ if not escapepattern then
+ if trace_visualize then
+ report_visualizers("registering escape token, name %a, token %a",name,token)
+ end
+ token = P(token)
+ local notoken = hack((1 - token)^1)
+ local cstoken = name_pattern * space_pattern
+ escapepattern = (
+ (token / "")
+ * (cstoken / (escapecommand or texcommand))
+ + (notoken / (normalmethod or defaultmethod))
+ )^0
+ escapepatterns[name] = escapepattern
+ end
+ return escapepattern
+end
+
+local escapedvisualizers = { }
+
+local function visualize(content,settings) -- maybe also method in settings
+ if content and content ~= "" then
+ local method = lower(settings.method or "default")
+ local m
+ local e = settings.escape
+ if e and e ~= "" then
+ local newname = format("%s : %s",method,e)
+ local newspec = specifications[newname]
+ if newspec then
+ m = newspec
+ else
+ local starts, stops, processors = { }, { }, { }
+ if e == v_yes then
+ starts[1] = "/BTEX"
+ stops [1] = "/ETEX"
+ else
+ local s = settings_to_array(e,true)
+ for i=1,#s do
+ local si = s[i]
+ local processor, pattern = split_processor(si)
+ si = processor and pattern or si
+ local start, stop = match(si,"^(.-),(.-)$")
+ if start then
+ local n = #starts + 1
+ starts[n] = start
+ stops [n] = stop or ""
+ processors[n] = processor
+ end
+ end
+ end
+ local oldvisualizer = specifications[method] or specifications.default
+ local oldparser = oldvisualizer.direct
+ local newparser
+ if starts[1] and stops[1] ~= "" then
+ newparser = visualizers.registerescapepattern(newname,starts,stops,oldparser,nil,processors)
+ elseif starts[1] then
+ newparser = visualizers.registerescapeline(newname,starts,oldparser,nil,processors)
+ else -- for old times sake: /em
+ newparser = visualizers.registerescapecommand(newname,e,oldparser,nil,processors)
+ end
+ m = visualizers.register(newname, {
+ parser = newparser,
+ handler = oldvisualizer.handler,
+ })
+ end
+ else
+ m = specifications[method] or specifications.default
+ end
+ local nature = settings.nature or "display"
+ local n = m and m[nature]
+ if n then
+ if trace_visualize then
+ report_visualizers("visualize using method %a and nature %a",method,nature)
+ end
+ n(content,settings)
+ else
+ if trace_visualize then
+ report_visualizers("visualize using method %a",method)
+ end
+ fallback(content,1,settings)
+ end
+ end
+end
+
+visualizers.visualize = visualize
+visualizers.getvisualizer = getvisualizer
+
+local fallbacks = { } table.setmetatableindex(fallbacks,function(t,k) local v = { nature = k } t[k] = v return v end)
+
+local function checkedsettings(settings,nature)
+ if not settings then
+ -- let's avoid dummy tables as much as possible
+ return fallbacks[nature]
+ else
+ if not settings.nature then
+ settings.nature = nature
+ end
+ return settings
+ end
+end
+
+function visualizers.visualizestring(content,settings)
+ visualize(content,checkedsettings(settings,"inline"))
+end
+
+function visualizers.visualizefile(name,settings)
+ visualize(resolvers.loadtexfile(name),checkedsettings(settings,"display"))
+end
+
+function visualizers.visualizebuffer(name,settings)
+ visualize(buffers.getcontent(name),checkedsettings(settings,"display"))
+end
+
+-- --
+
+local space = C(patterns.space) * CargOne / f_space
+local newline = C(patterns.newline) * CargOne / f_newline
+local emptyline = C(patterns.emptyline) * CargOne / f_emptyline
+local beginline = C(patterns.beginline) * CargOne / f_beginline
+local anything = C(patterns.somecontent) * CargOne / f_default
+
+----- verbosed = (space + newline * (emptyline^0) * beginline + anything)^0
+local verbosed = (space + newline * (emptyline^0) * beginline + emptyline + newline + anything)^0
+
+local function write(s,settings) -- bad name
+ lpegmatch(verbosed,s,1,settings or false)
+end
+
+visualizers.write = write
+visualizers.writenewline = f_newline
+visualizers.writeemptyline = f_emptyline
+visualizers.writespace = f_space
+visualizers.writedefault = f_default
+
+function visualizers.writeargument(...)
+ context("{") -- If we didn't have tracing then we could
+ write(...) -- use a faster print to tex variant for the
+ context("}") -- { } tokens as they always have ctxcatcodes.
+end
+
+-- helpers
+
+local function realign(lines,strip) -- "yes", <number>
+ local n
+ if strip == v_yes then
+ n = math.huge
+ for i=1, #lines do
+ local spaces = find(lines[i],"%S")
+ if not spaces then
+ -- empty line
+ elseif spaces == 0 then
+ n = 0
+ break
+ elseif spaces < n then
+ n = spaces
+ end
+ end
+ n = n - 1
+ else
+ n = tonumber(strip)
+ end
+ if n and n > 0 then
+ local copy = { }
+ for i=1,#lines do
+ copy[i] = sub(lines[i],n+1)
+ end
+ return copy
+ end
+ return lines
+end
+
+local function getstrip(lines,first,last)
+ local first, last = first or 1, last or #lines
+ for i=first,last do
+ local li = lines[i]
+ if #li == 0 or find(li,"^%s*$") then
+ first = first + 1
+ else
+ break
+ end
+ end
+ for i=last,first,-1 do
+ local li = lines[i]
+ if #li == 0 or find(li,"^%s*$") then
+ last = last - 1
+ else
+ break
+ end
+ end
+ return first, last, last - first + 1
+end
+
+local function getrange(lines,first,last,range) -- 1,3 1,+3 fromhere,tothere
+ local noflines = #lines
+ local first, last = first or 1, last or noflines
+ if last < 0 then
+ last = noflines + last
+ end
+ local range = settings.range
+ local what = settings_to_array(range)
+ local r_first, r_last = what[1], what[2]
+ local f, l = tonumber(r_first), tonumber(r_last)
+ if r_first then
+ if f then
+ if f > first then
+ first = f
+ end
+ else
+ for i=first,last do
+ if find(lines[i],r_first) then
+ first = i + 1
+ break
+ end
+ end
+ end
+ end
+ if r_last then
+ if l then
+ if l < 0 then
+ l = noflines + l
+ end
+ if find(r_last,"^[%+]") then -- 1,+3
+ l = first + l
+ end
+ if l < last then
+ last = l
+ end
+ else
+ for i=first,last do
+ if find(lines[i],r_last) then
+ last = i - 1
+ break
+ end
+ end
+ end
+ end
+ return first, last
+end
+
+local tablength = 7
+
+local function dotabs(content,settings)
+ local tab = settings.tab
+ tab = tab and (tab == v_yes and tablength or tonumber(tab))
+ if tab then
+ return tabtospace(content,tab)
+ else
+ return content
+ end
+end
+
+local function filter(lines,settings) -- todo: inline or display in settings
+ local strip = settings.strip
+ if strip and strip ~= "" then
+ lines = realign(lines,strip)
+ end
+ local line, n = 0, 0
+ local first, last, m = getstrip(lines)
+ if range then
+ first, last = getrange(lines,first,last,range)
+ first, last = getstrip(lines,first,last)
+ end
+ -- \r is \endlinechar but \n would is more generic so this choice is debatable
+ local content = concat(lines,(settings.nature == "inline" and " ") or "\n",first,last)
+ return content, m
+end
+
+local getlines = buffers.getlines
+
+-- interface
+
+function commands.doifelsevisualizer(name)
+ commands.doifelse(specifications[lower(name)])
+end
+
+commands.loadvisualizer = visualizers.load
+
+-- local decodecomment = resolvers.macros.decodecomment -- experiment
+
+function commands.typebuffer(settings)
+ local lines = getlines(settings.name)
+ if lines then
+ local content, m = filter(lines,settings)
+ if content and content ~= "" then
+ -- content = decodecomment(content)
+ content = dotabs(content,settings)
+ visualize(content,checkedsettings(settings,"display"))
+ end
+ end
+end
+
+function commands.processbuffer(settings)
+ local lines = getlines(settings.name)
+ if lines then
+ local content, m = filter(lines,settings)
+ if content and content ~= "" then
+ content = dotabs(content,settings)
+ visualize(content,checkedsettings(settings,"direct"))
+ end
+ end
+end
+
+-- not really buffers but it's closely related
+
+-- A string.gsub(str,"(\\.-) +$","%1") is faster than an lpeg when there is a
+-- match but slower when there is no match. But anyway, we need a more clever
+-- parser so we use lpeg.
+--
+-- [[\text ]] [[\text{}]] [[\text \text ]] [[\text \\ \text ]]
+
+----- strip = Cs((P(" ")^1 * P(-1)/"" + 1)^0)
+local strip = Cs((P("\\") * ((1-S("\\ "))^1) * (P(" ")/"") + 1)^0) --
+
+function commands.typestring(settings)
+ local content = settings.data
+ if content and content ~= "" then
+ content = #content > 1 and lpegmatch(strip,content) or content -- can be an option, but needed in e.g. tabulate
+ -- content = decodecomment(content)
+ -- content = dotabs(content,settings)
+ visualize(content,checkedsettings(settings,"inline"))
+ end
+end
+
+function commands.typefile(settings)
+ local filename = settings.name
+ local foundname = resolvers.findtexfile(filename)
+ if foundname and foundname ~= "" then
+ local str = resolvers.loadtexfile(foundname)
+ if str and str ~= "" then
+ local regime = settings.regime
+ if regime and regime ~= "" then
+ str = regimes.translate(str,regime)
+ end
+ if str and str~= "" then
+ -- content = decodecomment(content)
+ local lines = splitlines(str)
+ local content, m = filter(lines,settings)
+ if content and content ~= "" then
+ content = dotabs(content,settings)
+ visualize(content,checkedsettings(settings,"display"))
+ end
+ end
+ end
+ end
+end
|