summaryrefslogtreecommitdiff
path: root/tex/context/base/trac-lmx.lua
diff options
context:
space:
mode:
Diffstat (limited to 'tex/context/base/trac-lmx.lua')
-rw-r--r--tex/context/base/trac-lmx.lua1464
1 files changed, 732 insertions, 732 deletions
diff --git a/tex/context/base/trac-lmx.lua b/tex/context/base/trac-lmx.lua
index 18c7f6020..1a12d2078 100644
--- a/tex/context/base/trac-lmx.lua
+++ b/tex/context/base/trac-lmx.lua
@@ -1,732 +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 {
- ["&"] = "&amp;",
- [">"] = "&gt;",
- ["<"] = "&lt;",
- ['"'] = "&quot;",
-}
-
-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("<!--")
-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
-
-local pattern_1 = Cs((commentxml + includexml + includecss + P(1))^0) -- get rid of xml comments asap
-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
-
-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
-
-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
-
-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
-
-lmxmake = lmx.make
-
-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 }))
+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 {
+ ["&"] = "&amp;",
+ [">"] = "&gt;",
+ ["<"] = "&lt;",
+ ['"'] = "&quot;",
+}
+
+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("<!--")
+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
+
+local pattern_1 = Cs((commentxml + includexml + includecss + P(1))^0) -- get rid of xml comments asap
+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
+
+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
+
+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
+
+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
+
+lmxmake = lmx.make
+
+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 }))