编译器其实只是一段程序,它用来将“一种语言A”翻译成“另一种语言B”。其中,语言A通常叫做源代码,语言B通常叫做目标代码。编译器将源代码翻译成目标代码的过程叫做编译。完整的编译过程通常包含词法分析、语义分析、中间代码设生成、优化、目标代码生成等步骤。
vue编译器的步骤
主要有三个部分组成
- 用来将模板字符串解析为模板AST的解析器(parser);
- 用来将模板AST转换成JavaScript AST的转换器(transformer);
- 用来更具JavaScript AST生成渲染函数代码的生成器(generator); ```javascript const State = { initial: 1, tagOpen: 2, tagName: 3, text: 4, tagEnd: 5, tagEndName: 6, } //判断是否字母 function isAlpha(char) { return (char >= “a” && char <= “z”) || (char >= “A” && char <= “Z”) } //接收模板字符串作为参数,并切割成token返回
function tokenize(str) { //状态机的当前状态 let currentState = State.initial //缓存字符 const chars = [] //存储token const tokens = [] //开始循环,只要模板字符串还有则一直循环 while (str) { //查看第一个字符 const char = str[0] switch (currentState) { //当前状态为初始状态呀 case State.initial: //如果当前字符为<说明是标签开始 if (char === “<”) { //状态机切换到标签开始状态 currentState = State.tagOpen //消费字符< str = str.slice(1) } else if (isAlpha(char)) { //状态机切换到文本状态 currentState = State.text //缓存char chars.push(char) //消费字符 str = str.slice(1) } break //当前状态为标签打开状态 case State.tagOpen: //如果当前字符是字母 if (isAlpha(char)) { //状态机切换到标签名状态 currentState = State.tagName //字符存入缓存 chars.push(char) //消费当前字符 str = str.slice(1) //如果当前字符是/ } else if (char === “/“) { //状态机切换到标签关闭状态 currentState = State.tagEnd //消费字符’/‘ str = str.slice(1) } break //状态机处于标签名状态 case State.tagName: if (isAlpha(char)) { //缓存字符 chars.push(char) //消费字符 str = str.slice(1) } else if (char === “>”) { //状态机回到初始状态 currentState = State.initial //存入taken tokens.push({ type: “tag”, name: chars.join(“”), }) //清空字符缓存 chars.length = 0 //消费当前字符 str = str.slice(1) } break //状态机处于文本状态 case State.text: //如果当前字符是字母 if (isAlpha(char)) { //缓存char chars.push(char) //消费当前字符 str = str.slice(1) } else if (char === “<”) { //当前状态改为标签开始 currentState = State.tagOpen //存入文本节点 tokens.push({ type: “text”, content: chars.join(“”), }) //缓存置空 chars.length = 0 //消费当前字符 str = str.slice(1) } break case State.tagEnd: if (isAlpha(char)) { //状态机状态改成标签结束名称 currentState = State.tagEndName //缓存字符 chars.push(char) //消费当前字符 str = str.slice(1) } break case State.tagEndName: if (isAlpha(char)) { //缓存当前字符 chars.push(char) //消费当前字符 str = str.slice(1) } else if (char === “>”) { //切换当前状态为初始状态 currentState = State.initial //存入结束标签名称 tokens.push({ type: “tagEnd”, name: chars.join(“”), }) //清空字符缓存 chars.length = 0 //消费当前节点 str = str.slice(1) } break } } return tokens }
//解析函数 function parser(str) { //将模板字符串标记化返回tokens const tokens = tokenize(str) //创建root节点 const root = { type: “Root”, children: [], } //创建elementStark栈最开始只存了root const elementStark = [root] //循环tokens while (tokens.length) { //element指向栈顶 const element = elementStark[elementStark.length - 1] //当前处理的token const t = tokens[0] switch (t.type) { //开始标签 case “tag”: //创建节点 const elementNode = { type: “Element”, tag: t.name, children: [], } //将节点压入elementStark elementStark.push(elementNode) //将节点插在当前栈顶节点的子节点 element.children.push(elementNode) break case “text”: //创建文本节点 const textNode = { type: “Text”, content: t.content, } //将节点压入elementStark element.children.push(textNode) break case “tagEnd”: //弹出栈顶 elementStark.pop() break } //消费当前token tokens.shift() } return root } //深度遍历转换 function traverseNode(ast, context) { context.currentNode = ast const exitFns = [] const transForms = context.nodeTransforms for (let i = 0; i < transForms.length; i++) { const onExit = transFormsi onExit && exitFns.push(onExit) if (!context.currentNode) return } const children = context.currentNode.children if (children) { children.forEach((node, index) => { context.parent = context.currentNode context.childIndex = index traverseNode(node, context) }) } let i = exitFns.length while (i—) { exitFnsi } } function createStringLiteral(value) { return { type: “StringLiteral”, value, } } function createIdentifier(name) { return { type: “Identifier”, name, } } function createArrayExpression(elements) { return { type: “ArrayExpression”, elements, } } function createCallExpression(callee, arguments) { return { type: “CallExpression”, callee: createIdentifier(callee), arguments, } } //转换函数 function transform(ast) { function transformRoot(node) { return () => { if (node.type !== “Root”) { return } const vnodeJSAST = node.children[0].jsNode node.jsNode = { type: “FunctionDecl”, id: { type: “Identifier”, name: “render” }, params: [], body: [ { type: “ReturnStatement”, return: vnodeJSAST, }, ], } } } function transformElement(node) { return () => { if (node.type !== “Element”) return const callExp = createCallExpression(“h”, [createStringLiteral(node.tag)]) node.children.length === 1 ? callExp.arguments.push(node.children[0].jsNode) : callExp.arguments.push( createArrayExpression(node.children.map((c) => c.jsNode)) ) node.jsNode = callExp } } function transformText(node) { if (!node.type === “Text”) { return } node.jsNode = createStringLiteral(node.content) } const context = { //存储当前节点 currentNode: null, //当前节点index childIndex: 0, //父节点 parent: null, //替换当前节点 replaceNode(node) { context.parent.children[this.childIndex] = node context.currentNode = node }, //移除当前节点 removeNode() { if (context.parent) { context.parent.children.splice(context.childIndex, 1) context.currentNode = null } }, nodeTransforms: [transformElement, transformText, transformRoot], } traverseNode(ast, context) }
function dump(node, indent = 0) {
const type = node.type
const des =
node.type === “root”
? “”
: node.type === “Element”
? node.tag
: node.content
console.log(${"-".repeat(indent)}${type}:${des}
)
node.children &&
node.children.forEach((element) => {
dump(element, indent + 2)
})
}
function genNode(node, context) {
function genNodeList(nodes, context) {
const { push } = context
for (let i = 0; i < nodes.length; i++) {
const node = nodes[i]
genNode(node, context)
if (i < nodes.length - 1) {
push(“, “)
}
}
}
function getFunctionDecl(node, context) {
const { push, indent, deIndent } = context
push(function ${node.id.name}
)
push((
)
genNodeList(node.params, context)
push()
)
push({
)
indent()
node.body.forEach((n) => genNode(n, context))
deIndent()
push(}
)
}
function getArrayExpression(node, context) {
const { push } = context
push([
)
genNodeList(node.elements, context)
}
function getStringLiteral(node, context) {
const { push } = context
push('${node.value}'
)
}
function getReturnStatement(node, context) {
const { push } = context
push(return
)
genNode(node.return, context)
}
function getCallExpression(node, context) {
const { push } = context
const { callee, arguments: args } = node
push(${callee.name}(
)
genNodeList(args, context)
push()
)
}
switch (node.type) {
case “FunctionDecl”:
getFunctionDecl(node, context)
break
case “ReturnStatement”:
getReturnStatement(node, context)
break
case “CallExpression”:
getCallExpression(node, context)
break
case “ArrayExpression”:
getArrayExpression(node, context)
break
case “StringLiteral”:
getStringLiteral(node, context)
break
}
}
function generate(node) {
const context = {
code: “”,
push(code) {
context.code += code
},
currentIndex: 0,
newline() {
context.code += “\n” +
.repeat(context.currentIndex)
},
indent() {
context.currentIndex++
context.newline()
},
deIndent() {
context.currentIndex—
context.newline()
},
}
genNode(node, context)
return context.code
}
function complier(template) { const ast = parser(template)
transform(ast)
const code = generate(ast.jsNode) return code }
const code = complier(“
Vue
Template
```