当前位置:首页 > > 充电吧
[导读]中午在做HTTP服务器,内嵌了LUA引擎作为业务逻辑部分. 但考虑到LUA输出HTML的性能不高,况且 MVC 模式开发网页已经习惯了,何不用C给lua写个最简单的模板引擎呢?说做就做, 项目时间很紧

中午在做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法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法ID姓名年龄]=])
 for key,val in pairs(users) do
_([=[]=])
_(val['ID']) 
_([=[]=])
_(val['username'])
_([=[]=])
_(val['age'])
_([=[]=])
end
_([=[]=])
_(name)
_([=[]=])


输出结果如图:


最终生成的HTML代码如下:

测试模板变量.--方121212法ID姓名年龄1bywayboy311liigo311sunwei8某某童鞋





本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