用C为LUA写一个超迷你的模板引擎.
Posted bywayboy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了用C为LUA写一个超迷你的模板引擎.相关的知识,希望对你有一定的参考价值。
中午在做HTTP服务器,内嵌了LUA引擎作为业务逻辑部分. 但考虑到LUA输出html的性能不高,况且 MVC 模式开发网页已经习惯了,何不用C给lua写个最简单的模板引擎呢?说做就做, 项目时间很紧张,所以必须确定目标,想了会儿。定了几个目标:
- 我要的是编译型的模板,要支持缓存。
- 不需要强大的模板逻辑方面的指令。
- 将HTML模板文件编译成LUA源代码即可。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <io.h>
#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
再来一个模板文件示例:
<html>
<title>#_(title)#--方121212法</title>
<style>
tableborder:1px solid #CCC;
</style>
<body>
<table border="0" width="800">
<tr><td>ID</td><td>姓名</td><td>年龄</td></tr>
# for key,val in pairs(users) do#
<tr>
<td>#_(val['ID']) #</td>
<td>#_(val['username'])#</td>
<td>#_(val['age'])#</td>
</tr>
#end#
</table>
<p>#_(name)#</p>
</body>
</html>
该文件最终生成的缓存文件为:
local args = ...
local _=args["_"]
local name=args["name"]
local users=args["users"]
local title=args["title"]
_([=[<html>
<title>]=])
_(title)
_([=[--方121212法</title>
<style>
tableborder:1px solid #CCC;
</style>
<body>
<table border="0" width="800">
<tr><td>ID</td><td>姓名</td><td>年龄</td></tr>
]=])
for key,val in pairs(users) do
_([=[
<tr>
<td>]=])
_(val['ID'])
_([=[</td>
<td>]=])
_(val['username'])
_([=[</td>
<td>]=])
_(val['age'])
_([=[</td>
</tr>
]=])
end
_([=[
</table>
<p>]=])
_(name)
_([=[</p>
</body>
</html>]=])
输出结果如图:
最终生成的HTML代码如下:
<html>
<title>测试模板变量.--方121212法</title>
<style>
tableborder:1px solid #CCC;
</style>
<body>
<table border="0" width="800">
<tr><td>ID</td><td>姓名</td><td>年龄</td></tr>
<tr>
<td>1</td>
<td>bywayboy</td>
<td>31</td>
</tr>
<tr>
<td>1</td>
<td>liigo</td>
<td>31</td>
</tr>
<tr>
<td>1</td>
<td>sunwei</td>
<td>8</td>
</tr>
</table>
<p>某某童鞋</p>
</body>
目前该项目已经开源:
https://github.com/bywayboy/lua-modules
以上是关于用C为LUA写一个超迷你的模板引擎.的主要内容,如果未能解决你的问题,请参考以下文章
用 C 语言和 nkCEngine 写了一个基于命令的迷你脚本编译器与脚本播放器