You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
if 表达式的节点和for表达式节点可以嵌套其他语句,所以要多一个childNodes数组来装语句内的表达式,childNodes 可以装任意的node,然后我们解析的时候递归向下解析。elseifNodes 装elseif/else 节点,解析的时候,当if的conditon为false的时候,按顺序取elseifNodes数组里的节点,谁的condition为true,就执行谁的childNodes,然后返回结果。
let html = '';
for (let node of rootNode.childNodes) {
html += calStatement(env, node);
}
calStatement为所有语句的解析入口
function calStatement(env, node) {
let html = '';
switch (node.type) {
case NodeType.Character:
html += node.value;
break;
case NodeType.Variable:
html += calVariable(env, node.valueName);
break;
case NodeType.IfStatement:
html += calIfStatement(env, node);
break;
case NodeType.ForStatement:
html += calForStatement(env, node);
break;
default:
throw Error('未知node type');
}
return html;
}
解析变量
// env为数据变量如{value:'hello world'},valueName为变量名
function calVariable(env, valueName) {
if (!valueName) {
return '';
}
let result = env;
for (let name of valueName.split('.')) {
result = result[name];
}
return result;
}
解析if 语句及condition 条件
// 目前只支持变量值判断,不支持||,&&,<=之类的表达式
function calConditionStatement(env, condition) {
if (typeof condition === 'string') {
return calVariable(env, condition) ? true : false;
}
return condition ? true : false;
}
function calIfStatement(env, node) {
let status = calConditionStatement(env, node.condition);
let result = '';
if (status) {
for (let childNode of node.childNodes) {
// 递归向下解析子节点
result += calStatement(env, childNode);
}
return result;
}
for (let elseifNode of node.elseifNodes) {
let elseIfStatus = calConditionStatement(env, elseifNode.condition);
if (elseIfStatus) {
for (let childNode of elseifNode.childNodes) {
// 递归向下解析子节点
result += calStatement(env, childNode);
}
return result;
}
}
return result;
}
解析for节点
function calForStatement(env, node) {
let result = '';
let obj = {};
let name = node.itemName.split('.')[0];
for (let item of env[node.listName]) {
obj[name] = item;
let statementEnv = Object.assign(env, obj);
for (let childNode of node.childNodes) {
// 递归向下解析子节点
result += calStatement(statementEnv, childNode);
}
}
return result;
}
前言
模板引擎的作用就是将模板渲染成html,
html = render(template,data)
,常见的js模板引擎有Pug,Nunjucks,Mustache等。网上一些制作模板引擎的文章大部分是用正则表达式做一些hack工作,看完能收获的东西很少。本文将使用编译原理那套理论来打造自己的模板引擎。之前玩过一年Django,还是偏爱那套模板引擎,这次就打算自己用js写一个,就叫jstemp预览功能
写一个库,不可能一次性把所有功能全部实现,所以我们第一版就挑一些比较核心的功能
词法分析
词法分析就是将字符串分割成一个一个有意义的token,每个token都有它要表达的意义,供语法分析器去建AST。
jstemp的token类型如下
一般来说,词法分析有几种方法(欢迎补充)
作者本着自虐的心理,采取了第三种方法。
举例说明有穷状态自动机,解析
<p>{{value}}</p>
的过程结果是
{type:Character,value:'<p>'}
,{type:Variable}
,{type:VariableName, valueName: 'value'}
,{type:EndTag}
,{type:Character,value:'</p>'}
这五个token。(当然如果你喜欢,可以把{{value}}
当作一个token,但是我这里分成了五个)。最后因为考虑到空格和if/elseif/else,for等情况,状态机又复杂了许多。代码的话就是一个循环加一堆switch 转化状态(特别很累,也很容易出错),有一些情况我也没考虑全。截一部分代码下来看
具体代码看这里
语法分析
当我们将字符串序列化成一个个token后,就需要建AST树。树的根节点rootNode就一个childNodes数组用来连接子节点
字符串节点
变量节点
if 表达式的节点和for表达式节点可以嵌套其他语句,所以要多一个childNodes数组来装语句内的表达式,childNodes 可以装任意的node,然后我们解析的时候递归向下解析。elseifNodes 装elseif/else 节点,解析的时候,当if的conditon为false的时候,按顺序取elseifNodes数组里的节点,谁的condition为true,就执行谁的childNodes,然后返回结果。
for节点
举例:
具体建树逻辑可以看代码
解析AST树
从rootNode节点开始解析
calStatement为所有语句的解析入口
解析变量
解析if 语句及condition 条件
解析for节点
结束语
目前的实现的jstemp功能还比较单薄,存在以下不足:
...
未来将一步步完善,另外无耻求个star
github地址
The text was updated successfully, but these errors were encountered: