summaryrefslogtreecommitdiff
path: root/tex/context/base/buff-ver.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/buff-ver.lua')
-rw-r--r--tex/context/base/buff-ver.lua1536
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