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

什麼是Babel

  • 什麼是Babel?javascript

    • Babel 是咱們知道的將 ES六、ES7等代碼轉譯爲 ES5 代碼且能安全穩定運行最好的工具
    • 同時它容許開發者開發插件, 可以在編譯時期轉換 JavaScript 的結構。

    Babel概述:

  • 咱們須要知道3個Babel處理流程中的重要工具;html

    • 解析
      • 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?java

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

    • javascript 語句要想知道抽象語法樹以後的代碼是什麼,裏面的字段都表明什麼含義以及遍歷的規則,能夠經過javascript語法轉換AST工具來實現javascript語法的在線轉換; 例如:

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

  • 例如:語法轉換插件須要藉助 babel-core 和 babel-types 兩個模塊,就是依賴 esprima、estraverse 和 escodegengithub

  • 轉換的抽象語法樹:

    每個含有type屬性的對象,咱們稱之爲節點,修改是指獲取對應的類型並修改改節點的屬性便可;npm

    {
      "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

    • 查看遍歷過程: babel

      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);
            }
        });
      複製代碼
    • 打印結果以下:模塊化

  • 以上代碼經過estraverse模塊的traverse方法,將esprima模塊裝換的AST進行了遍歷。

  • 經過打印type屬性,能夠知道深度遍歷AST就是遍歷每一層的type屬性,因此遍歷會分爲兩個階段,進入階段和離開階段,在traverse方法中分別用參數enter和leave兩個函數監聽;

  • escodegen 將 AST 轉換成 JS

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 中的體現以及在 NodeJS 中用於生成、遍歷和修改 AST 抽象語法樹的核心依賴;讓咱們有了更加深入地認識;

相關文章
相關標籤/搜索