diff options
Diffstat (limited to 'tex/context/base/trac-lmx.lua')
-rw-r--r-- | tex/context/base/trac-lmx.lua | 606 |
1 files changed, 113 insertions, 493 deletions
diff --git a/tex/context/base/trac-lmx.lua b/tex/context/base/trac-lmx.lua index 97938fb77..452d03002 100644 --- a/tex/context/base/trac-lmx.lua +++ b/tex/context/base/trac-lmx.lua @@ -6,62 +6,42 @@ if not modules then modules = { } end modules ['trac-lmx'] = { license = "see context related readme files" } -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 P, Cc, Cs, C, Carg, lpegmatch = lpeg.P, lpeg.Cc, lpeg.Cs, lpeg.C, lpeg.Carg, lpeg.match +-- todo: use lpeg instead (although not really needed) -local allocate = utilities.storage.allocate -local setmetatableindex = table.setmetatableindex +local gsub, format, concat, byte = string.gsub, string.format, table.concat, string.byte ------ 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 allocate = utilities.storage.allocate -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) +lmx = lmx or { } +local lmx = lmx -local report_lmx = logs.reporter("lmx") -local report_error = logs.reporter("lmx","error") +lmx.variables = allocate() +local lmxvariables = lmx.variables -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 escapes = allocate { + ['&'] = '&', + ['<'] = '<', + ['>'] = '>', + ['"'] = '"' } -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', -} +-- variables -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) +lmxvariables['title-default'] = 'ConTeXt LMX File' +lmxvariables['title'] = lmx.variables['title-default'] +lmxvariables['color-background-green'] = '#4F6F6F' +lmxvariables['color-background-blue'] = '#6F6F8F' +lmxvariables['color-background-yellow'] = '#8F8F6F' +lmxvariables['color-background-purple'] = '#8F6F8F' +lmxvariables['color-background-body'] = '#808080' +lmxvariables['color-background-main'] = '#3F3F3F' +lmxvariables['color-background-one'] = lmxvariables['color-background-green'] +lmxvariables['color-background-two'] = lmxvariables['color-background-blue'] -function lmx.set(key,value) +lmxvariables['color-background-three'] = function() return lmxvariables['color-background-one'] end +lmxvariables['color-background-four'] = function() return lmxvariables['color-background-two'] end + +function lmx.set(key, value) lmxvariables[key] = value end @@ -69,16 +49,9 @@ 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 variables, result = { } -- we assume no nesting local function do_print(one,two,...) if two then @@ -88,125 +61,40 @@ local function do_print(one,two,...) 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) +local function do_escape(str) + str = tostring(str) + str = gsub(str,'&','&') + str = gsub(str,'[<>"]',escapes) + return str 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("empty file: %s",name) - end - return data +local function do_urlescaped(str) + return (gsub(str,"[^%a%d]",format("%%0x",byte("%1")))) end -lmx.loadedfile = loadedfile - --- A few helpers (the next one could end up in l-lpeg): - -local pattern = lpeg.replacer { - ["&"] = "&", - [">"] = ">", - ["<"] = "<", - ['"'] = """, -} - -local function do_escape(str) - return lpegmatch(pattern,str) or str +local function do_type(str) + if str then do_print("<tt>" .. do_escape(str) .. "</tt>") end 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 %q => %s ...",str,string.collapsespaces(sub(value,1,80))) - else - report_lmx("variable %q => %s",str,string.collapsespaces(value)) - end - elseif type(value) == "nil" then - report_lmx("variable %q => <!-- unset -->",str) - else - report_lmx("variable %q => %q",str,tostring(value)) - end - if type(value) == "function" then -- obsolete ... will go away + local value = variables[str] or lmxvariables[str] -- or format("<!-- unset lmx instance variable: %s -->",str or "?") + if type(value) == "function" then 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_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 +function lmx.loadedfile(name) + name = (resolvers and resolvers.findfile and resolvers.findfile(name)) or name + return io.loaddata(name) end -local function do_include(filename) -- todo: store paths of loaded files - local stylepath = lmxvariables.includepath - local data = loadedfile(filename) +local function do_include(filename) + local stylepath = do_variable('includepath') -- todo: store paths of loaded files + local data = lmx.loadedfile(filename) if (not data or data == "") and stylepath and stylepath ~= "" then - data = loadedfile(file.join(stylepath,filename)) + data = lmx.loadedfile(file.join(stylepath,filename)) end if not data or data == "" then data = format("<!-- unknown lmx include file: %s -->",filename) @@ -214,400 +102,132 @@ local function do_include(filename) -- todo: store paths of loaded files return data end --- Flushers: - lmx.print = do_print lmx.type = do_type -lmx.fprint = do_fprint - lmx.escape = do_escape -lmx.urlescape = url.escape +lmx.urlescape = do_escape lmx.variable = do_variable lmx.include = do_include -lmx.inject = do_print -lmx.finject = do_fprint - -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("variables => given defaults => lmx variables") - end - elseif trace_variables then - report_lmx("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("variables => given variables => given defaults => lmx variables") - end - elseif trace_variables then - report_lmx("variables == given variables => given defaults => lmx variables") - end - else - if variables ~= v then - setmetatableindex(variables,v) - if trace_variables then - report_lmx("variabes => given variables => given defaults") - end - elseif trace_variables then - report_lmx("variables == given variables => given defaults") - end - end - else - setmetatableindex(v,lmxvariables) - if variables ~= v then - setmetatableindex(variables,v) - if trace_variables then - report_lmx("variables => given variables => lmx variables") - end - elseif trace_variables then - report_lmx("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 +function lmx.pv(str) + do_print(do_variable(str) or "") end -function lmx.reset() - -- obsolete +function lmx.tv(str) + lmx.type(do_variable(str) or "") 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 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 definitions = { } + local p, v, e, t, pv, tv = lmx.print, lmx.variable, lmx.escape, lmx.type, lmx.pv, lmx.tv + %s ]] -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 begincomment = P("<!--") -local endcomment = P("-->") - -local beginembedxml = P("<?") -local endembedxml = P("?>") - -local beginembedcss = P("/*") -local endembedcss = P("*/") - -local gobbledend = (optionalspaces * endembedxml) / "" -local argument = (1-gobbledend)^0 - -local comment = (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 / " p[==[%0]==] " - -local include = ((beginembedxml * P("lmx-include") * optionalspaces) / "") - * (argument / lmx.include) - * gobbledend - -local define_b = ((beginembedxml * P("lmx-define-begin") * optionalspaces) / "") - * argument - * gobbledend - -local define_e = ((beginembedxml * P("lmx-define-end") * optionalspaces) / "") - * argument - * gobbledend - -local define_c = C((1-define_e)^0) - -local define = (Carg(1) * C(define_b) * define_c * define_e) / savedefinition - -local resolve = ((beginembedxml * P("lmx-resolve") * optionalspaces) / "") - * ((Carg(1) * C(argument)) / getdefinition) - * gobbledend - -local pattern_1 = Cs((comment + include + P(1))^0) -- get rid of comments asap -local pattern_2 = Cs((define + resolve + 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 +local trace = false -function lmxnew(data,defaults,nocache) -- todo: use defaults in calling routines +function lmx.new(data,variables) data = data or "" local known = cache[data] if not known then - 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 - converter = function() lmxerror("error in template") end - end + local definitions = { } + data = gsub(data,"<%?lmx%-include%s+(.-)%s-%?>", function(filename) + return lmx.include(filename) + end) + local definitions = { } + data = gsub(data,"<%?lmx%-define%-begin%s+(%S-)%s-%?>(.-)<%?lmx%-define%-end%s-%?>", function(tag,content) + definitions[tag] = content + return "" + end) + data = gsub(data,"<%?lmx%-resolve%s+(%S-)%s-%?>", function(tag) + return definitions[tag] or "" + end) + data = gsub(data .. "<?lua ?>","(.-)<%?lua%s+(.-)%s*%?>", function(txt,lua) + txt = gsub(txt,"%c+","\n") + return format("p(%q)%s ",txt,lua) -- nb! space + end) + data = format(template,data) known = { - data = defaults.trace and data or "", - variables = defaults, - converter = converter, + data = trace and data, + variables = variables or { }, + converter = loadstring(data), } - 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 +function lmx.reset(self) + self.variables = { } +end + +function lmx.result(self) + if trace then + return self.data else - return lmxerror("invalid specification") + variables, result = self.variables, { } + self.converter() + return concat(result) end end -lmx.new = lmxnew -lmx.result = lmxresult +-- file converter -local loadedfiles = { } +local loaded = { } -function lmx.convertstring(templatestring,variables,nocache) - return lmxresult(lmxnew(templatestring,nil,nocache),variables) -end - -function lmx.convertfile(templatefile,variables,nocache) - if trace_variables then -- will become templates - report_lmx("converting file: %s",templatefile) - end - local converter = loadedfiles[templatefile] - if not converter then - converter = lmxnew(loadedfile(templatefile),nil,nocache) - 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: %s",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) - if cache_files then - loadedfiles[templatefile] = converter - end +function lmx.convert(templatefile,resultfile,variables) + local data = loaded[templatefile] + if not data then + data = lmx.new(lmx.loadedfile(templatefile),variables) + loaded[template] = data + elseif variables then + data.variables = variables end - local result = lmxresult(converter,variables) + local result = lmx.result(data) if resultfile then io.savedata(resultfile,result) else - return result + return lmx.result(data,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) +-- these can be overloaded; we assume that the os handles filename associations -function lmx.stripcss(str) - return lpegmatch(stripper,str) -end +lmx.lmxfile = function(filename) return filename end -- beware, these can be set! +lmx.htmfile = function(filename) return filename end -- beware, these can be set! -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 string.format("rgba(%s%%,%s%%,%s%%,%s)",r*100,g*100,b*100,a) - else - return string.format("rgb(%s%%,%s%%,%s%%)",r*100,g*100,b*100) - end +if os.type == "windows" then + lmx.popupfile = function(filename) os.execute("start " .. filename) end +else + lmx.popupfile = function(filename) os.execute(filename) end end - --- these can be overloaded - -lmx.lmxfile = string.itself -lmx.htmfile = string.itself -lmx.popupfile = os.launch - -function lmxmake(name,variables) +function lmx.make(name,variables) local lmxfile = lmx.lmxfile(name) local htmfile = lmx.htmfile(name) if lmxfile == htmfile then - htmfile = file.replacesuffix(lmxfile,"html") + htmfile = gsub(lmxfile, "%.%a+$", "html") end - lmxconvert(lmxfile,htmfile,variables) + lmx.convert(lmxfile,htmfile,variables) return htmfile end -lmxmake = lmx.make - function lmx.show(name,variables) - local htmfile = lmxmake(name,variables) + local htmfile = lmx.make(name,variables) lmx.popupfile(htmfile) return htmfile end --- Command line (will become mtx-lmx): +-- test + +--~ print(lmx.result(lmx.new(io.loaddata("t:/sources/context-timing.lmx")))) + +-- command line 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 somefile.css ?> --- <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 })) |