Ast abstract syntax tree and Babel plug in

Time:2021-5-5

AST(abstract syntax tree, AST) abstract syntax tree, which can translate code into syntax tree

For example, the following code:

var a = 3;
a + 5

ASTAbstract tree structure:

Ast abstract syntax tree and Babel plug in

ProgramRepresents the root node

  • VariableDeclarationVariable declaration

    • IdentifierIdentifier+Numeric LiteralNumber literal quantity
  • BinaryExpression(binomial)

    • IdentifierIdentifier,operatorBinomial operators,Numeric LiteralNumber literal quantity

You can go thereastexplorer.netseeASTAnalytical results of

Compiler procedure

The working process of most compilers can be divided into three parts

  • Parse
  • Transform
  • Generate (code generation)

Ast abstract syntax tree and Babel plug in

installesprimaTo understand the compilation process:

npm install esprima estraverse escodegen
const esprima = require('esprima')
const estraverse = require('estraverse')
const escodegen = require('escodegen')

let code = `var a = 3`

//Parse
let ast = esprima.parseScript(code);

//Transform
estraverse.traverse(ast, {
  enter(node) {
    console.log("enter",node.type);
  },
  leave(node) {
    console.log("leave",node.type);
  }
});

//Generate (code generation)
const result = escodegen.generate(ast);

Babel’s traversal of AST is depth first traversal. For each branch of AST, Babel will first traverse down to the end, then traverse up to exit the node just traversed, and then find the next branch.

ASTThe traversal of the syntax tree isDepth first traversal, so it will first traverse down to the end, and then traverse up to exit the node just traversed, looking for the next branch, so during the traversal process, the console will print the following information:

enter Program
enter VariableDeclaration
enter VariableDeclarator
enter Identifier
leave Identifier
enter Literal
leave Literal
leave VariableDeclarator
leave VariableDeclaration
leave Program

adopttypeWe can modify the value of the variable

estraverse.traverse(ast, {
  enter(node) {
    if(node.type === "Literal"){
      node.value = "change";
    }
  }
});

// var a = "change";

Babel plug in

Take a lookbabelHow does it work? First, install it through NPM@babel/coreandbabel-types:

npm install @babel/core

We know thatbabelCan compile ES6 code, such as the most basicconstAnd arrow function:

//Const and arrow function of es2015
const add = (a, b) => a + b;

//After Babel's Translation
var add = function add(a, b) {
  return a + b;
};

We can get thereastexplorerTo view the generated syntax tree:

{
  "type": "Program",
  "body": [
    {
      "Type": "VariableDeclaration", // variable declaration
      "Declarations": [// specific statements
        {
          "Type": "variabledeclarator", // variable declaration
          "id": {
            "Type": "identifier", // identifier (the most basic)
            "Name": "add" // function name
          },
          "init": {
            "Type": "arrowfunctionexpression", // arrow function
            "id": null,
            "expression": true,
            "generator": false,
            "Params": [// parameter
              {
                "type": "Identifier",
                "name": "a"
              },
              {
                "type": "Identifier",
                "name": "b"
              }
            ],
            "Body": {// function body
              "Type": "binaryexpression", // binomial
              "Left": {// left side of binomial
                "type": "Identifier",
                "name": "a"
              },
              "Operator": "+", // binomial operator
              "Right": {// right side of binomial
                "type": "Identifier",
                "name": "b"
              }
            }
          }
        }
      ],
      "kind": "const"
    }
  ],
  "sourceType": "module"
}

Through the code simulation:

const babel = require('babel-core');
const t = require('babel-types');

let code = `let add = (a, b)=>{return a+b}`;
let ArrowPlugins = {
visitor: {
    ArrowFunctionExpression(path) {
      let { node } = path;
      let body = node.body;
      let params = node.params;
      let r = t.functionExpression(null, params, body, false, false);
      path.replaceWith(r);
    }
  }
}
let result = babel.transform(code, {
  plugins: [
    ArrowPlugins
  ]
})
console.log(result.code);

We can visit visitors in thevisitorA matchingtypeTo replace the arrow function in the callback function.

Class conversion

const babel = require("@babel/core");
const typs = require("@babel/types");

const code = `
class Animal {
    constructor(name){
        this.name = name
    }
    getName(){
        return this.name
    }
}
`

const classPlugins = {
    visitor:{
        ClassDeclaration(path){
            let node = path.node;
            let body = node.body.body;
            let id = node.id;
            let params = node.params;
            let methods = body.map(method=>{
                if(method.kind === "constructor"){
                    return typs.functionDeclaration(id, method.params, method.body)
                }else{
                    // Animal.prototype
                    let left = typs.memberExpression(id,typs.identifier("prototype"));
                    // Animal.prototype.getName
                    left = typs.memberExpression(left,method.key);
                    let right = typs.functionExpression(null,method.params,method.body);
                    return typs.assignmentExpression("=",left,right);
                }
            })
            path.replaceWithMultiple(methods);
        }
    }
}

const result = babel.transform(code, {
  plugins: [classPlugins]
})

console.log(result.code)

Import conversion

const babel = require('@babel/core');
const types = require('@babel/types');

const code = `import antd,{Button} from "antd"`;

const importPlugin = {
  visitor: {
    ImportDeclaration(path) {
      let node = path.node
      let specifiers = node.specifiers
      if (
        !(
          specifiers.length == 1 &&
          types.isImportDefaultSpecifier(specifiers[0])
        )
      ) {
        specifiers = specifiers.map((specifier) => {
          let local = types.importDefaultSpecifier(specifier.local);
          if (types.isImportDefaultSpecifier(specifier)) {
            return types.importDeclaration([local],types.stringLiteral(node.source.value))
        } else {
            return types.importDeclaration([local],types.stringLiteral(node.source.value+"/lib/"+specifier.local.name))
          }
        });
        path.replaceWithMultiple(specifiers)
      }
    },
  },
}

const result = babel.transform(code, {
  plugins: [importPlugin],
});

console.log(result.code)

Reference link

Recommended Today

Analysis of super comprehensive MySQL statement locking (Part 1)

A series of articles: Analysis of super comprehensive MySQL statement locking (Part 1) Analysis of super comprehensive MySQL statement locking (Part 2) Analysis of super comprehensive MySQL statement locking (Part 2) Preparation in advance Build a system to store heroes of the Three KingdomsheroTable: CREATE TABLE hero ( number INT, name VARCHAR(100), country varchar(100), PRIMARY […]