diff options
Diffstat (limited to 'Master/texmf-dist/tex/context/base/mkiv/trac-lmx.lua')
-rw-r--r-- | Master/texmf-dist/tex/context/base/mkiv/trac-lmx.lua | 732 |
1 files changed, 732 insertions, 0 deletions
diff --git a/Master/texmf-dist/tex/context/base/mkiv/trac-lmx.lua b/Master/texmf-dist/tex/context/base/mkiv/trac-lmx.lua new file mode 100644 index 00000000000..4f4ea62c489 --- /dev/null +++ b/Master/texmf-dist/tex/context/base/mkiv/trac-lmx.lua @@ -0,0 +1,732 @@ +if not modules then modules = { } end modules ['trac-lmx'] = { + version = 1.002, + comment = "companion to trac-lmx.mkiv", + author = "Hans Hagen, PRAGMA-ADE, Hasselt NL", + copyright = "PRAGMA ADE / ConTeXt Development Team", + license = "see context related readme files" +} + +-- this one will be adpated to the latest helpers + +local type, tostring, rawget, loadstring, pcall = type, tostring, rawget, loadstring, pcall +local format, sub, gsub = string.format, string.sub, string.gsub +local concat = table.concat +local collapsespaces = string.collapsespaces +local P, Cc, Cs, C, Carg, lpegmatch = lpeg.P, lpeg.Cc, lpeg.Cs, lpeg.C, lpeg.Carg, lpeg.match +local joinpath, replacesuffix, pathpart, filesuffix = file.join, file.replacesuffix, file.pathpart, file.suffix + +local allocate = utilities.storage.allocate +local setmetatableindex = table.setmetatableindex + +----- trace_templates = false trackers .register("lmx.templates", function(v) trace_templates = v end) +local trace_variables = false trackers .register("lmx.variables", function(v) trace_variables = v end) + +local cache_templates = true directives.register("lmx.cache.templates",function(v) cache_templates = v end) +local cache_files = true directives.register("lmx.cache.files", function(v) cache_files = v end) + +local report_lmx = logs.reporter("lmx") +local report_error = logs.reporter("lmx","error") + +lmx = lmx or { } +local lmx = lmx + +-- This will change: we will just pass the global defaults as argument, but then we need +-- to rewrite some older code or come up with an ugly trick. + +local lmxvariables = { + ['title-default'] = 'ConTeXt LMX File', + ['color-background-green'] = '#4F6F6F', + ['color-background-blue'] = '#6F6F8F', + ['color-background-yellow'] = '#8F8F6F', + ['color-background-purple'] = '#8F6F8F', + ['color-background-body'] = '#808080', + ['color-background-main'] = '#3F3F3F', +} + +local lmxinherited = { + ['title'] = 'title-default', + ['color-background-one'] = 'color-background-green', + ['color-background-two'] = 'color-background-blue', + ['color-background-three'] = 'color-background-one', + ['color-background-four'] = 'color-background-two', +} + +lmx.variables = lmxvariables +lmx.inherited = lmxinherited + +setmetatableindex(lmxvariables,function(t,k) + k = lmxinherited[k] + while k do + local v = rawget(lmxvariables,k) + if v then + return v + end + k = lmxinherited[k] + end +end) + +function lmx.set(key,value) + lmxvariables[key] = value +end + +function lmx.get(key) + return lmxvariables[key] or "" +end + +lmx.report = report_lmx + +-- helpers + +-- the variables table is an empty one that gets linked to a defaults table +-- that gets passed with a creation (first time only) and that itself links +-- to one that gets passed to the converter + +local variables = { } -- we assume no nesting +local result = { } -- we assume no nesting + +local function do_print(one,two,...) + if two then + result[#result+1] = concat { one, two, ... } + else + result[#result+1] = one + end +end + +-- Although it does not make much sense for most elements, we provide a mechanism +-- to print wrapped content, something that is more efficient when we are constructing +-- tables. + +local html = { } +lmx.html = html + +function html.td(str) + if type(str) == "table" then + for i=1,#str do -- spoils t ! + str[i] = format("<td>%s</td>",str[i] or "") + end + result[#result+1] = concat(str) + else + result[#result+1] = format("<td>%s</td>",str or "") + end +end + +function html.th(str) + if type(str) == "table" then + for i=1,#str do -- spoils t ! + str[i] = format("<th>%s</th>",str[i]) + end + result[#result+1] = concat(str) + else + result[#result+1] = format("<th>%s</th>",str or "") + end +end + +function html.a(text,url) + result[#result+1] = format("<a href=%q>%s</a>",url,text) +end + +setmetatableindex(html,function(t,k) + local f = format("<%s>%%s</%s>",k,k) + local v = function(str) result[#result+1] = format(f,str or "") end + t[k] = v + return v +end) + +-- Loading templates: + +local function loadedfile(name) + name = resolvers and resolvers.findfile and resolvers.findfile(name) or name + local data = io.loaddata(name) + if not data or data == "" then + report_lmx("file %a is empty",name) + end + return data +end + +local function loadedsubfile(name) + return io.loaddata(resolvers and resolvers.findfile and resolvers.findfile(name) or name) +end + +lmx.loadedfile = loadedfile + +-- A few helpers (the next one could end up in l-lpeg): + +local usedpaths = { } +local givenpath = nil + +local do_nested_include = nil + +local pattern = lpeg.replacer { + ["&"] = "&", + [">"] = ">", + ["<"] = "<", + ['"'] = """, +} + +local function do_escape(str) + return lpegmatch(pattern,str) or str +end + +local function do_variable(str) + local value = variables[str] + if not trace_variables then + -- nothing + elseif type(value) == "string" then + if #value > 80 then + report_lmx("variable %a is set to: %s ...",str,collapsespaces(sub(value,1,80))) + else + report_lmx("variable %a is set to: %s",str,collapsespaces(value)) + end + elseif type(value) == "nil" then + report_lmx("variable %a is set to: %s",str,"<!-- unset -->") + else + report_lmx("variable %a is set to: %S",str,value) + end + if type(value) == "function" then -- obsolete ... will go away + return value(str) + else + return value + end +end + +local function do_type(str) + if str and str ~= "" then + result[#result+1] = format("<tt>%s</tt>",do_escape(str)) + end +end + +local function do_fprint(str,...) + if str and str ~= "" then + result[#result+1] = format(str,...) + end +end + +local function do_eprint(str,...) + if str and str ~= "" then + result[#result+1] = lpegmatch(pattern,format(str,...)) + end +end + +local function do_print_variable(str) + local str = do_variable(str) -- variables[str] + if str and str ~= "" then + result[#result+1] = str + end +end + +local function do_type_variable(str) + local str = do_variable(str) -- variables[str] + if str and str ~= "" then + result[#result+1] = format("<tt>%s</tt>",do_escape(str)) + end +end + +local function do_include(filename,option) + local data = loadedsubfile(filename) + if (not data or data == "") and givenpath then + data = loadedsubfile(joinpath(givenpath,filename)) + end + if (not data or data == "") and type(usedpaths) == "table" then + for i=1,#usedpaths do + data = loadedsubfile(joinpath(usedpaths[i],filename)) + if data and data ~= "" then + break + end + end + end + if not data or data == "" then + data = format("<!-- unknown lmx include file: %s -->",filename) + report_lmx("include file %a is empty",filename) + else + -- report_lmx("included file: %s",filename) + data = do_nested_include(data) + end + if filesuffix(filename,"css") and option == "strip" then -- new + data = lmx.stripcss(data) + end + return data +end + +-- Flushers: + +lmx.print = do_print +lmx.type = do_type +lmx.eprint = do_eprint +lmx.fprint = do_fprint + +lmx.escape = do_escape +lmx.urlescape = url.escape +lmx.variable = do_variable +lmx.include = do_include + +lmx.inject = do_print +lmx.finject = do_fprint +lmx.einject = do_eprint + +lmx.pv = do_print_variable +lmx.tv = do_type_variable + +-- The next functions set up the closure. + +function lmx.initialize(d,v) + if not v then + setmetatableindex(d,lmxvariables) + if variables ~= d then + setmetatableindex(variables,d) + if trace_variables then + report_lmx("using chain: variables => given defaults => lmx variables") + end + elseif trace_variables then + report_lmx("using chain: variables == given defaults => lmx variables") + end + elseif d ~= v then + setmetatableindex(v,d) + if d ~= lmxvariables then + setmetatableindex(d,lmxvariables) + if variables ~= v then + setmetatableindex(variables,v) + if trace_variables then + report_lmx("using chain: variables => given variables => given defaults => lmx variables") + end + elseif trace_variables then + report_lmx("using chain: variables == given variables => given defaults => lmx variables") + end + else + if variables ~= v then + setmetatableindex(variables,v) + if trace_variables then + report_lmx("using chain: variabes => given variables => given defaults") + end + elseif trace_variables then + report_lmx("using chain: variables == given variables => given defaults") + end + end + else + setmetatableindex(v,lmxvariables) + if variables ~= v then + setmetatableindex(variables,v) + if trace_variables then + report_lmx("using chain: variables => given variables => lmx variables") + end + elseif trace_variables then + report_lmx("using chain: variables == given variables => lmx variables") + end + end + result = { } +end + +function lmx.finalized() + local collapsed = concat(result) + result = { } -- free memory + return collapsed +end + +function lmx.getvariables() + return variables +end + +function lmx.reset() + -- obsolete +end + +-- Creation: (todo: strip <!-- -->) + +-- local template = [[ +-- return function(defaults,variables) +-- +-- -- initialize +-- +-- lmx.initialize(defaults,variables) +-- +-- -- interface +-- +-- local definitions = { } +-- local variables = lmx.getvariables() +-- local html = lmx.html +-- local inject = lmx.print +-- local finject = lmx.fprint +-- local einject = lmx.eprint +-- local escape = lmx.escape +-- local verbose = lmx.type +-- +-- -- shortcuts (sort of obsolete as there is no gain) +-- +-- local p = lmx.print +-- local f = lmx.fprint +-- local v = lmx.variable +-- local e = lmx.escape +-- local t = lmx.type +-- local pv = lmx.pv +-- local tv = lmx.tv +-- +-- -- generator +-- +-- %s +-- +-- -- finalize +-- +-- return lmx.finalized() +-- +-- end +-- ]] + +local template = [[ +-- interface + +local html = lmx.html +local inject = lmx.print +local finject = lmx.fprint -- better use the following +local einject = lmx.eprint -- better use the following +local injectf = lmx.fprint +local injecte = lmx.eprint +local injectfmt = lmx.fprint +local injectesc = lmx.eprint +local escape = lmx.escape +local verbose = lmx.type + +local i_n_j_e_c_t = lmx.print + +-- shortcuts (sort of obsolete as there is no gain) + +local p = lmx.print +local f = lmx.fprint +local v = lmx.variable +local e = lmx.escape +local t = lmx.type +local pv = lmx.pv +local tv = lmx.tv + +local lmx_initialize = lmx.initialize +local lmx_finalized = lmx.finalized +local lmx_getvariables = lmx.getvariables + +-- generator + +return function(defaults,variables) + + lmx_initialize(defaults,variables) + + local definitions = { } + local variables = lmx_getvariables() + + %s -- the action: appends to result + + return lmx_finalized() + +end +]] + +local function savedefinition(definitions,tag,content) + definitions[tag] = content + return "" +end + +local function getdefinition(definitions,tag) + return definitions[tag] or "" +end + +local whitespace = lpeg.patterns.whitespace +local optionalspaces = whitespace^0 + +local dquote = P('"') + +local begincomment = P("<!--") -- only makes sense when we in intercept pre|script|style +local endcomment = P("-->") + +local beginembedxml = P("<?") +local endembedxml = P("?>") + +local beginembedcss = P("/*") +local endembedcss = P("*/") + +local gobbledendxml = (optionalspaces * endembedxml) / "" +----- argumentxml = (1-gobbledendxml)^0 +local argumentxml = (whitespace^1 + dquote * C((1-dquote)^1) * dquote + C((1-gobbledendxml-whitespace)^1))^0 + +local gobbledendcss = (optionalspaces * endembedcss) / "" +----- argumentcss = (1-gobbledendcss)^0 +local argumentcss = (whitespace^1 + dquote * C((1-dquote)^1) * dquote + C((1-gobbledendcss-whitespace)^1))^0 + +local commentxml = (begincomment * (1-endcomment)^0 * endcomment) / "" + +local beginluaxml = (beginembedxml * P("lua")) / "" +local endluaxml = endembedxml / "" + +local luacodexml = beginluaxml + * (1-endluaxml)^1 + * endluaxml + +local beginluacss = (beginembedcss * P("lua")) / "" +local endluacss = endembedcss / "" + +local luacodecss = beginluacss + * (1-endluacss)^1 + * endluacss + +local othercode = (1-beginluaxml-beginluacss)^1 / " i_n_j_e_c_t[==[%0]==] " + +local includexml = ((beginembedxml * P("lmx-include") * optionalspaces) / "") + * (argumentxml / do_include) + * gobbledendxml + +local includecss = ((beginembedcss * P("lmx-include") * optionalspaces) / "") + * (argumentcss / do_include) + * gobbledendcss + +local definexml_b = ((beginembedxml * P("lmx-define-begin") * optionalspaces) / "") + * argumentxml + * gobbledendxml + +local definexml_e = ((beginembedxml * P("lmx-define-end") * optionalspaces) / "") + * argumentxml + * gobbledendxml + +local definexml_c = C((1-definexml_e)^0) + +local definexml = (Carg(1) * C(definexml_b) * definexml_c * definexml_e) / savedefinition + +local resolvexml = ((beginembedxml * P("lmx-resolve") * optionalspaces) / "") + * ((Carg(1) * C(argumentxml)) / getdefinition) + * gobbledendxml + +local definecss_b = ((beginembedcss * P("lmx-define-begin") * optionalspaces) / "") + * argumentcss + * gobbledendcss + +local definecss_e = ((beginembedcss * P("lmx-define-end") * optionalspaces) / "") + * argumentcss + * gobbledendcss + +local definecss_c = C((1-definecss_e)^0) + +local definecss = (Carg(1) * C(definecss_b) * definecss_c * definecss_e) / savedefinition + +local resolvecss = ((beginembedcss * P("lmx-resolve") * optionalspaces) / "") + * ((Carg(1) * C(argumentcss)) / getdefinition) + * gobbledendcss + +----- pattern_1 = Cs((commentxml + includexml + includecss + P(1))^0) -- get rid of xml comments asap .. not good enough: embedded css and script is in <!-- .. also <pre> +local pattern_1 = Cs((includexml + includecss + P(1))^0) +local pattern_2 = Cs((definexml + resolvexml + definecss + resolvecss + P(1))^0) +local pattern_3 = Cs((luacodexml + luacodecss + othercode)^0) + +local cache = { } + +local function lmxerror(str) + report_error(str) + return html.tt(str) +end + +local function wrapper(converter,defaults,variables) + local outcome, message = pcall(converter,defaults,variables) + if not outcome then + return lmxerror(format("error in conversion: %s",message)) + else + return message + end +end + +do_nested_include = function(data) -- also used in include + return lpegmatch(pattern_1,data) +end + +local function lmxnew(data,defaults,nocache,path) -- todo: use defaults in calling routines + data = data or "" + local known = cache[data] + if not known then + givenpath = path + usedpaths = lmxvariables.includepath or { } + if type(usedpaths) == "string" then + usedpaths = { usedpaths } + end + data = lpegmatch(pattern_1,data) + data = lpegmatch(pattern_2,data,1,{}) + data = lpegmatch(pattern_3,data) + local converted = loadstring(format(template,data)) + if converted then + converted = converted() + end + defaults = defaults or { } + local converter + if converted then + converter = function(variables) + return wrapper(converted,defaults,variables) + end + else + report_error("error in:\n%s\n:",data) + converter = function() lmxerror("error in template") end + end + known = { + data = defaults.trace and data or "", + variables = defaults, + converter = converter, + } + if cache_templates and nocache ~= false then + cache[data] = known + end + elseif variables then + known.variables = variables + end + return known, known.variables +end + +local function lmxresult(self,variables) + if self then + local converter = self.converter + if converter then + local converted = converter(variables) + if trace_variables then -- will become templates + report_lmx("converted size: %s",#converted) + end + return converted or lmxerror("no result from converter") + else + return lmxerror("invalid converter") + end + else + return lmxerror("invalid specification") + end +end + +lmx.new = lmxnew +lmx.result = lmxresult + +local loadedfiles = { } + +function lmx.convertstring(templatestring,variables,nocache,path) + return lmxresult(lmxnew(templatestring,nil,nocache,path),variables) +end + +function lmx.convertfile(templatefile,variables,nocache) + if trace_variables then -- will become templates + report_lmx("converting file %a",templatefile) + end + local converter = loadedfiles[templatefile] + if not converter then + converter = lmxnew(loadedfile(templatefile),nil,nocache,pathpart(templatefile)) + loadedfiles[templatefile] = converter + end + return lmxresult(converter,variables) +end + +local function lmxconvert(templatefile,resultfile,variables,nocache) -- or (templatefile,variables) + if trace_variables then -- will become templates + report_lmx("converting file %a",templatefile) + end + if not variables and type(resultfile) == "table" then + variables = resultfile + end + local converter = loadedfiles[templatefile] + if not converter then + converter = lmxnew(loadedfile(templatefile),nil,nocache,pathpart(templatefile)) + if cache_files then + loadedfiles[templatefile] = converter + end + end + local result = lmxresult(converter,variables) + if resultfile then + io.savedata(resultfile,result) + else + return result + end +end + +lmx.convert = lmxconvert + +-- helpers + +local nocomment = (beginembedcss * (1 - endembedcss)^1 * endembedcss) / "" +local nowhitespace = whitespace^1 / " " -- "" +local semistripped = whitespace^1 / "" * P(";") +local stripper = Cs((nocomment + semistripped + nowhitespace + 1)^1) + +function lmx.stripcss(str) + return lpegmatch(stripper,str) +end + +function lmx.color(r,g,b,a) + if r > 1 then + r = 1 + end + if g > 1 then + g = 1 + end + if b > 1 then + b = 1 + end + if not a then + a= 0 + elseif a > 1 then + a = 1 + end + if a > 0 then + return format("rgba(%s%%,%s%%,%s%%,%s)",r*100,g*100,b*100,a) + else + return format("rgb(%s%%,%s%%,%s%%)",r*100,g*100,b*100) + end +end + +-- these can be overloaded + +lmx.lmxfile = string.itself +lmx.htmfile = string.itself +lmx.popupfile = os.launch + +local function lmxmake(name,variables) + local lmxfile = lmx.lmxfile(name) + local htmfile = lmx.htmfile(name) + if lmxfile == htmfile then + htmfile = replacesuffix(lmxfile,"html") + end + lmxconvert(lmxfile,htmfile,variables) + return htmfile +end + +lmx.make = lmxmake + +function lmx.show(name,variables) + local htmfile = lmxmake(name,variables) + lmx.popupfile(htmfile) + return htmfile +end + +-- Command line (will become mtx-lmx): + +if arg then + if arg[1] == "--show" then if arg[2] then lmx.show (arg[2]) end + elseif arg[1] == "--convert" then if arg[2] then lmx.convert(arg[2], arg[3] or "temp.html") end + end +end + +-- Test 1: + +-- inspect(lmx.result(lmx.new(io.loaddata("t:/sources/context-timing.lmx")))) + +-- Test 2: + +-- local str = [[ +-- <?lmx-include context.css strip ?> +-- <test> +-- <?lmx-define-begin whatever?>some content a<?lmx-define-end ?> +-- <?lmx-define-begin somemore?>some content b<?lmx-define-end ?> +-- <more> +-- <?lmx-resolve whatever ?> +-- <?lua +-- for i=1,10 do end +-- ?> +-- <?lmx-resolve somemore ?> +-- </more> +-- <td><?lua p(100) ?></td> +-- <td><?lua p(variables.a) ?></td> +-- <td><?lua p(variables.b) ?></td> +-- <td><?lua p(variables.c) ?></td> +-- <td><?lua pv('title-default') ?></td> +-- </test> +-- ]] + +-- local defaults = { trace = true, a = 3, b = 3 } +-- local result = lmx.new(str,defaults) +-- inspect(result.data) +-- inspect(result.converter(defaults)) +-- inspect(result.converter { a = 1 }) +-- inspect(lmx.result(result, { b = 2 })) +-- inspect(lmx.result(result, { a = 20000, b = 40000 })) |