抽象語法樹-AST

抽象語法樹(Abstract Syntax Tree)

webpack和Lint等不少的工具和庫的核心都是經過Abstract Syntax Tree抽象語法樹這個概念來實現對代碼的檢查、分析等操做的node

抽象語法樹定義

這些工具的原理都是經過JavaScript Parser把代碼轉化爲一顆抽象語法樹(AST),這顆樹定義了代碼的結構,經過操縱這顆樹,咱們能夠精準的定位到聲明語句、賦值語句、運算語句等等,實現對代碼的分析、優化、變動等操做webpack

在計算機科學中,抽象語法樹(abstract syntax tree或者縮寫爲AST),或者語法樹(syntax tree),是源代碼的抽象語法結構的樹狀表現形式,這裏特指編程語言的源代碼。web

Javascript的語法是爲了給開發者更好的編程而設計的,可是不適合程序的理解。因此須要轉化爲AST來更適合程序分析,瀏覽器編譯器通常會把源碼轉化爲AST來進行進一步的分析等其餘操做。編程

JavaScript Parser

  • JavaScript Parser,把js源碼轉化爲抽象語法樹的解析器。
  • 瀏覽器會把js源碼經過解析器轉爲抽象語法樹,再進一步轉化爲字節碼或直接生成機器碼。
  • 通常來講每一個js引擎都會有本身的抽象語法樹格式,Chrome的v8引擎,firefox的SpiderMonkey引擎等等,MDN提供了詳細SpiderMonkey AST format的詳細說明,算是業界的標準。
  • 經過 esprima 把源碼轉化爲AST
  • 經過 estraverse 遍歷並更新AST
  • 經過 escodegen 將AST從新生成源碼
// 重命名
    let esprima = require('esprima');
    var estraverse = require('estraverse');
    var escodegen = require("escodegen");
    let code = 'function ast(){}';
    let ast=esprima.parse(code);
    let indent=0;
    function pad() {
        return ' '.repeat(indent);
    }
    estraverse.traverse(ast,{
        enter(node) {
            console.log(pad()+node.type);
            if(node.type == 'FunctionDeclaration'){
                node.id.name = 'ast_rename';
            }
            indent+=2;
         },
        leave(node) {
            indent-=2;
            console.log(pad()+node.type);
    
         }
     });
    let generated = escodegen.generate(ast);
    console.log(generated);
複製代碼

轉換箭頭函數

@babel/core、babel-types瀏覽器

轉換前bash

const sum = (a,b)=>a+bbabel

轉換後編程語言

var sum = function sum(a, b) { return a + b; };ide

代碼實現函數

let babel = require('@babel/core');
    let t = require('babel-types');
    const code = `const sum = (a,b)=>a+b`;
    // path.node  父節點
    // path.parentPath 父路徑
    let transformArrowFunctions = {
        visitor: {
            ArrowFunctionExpression: (path, state) => {
                let node = path.node;
                let id = path.parent.id;
                let params = node.params;
                let body=t.blockStatement([
                    t.returnStatement(node.body)
                ]);
                let functionExpression = t.functionExpression(id,params,body,false,false);
                path.replaceWith(functionExpression);
            }
        }
    }
    const result = babel.transform(code, {
        plugins: [transformArrowFunctions]
    });
    console.log(result.code); 
複製代碼

把類編譯爲Function

轉換前

class Person {
      constructor(name) {
          this.name=name;
      }
      getName() {
          return this.name;
      }
  }
複製代碼

轉換後

function Person(name) {
        this.name=name;
    }
    Person.prototype.getName=function () {
        return this.name;
    }
複製代碼

實現

let babel = require('@babel/core');
    let t=require('babel-types');
    let source=`
        class Person {
            constructor(name) {
                this.name=name;
            }
            getName() {
                return this.name;
            }
        }
    `;
    let ClassPlugin={
        visitor: {
            ClassDeclaration(path) {
                let node=path.node;
                let id=node.id;
                let constructorFunction = t.functionDeclaration(id,[],t.blockStatement([]),false,false);
                let methods=node.body.body;
                let functions = [];
                methods.forEach(method => {
                    if (method.kind == 'constructor') {
                        constructorFunction = t.functionDeclaration(id,method.params,method.body,false,false);
                        functions.push(constructorFunction);
                    } else {
                        let memberObj=t.memberExpression(t.memberExpression(id,t.identifier('prototype')),method.key);
                        let memberFunction = t.functionExpression(id,method.params,method.body,false,false);
                        let assignment = t.assignmentExpression('=',memberObj,memberFunction);
                        functions.push(assignment);
                    }
                });
                if (functions.length ==1) {
                    path.replaceWith(functions[0]);
                } else {
                    path.replaceWithMultiple(functions);
                }
            }
        }
    }
    
    
    const result = babel.transform(source,{
        plugins:[
            ClassPlugin
        ]
    });
    console.log(result.code);
複製代碼
相關文章
相關標籤/搜索