mint
function mint( template [, ename] ) --> ( sandbox ) --> expstr, err
Description
Expand the Lua code contained in the template. The following pattern in the template will be expanded:
- @{luaexp}
-
Will be substituted with the result of the Lua expression.
- @{{luastm}}
-
Embeds the Lua statement. This allow to mix Lua code and verbatim text.
The function works in two steps:
-
returns a generator function that takes a table(sandbox)
-
returns the generator function the expanded string(expstr)
Parameters
- template
-
The string that should be expanded.
- ename
-
The Lua code will have access to a global with this name. It will be a function that will append its first argument to the template output string. This parameter is optional, the default value is _o.
- sandbox
-
The contents of this table will be injected into the environment of the Lua code (both expressions and statements). This allows you to pass parameters to the template.
Return Values
- expstr
-
The resulting expanded text or nil if an error occurse.
- err
-
A message if an error occurse, otherwise nil.
Code
--ZFUNC-mint-v0 local function mint( template, ename ) --> ( sandbox ) --> expstr, err if not ename then ename = '_o' end local function expr(e) return ' '..ename..'('..e..')' end local function compat_load( str, env ) local chunkname = 'mint_script' local func, err if _VERSION ~= 'Lua 5.1' then func, err = load( str, chunkname, 't', env ) else func, err = loadstring( str, chunkname) if func then setfenv( func, env ) end end return func, err end -- Generate a script that expands the template local script = template:gsub( '(.-)@(%b{})([^@]*)', function( prefix, code, suffix ) prefix = expr( string.format( '%q', prefix ) ) suffix = expr( string.format( '%q', suffix ) ) code = code:sub( 2, #code-1 ) if code:match( '^{.*}$' ) then return prefix .. code:sub( 2, #code-1 ) .. suffix else return prefix .. expr( code ) .. suffix end end ) -- The generator must be run only if at least one @{} was found local run_generator = ( script ~= template ) -- Return a function that executes the script with a custom environment return function( sandbox ) if not run_generator then return script end local expstr = '' if 'table' ~= type( sandbox ) then return nil, "mint generator requires a sandbox" end local oldout = sandbox[ ename ] sandbox[ ename ] = function( out ) expstr = expstr..tostring(out) end local generate, err = compat_load( script, sandbox ) if not generate or err then sandbox[ ename ] = oldout return nil, err..'\nTemplate script: [[\n'..script..'\n]]' end local ok, err = pcall(generate) sandbox[ ename ] = oldout if not ok or err then return nil, err..'\nTemplate script: [[\n'..script..'\n]]' end return expstr end end return mint
Examples
local t = require( "taptest" ) local mint = require( "mint" ) local function contain( a, b ) return nil ~= a:find( b, 1, 'plain' ) end -- Blank templates are not touched local m = mint( "ok" ) t( 'ok', m{} ) -- Lua expression expansion with @{} m = mint( "@{item.a} @{item.b:upper()}" ) t( 'a B', m{ item = { a = 'a', b = 'b' } } ) t( 'B A', m{ item = { a = 'B', b = 'a' } } ) -- Mix lua statements and text with @{{}} m = mint( "@{{for i=1,3 do}} hello @{item}!@{{end}}" ) t( ' hello world! hello world! hello world!', m{ item = 'world' } ) -- Use the output function to expand text from complex lua code m = mint( "@{{for i=1,3 do o(' hello '..item..'!') end}}", 'o' ) t( ' hello dude! hello dude! hello dude!', m{ item = 'dude' } ) -- Last text appending m = mint( "@{'true'} ok" ) t( 'true ok', m{} ) -- Value cast in the output function m = mint( "@{true} ok" ) t( 'true ok', m{} ) -- The script is reported when a compile error is found m = mint( "@{{][}}" ) local s, e = m{} t( s, nil ) t( e, 'Template script: ', contain ) t( e, '_o("")][ _o("")', contain ) -- The script is reported when a running error is found m = mint( "@{{undefined_function()}}" ) local s, e = m{} t( s, nil ) t( e, 'Template script: ', contain ) t( e, '_o("")undefined_function() _o("")', contain ) -- Nested template local s = {} function s.nestcall() return mint( "@{'B'}" )( s ) end t( mint( "@{nestcall()}@{nestcall()}" )( s ), 'BB' ) t()