跳到主要内容

codeGenerator.js

codeGenerator 功能是将经过转换器transformer 转换的AST抽象语法树 转换为 代码

Demo示例一


<h1 id="title"><span>hello</span>world</h1>转换为JSX语句React.createElement("h1",{id:"title"},React.createElement("span",null,"hello"),"world")

分析

<h1 id="title"><span>hello</span>world</h1> 经过 parser 编译器转换成的抽象语法树ASt为:

{
"type": "Program",
"start": 0,
"end": 43,
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 43,
"expression": {
"type": "JSXElement",
"start": 0,
"end": 43,
"openingElement": {
"type": "JSXOpeningElement",
"start": 0,
"end": 15,
"attributes": [
{
"type": "JSXAttribute",
"start": 4,
"end": 14,
"name": {
"type": "JSXIdentifier",
"start": 4,
"end": 6,
"name": "id"
},
"value": {
"type": "Literal",
"start": 7,
"end": 14,
"value": "title",
"raw": "\"title\""
}
}
],
"name": {
"type": "JSXIdentifier",
"start": 1,
"end": 3,
"name": "h1"
},
"selfClosing": false
},
"closingElement": {
"type": "JSXClosingElement",
"start": 38,
"end": 43,
"name": {
"type": "JSXIdentifier",
"start": 40,
"end": 42,
"name": "h1"
}
},
"children": [
{
"type": "JSXElement",
"start": 15,
"end": 33,
"openingElement": {
"type": "JSXOpeningElement",
"start": 15,
"end": 21,
"attributes": [],
"name": {
"type": "JSXIdentifier",
"start": 16,
"end": 20,
"name": "span"
},
"selfClosing": false
},
"closingElement": {
"type": "JSXClosingElement",
"start": 26,
"end": 33,
"name": {
"type": "JSXIdentifier",
"start": 28,
"end": 32,
"name": "span"
}
},
"children": [
{
"type": "JSXText",
"start": 21,
"end": 26,
"value": "hello",
"raw": "hello"
}
]
},
{
"type": "JSXText",
"start": 33,
"end": 38,
"value": "world",
"raw": "world"
}
]
}
}
],
"sourceType": "module"
}

抽象语法树AST 经过 transformer 转换器转换后的结果为:

{
"type": "Program",
"start": 0,
"end": 87,
"body": [
{
"type": "ExpressionStatement",
"start": 0,
"end": 87,
"expression": {
"type": "CallExpression",
"start": 0,
"end": 87,
"callee": {
"type": "MemberExpression",
"start": 0,
"end": 19,
"object": {
"type": "Identifier",
"start": 0,
"end": 5,
"name": "React"
},
"property": {
"type": "Identifier",
"start": 6,
"end": 19,
"name": "createElement"
},
"computed": false,
"optional": false
},
"arguments": [
{
"type": "Literal",
"start": 20,
"end": 24,
"value": "h1",
"raw": "\"h1\""
},
{
"type": "ObjectExpression",
"start": 25,
"end": 37,
"properties": [
{
"type": "Property",
"start": 26,
"end": 36,
"method": false,
"shorthand": false,
"computed": false,
"key": {
"type": "Identifier",
"start": 26,
"end": 28,
"name": "id"
},
"value": {
"type": "Literal",
"start": 29,
"end": 36,
"value": "title",
"raw": "\"title\""
},
"kind": "init"
}
]
},
{
"type": "CallExpression",
"start": 38,
"end": 78,
"callee": {
"type": "MemberExpression",
"start": 38,
"end": 57,
"object": {
"type": "Identifier",
"start": 38,
"end": 43,
"name": "React"
},
"property": {
"type": "Identifier",
"start": 44,
"end": 57,
"name": "createElement"
},
"computed": false,
"optional": false
},
"arguments": [
{
"type": "Literal",
"start": 58,
"end": 64,
"value": "span",
"raw": "\"span\""
},
{
"type": "Literal",
"start": 65,
"end": 69,
"value": null,
"raw": "null"
},
{
"type": "Literal",
"start": 70,
"end": 77,
"value": "hello",
"raw": "\"hello\""
}
],
"optional": false
},
{
"type": "Literal",
"start": 79,
"end": 86,
"value": "world",
"raw": "\"world\""
}
],
"optional": false
}
}
],
"sourceType": "module"
}

所以,codeGenerator 的作用就是将 AST 抽象语法树 经过 transformer 转换后的结果进一步生成JSX

实现

const nodeTypes = require("./nodeTypes");

function codeGenerator(node) {
switch (node.type) {
case nodeTypes.Program:
return node.body.map(codeGenerator).join("\n");
case nodeTypes.ExpressionStatement:
return codeGenerator(node.expression);
case nodeTypes.CallExpression:
return (
codeGenerator(node.callee) +
"(" +
node.arguments.map(codeGenerator).join(",") +
")"
);
case nodeTypes.MemberExpression:
return codeGenerator(node.object) + "." + codeGenerator(node.property);
case nodeTypes.ObjectExpression:
return `{${node.properties.map(codeGenerator).join(",")}}`;
case nodeTypes.Property:
return `${node.key.name}:"${node.value.value}"`;
case nodeTypes.Identifier:
return node.name;
case nodeTypes.StringLiteral:
return `"${node.value}"`;
case nodeTypes.NullLiteral:
return `null`;
}
}

module.exports = codeGenerator;

测试

const parser = require("./parser");
const transformer = require("./transformer");
const codeGenerator = require("./codeGenerator.md");

const sourceCode = '<h1 id="title"><span>hello</span>world</h1>';
const ast = parser(sourceCode);
transformer(ast);
const result = codeGenerator(ast);
console.log(result); // React.createElement("h1",{id:"title"},React.createElement("span",null,"hello"),"world")