前两篇博客已经把源代码分析和分析结果转换的这两个过程写完了,还剩下最后一个过程 —— 新代码的生成。
新代码的生成
codeGenerator.js
export default function codeGenerator(node) {
switch (node.type) {
case 'Program':
return node.body.map(codeGenerator)
.join('\n');
case 'ExpressionStatement':
return (
codeGenerator(node.expression) +
';'
);
case 'CallExpression':
return (
codeGenerator(node.callee) +
'(' +
node.arguments.map(codeGenerator)
.join(', ') +
')'
);
case 'Identifier':
return node.name;
case 'NumberLiteral':
return node.value;
case 'StringLiteral':
return '"' + node.value + '"';
default:
throw new TypeError(node.type);
}
}
test.js
import tokenizer from './tokenizer'
import parser from './parser'
import transformer from './transformer'
import codeGenerator from './codeGenerator'
const tokenizer_res = tokenizer('(add 2 (subtract 4 2))')
const parser_res = parser(tokenizer_res)
const transformer_res = transformer(parser_res)
const codeGenerator_res = codeGenerator(transformer_res)
console.log(codeGenerator_res)
// add(2, subtract(4, 2));
这一步就是将 transformer 输出的新 AST 处理并生成 JS 代码字符串的过程。
具体来说,switch 语句根据传给 codeGenerator 的节点对象(node)的 type 属性来决定采用哪种处理方式:
-
如果
type为Program,将节点对象的body属性进行map,处理函数为codeGenerator,实际上是将body中的元素一一递归,返回了数组后,元素间进行换行,但该例中body只有一个元素不会换行。最终将输出的字符串返回。 -
如果
type为ExpressionStatement,那么将节点对象的expression属性交给codeGenerator进行递归,并将返回的结果加上分号之后一并返回。 -
如果
type为CallExpression,那么返回的字符串是这样的,将节点对象的callee属性交给codeGenerator递归,递归的结果拼上左括号,再拼上node.arguments.map(codeGenerator).join(', ')(这类似于type为Program时的处理方式,只是这时要map的数组是arguments,产生的数组元素间加上逗号和空格),最后再拼上右括号,完成之后将结果返回。 -
如果
type为Identifier,返回节点对象的name属性。 -
如果
type为NumberLiteral,返回节点对象的value属性。 -
如果
type为StringLiteral,节点对象的value属性加上双引号之后返回,但该例中不存在StringLiteral。 -
如果都不是,抛出一个错误。










网友评论