lua html模板引擎,用C为LUA写一个超迷你的模板引擎.

本文介绍了一个使用C语言为LUA编写的简单模板引擎,旨在提高HTML输出性能并支持缓存。该引擎将HTML模板编译为LUA源代码,支持预定义的输出函数和边界符定制,并通过LUA接口进行包装,简化使用。通过示例展示了如何在LUA中调用该模板引擎以及模板文件的编译和渲染过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

中午在做HTTP服务器,内嵌了LUA引擎作为业务逻辑部分. 但考虑到LUA输出HTML的性能不高,况且 MVC 模式开发网页已经习惯了,何不用C给lua写个最简单的模板引擎呢?说做就做, 项目时间很紧张,所以必须确定目标,想了会儿。定了几个目标:

我要的是编译型的模板,要支持缓存。

不需要强大的模板逻辑方面的指令。

将HTML模板文件编译成LUA源代码即可。

LUA 还是比较容易扩展的,除了那栈用起来比较变态之外,经过了几个小时的奋斗,C+LUA代码。算是大功告成了。

#include

#include

#include

#include

#include

#include

#include

#include "lib/automem.h"

#include "lua.h"

#include "lauxlib.h"

#include "lua-tpl.h"

/*

LUA 最简单的模板引擎.

*/

const char LUAFMT_TPL_META[]="LUAFMT_TPL";

void lua_cfmt_createmetatable (lua_State *L);

void lua_register_class(lua_State * L,const luaL_Reg * methods,const char * name,lua_CFunction has_index);

#if defined(_WIN32) || defined(_WIN64)

#define open_open

#define close_close

#define read_read

#define stat( x , y )_stat( x , y )

#endif

static char * file_get_contents(const char * file,int * sz){

char * ret = NULL;

struct stat st;

int fd;

*sz = 0;

if ((fd = open(file, O_RDONLY|O_BINARY)) != -1)

{

if (fstat(fd, &st) != -1)

{

ret = (char *)malloc(st.st_size);

if(NULL != ret){

while( *sz < st.st_size){

*sz += read(fd, ret + *sz, st.st_size - (*sz));

_lseek(fd, *sz, SEEK_SET);

}

}

close(fd);

}

}

return ret;

}

int file_put_contents(const char* file_name, automem_t* mem)

{

FILE * fp = fopen(file_name,"wb+");

if(NULL != fp)

{

fwrite(mem->pdata, mem->size, 1, fp);

fclose(fp);

return 1;

}

return 0;

}

enum{

tpl_state_normal,

tpl_state_scode_1,

tpl_state_code,

tpl_state_ecode_1,

tpl_state_escape,

};

#define append_end_stringfield(a,b,c) \

automem_append_voidp( (a) , cmd, lcmd); \

automem_append_voidp( (a), (b) , (c)); \

automem_append_voidp( (a) , ecmd, lecmd); \

automem_append_voidp( (a), ")\n", 2);

static void lua_tpl_compile_local(lua_State * L, automem_t * mem, const char * buf,int lbuf){

int state = tpl_state_normal, i = 0;

const char * sbuf; char c;

size_t lcmd = sizeof("request.print([[") -1,

lecmd = sizeof("]])") -1,

lpre = 0;

const char * cmd = "request.print([[",

* ecmd = "]])",

*pre = NULL;

if(lua_isstring(L, 3))

cmd =luaL_checklstring(L, 3, &lcmd);

if(lua_isstring(L, 4))

ecmd =luaL_checklstring(L, 4, &lecmd);

if(lua_isstring(L, 5))

pre =luaL_checklstring(L, 5, &lpre);

if(NULL != pre){

automem_append_voidp(mem, pre, lpre);

automem_append_byte(mem,'\n');

}

sbuf = buf;

while(i < lbuf){

c = buf[i];

switch (state)

{

case tpl_state_normal:

switch(c){

case '{':

state = tpl_state_scode_1;

break;

}

break;

case tpl_state_scode_1:

switch(c){

case '#':

state =tpl_state_code;

append_end_stringfield(mem,sbuf, &buf[i] - sbuf-1);

sbuf = &buf[i+1];

break;

default:

state = tpl_state_normal;

break;

}

break;

case tpl_state_code:

switch(c){

case '#':

state = tpl_state_ecode_1;

break;

}

case tpl_state_ecode_1:

switch(c){

case '}':

automem_append_voidp(mem,sbuf, &buf[i] - sbuf-1);

automem_append_byte(mem,'\n');

sbuf = &buf[i+1];

state = tpl_state_normal;

break;

default:

state=tpl_state_code;

}

default:

break;

}

i++;

}

if(tpl_state_normal == state){

append_end_stringfield(mem,sbuf, &buf[i] - sbuf);

}

}

/* 对模板文件进行编译.*/

static int lua_tpl_compile(lua_State * L)

