编译器其实只是一段程序,它用来将“一种语言A”翻译成“另一种语言B”。其中,语言A通常叫做源代码,语言B通常叫做目标代码。编译器将源代码翻译成目标代码的过程叫做编译。完整的编译过程通常包含词法分析、语义分析、中间代码设生成、优化、目标代码生成等步骤。
    vue编译器的步骤
    第十五章-编译器技术核心技术概览 - 图1
    主要有三个部分组成

    • 用来将模板字符串解析为模板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

    “) console.log(code)

    ```