local NAME = 'I2CETLua.TemplateEngine' local CLASS_NAME = 'TemplateEngine' local VERSION = '1.0.1' local DESCRIPTION = 'I2 Cheat Engine Lua Modules' local AUTHOR = 'TheyCallMeTim13@Gmail.com' if BEBUG then print(NAME, ...) end -- ---- TemplateEngine TemplateEngine = { Name = NAME, ClassName = CLASS_NAME, Description = DESCRIPTION, Version = VERSION, Author = AUTHOR, License = LICENSE, ---- } ---- https://nachtimwald.com/2014/08/06/using-lua-as-a-templating-engine/ local function appender(builder, text, code) if code then builder[#builder+1] = code else ---- [[ has a \n immediately after it. Lua will strip the first \n ---- so we add one knowing it will be removed to ensure that ---- if text starts with a \n it won't be lost. local fmt = '_ret[#_ret+1] = [%s[\n%s]%s]' local mlsFix = string.rep('=', 100) builder[#builder+1] = string.format(fmt, mlsFix, text, mlsFix) end end local function runBlock(builder, text) local func local tag tag = text:sub(1, 2) if tag == '<<' then func = function(code) return ('_ret[#_ret+1] = %s'):format(code) end elseif tag == '<%' then func = function(code) return code end end if func then appender(builder, nil, func(text:sub(3, #text-3))) else appender(builder, text) end end local function compile(template, environmentOrLoadLocalEnv, loadLocalEnv) ---- Turn the template into a string that can be run though Lua. ---- Builder will be used to efficiently build the string we'll run. ---- The string will use it's own builder (_ret). Each part that comprises ---- _ret will be the various pieces of the template. Strings, variables ---- that should be printed and functions that should be run. if not template or template == '' then return '' end local env = { } if type(environmentOrLoadLocalEnv) == 'table' then env = environmentOrLoadLocalEnv elseif type(environmentOrLoadLocalEnv) == 'boolean' then loadLocalEnv = environmentOrLoadLocalEnv end env.__index = env setmetatable(env, { __index = _G }) if loadLocalEnv then local i = 1 while true do local name, value = debug.getlocal(2, i) if not name then break end env[name] = value i = i + 1 end end local builder = { '_ret = {}\n' } local pos = 1 local b local func local err while pos < #template do ---- Look for start of a Lua block. b = template:find('<[<%%]', pos) if not b then break end ---- Check if this is a block or escaped <. if template:sub(b-1, b-1) == '\\' then appender(builder, template:sub(pos, b-2)) appender(builder, '<') pos = b+1 else ---- Add all text up until this block. appender(builder, template:sub(pos, b-1)) ---- Find the end of the block. pos = template:find('[>%%]>', b) if not pos then appender(builder, 'End tag missing') break end runBlock(builder, template:sub(b, pos+2)) ---- Skip back the >> (pos points to the start of >>). pos = pos+2 end end ---- Add any text after the last block. Or all of it if there ---- are no blocks. if pos then appender(builder, template:sub(pos, #template)) end builder[#builder+1] = 'return table.concat(_ret)' ---- Run the Lua code we built though Lua and get the result. func, err = load(table.concat(builder, '\n'), 'template', 't', env) if not func then return nil, err end return func() end function TemplateEngine.compile(template, environmentOrLoadLocalEnv, loadLocalEnv) return compile(template, environmentOrLoadLocalEnv, loadLocalEnv) end function TemplateEngine.compileFile(filePath, environmentOrLoadLocalEnv, loadLocalEnv) local f, err = io.open(filePath, 'r') if f and not err then local template = f:read('*all') f:close() return compile(template, environmentOrLoadLocalEnv, loadLocalEnv) else return nil, err end end return TemplateEngine