xref: /freebsd-13.1/tools/lua/template.lua (revision 94cba803)
1*94cba803SRyan Moeller-- From lua-resty-template (modified to remove external dependencies)
2*94cba803SRyan Moeller--[[
3*94cba803SRyan MoellerCopyright (c) 2014 - 2020 Aapo Talvensaari
4*94cba803SRyan MoellerAll rights reserved.
5*94cba803SRyan Moeller
6*94cba803SRyan MoellerRedistribution and use in source and binary forms, with or without modification,
7*94cba803SRyan Moellerare permitted provided that the following conditions are met:
8*94cba803SRyan Moeller
9*94cba803SRyan Moeller* Redistributions of source code must retain the above copyright notice, this
10*94cba803SRyan Moeller  list of conditions and the following disclaimer.
11*94cba803SRyan Moeller
12*94cba803SRyan Moeller* Redistributions in binary form must reproduce the above copyright notice, this
13*94cba803SRyan Moeller  list of conditions and the following disclaimer in the documentation and/or
14*94cba803SRyan Moeller  other materials provided with the distribution.
15*94cba803SRyan Moeller
16*94cba803SRyan Moeller* Neither the name of the {organization} nor the names of its
17*94cba803SRyan Moeller  contributors may be used to endorse or promote products derived from
18*94cba803SRyan Moeller  this software without specific prior written permission.
19*94cba803SRyan Moeller
20*94cba803SRyan MoellerTHIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
21*94cba803SRyan MoellerANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
22*94cba803SRyan MoellerWARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23*94cba803SRyan MoellerDISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
24*94cba803SRyan MoellerANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
25*94cba803SRyan Moeller(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
26*94cba803SRyan MoellerLOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27*94cba803SRyan MoellerANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28*94cba803SRyan Moeller(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
29*94cba803SRyan MoellerSOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30*94cba803SRyan Moeller]]--
31*94cba803SRyan Moeller-- $FreeBSD$
32*94cba803SRyan Moeller
33*94cba803SRyan Moellerlocal setmetatable = setmetatable
34*94cba803SRyan Moellerlocal loadstring = loadstring
35*94cba803SRyan Moellerlocal tostring = tostring
36*94cba803SRyan Moellerlocal setfenv = setfenv
37*94cba803SRyan Moellerlocal require = require
38*94cba803SRyan Moellerlocal concat = table.concat
39*94cba803SRyan Moellerlocal assert = assert
40*94cba803SRyan Moellerlocal write = io.write
41*94cba803SRyan Moellerlocal pcall = pcall
42*94cba803SRyan Moellerlocal phase
43*94cba803SRyan Moellerlocal open = io.open
44*94cba803SRyan Moellerlocal load = load
45*94cba803SRyan Moellerlocal type = type
46*94cba803SRyan Moellerlocal dump = string.dump
47*94cba803SRyan Moellerlocal find = string.find
48*94cba803SRyan Moellerlocal gsub = string.gsub
49*94cba803SRyan Moellerlocal byte = string.byte
50*94cba803SRyan Moellerlocal null
51*94cba803SRyan Moellerlocal sub = string.sub
52*94cba803SRyan Moellerlocal var
53*94cba803SRyan Moeller
54*94cba803SRyan Moellerlocal _VERSION = _VERSION
55*94cba803SRyan Moellerlocal _ENV = _ENV -- luacheck: globals _ENV
56*94cba803SRyan Moellerlocal _G = _G
57*94cba803SRyan Moeller
58*94cba803SRyan Moellerlocal HTML_ENTITIES = {
59*94cba803SRyan Moeller    ["&"] = "&",
60*94cba803SRyan Moeller    ["<"] = "&lt;",
61*94cba803SRyan Moeller    [">"] = "&gt;",
62*94cba803SRyan Moeller    ['"'] = "&quot;",
63*94cba803SRyan Moeller    ["'"] = "&#39;",
64*94cba803SRyan Moeller    ["/"] = "&#47;"
65*94cba803SRyan Moeller}
66*94cba803SRyan Moeller
67*94cba803SRyan Moellerlocal CODE_ENTITIES = {
68*94cba803SRyan Moeller    ["{"] = "&#123;",
69*94cba803SRyan Moeller    ["}"] = "&#125;",
70*94cba803SRyan Moeller    ["&"] = "&amp;",
71*94cba803SRyan Moeller    ["<"] = "&lt;",
72*94cba803SRyan Moeller    [">"] = "&gt;",
73*94cba803SRyan Moeller    ['"'] = "&quot;",
74*94cba803SRyan Moeller    ["'"] = "&#39;",
75*94cba803SRyan Moeller    ["/"] = "&#47;"
76*94cba803SRyan Moeller}
77*94cba803SRyan Moeller
78*94cba803SRyan Moellerlocal VAR_PHASES
79*94cba803SRyan Moeller
80*94cba803SRyan Moellerlocal ESC    = byte("\27")
81*94cba803SRyan Moellerlocal NUL    = byte("\0")
82*94cba803SRyan Moellerlocal HT     = byte("\t")
83*94cba803SRyan Moellerlocal VT     = byte("\v")
84*94cba803SRyan Moellerlocal LF     = byte("\n")
85*94cba803SRyan Moellerlocal SOL    = byte("/")
86*94cba803SRyan Moellerlocal BSOL   = byte("\\")
87*94cba803SRyan Moellerlocal SP     = byte(" ")
88*94cba803SRyan Moellerlocal AST    = byte("*")
89*94cba803SRyan Moellerlocal NUM    = byte("#")
90*94cba803SRyan Moellerlocal LPAR   = byte("(")
91*94cba803SRyan Moellerlocal LSQB   = byte("[")
92*94cba803SRyan Moellerlocal LCUB   = byte("{")
93*94cba803SRyan Moellerlocal MINUS  = byte("-")
94*94cba803SRyan Moellerlocal PERCNT = byte("%")
95*94cba803SRyan Moeller
96*94cba803SRyan Moellerlocal EMPTY  = ""
97*94cba803SRyan Moeller
98*94cba803SRyan Moellerlocal VIEW_ENV
99*94cba803SRyan Moellerif _VERSION == "Lua 5.1" then
100*94cba803SRyan Moeller    VIEW_ENV = { __index = function(t, k)
101*94cba803SRyan Moeller        return t.context[k] or t.template[k] or _G[k]
102*94cba803SRyan Moeller    end }
103*94cba803SRyan Moellerelse
104*94cba803SRyan Moeller    VIEW_ENV = { __index = function(t, k)
105*94cba803SRyan Moeller        return t.context[k] or t.template[k] or _ENV[k]
106*94cba803SRyan Moeller    end }
107*94cba803SRyan Moellerend
108*94cba803SRyan Moeller
109*94cba803SRyan Moellerlocal newtab
110*94cba803SRyan Moellerdo
111*94cba803SRyan Moeller    local ok
112*94cba803SRyan Moeller    ok, newtab = pcall(require, "table.new")
113*94cba803SRyan Moeller    if not ok then newtab = function() return {} end end
114*94cba803SRyan Moellerend
115*94cba803SRyan Moeller
116*94cba803SRyan Moellerlocal function enabled(val)
117*94cba803SRyan Moeller    if val == nil then return true end
118*94cba803SRyan Moeller    return val == true or (val == "1" or val == "true" or val == "on")
119*94cba803SRyan Moellerend
120*94cba803SRyan Moeller
121*94cba803SRyan Moellerlocal function trim(s)
122*94cba803SRyan Moeller    return gsub(gsub(s, "^%s+", EMPTY), "%s+$", EMPTY)
123*94cba803SRyan Moellerend
124*94cba803SRyan Moeller
125*94cba803SRyan Moellerlocal function rpos(view, s)
126*94cba803SRyan Moeller    while s > 0 do
127*94cba803SRyan Moeller        local c = byte(view, s, s)
128*94cba803SRyan Moeller        if c == SP or c == HT or c == VT or c == NUL then
129*94cba803SRyan Moeller            s = s - 1
130*94cba803SRyan Moeller        else
131*94cba803SRyan Moeller            break
132*94cba803SRyan Moeller        end
133*94cba803SRyan Moeller    end
134*94cba803SRyan Moeller    return s
135*94cba803SRyan Moellerend
136*94cba803SRyan Moeller
137*94cba803SRyan Moellerlocal function escaped(view, s)
138*94cba803SRyan Moeller    if s > 1 and byte(view, s - 1, s - 1) == BSOL then
139*94cba803SRyan Moeller        if s > 2 and byte(view, s - 2, s - 2) == BSOL then
140*94cba803SRyan Moeller            return false, 1
141*94cba803SRyan Moeller        else
142*94cba803SRyan Moeller            return true, 1
143*94cba803SRyan Moeller        end
144*94cba803SRyan Moeller    end
145*94cba803SRyan Moeller    return false, 0
146*94cba803SRyan Moellerend
147*94cba803SRyan Moeller
148*94cba803SRyan Moellerlocal function read_file(path)
149*94cba803SRyan Moeller    local file, err = open(path, "rb")
150*94cba803SRyan Moeller    if not file then return nil, err end
151*94cba803SRyan Moeller    local content
152*94cba803SRyan Moeller    content, err = file:read "*a"
153*94cba803SRyan Moeller    file:close()
154*94cba803SRyan Moeller    return content, err
155*94cba803SRyan Moellerend
156*94cba803SRyan Moeller
157*94cba803SRyan Moellerlocal function load_view(template)
158*94cba803SRyan Moeller    return function(view, plain)
159*94cba803SRyan Moeller	if plain == true then return view end
160*94cba803SRyan Moeller	local path, root = view, template.root
161*94cba803SRyan Moeller	if root and root ~= EMPTY then
162*94cba803SRyan Moeller	    if byte(root, -1) == SOL then root = sub(root, 1, -2) end
163*94cba803SRyan Moeller	    if byte(view,  1) == SOL then path = sub(view, 2) end
164*94cba803SRyan Moeller	    path = root .. "/" .. path
165*94cba803SRyan Moeller	end
166*94cba803SRyan Moeller	return plain == false and assert(read_file(path)) or read_file(path) or view
167*94cba803SRyan Moeller    end
168*94cba803SRyan Moellerend
169*94cba803SRyan Moeller
170*94cba803SRyan Moellerlocal function load_file(func)
171*94cba803SRyan Moeller    return function(view) return func(view, false) end
172*94cba803SRyan Moellerend
173*94cba803SRyan Moeller
174*94cba803SRyan Moellerlocal function load_string(func)
175*94cba803SRyan Moeller    return function(view) return func(view, true) end
176*94cba803SRyan Moellerend
177*94cba803SRyan Moeller
178*94cba803SRyan Moellerlocal function loader(template)
179*94cba803SRyan Moeller    return function(view)
180*94cba803SRyan Moeller	return assert(load(view, nil, nil, setmetatable({ template = template }, VIEW_ENV)))
181*94cba803SRyan Moeller    end
182*94cba803SRyan Moellerend
183*94cba803SRyan Moeller
184*94cba803SRyan Moellerlocal function visit(visitors, content, tag, name)
185*94cba803SRyan Moeller    if not visitors then
186*94cba803SRyan Moeller        return content
187*94cba803SRyan Moeller    end
188*94cba803SRyan Moeller
189*94cba803SRyan Moeller    for i = 1, visitors.n do
190*94cba803SRyan Moeller        content = visitors[i](content, tag, name)
191*94cba803SRyan Moeller    end
192*94cba803SRyan Moeller
193*94cba803SRyan Moeller    return content
194*94cba803SRyan Moellerend
195*94cba803SRyan Moeller
196*94cba803SRyan Moellerlocal function new(template, safe)
197*94cba803SRyan Moeller    template = template or newtab(0, 26)
198*94cba803SRyan Moeller
199*94cba803SRyan Moeller    template._VERSION    = "2.0"
200*94cba803SRyan Moeller    template.cache       = {}
201*94cba803SRyan Moeller    template.load        = load_view(template)
202*94cba803SRyan Moeller    template.load_file   = load_file(template.load)
203*94cba803SRyan Moeller    template.load_string = load_string(template.load)
204*94cba803SRyan Moeller    template.print       = write
205*94cba803SRyan Moeller
206*94cba803SRyan Moeller    local load_chunk = loader(template)
207*94cba803SRyan Moeller
208*94cba803SRyan Moeller    local caching
209*94cba803SRyan Moeller    if VAR_PHASES and VAR_PHASES[phase()] then
210*94cba803SRyan Moeller        caching = enabled(var.template_cache)
211*94cba803SRyan Moeller    else
212*94cba803SRyan Moeller        caching = true
213*94cba803SRyan Moeller    end
214*94cba803SRyan Moeller
215*94cba803SRyan Moeller    local visitors
216*94cba803SRyan Moeller    function template.visit(func)
217*94cba803SRyan Moeller        if not visitors then
218*94cba803SRyan Moeller            visitors = { func, n = 1 }
219*94cba803SRyan Moeller            return
220*94cba803SRyan Moeller        end
221*94cba803SRyan Moeller        visitors.n = visitors.n + 1
222*94cba803SRyan Moeller        visitors[visitors.n] = func
223*94cba803SRyan Moeller    end
224*94cba803SRyan Moeller
225*94cba803SRyan Moeller    function template.caching(enable)
226*94cba803SRyan Moeller        if enable ~= nil then caching = enable == true end
227*94cba803SRyan Moeller        return caching
228*94cba803SRyan Moeller    end
229*94cba803SRyan Moeller
230*94cba803SRyan Moeller    function template.output(s)
231*94cba803SRyan Moeller        if s == nil or s == null then return EMPTY end
232*94cba803SRyan Moeller        if type(s) == "function" then return template.output(s()) end
233*94cba803SRyan Moeller        return tostring(s)
234*94cba803SRyan Moeller    end
235*94cba803SRyan Moeller
236*94cba803SRyan Moeller    function template.escape(s, c)
237*94cba803SRyan Moeller        if type(s) == "string" then
238*94cba803SRyan Moeller            if c then return gsub(s, "[}{\">/<'&]", CODE_ENTITIES) end
239*94cba803SRyan Moeller            return gsub(s, "[\">/<'&]", HTML_ENTITIES)
240*94cba803SRyan Moeller        end
241*94cba803SRyan Moeller        return template.output(s)
242*94cba803SRyan Moeller    end
243*94cba803SRyan Moeller
244*94cba803SRyan Moeller    function template.new(view, layout)
245*94cba803SRyan Moeller        local vt = type(view)
246*94cba803SRyan Moeller
247*94cba803SRyan Moeller        if vt == "boolean" then return new(nil,  view) end
248*94cba803SRyan Moeller        if vt == "table"   then return new(view, safe) end
249*94cba803SRyan Moeller        if vt == "nil"     then return new(nil,  safe) end
250*94cba803SRyan Moeller
251*94cba803SRyan Moeller        local render
252*94cba803SRyan Moeller        local process
253*94cba803SRyan Moeller        if layout then
254*94cba803SRyan Moeller            if type(layout) == "table" then
255*94cba803SRyan Moeller                render = function(self, context)
256*94cba803SRyan Moeller                    context = context or self
257*94cba803SRyan Moeller                    context.blocks = context.blocks or {}
258*94cba803SRyan Moeller                    context.view = template.process(view, context)
259*94cba803SRyan Moeller                    layout.blocks = context.blocks or {}
260*94cba803SRyan Moeller                    layout.view = context.view or EMPTY
261*94cba803SRyan Moeller                    layout:render()
262*94cba803SRyan Moeller                end
263*94cba803SRyan Moeller                process = function(self, context)
264*94cba803SRyan Moeller                    context = context or self
265*94cba803SRyan Moeller                    context.blocks = context.blocks or {}
266*94cba803SRyan Moeller                    context.view = template.process(view, context)
267*94cba803SRyan Moeller                    layout.blocks = context.blocks or {}
268*94cba803SRyan Moeller                    layout.view = context.view
269*94cba803SRyan Moeller                    return tostring(layout)
270*94cba803SRyan Moeller                end
271*94cba803SRyan Moeller            else
272*94cba803SRyan Moeller                render = function(self, context)
273*94cba803SRyan Moeller                    context = context or self
274*94cba803SRyan Moeller                    context.blocks = context.blocks or {}
275*94cba803SRyan Moeller                    context.view = template.process(view, context)
276*94cba803SRyan Moeller                    template.render(layout, context)
277*94cba803SRyan Moeller                end
278*94cba803SRyan Moeller                process = function(self, context)
279*94cba803SRyan Moeller                    context = context or self
280*94cba803SRyan Moeller                    context.blocks = context.blocks or {}
281*94cba803SRyan Moeller                    context.view = template.process(view, context)
282*94cba803SRyan Moeller                    return template.process(layout, context)
283*94cba803SRyan Moeller                end
284*94cba803SRyan Moeller            end
285*94cba803SRyan Moeller        else
286*94cba803SRyan Moeller            render = function(self, context)
287*94cba803SRyan Moeller                return template.render(view, context or self)
288*94cba803SRyan Moeller            end
289*94cba803SRyan Moeller            process = function(self, context)
290*94cba803SRyan Moeller                return template.process(view, context or self)
291*94cba803SRyan Moeller            end
292*94cba803SRyan Moeller        end
293*94cba803SRyan Moeller
294*94cba803SRyan Moeller        if safe then
295*94cba803SRyan Moeller            return setmetatable({
296*94cba803SRyan Moeller                render = function(...)
297*94cba803SRyan Moeller                    local ok, err = pcall(render, ...)
298*94cba803SRyan Moeller                    if not ok then
299*94cba803SRyan Moeller                        return nil, err
300*94cba803SRyan Moeller                    end
301*94cba803SRyan Moeller                end,
302*94cba803SRyan Moeller                process = function(...)
303*94cba803SRyan Moeller                    local ok, output = pcall(process, ...)
304*94cba803SRyan Moeller                    if not ok then
305*94cba803SRyan Moeller                        return nil, output
306*94cba803SRyan Moeller                    end
307*94cba803SRyan Moeller                    return output
308*94cba803SRyan Moeller                end,
309*94cba803SRyan Moeller             }, {
310*94cba803SRyan Moeller                __tostring = function(...)
311*94cba803SRyan Moeller                    local ok, output = pcall(process, ...)
312*94cba803SRyan Moeller                    if not ok then
313*94cba803SRyan Moeller                        return ""
314*94cba803SRyan Moeller                    end
315*94cba803SRyan Moeller                    return output
316*94cba803SRyan Moeller            end })
317*94cba803SRyan Moeller        end
318*94cba803SRyan Moeller
319*94cba803SRyan Moeller        return setmetatable({
320*94cba803SRyan Moeller            render = render,
321*94cba803SRyan Moeller            process = process
322*94cba803SRyan Moeller        }, {
323*94cba803SRyan Moeller            __tostring = process
324*94cba803SRyan Moeller        })
325*94cba803SRyan Moeller    end
326*94cba803SRyan Moeller
327*94cba803SRyan Moeller    function template.precompile(view, path, strip, plain)
328*94cba803SRyan Moeller        local chunk = dump(template.compile(view, nil, plain), strip ~= false)
329*94cba803SRyan Moeller        if path then
330*94cba803SRyan Moeller            local file = open(path, "wb")
331*94cba803SRyan Moeller            file:write(chunk)
332*94cba803SRyan Moeller            file:close()
333*94cba803SRyan Moeller        end
334*94cba803SRyan Moeller        return chunk
335*94cba803SRyan Moeller    end
336*94cba803SRyan Moeller
337*94cba803SRyan Moeller    function template.precompile_string(view, path, strip)
338*94cba803SRyan Moeller        return template.precompile(view, path, strip, true)
339*94cba803SRyan Moeller    end
340*94cba803SRyan Moeller
341*94cba803SRyan Moeller    function template.precompile_file(view, path, strip)
342*94cba803SRyan Moeller        return template.precompile(view, path, strip, false)
343*94cba803SRyan Moeller    end
344*94cba803SRyan Moeller
345*94cba803SRyan Moeller    function template.compile(view, cache_key, plain)
346*94cba803SRyan Moeller        assert(view, "view was not provided for template.compile(view, cache_key, plain)")
347*94cba803SRyan Moeller        if cache_key == "no-cache" then
348*94cba803SRyan Moeller            return load_chunk(template.parse(view, plain)), false
349*94cba803SRyan Moeller        end
350*94cba803SRyan Moeller        cache_key = cache_key or view
351*94cba803SRyan Moeller        local cache = template.cache
352*94cba803SRyan Moeller        if cache[cache_key] then return cache[cache_key], true end
353*94cba803SRyan Moeller        local func = load_chunk(template.parse(view, plain))
354*94cba803SRyan Moeller        if caching then cache[cache_key] = func end
355*94cba803SRyan Moeller        return func, false
356*94cba803SRyan Moeller    end
357*94cba803SRyan Moeller
358*94cba803SRyan Moeller    function template.compile_file(view, cache_key)
359*94cba803SRyan Moeller        return template.compile(view, cache_key, false)
360*94cba803SRyan Moeller    end
361*94cba803SRyan Moeller
362*94cba803SRyan Moeller    function template.compile_string(view, cache_key)
363*94cba803SRyan Moeller        return template.compile(view, cache_key, true)
364*94cba803SRyan Moeller    end
365*94cba803SRyan Moeller
366*94cba803SRyan Moeller    function template.parse(view, plain)
367*94cba803SRyan Moeller        assert(view, "view was not provided for template.parse(view, plain)")
368*94cba803SRyan Moeller        if plain ~= true then
369*94cba803SRyan Moeller            view = template.load(view, plain)
370*94cba803SRyan Moeller            if byte(view, 1, 1) == ESC then return view end
371*94cba803SRyan Moeller        end
372*94cba803SRyan Moeller        local j = 2
373*94cba803SRyan Moeller        local c = {[[
374*94cba803SRyan Moellercontext=... or {}
375*94cba803SRyan Moellerlocal ___,blocks,layout={},blocks or {}
376*94cba803SRyan Moellerlocal function include(v, c) return template.process(v, c or context) end
377*94cba803SRyan Moellerlocal function echo(...) for i=1,select("#", ...) do ___[#___+1] = tostring(select(i, ...)) end end
378*94cba803SRyan Moeller]] }
379*94cba803SRyan Moeller        local i, s = 1, find(view, "{", 1, true)
380*94cba803SRyan Moeller        while s do
381*94cba803SRyan Moeller            local t, p = byte(view, s + 1, s + 1), s + 2
382*94cba803SRyan Moeller            if t == LCUB then
383*94cba803SRyan Moeller                local e = find(view, "}}", p, true)
384*94cba803SRyan Moeller                if e then
385*94cba803SRyan Moeller                    local z, w = escaped(view, s)
386*94cba803SRyan Moeller                    if i < s - w then
387*94cba803SRyan Moeller                        c[j] = "___[#___+1]=[=[\n"
388*94cba803SRyan Moeller                        c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
389*94cba803SRyan Moeller                        c[j+2] = "]=]\n"
390*94cba803SRyan Moeller                        j=j+3
391*94cba803SRyan Moeller                    end
392*94cba803SRyan Moeller                    if z then
393*94cba803SRyan Moeller                        i = s
394*94cba803SRyan Moeller                    else
395*94cba803SRyan Moeller                        c[j] = "___[#___+1]=template.escape("
396*94cba803SRyan Moeller                        c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "{")
397*94cba803SRyan Moeller                        c[j+2] = ")\n"
398*94cba803SRyan Moeller                        j=j+3
399*94cba803SRyan Moeller                        s, i = e + 1, e + 2
400*94cba803SRyan Moeller                    end
401*94cba803SRyan Moeller                end
402*94cba803SRyan Moeller            elseif t == AST then
403*94cba803SRyan Moeller                local e = find(view, "*}", p, true)
404*94cba803SRyan Moeller                if e then
405*94cba803SRyan Moeller                    local z, w = escaped(view, s)
406*94cba803SRyan Moeller                    if i < s - w then
407*94cba803SRyan Moeller                        c[j] = "___[#___+1]=[=[\n"
408*94cba803SRyan Moeller                        c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
409*94cba803SRyan Moeller                        c[j+2] = "]=]\n"
410*94cba803SRyan Moeller                        j=j+3
411*94cba803SRyan Moeller                    end
412*94cba803SRyan Moeller                    if z then
413*94cba803SRyan Moeller                        i = s
414*94cba803SRyan Moeller                    else
415*94cba803SRyan Moeller                        c[j] = "___[#___+1]=template.output("
416*94cba803SRyan Moeller                        c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "*")
417*94cba803SRyan Moeller                        c[j+2] = ")\n"
418*94cba803SRyan Moeller                        j=j+3
419*94cba803SRyan Moeller                        s, i = e + 1, e + 2
420*94cba803SRyan Moeller                    end
421*94cba803SRyan Moeller                end
422*94cba803SRyan Moeller            elseif t == PERCNT then
423*94cba803SRyan Moeller                local e = find(view, "%}", p, true)
424*94cba803SRyan Moeller                if e then
425*94cba803SRyan Moeller                    local z, w = escaped(view, s)
426*94cba803SRyan Moeller                    if z then
427*94cba803SRyan Moeller                        if i < s - w then
428*94cba803SRyan Moeller                            c[j] = "___[#___+1]=[=[\n"
429*94cba803SRyan Moeller                            c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
430*94cba803SRyan Moeller                            c[j+2] = "]=]\n"
431*94cba803SRyan Moeller                            j=j+3
432*94cba803SRyan Moeller                        end
433*94cba803SRyan Moeller                        i = s
434*94cba803SRyan Moeller                    else
435*94cba803SRyan Moeller                        local n = e + 2
436*94cba803SRyan Moeller                        if byte(view, n, n) == LF then
437*94cba803SRyan Moeller                            n = n + 1
438*94cba803SRyan Moeller                        end
439*94cba803SRyan Moeller                        local r = rpos(view, s - 1)
440*94cba803SRyan Moeller                        if i <= r then
441*94cba803SRyan Moeller                            c[j] = "___[#___+1]=[=[\n"
442*94cba803SRyan Moeller                            c[j+1] = visit(visitors, sub(view, i, r))
443*94cba803SRyan Moeller                            c[j+2] = "]=]\n"
444*94cba803SRyan Moeller                            j=j+3
445*94cba803SRyan Moeller                        end
446*94cba803SRyan Moeller                        c[j] = visit(visitors, trim(sub(view, p, e - 1)), "%")
447*94cba803SRyan Moeller                        c[j+1] = "\n"
448*94cba803SRyan Moeller                        j=j+2
449*94cba803SRyan Moeller                        s, i = n - 1, n
450*94cba803SRyan Moeller                    end
451*94cba803SRyan Moeller                end
452*94cba803SRyan Moeller            elseif t == LPAR then
453*94cba803SRyan Moeller                local e = find(view, ")}", p, true)
454*94cba803SRyan Moeller                if e then
455*94cba803SRyan Moeller                    local z, w = escaped(view, s)
456*94cba803SRyan Moeller                    if i < s - w then
457*94cba803SRyan Moeller                        c[j] = "___[#___+1]=[=[\n"
458*94cba803SRyan Moeller                        c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
459*94cba803SRyan Moeller                        c[j+2] = "]=]\n"
460*94cba803SRyan Moeller                        j=j+3
461*94cba803SRyan Moeller                    end
462*94cba803SRyan Moeller                    if z then
463*94cba803SRyan Moeller                        i = s
464*94cba803SRyan Moeller                    else
465*94cba803SRyan Moeller                        local f = visit(visitors, sub(view, p, e - 1), "(")
466*94cba803SRyan Moeller                        local x = find(f, ",", 2, true)
467*94cba803SRyan Moeller                        if x then
468*94cba803SRyan Moeller                            c[j] = "___[#___+1]=include([=["
469*94cba803SRyan Moeller                            c[j+1] = trim(sub(f, 1, x - 1))
470*94cba803SRyan Moeller                            c[j+2] = "]=],"
471*94cba803SRyan Moeller                            c[j+3] = trim(sub(f, x + 1))
472*94cba803SRyan Moeller                            c[j+4] = ")\n"
473*94cba803SRyan Moeller                            j=j+5
474*94cba803SRyan Moeller                        else
475*94cba803SRyan Moeller                            c[j] = "___[#___+1]=include([=["
476*94cba803SRyan Moeller                            c[j+1] = trim(f)
477*94cba803SRyan Moeller                            c[j+2] = "]=])\n"
478*94cba803SRyan Moeller                            j=j+3
479*94cba803SRyan Moeller                        end
480*94cba803SRyan Moeller                        s, i = e + 1, e + 2
481*94cba803SRyan Moeller                    end
482*94cba803SRyan Moeller                end
483*94cba803SRyan Moeller            elseif t == LSQB then
484*94cba803SRyan Moeller                local e = find(view, "]}", p, true)
485*94cba803SRyan Moeller                if e then
486*94cba803SRyan Moeller                    local z, w = escaped(view, s)
487*94cba803SRyan Moeller                    if i < s - w then
488*94cba803SRyan Moeller                        c[j] = "___[#___+1]=[=[\n"
489*94cba803SRyan Moeller                        c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
490*94cba803SRyan Moeller                        c[j+2] = "]=]\n"
491*94cba803SRyan Moeller                        j=j+3
492*94cba803SRyan Moeller                    end
493*94cba803SRyan Moeller                    if z then
494*94cba803SRyan Moeller                        i = s
495*94cba803SRyan Moeller                    else
496*94cba803SRyan Moeller                        c[j] = "___[#___+1]=include("
497*94cba803SRyan Moeller                        c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "[")
498*94cba803SRyan Moeller                        c[j+2] = ")\n"
499*94cba803SRyan Moeller                        j=j+3
500*94cba803SRyan Moeller                        s, i = e + 1, e + 2
501*94cba803SRyan Moeller                    end
502*94cba803SRyan Moeller                end
503*94cba803SRyan Moeller            elseif t == MINUS then
504*94cba803SRyan Moeller                local e = find(view, "-}", p, true)
505*94cba803SRyan Moeller                if e then
506*94cba803SRyan Moeller                    local x, y = find(view, sub(view, s, e + 1), e + 2, true)
507*94cba803SRyan Moeller                    if x then
508*94cba803SRyan Moeller                        local z, w = escaped(view, s)
509*94cba803SRyan Moeller                        if z then
510*94cba803SRyan Moeller                            if i < s - w then
511*94cba803SRyan Moeller                                c[j] = "___[#___+1]=[=[\n"
512*94cba803SRyan Moeller                                c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
513*94cba803SRyan Moeller                                c[j+2] = "]=]\n"
514*94cba803SRyan Moeller                                j=j+3
515*94cba803SRyan Moeller                            end
516*94cba803SRyan Moeller                            i = s
517*94cba803SRyan Moeller                        else
518*94cba803SRyan Moeller                            y = y + 1
519*94cba803SRyan Moeller                            x = x - 1
520*94cba803SRyan Moeller                            if byte(view, y, y) == LF then
521*94cba803SRyan Moeller                                y = y + 1
522*94cba803SRyan Moeller                            end
523*94cba803SRyan Moeller                            local b = trim(sub(view, p, e - 1))
524*94cba803SRyan Moeller                            if b == "verbatim" or b == "raw" then
525*94cba803SRyan Moeller                                if i < s - w then
526*94cba803SRyan Moeller                                    c[j] = "___[#___+1]=[=[\n"
527*94cba803SRyan Moeller                                    c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
528*94cba803SRyan Moeller                                    c[j+2] = "]=]\n"
529*94cba803SRyan Moeller                                    j=j+3
530*94cba803SRyan Moeller                                end
531*94cba803SRyan Moeller                                c[j] = "___[#___+1]=[=["
532*94cba803SRyan Moeller                                c[j+1] = visit(visitors, sub(view, e + 2, x))
533*94cba803SRyan Moeller                                c[j+2] = "]=]\n"
534*94cba803SRyan Moeller                                j=j+3
535*94cba803SRyan Moeller                            else
536*94cba803SRyan Moeller                                if byte(view, x, x) == LF then
537*94cba803SRyan Moeller                                    x = x - 1
538*94cba803SRyan Moeller                                end
539*94cba803SRyan Moeller                                local r = rpos(view, s - 1)
540*94cba803SRyan Moeller                                if i <= r then
541*94cba803SRyan Moeller                                    c[j] = "___[#___+1]=[=[\n"
542*94cba803SRyan Moeller                                    c[j+1] = visit(visitors, sub(view, i, r))
543*94cba803SRyan Moeller                                    c[j+2] = "]=]\n"
544*94cba803SRyan Moeller                                    j=j+3
545*94cba803SRyan Moeller                                end
546*94cba803SRyan Moeller                                c[j] = 'blocks["'
547*94cba803SRyan Moeller                                c[j+1] = b
548*94cba803SRyan Moeller                                c[j+2] = '"]=include[=['
549*94cba803SRyan Moeller                                c[j+3] = visit(visitors, sub(view, e + 2, x), "-", b)
550*94cba803SRyan Moeller                                c[j+4] = "]=]\n"
551*94cba803SRyan Moeller                                j=j+5
552*94cba803SRyan Moeller                            end
553*94cba803SRyan Moeller                            s, i = y - 1, y
554*94cba803SRyan Moeller                        end
555*94cba803SRyan Moeller                    end
556*94cba803SRyan Moeller                end
557*94cba803SRyan Moeller            elseif t == NUM then
558*94cba803SRyan Moeller                local e = find(view, "#}", p, true)
559*94cba803SRyan Moeller                if e then
560*94cba803SRyan Moeller                    local z, w = escaped(view, s)
561*94cba803SRyan Moeller                    if i < s - w then
562*94cba803SRyan Moeller                        c[j] = "___[#___+1]=[=[\n"
563*94cba803SRyan Moeller                        c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
564*94cba803SRyan Moeller                        c[j+2] = "]=]\n"
565*94cba803SRyan Moeller                        j=j+3
566*94cba803SRyan Moeller                    end
567*94cba803SRyan Moeller                    if z then
568*94cba803SRyan Moeller                        i = s
569*94cba803SRyan Moeller                    else
570*94cba803SRyan Moeller                        e = e + 2
571*94cba803SRyan Moeller                        if byte(view, e, e) == LF then
572*94cba803SRyan Moeller                            e = e + 1
573*94cba803SRyan Moeller                        end
574*94cba803SRyan Moeller                        s, i = e - 1, e
575*94cba803SRyan Moeller                    end
576*94cba803SRyan Moeller                end
577*94cba803SRyan Moeller            end
578*94cba803SRyan Moeller            s = find(view, "{", s + 1, true)
579*94cba803SRyan Moeller        end
580*94cba803SRyan Moeller        s = sub(view, i)
581*94cba803SRyan Moeller        if s and s ~= EMPTY then
582*94cba803SRyan Moeller            c[j] = "___[#___+1]=[=[\n"
583*94cba803SRyan Moeller            c[j+1] = visit(visitors, s)
584*94cba803SRyan Moeller            c[j+2] = "]=]\n"
585*94cba803SRyan Moeller            j=j+3
586*94cba803SRyan Moeller        end
587*94cba803SRyan Moeller        c[j] = "return layout and include(layout,setmetatable({view=table.concat(___),blocks=blocks},{__index=context})) or table.concat(___)" -- luacheck: ignore
588*94cba803SRyan Moeller        return concat(c)
589*94cba803SRyan Moeller    end
590*94cba803SRyan Moeller
591*94cba803SRyan Moeller    function template.parse_file(view)
592*94cba803SRyan Moeller        return template.parse(view, false)
593*94cba803SRyan Moeller    end
594*94cba803SRyan Moeller
595*94cba803SRyan Moeller    function template.parse_string(view)
596*94cba803SRyan Moeller        return template.parse(view, true)
597*94cba803SRyan Moeller    end
598*94cba803SRyan Moeller
599*94cba803SRyan Moeller    function template.process(view, context, cache_key, plain)
600*94cba803SRyan Moeller        assert(view, "view was not provided for template.process(view, context, cache_key, plain)")
601*94cba803SRyan Moeller        return template.compile(view, cache_key, plain)(context)
602*94cba803SRyan Moeller    end
603*94cba803SRyan Moeller
604*94cba803SRyan Moeller    function template.process_file(view, context, cache_key)
605*94cba803SRyan Moeller        assert(view, "view was not provided for template.process_file(view, context, cache_key)")
606*94cba803SRyan Moeller        return template.compile(view, cache_key, false)(context)
607*94cba803SRyan Moeller    end
608*94cba803SRyan Moeller
609*94cba803SRyan Moeller    function template.process_string(view, context, cache_key)
610*94cba803SRyan Moeller        assert(view, "view was not provided for template.process_string(view, context, cache_key)")
611*94cba803SRyan Moeller        return template.compile(view, cache_key, true)(context)
612*94cba803SRyan Moeller    end
613*94cba803SRyan Moeller
614*94cba803SRyan Moeller    function template.render(view, context, cache_key, plain)
615*94cba803SRyan Moeller        assert(view, "view was not provided for template.render(view, context, cache_key, plain)")
616*94cba803SRyan Moeller        template.print(template.process(view, context, cache_key, plain))
617*94cba803SRyan Moeller    end
618*94cba803SRyan Moeller
619*94cba803SRyan Moeller    function template.render_file(view, context, cache_key)
620*94cba803SRyan Moeller        assert(view, "view was not provided for template.render_file(view, context, cache_key)")
621*94cba803SRyan Moeller        template.render(view, context, cache_key, false)
622*94cba803SRyan Moeller    end
623*94cba803SRyan Moeller
624*94cba803SRyan Moeller    function template.render_string(view, context, cache_key)
625*94cba803SRyan Moeller        assert(view, "view was not provided for template.render_string(view, context, cache_key)")
626*94cba803SRyan Moeller        template.render(view, context, cache_key, true)
627*94cba803SRyan Moeller    end
628*94cba803SRyan Moeller
629*94cba803SRyan Moeller    if safe then
630*94cba803SRyan Moeller        return setmetatable({}, {
631*94cba803SRyan Moeller            __index = function(_, k)
632*94cba803SRyan Moeller                if type(template[k]) == "function" then
633*94cba803SRyan Moeller                    return function(...)
634*94cba803SRyan Moeller                        local ok, a, b = pcall(template[k], ...)
635*94cba803SRyan Moeller                        if not ok then
636*94cba803SRyan Moeller                            return nil, a
637*94cba803SRyan Moeller                        end
638*94cba803SRyan Moeller                        return a, b
639*94cba803SRyan Moeller                    end
640*94cba803SRyan Moeller                end
641*94cba803SRyan Moeller                return template[k]
642*94cba803SRyan Moeller            end,
643*94cba803SRyan Moeller            __new_index = function(_, k, v)
644*94cba803SRyan Moeller                template[k] = v
645*94cba803SRyan Moeller            end,
646*94cba803SRyan Moeller        })
647*94cba803SRyan Moeller    end
648*94cba803SRyan Moeller
649*94cba803SRyan Moeller    return template
650*94cba803SRyan Moellerend
651*94cba803SRyan Moeller
652*94cba803SRyan Moellerreturn new()
653