{

int cache = 0,i = 0, lbuf = 0;

size_t lfile;

const char *cfile = NULL, * file= luaL_checklstring(L, 1,&lfile),* buf;

automem_t mem;

if(lua_isboolean(L, 2))

cache =lua_toboolean(L, 2);

if(0 != cache){

struct stat st1,st2;

cfile=(char *)malloc(lfile+5);

memcpy((char *)cfile,file,lfile);

strcpy((char *)cfile+lfile,".tpl");

if((0 == stat(file,&st1)) && (0 == stat(cfile, &st2)))

{

if(st1.st_mtime <= st2.st_mtime)

{

if(NULL != (buf = file_get_contents(cfile, &lbuf)))

{

free((void *)cfile);

lua_pushlstring(L,buf, lbuf);

goto lua_tpl_compile_final;

}

}

}

}

if(NULL != (buf = file_get_contents(file, &lbuf)))

{

automem_init(&mem,lbuf + 1024);

lua_tpl_compile_local(L, &mem, buf, lbuf);

free((void*)buf);

lua_pushlstring(L,(char *)mem.pdata,mem.size);

if(0 != cache && NULL !=cfile)

file_put_contents(cfile,&mem);

automem_uninit(&mem);

}

if(NULL != cfile)

free((void *)cfile);

lua_tpl_compile_final:

return 1;

}

static luaL_Reg fmt_tpl_reg[] = {

{"compile",lua_tpl_compile},

{NULL,NULL}

};

int luaopen_cfmt_tpl(lua_State * L)

{

luaL_newlib(L, fmt_tpl_reg);

lua_cfmt_createmetatable(L);

return 1;

}

整个LUA模块扩展就1个函数 compile()。在LUA中的原型如下:

string compile(filePath,cached, write, prefix,suffix,init)

功能: 将html模板编译为lua代码.

参数:

filePath: html源文件的路径.

cached: 是否需要缓存.

writer: 内容输出函数名.

prefix: 文件分界符前缀.

init:初始代码,用于做参数展开之类的工作.

当然,光有C的接口还不够,为了使它变得简单易用,还需要用LUA对其包装一下^_^, 包装代码如下:

function util.tpl(writer)

local t = require "cfmt.tpl"

local tpl = {}

local args = {}

local _prefix='[=['

local _suffix=']=]'

-- 创建的时候指定 writer

if nil ~= writer then args['_']=writer end

function tpl:assign(name,value)

args[name]=value

end

function tpl:boundary(prefix, suffix) --修改字符串边界符

if nil ~= prefix then _prefix=prefix end

if nil ~= suffix then _suffix=suffix end

end

function tpl:display(tpl,cache,writer) -- tpl 模板文件位置, cache 是否需要缓存 writer 可选,如果创建对象的时候指定了的话.

local i=1

if nil~=writer then args['_']=writer end

local init = {'local args = ...'}

for key,val in pairs(args) do

init[#init+1]='local '..key..'=args["'..key..'"]'

i=i+1

end

init = table.concat(init,'\n')

local code =t.compile(tpl,cache,'_('.._prefix, _suffix,init)

load(code)(args)

end

return tpl;

end

接下来用起来就简单多了,上测试代码.

---外部进来的数据在这里做检测

function login:request(r)

local tpl = (require "util").tpl(r.print)

local users ={

{['ID']=1,['username']='bywayboy',['age']=31},

{['ID']=1,['username']='liigo',['age']=31},

{['ID']=1,['username']='sunwei',['age']=8},

}

local b={['a']=12}

tpl:assign('users',users);

tpl:assign('title',"测试模板变量.")

tpl:assign('name',"某某童鞋")

tpl:display("D:\\VC\\CmdChannel\\win32\\Debug\\test.html",true)

end

再来一个模板文件示例:

{#_(title)#}--方121212法

table{border:1px solid #CCC;}

ID姓名年龄

{# for key,val in pairs(users) do#}

{#_(val['ID']) #}{#_(val['username'])#}{#_(val['age'])#}

{#end#}

{#_(name)#}

该文件最终生成的缓存文件为:

local args = ...

local _=args["_"]

local name=args["name"]

local users=args["users"]

local title=args["title"]

_([=[

]=])

_(title)

_([=[--方121212法

table{border:1px solid #CCC;}

ID姓名年龄

]=])

for key,val in pairs(users) do

_([=[

]=])

_(val['ID'])

_([=[

]=])

_(val['username'])

_([=[

]=])

_(val['age'])

_([=[

]=])

end

_([=[

]=])

_(name)

_([=[

]=])

输出结果如图:

0818b9ca8b590ca3270a3433284dd417.png

0818b9ca8b590ca3270a3433284dd417.png

最终生成的HTML代码如下:

0818b9ca8b590ca3270a3433284dd417.png

测试模板变量.--方121212法

table{border:1px solid #CCC;}

ID姓名年龄
1bywayboy31
1liigo31
1sunwei8

某某童鞋

目前该项目已经开源:

https://github.com/bywayboy/lua-modules

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值