AST抽象语法树
index.js
index.js文件是入口文件,主要调用parse.js文件来将DOM字符串转换成ast抽象语法树。
import parse from "./ast/parse";
var tempalteString = `<div><div></div></div><div></div>`
const ast = parse(tempalteString)
console.log(ast);
parse.js
parse.js文件是将DOM字符串转换成ast抽象语法树的主要文件
基本流程:
定义指针index,两个栈startStack和endStack
- 当遇到开始标签时获取标签名将标签名push到startStack中,将{“tag”:tag,“children”:[],“attrs”:parseAttsString(attrsString)}push到endStack中
- 当遇到文字时,endStack[endStack.length - 1].children.push({“text”:word,“type”:3})就是将文字push到endStack最后一个元素的children属性里面
- 当遇到结束标签时将endStack的最后一个元素弹出,push到endStack最后一个元素的children里面
举例说明:
//使用下面的DOM字符串测试
<div><div></div></div><div></div>
- 遇到开始div后
endStack=[{children:[]},{"tag":div,"children":[],"attrs":[]}]
- 遇到第二个开始div后
endStack=[{children:[]},{"tag":div,"children":[],"attrs":[]},{"tag":div,"children":[],"attrs":[]}]
- 遇到结束div后
endStack=[{children:[]},{"tag":div,"children":[{"tag":div,"children":[],"attrs":[]}],"attrs":[]}]
- 第二次遇到结束div后
endStack=[{children:[{"tag":div,"children":[{"tag":div,"children":[],"attrs":[]}],"attrs":[]}]}]
- 第三次遇到开始div
endStack=[{children:[{"tag":div,"children":[{"tag":div,"children":[],"attrs":[]}],"attrs":[]}]},{"tag":div,"children":[],"attrs":[]}]
- 第三次遇到结束div
endStack=[{children:[{"tag":div,"children":[{"tag":div,"children":[],"attrs":[]}],"attrs":[]}]},{"tag":div,"children":[],"attrs":[]}]
import parseAttsString from "./parseAttsString"
//parse函数,主函数
export default function(tempalteString){
//指针
var index = 0
//剩余部分
var rest = ''
//判断是否是开始标签的正则
var startRegExp = /^\<([a-z]+[1-6]?)(\s[^\<]+)?\>/
//判断是否是结束标签的正则
var endRegExp = /^\<\/([a-z]+[1-6]?)\>/
//抓取结束标记前的文字
var wordRegExp = /^([^\<]+)\<\/[a-z]+[1-6]?\>/
//准备两个栈
var startStack = []
var endStack = [{'children':[]}]
while(index < tempalteString.length-1){
//获取剩余部分
rest = tempalteString.substring(index)
//识别遍历到的这个字符是不是一个开始标签
if(startRegExp.test(rest)){
//检测开始标签
let tag = rest.match(startRegExp)[1]
let attrsString = rest.match(startRegExp)[2]
// console.log('检测到的开始标签是',tag)
//将开始标记推入开始栈
startStack.push(tag)
//将对象推入结束栈
endStack.push({"tag":tag,"children":[],"attrs":parseAttsString(attrsString)})
//判断attrsString是否为undefined
const attrsStringLength = attrsString!=null ? attrsString.length : 0
//指针长度加2,因为<>占两位
index += tag.length + 2 + attrsStringLength
// console.log('开始栈',startStack,'结束栈',endStack)
}else if(endRegExp.test(rest)){
//检测结束标签
let tag = rest.match(endRegExp)[1]
// console.log('检测到的结束标签是',tag)
let pop_tag = startStack.pop()
//此时,tag一定是和开始栈的顶部是相同的
if(tag == pop_tag){
//获取标签中的文字
let pop_arr = endStack.pop()
if(endStack.length > 0){
//将文字
endStack[endStack.length - 1].children.push(pop_arr)
}
}else{
throw new Error(startStack[startStack.length - 1],'没有标签封闭')
}
//指针长度加3,因为</>占三位
index += tag.length + 3
// console.log('开始栈',startStack,'结束栈',endStack,'数组',JSON.stringify(endStack))
}else if(wordRegExp.test(rest)){
let word = rest.match(wordRegExp)[1]
//检测文字是不是全是空
if(!/^\s+$/.test(word)){
//不全是空
// console.log('检测文字',word);
//改变此时结束栈顶的元素
endStack[endStack.length - 1].children.push({"text":word,"type":3})
}
//指针后移
index += word.length
}else{
index++
}
}
return endStack[0].children
}
parseAttsString.js
parseAttsString.js是将标签中的class、id登属性以数组的形式返回
export default function(attrsString){
console.log('输入的字符串',attrsString)
if(attrsString == undefined){
return []
}
//当前是否在引号内
var isYihao = false
//断电
var point = 0
//结果数组
var result = []
//遍历attrsString
for(let i=0;i<attrsString.length;i++){
let char = attrsString[i]
if(char == '"'){
isYihao = !isYihao
}else if(char == ' '&&!isYihao){//遇到空格并且不在引号中
if(!/^\s*?$/.test(attrsString.substring(point,i))){
console.log(point,i);
console.log('字符串截取',attrsString.substring(point,i).trim());
result.push(attrsString.substring(point,i))
point = i
}
}
}
result.push(attrsString.substring(point).trim())
result = result.map(item=>{
//根据等号拆分
const o = item.match(/^(.+)="(.+)"$/)
return {
name: o[1],
value: o[2]
}
})
return result
}
|