前端Javascript: Babel 怎麼把字串解析成 AST,是怎麼進行詞法/語法分析的?

GeekQiaQia發表於2019-11-12

什麼是Babel

  • 什麼是Babel?

    • Babel 是我們知道的將 ES6、ES7等程式碼轉譯為 ES5 程式碼且能安全穩定執行最好的工具
    • 同時它允許開發者開發外掛, 能夠在編譯時期轉換 JavaScript 的結構。

    Babel概述:

  • 我們需要知道3個Babel處理流程中的重要工具;

    • 解析
      • Babylon是一個解析器,它可以將javascript字串,轉化為更加友好的表現形式,稱之為抽象語法樹;
      • 在解析過程中有兩個階段:詞法分析語法分析
        • 詞法分析階段:字串形式的程式碼轉換為令牌(tokens)流,令牌類似於AST中的節點;
        • 語法分析階段:把一個令牌流轉化為AST的形式,同時這個階段會把令牌中的資訊轉化為AST的表述結構
    • 轉換
      • babel-traverse 模組允許你瀏覽、分析和修改抽象語法樹(AST Abstract Syntax Tree)
        • Babel接收解析得到的AST並通過babel-traverse對其進行深度優先遍歷,在此過程中對節點進行新增、更新及移除操作。
    • 生成
      • babel-generator 模組用來將轉換後的抽象語法樹(AST Abstract Syntax Tree)轉化為Javascript 字串
        • 將經過轉換的AST通過babel-generator再轉換為js程式碼,過程及時深度遍歷整個AST,然後構建轉換後的程式碼字串。

Babel 中重要的物件Vistor

babel在處理一個節點時,是以訪問者的形式獲取節點的資訊,並進行相關的操作,這種操作是通過visitor物件實現的。
複製程式碼

在visitor中定義了處理不同節點的函式。

    visitor: {
        Program: {
            enter(path, state) {
                console.log('start processing this module...');
            },
            exit(path, state) {
                console.log('end processing this module!');
            }
        },
        ImportDeclaration:{
            enter(path, state) {
                console.log('start processing ImportDeclaration...');
                // do something
            },
            exit(path, state) {
                console.log('end processing ImportDeclaration!');
                // do something
            }
        }
    }
複製程式碼

什麼是AST

  • 什麼是AST?

    • AST (Abstract Syntax Tree)是抽象語法樹英文的縮寫,AST語法樹每一層都擁有相同的結構,這樣的每一層結構也被叫做節點(Node)。
    • AST 是原始碼的抽象語法結構樹狀表現形式,Webpack、ESLint、JSX、TypeScript 的編譯和模組化規則之間的轉化都是通過 AST 來實現對程式碼的檢查、分析以及編譯等操作。
    • 一個 AST 可以由單一的節點或是成百上千個節點構成。 它們組合在一起可以描述用於靜態分析的程式語法。
  • Javascript 語法的AST(抽象語法樹)

    • javascript 語句要想知道抽象語法樹之後的程式碼是什麼,裡面的欄位都代表什麼含義以及遍歷的規則,可以通過javascript語法轉換AST工具來實現javascript語法的線上轉換; 例如:

前端Javascript: Babel 怎麼把字串解析成 AST,是怎麼進行詞法/語法分析的?

  • esprima、estraverse 和 escodegen 模組是操作 AST 的三個重要模組,也是實現 babel 的核心依賴。

  • 例如:語法轉換外掛需要藉助 babel-core 和 babel-types 兩個模組,就是依賴 esprima、estraverse 和 escodegen

  • 轉換的抽象語法樹:

    每一個含有type屬性的物件,我們稱之為節點,修改是指獲取對應的型別並修改改節點的屬性即可;

       {
      "type": "Program",
      "body": [
          {
              "type": "VariableDeclaration",
              "declarations": [
                  {
                      "type": "VariableDeclarator",
                      "id": {
                          "type": "Identifier",
                          "name": "answer"
                      },
                      "init": {
                          "type": "BinaryExpression",
                          "operator": "*",
                          "left": {
                              "type": "Literal",
                              "value": 6,
                              "raw": "6"
                          },
                          "right": {
                              "type": "Literal",
                              "value": 7,
                              "raw": "7"
                          }
                      }
                  }
              ],
              "kind": "var"
          }
      ],
      "sourceType": "script"
    複製程式碼

}

  • estraverse 遍歷和修改AST

    • 檢視遍歷過程:

      前端Javascript: Babel 怎麼把字串解析成 AST,是怎麼進行詞法/語法分析的?

        const esprima = require("esprima");
        const estraverse = require("estraverse");
        
        let code = "var answer=6 * 7";
        
        // 遍歷語法樹
        estraverse.traverse(esprima.parseScript(code), {
            enter(node) {
                console.log("enter", node.type);
            },
            leave(node) {
                console.log("leave", node.type);
            }
        });
      複製程式碼
    • 列印結果如下:

前端Javascript: Babel 怎麼把字串解析成 AST,是怎麼進行詞法/語法分析的?

  • 以上程式碼通過estraverse模組的traverse方法,將esprima模組裝換的AST進行了遍歷。

  • 通過列印type屬性,可以知道深度遍歷AST就是遍歷每一層的type屬性,所以遍歷會分為兩個階段,進入階段和離開階段,在traverse方法中分別用引數enter和leave兩個函式監聽;

  • escodegen 將 AST 轉換成 JS

前端Javascript: Babel 怎麼把字串解析成 AST,是怎麼進行詞法/語法分析的?

const esprima = require("esprima");
const estraverse = require("estraverse");
const escodegen= require("escodegen");

let code = "var answer=6 * 7";

let tree=esprima.parseScript(code); // 生成語法樹
// 遍歷語法樹
estraverse.traverse(tree, {
    enter(node) {
        console.log("enter", node.type);
        // 修改變數名
        if(node.type==='VariableDeclarator'){
                node.id.name='result';
        }
    },
    leave(node) {
        console.log("leave", node.type);
    }
});

// 編譯修改後的語法樹;
let compileTreeJS=escodegen.generate(tree);
console.log(compileTreeJS);
複製程式碼
  • 列印結果如下 :
  • var result=6*7
    前端Javascript: Babel 怎麼把字串解析成 AST,是怎麼進行詞法/語法分析的?

通過工具瞭解抽象語法樹在 JavaScript 中的體現以及在 NodeJS 中用於生成、遍歷和修改 AST 抽象語法樹的核心依賴;讓我們有了更加深刻地認識;

相關文章