热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

中午在做HTTP服务器,内嵌了LUA引擎作为业务逻辑部分.但考虑到LUA输出HTML的性能不高,况且MVC模式开发网页已经习惯了,何不用C

中午在做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

*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

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 <&#61; st2.st_mtime)

{

if(NULL !&#61; (buf &#61; file_get_contents(cfile, &lbuf)))

{

free((void *)cfile);

lua_pushlstring(L,buf, lbuf);

goto lua_tpl_compile_final;

}

}

}

}

if(NULL !&#61; (buf &#61; file_get_contents(file, &lbuf)))

{

automem_init(&mem,lbuf &#43; 1024);

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

free((void*)buf);

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

if(0 !&#61; cache && NULL !&#61;cfile)

file_put_contents(cfile,&mem);

automem_uninit(&mem);

}

if(NULL !&#61; cfile)

free((void *)cfile);

lua_tpl_compile_final:

return 1;

}

static luaL_Reg fmt_tpl_reg[] &#61; {

{"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中的原型如下&#xff1a;

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

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

参数:

filePath: html源文件的路径.

cached: 是否需要缓存.

writer: 内容输出函数名.

prefix: 文件分界符前缀.

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

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

function util.tpl(writer)

local t &#61; require "cfmt.tpl"

local tpl &#61; {}

local args &#61; {}

local _prefix&#61;&#39;[&#61;[&#39;

local _suffix&#61;&#39;]&#61;]&#39;

-- 创建的时候指定 writer

if nil ~&#61; writer then args[&#39;_&#39;]&#61;writer end

function tpl:assign(name,value)

args[name]&#61;value

end

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

if nil ~&#61; prefix then _prefix&#61;prefix end

if nil ~&#61; suffix then _suffix&#61;suffix end

end

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

local i&#61;1

if nil~&#61;writer then args[&#39;_&#39;]&#61;writer end

local init &#61; {&#39;local args &#61; ...&#39;}

for key,val in pairs(args) do

init[#init&#43;1]&#61;&#39;local &#39;..key..&#39;&#61;args["&#39;..key..&#39;"]&#39;

i&#61;i&#43;1

end

init &#61; table.concat(init,&#39;\n&#39;)

local code &#61;t.compile(tpl,cache,&#39;_(&#39;.._prefix, _suffix,init)

load(code)(args)

end

return tpl;

end

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

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

function login:request(r)

local tpl &#61; (require "util").tpl(r.print)

local users &#61;{

{[&#39;ID&#39;]&#61;1,[&#39;username&#39;]&#61;&#39;bywayboy&#39;,[&#39;age&#39;]&#61;31},

{[&#39;ID&#39;]&#61;1,[&#39;username&#39;]&#61;&#39;liigo&#39;,[&#39;age&#39;]&#61;31},

{[&#39;ID&#39;]&#61;1,[&#39;username&#39;]&#61;&#39;sunwei&#39;,[&#39;age&#39;]&#61;8},

}

local b&#61;{[&#39;a&#39;]&#61;12}

tpl:assign(&#39;users&#39;,users);

tpl:assign(&#39;title&#39;,"测试模板变量.")

tpl:assign(&#39;name&#39;,"某某童鞋")

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[&#39;ID&#39;]) #}{#_(val[&#39;username&#39;])#}{#_(val[&#39;age&#39;])#}

{#end#}

{#_(name)#}

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

local args &#61; ...

local _&#61;args["_"]

local name&#61;args["name"]

local users&#61;args["users"]

local title&#61;args["title"]

_([&#61;[

]&#61;])

_(title)

_([&#61;[--方121212法

table{border:1px solid #CCC;}

ID姓名年龄

]&#61;])

for key,val in pairs(users) do

_([&#61;[

]&#61;])

_(val[&#39;ID&#39;])

_([&#61;[

]&#61;])

_(val[&#39;username&#39;])

_([&#61;[

]&#61;])

_(val[&#39;age&#39;])

_([&#61;[

]&#61;])

end

_([&#61;[

]&#61;])

_(name)

_([&#61;[

]&#61;])

输出结果如图:

0818b9ca8b590ca3270a3433284dd417.png

0818b9ca8b590ca3270a3433284dd417.png

最终生成的HTML代码如下:

0818b9ca8b590ca3270a3433284dd417.png

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

table{border:1px solid #CCC;}

ID姓名年龄
1bywayboy31
1liigo31
1sunwei8

某某童鞋

目前该项目已经开源:

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



推荐阅读
  • 本文讨论了使用差分约束系统求解House Man跳跃问题的思路与方法。给定一组不同高度,要求从最低点跳跃到最高点,每次跳跃的距离不超过D,并且不能改变给定的顺序。通过建立差分约束系统,将问题转化为图的建立和查询距离的问题。文章详细介绍了建立约束条件的方法,并使用SPFA算法判环并输出结果。同时还讨论了建边方向和跳跃顺序的关系。 ... [详细]
  • 海马s5近光灯能否直接更换为H7?
    本文主要介绍了海马s5车型的近光灯是否可以直接更换为H7灯泡,并提供了完整的教程下载地址。此外,还详细讲解了DSP功能函数中的数据拷贝、数据填充和浮点数转换为定点数的相关内容。 ... [详细]
  • 本文详细介绍了GetModuleFileName函数的用法,该函数可以用于获取当前模块所在的路径,方便进行文件操作和读取配置信息。文章通过示例代码和详细的解释,帮助读者理解和使用该函数。同时,还提供了相关的API函数声明和说明。 ... [详细]
  • vue使用
    关键词: ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • IhaveconfiguredanactionforaremotenotificationwhenitarrivestomyiOsapp.Iwanttwodiff ... [详细]
  • 本文介绍了Java工具类库Hutool,该工具包封装了对文件、流、加密解密、转码、正则、线程、XML等JDK方法的封装,并提供了各种Util工具类。同时,还介绍了Hutool的组件,包括动态代理、布隆过滤、缓存、定时任务等功能。该工具包可以简化Java代码,提高开发效率。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 本文介绍了Linux Shell中括号和整数扩展的使用方法,包括命令组、命令替换、初始化数组以及算术表达式和逻辑判断的相关内容。括号中的命令将会在新开的子shell中顺序执行,括号中的变量不能被脚本余下的部分使用。命令替换可以用于将命令的标准输出作为另一个命令的输入。括号中的运算符和表达式符合C语言运算规则,可以用在整数扩展中进行算术计算和逻辑判断。 ... [详细]
  • 一句话解决高并发的核心原则
    本文介绍了解决高并发的核心原则,即将用户访问请求尽量往前推,避免访问CDN、静态服务器、动态服务器、数据库和存储,从而实现高性能、高并发、高可扩展的网站架构。同时提到了Google的成功案例,以及适用于千万级别PV站和亿级PV网站的架构层次。 ... [详细]
  • 本文介绍了Composer依赖管理的重要性及使用方法。对于现代语言而言,包管理器是标配,而Composer作为PHP的包管理器,解决了PEAR的问题,并且使用简单,方便提交自己的包。文章还提到了使用Composer能够避免各种include的问题,避免命名空间冲突,并且能够方便地安装升级扩展包。 ... [详细]
  • Vagrant虚拟化工具的安装和使用教程
    本文介绍了Vagrant虚拟化工具的安装和使用教程。首先介绍了安装virtualBox和Vagrant的步骤。然后详细说明了Vagrant的安装和使用方法,包括如何检查安装是否成功。最后介绍了下载虚拟机镜像的步骤,以及Vagrant镜像网站的相关信息。 ... [详细]
author-avatar
修魔海的传说
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有