etaoux/brix

brix 的模板解析问题二次探讨

keyapril opened this issue · 5 comments

上次的讨论在 #39

注释的写法很好的解决了子模板的匹配和提取,却留下了遗憾和不优雅。

在上次的分析中我们已经知道,我们需要的是从一段Html文本中找出特定标签的innerHTML,这里面最大的难点就是,Html标签是支持嵌套的,怎么能够找到指定标签相对应的闭合标签呢?

思路

我们可以这样想,先匹配最前面的起始标签,假设是div吧(<div),接着一旦遇到嵌套div,就“压入堆栈”,后面如果遇到div闭合标签了,就“弹出堆栈”。如果遇到闭合标签的时候,堆栈里面已经没有东西了,那么匹配结束,此结束标签为正确的闭合标签。

之所以这样去思考,是因为正则里有“平衡组”这样的特性能够实现刚才说的“堆栈”操作(平衡组参见),可惜的是js里的正则不支持这样的特性。

回归原始

我们回到原始,不考虑嵌套,我们的正则应该这样写

<([\w]+)\s+[^>]*?bx-tmpl=["']?([^"'\s]+?)["']?\s+[^>]*?bx-datakey=["']?([^"'\s]+)["']?[^>]*?>([\s\S]*?)</\1> 

夹杂在标签中的内容无非两种情况

  • 内容A: 标签,并且此标签内无嵌套div
  • 内容B: 任意其他内容

然后就是这两种内容的不断重复而已。正则表示如下:

(<\1[^>]*>([\s\S]*?)</\1>|[\s\S])*?

这样,我们就能获取最多嵌套一级的正则了

<([\w]+)\s+[^>]*?bx-tmpl=["']?([^"'\s]+?)["']?\s+[^>]*?bx-datakey=["']?([^"'\s]+)["']?[^>]*?>((?:<\1[^>]*>(?:[\s\S]*?)</\1>|[\s\S])*?)</\1> 

以此类推二级、三级

<([\w]+)\s+[^>]*?bx-tmpl=["']?([^"'\s]+?)["']?\s+[^>]*?bx-datakey=["']?([^"'\s]+)["']?[^>]*?>((?:<\1[^>]*>(?:<\1[^>]*>(?:[\s\S]*?)</\1>|[\s\S])*?</\1>|[\s\S])*?)</\1>


<([\w]+)\s+[^>]*?bx-tmpl=["']?([^"'\s]+?)["']?\s+[^>]*?bx-datakey=["']?([^"'\s]+)["']?[^>]*?>((?:<\1[^>]*>(?:<\1[^>]*>(?:<\1[^>]*>(?:[\s\S]*?)</\1>|[\s\S])*?</\1>|[\s\S])*?</\1>|[\s\S])*?)</\1> 

程序构建

所以实际上,只要你的html结构不是特别复杂的话,也就是说嵌套不会很深的话,那么你完全可以使用这种方式来匹配嵌套html标签,幸运的是,因为模板是用户传入的,在传入之前,我们其实就知道同一个标签的嵌套次数,那通过程序动态构建正则,我们也是能够实现无限级标签嵌套的获取,代码如下:

var level = 3;
var r = '<([\\w]+)\\s+[^>]*?bx-tmpl=["\']?([^"\'\\s]+?)["\']?\\s+[^>]*?bx-datakey=["\']?([^"\'\\s]+)["\']?[^>]*?>(@brix@)</\\1>';
while(level--){
    r = r.replace('@brix@','(?:<\\1[^>]*>@brix@</\\1>|[\\s\\S])*?');
}
r = r.replace('@brix@','(?:[\\s\\S]*?)');

欢天喜地

现在,我们终于可以不用再写

<!--bx-tmpl="list" bx-datakey="list"-->
<!--bx-tmpl="list">

更好的建议

你是否有更好的建议?

用传统的编译原理来处理,而不是用正则。

@goto100 能不能举个例子?

1 可以说brix创造了一个新的模板语言,同js、c不同,是一门描述语言,同xml、css类似;
2 对于一门语言,计算机技术传统的处理方法是用编译原理,词法分析、语法分析等等步骤出来的,不是正则表达式那么简单,比如说浏览器解析css,肯定不是用正则表达式解析出来的,可以看一下uglify-js对js的解析,完全没有正则噢;
3 前提是先确定这个模板语言的语言规范,没有冲突和模糊的地方。
4 编译原理这块我也不是很熟悉,所以没有例子,一起学习吧,如果正则能够解决现在的问题,建议保持现状,要用编译原理去解析,因为没人会,成本会比较高哦。

limu commented

正则是一个解析字符串的手段了,编译原理与否是两条路线,草根路线,高端路线。
草根路线可以很好很快的适应我们的应用,快速实现,实现不美观,但保证接口是清晰的,也可以说是领域专用语言。
高端路线不仅仅可以把我们的内容支持好,还可以share出来服务更多场景,但是现阶段无法快速实现,作为长远规划吧。

所以左莫继续快速推你的改进,同时多总结提纯需求,作为高端路线的输入,只要有合适资源就开始并行实施。。

用栈结构+正则匹配,正则匹配每次只匹配 一个 <xx></xx>{{}} 就可以了,不需要编译原理