AST应用-根据AST生成代码

219 阅读2分钟

核心内容:根据AST生成代码的算法和步骤

1. 介绍

  1. 访问者模式:为每种AST节点类型实现对应的生成方法,通过动态分发处理不同节点
  2. 递归遍历:深度优先遍历;自顶向下递归处理AST树的每个节点。
  3. 上下文维护:
    • 通过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 解释

  1. 入口为generate
  2. 根据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);