| --- A template preprocessor. |
| -- Originally by <a href="http://lua-users.org/wiki/SlightlyLessSimpleLuaPreprocessor">Ricki Lake</a> |
| -- <p>There are two rules: <ul> |
| -- <li>lines starting with # are Lua</li> |
| -- <li> otherwise, `$(expr)` is the result of evaluating `expr`</li> |
| -- </ul> |
| -- <pre class=example> |
| -- # for i = 1,3 do |
| -- $(i) Hello, Word! |
| -- # end |
| -- </pre> |
| -- Other escape characters can be used, when the defaults conflict |
| -- with the output language. |
| -- <pre class=example> |
| -- > for _,n in pairs{'one','two','three'} do |
| -- static int l_${n} (luaState *state); |
| -- > end |
| -- </pre> |
| -- See <a href="../../index.html#rici_templates">the Guide</a>. |
| -- @class module |
| -- @name pl.template |
| |
| --[[ |
| module('pl.template') |
| ]] |
| |
| local utils = require 'pl.utils' |
| local append,format = table.insert,string.format |
| |
| local function parseHashLines(chunk,brackets,esc) |
| local exec_pat = "()$(%b"..brackets..")()" |
| |
| local function parseDollarParen(pieces, chunk, s, e) |
| local s = 1 |
| for term, executed, e in chunk:gmatch (exec_pat) do |
| executed = '('..executed:sub(2,-2)..')' |
| append(pieces, |
| format("%q..(%s or '')..",chunk:sub(s, term - 1), executed)) |
| s = e |
| end |
| append(pieces, format("%q", chunk:sub(s))) |
| end |
| |
| local esc_pat = esc.."+([^\n]*\n?)" |
| local esc_pat1, esc_pat2 = "^"..esc_pat, "\n"..esc_pat |
| local pieces, s = {"return function(_put) ", n = 1}, 1 |
| while true do |
| local ss, e, lua = chunk:find (esc_pat1, s) |
| if not e then |
| ss, e, lua = chunk:find(esc_pat2, s) |
| append(pieces, "_put(") |
| parseDollarParen(pieces, chunk:sub(s, ss)) |
| append(pieces, ")") |
| if not e then break end |
| end |
| append(pieces, lua) |
| s = e + 1 |
| end |
| append(pieces, " end") |
| return table.concat(pieces) |
| end |
| |
| local template = {} |
| |
| --- expand the template using the specified environment. |
| -- @param str the template string |
| -- @param env the environment (by default empty). <br> |
| -- There are three special fields in the environment table <ul> |
| -- <li><code>_parent</code> continue looking up in this table</li> |
| -- <li><code>_brackets</code>; default is '()', can be any suitable bracket pair</li> |
| -- <li><code>_escape</code>; default is '#' </li> |
| -- </ul> |
| local cache ={} |
| function template.substitute(str,env) |
| local fn = cache[str] |
| if not fn then |
| env = env or {} |
| if rawget(env,"_parent") then |
| setmetatable(env,{__index = env._parent}) |
| end |
| local brackets = rawget(env,"_brackets") or '()' |
| local escape = rawget(env,"_escape") or '#' |
| local code = parseHashLines(str,brackets,escape) |
| local err |
| fn,err = utils.load(code,'TMP','t',env) |
| if not fn then return nil,err end |
| cache[str] = fn |
| end |
| setfenv(fn,env) |
| fn = fn() |
| local out = {} |
| local res,err = xpcall(function() fn(function(s) |
| out[#out+1] = s |
| end) end,debug.traceback) |
| if not res then |
| if env._debug then print(code) end |
| return nil,err |
| end |
| return table.concat(out) |
| end |
| |
| return template |
| |
| |
| |
| |