核心内容:根据AST生成代码的算法和步骤
1. 介绍
- 访问者模式:为每种AST节点类型实现对应的生成方法,通过动态分发处理不同节点
- 递归遍历:深度优先遍历;自顶向下递归处理AST树的每个节点。
- 上下文维护:
- 通过indent_level跟踪代码缩进级别
- 使用output列表收集生成的代码片段
- ...
2. 代码结构展示
2.1 代码结构
class JSCodeGenerator {
constructor() {
this.indent_level = 0; // 跟踪代码缩进级别
this.output = []; // 收集生成的代码片段
}
indent() {
return " ".repeat(this.indent_level);
}
generate(node) {
const method = `generate_${node.type}`;
if (this[method]) {
return this[method](node);
}
throw new Error(`Unsupported node type: ${node.type}`);
}
generate_[node_type](node) {
// 处理
for (const statement of node.body) {
this.output.push(this.generate(statement));
}
this.output.push(`xxxxxx`);
}
generate_Program(node) {}
generate_ClassDeclaration(node) {}
generate_xxx(node) {}
}
2.2 解释
- 入口为
generate - 根据
generate_${node.type}找对应的方法调用,递归。
3. 举例子
3.1 Identifier
interface Identifier {
type: 'Identifier';
name: string;
}
{ type: "Identifier", name: "name" }
// output: "name"
function generate_Identifier(node) {
return node.name;
}
3.2 ThisExpression
interface ThisExpression {
type: 'ThisExpression';
}
{ type: "ThisExpression" }
// output: this
function generate_ThisExpression(node) {
return "this";
}
3.3 MemberExpression
interface MemberExpression {
type: 'MemberExpression';
computed: boolean;
object: Expression;
property: Expression;
}
{
type: "MemberExpression",
object: { type: "ThisExpression" },
property: { type: "Identifier", name: "name" }
}
// output: this.name
function generate_MemberExpression(node) {
const object = this.generate(node.object); // this.generate会找打对应的节点
const property = this.generate(node.property);
return `${object}.${property}`;
}
3.4 AssignmentExpression 赋值表达式
interface AssignmentExpression {
type: 'AssignmentExpression';
operator: '=' | '*=' | '**=' | '/=' | '%=' | '+=' | '-=' |
'<<=' | '>>=' | '>>>=' | '&=' | '^=' | '|=';
left: Expression;
right: Expression;
}
{
type: "AssignmentExpression",
operator: "=",
left: {
type: "MemberExpression",
object: { type: "ThisExpression" },
property: { type: "Identifier", name: "name" }
},
right: { type: "Identifier", name: "name" }
}
// output: this.name = name
function generate_AssignmentExpression(node) {
const left = this.generate(node.left);
const right = this.generate(node.right);
return `${left} ${node.operator} ${right}`;
}
3.4 ExpressionStatement
interface ExpressionStatement {
type: 'ExpressionStatement';
expression: Expression;
directive?: string;
}
{
type: "ExpressionStatement",
expression: {...}
}
// output:
// this.name = name ;
function generate_ExpressionStatement(node) {
return this.generate(node.expression) + ";";
}
3.5 MethodDefinition
interface MethodDefinition {
type: 'MethodDefinition';
key: Expression | null;
computed: boolean;
value: FunctionExpression | null;
kind: 'method' | 'constructor';
static: boolean;
}
{
type: "MethodDefinition",
kind: "constructor",
static: false,
computed: false,
key: { type: "Identifier", name: "constructor" },
value: {
type: "FunctionExpression",
params: [
{ type: "Identifier", name: "name" },
{ type: "Identifier", name: "age" }
],
body: {
type: "BlockStatement",
body: [
{
type: "ExpressionStatement",
expression: {...}
}
]
}
}
}
// output
constructor(name, age) {
this.name = name;
}
function generate_MethodDefinition(node) {
const kind = node.kind === "constructor" ? "constructor" : "";
const key = this.generate(node.key);
const computed = node.computed ? `[${key}]` : key;
const async = node.value.async ? "async " : "";
const generator = node.value.generator ? "* " : "";
const static_ = node.static ? "static " : "";
const params = node.value.params
.map(param => this.generate(param))
.join(", ");
this.output.push(
`${this.indent()}${static_}${async}${generator}${kind || computed}(${params}) {`
);
this.indent_level++;
for (const stmt of node.value.body.body) {
this.output.push(this.indent() + this.generate(stmt));
}
this.indent_level--;
this.output.push(`${this.indent()}}`);
return "";
}
3.6 ClassDeclaration
interface ClassDeclaration {
type: 'ClassDeclaration';
id: Identifier | null;
superClass: Identifier | null;
body: ClassBody;
}
interface ClassBody {
type: 'ClassBody';
body: MethodDefinition[];
}
{
type: "ClassDeclaration",
id: { type: "Identifier", name: "Person" },
superClass: null,
body: {
type: "ClassBody",
body: [
{
type: "MethodDefinition",
kind: "constructor",
static: false,
computed: false,
key: { type: "Identifier", name: "constructor" },
value: {
type: "FunctionExpression",
params: [
{ type: "Identifier", name: "name" },
{ type: "Identifier", name: "age" }
],
body: {
type: "BlockStatement",
body: [...]
}
}
}
]
}
}
// output:
class Person {
constructor(name, age) {
this.name = name;
}
}
function generate_ClassDeclaration(node) {
const name = this.generate(node.id);
const extend = node.superClass
? ` extends ${this.generate(node.superClass)}`
: "";
this.output.push(`${this.indent()}class ${name}${extend} {`);
this.indent_level++;
for (const method of node.body.body) {
this.generate(method);
}
this.indent_level--;
this.output.push(`${this.indent()}}`);
return "";
}
3.7 Program
interface Program {
type: 'Program';
sourceType: 'script';
body: StatementListItem[];
}
{
type: "Program",
body: [...]
}
function generate_Program(node) {
for (const statement of node.body) {
this.output.push(this.generate(statement));
}
return this.output.filter(Boolean).join("\n");
}
完整代码
// codeGenerator.js
class JSCodeGenerator {
constructor() {
this.indent_level = 0;
this.output = [];
}
generate(node) {
const method = `generate_${node.type}`;
if (this[method]) {
return this[method](node);
}
throw new Error(`Unsupported node type: ${node.type}`);
}
indent() {
return " ".repeat(this.indent_level);
}
generate_Program(node) {
for (const statement of node.body) {
this.output.push(this.generate(statement));
}
return this.output.filter(Boolean).join("\n");
}
generate_ClassDeclaration(node) {
const name = this.generate(node.id);
const extend = node.superClass
? ` extends ${this.generate(node.superClass)}`
: "";
this.output.push(`${this.indent()}class ${name}${extend} {`);
this.indent_level++;
for (const method of node.body.body) {
this.generate(method);
}
this.indent_level--;
this.output.push(`${this.indent()}}`);
return "";
}
generate_MethodDefinition(node) {
const kind = node.kind === "constructor" ? "constructor" : "";
const key = this.generate(node.key);
const computed = node.computed ? `[${key}]` : key;
const async = node.value.async ? "async " : "";
const generator = node.value.generator ? "* " : "";
const static_ = node.static ? "static " : "";
const params = node.value.params
.map(param => this.generate(param))
.join(", ");
this.output.push(
`${this.indent()}${static_}${async}${generator}${kind || computed}(${params}) {`
);
this.indent_level++;
for (const stmt of node.value.body.body) {
this.output.push(this.indent() + this.generate(stmt));
}
this.indent_level--;
this.output.push(`${this.indent()}}`);
return "";
}
generate_ExpressionStatement(node) {
return this.generate(node.expression) + ";";
}
generate_AssignmentExpression(node) {
const left = this.generate(node.left);
const right = this.generate(node.right);
return `${left} ${node.operator} ${right}`;
}
generate_MemberExpression(node) {
const object = this.generate(node.object);
const property = this.generate(node.property);
return `${object}.${property}`;
}
generate_ThisExpression() {
return "this";
}
generate_Identifier(node) {
return node.name;
}
}
// 示例AST
const ast = {
type: "Program",
body: [
{
type: "ClassDeclaration",
id: { type: "Identifier", name: "Person" },
superClass: null,
body: {
type: "ClassBody",
body: [
{
type: "MethodDefinition",
kind: "constructor",
static: false,
computed: false,
key: { type: "Identifier", name: "constructor" },
value: {
type: "FunctionExpression",
params: [
{ type: "Identifier", name: "name" },
{ type: "Identifier", name: "age" }
],
body: {
type: "BlockStatement",
body: [
{
type: "ExpressionStatement",
expression: {
type: "AssignmentExpression",
operator: "=",
left: {
type: "MemberExpression",
object: { type: "ThisExpression" },
property: { type: "Identifier", name: "name" }
},
right: { type: "Identifier", name: "name" }
}
}
]
}
}
}
]
}
}
]
};
// 使用代码生成器
const generator = new JSCodeGenerator();
const code = generator.generate(ast);
console.log(code);