[原始碼-webpack01-前置知識] AST抽象語法樹

woow_wu7發表於2020-04-05

導航

[深入01] 執行上下文
[深入02] 原型鏈
[深入03] 繼承
[深入04] 事件迴圈
[深入05] 柯里化 偏函式 函式記憶
[深入06] 隱式轉換 和 運算子
[深入07] 瀏覽器快取機制(http快取機制)
[深入08] 前端安全
[深入09] 深淺拷貝
[深入10] Debounce Throttle
[深入11] 前端路由
[深入12] 前端模組化
[深入13] 觀察者模式 釋出訂閱模式 雙向資料繫結
[深入14] canvas
[深入15] webSocket
[深入16] webpack
[深入17] http 和 https
[深入18] CSS-interview
[react] Hooks

[部署01] Nginx
[部署02] Docker 部署vue專案
[部署03] gitlab-CI

[原始碼-webpack01-前置知識] AST抽象語法樹
[原始碼-webpack02-前置知識] Tapable

前置知識

一些單詞

abstract:抽象的
( abstract syntax tree:抽象語法樹 )

Identifier:識別符號
Punctuator :標點符號

declaration:宣告
VariableDeclaration:變數宣告
declarator:宣告人

traverse:遍歷

expression:表達,表示式
performance:效能
// while parseExpression() tries to parse a single Expression with performance in mind.
// 而parseExpression()會嘗試在考慮效能的情況下解析單個Expression

doubt:疑惑
// When in doubt, use .parse() 
// 如果有疑惑的情況,請使用.parse()而不要使用.parseExpression()

Numeric:數字
複製程式碼

一些網站

AST explorer 檢視原始碼對應的AST和JSON結構
esprima 可以檢視詞法分詞階段生成的tokens
AST 視覺化工具 檢視AST視覺化樹狀圖
AST 物件文件

從javascript程式到機器可執行的機器碼需要經歷三個階段

  • 語法檢查:詞法分析,語法分析
  • 編譯執行
  • 總結:詞法分析 -> 語法分析 -> 編譯執行

AST

abstract syntax tree:抽象語法樹
abstract:抽象的

AST應用場景

  • 程式碼 ( 語法檢測 ),程式碼 ( 風格檢測 ),程式碼 ( 格式化 ),程式碼 ( 高亮 ),程式碼 ( 錯誤提示 ),程式碼 ( 自動補全 )
  • eslint amd cmd
  • webpack 通過 babel 轉義 js 語法

AST解析過程

  • (1) 讀取js檔案中的 ( 字元流 )
  • (2) 通過 ( 詞法分析 ) 生成 token ----------- 詞法分析也叫掃描scanner,分詞階段,token是一維陣列
  • (3) 通過 ( 語法分析 ) 生成 AST ------------- 語法分析也叫解析器
  • (4) 生成 ( 機器碼 ) 執行 -------------------- 編譯階段也叫編譯器

詞法分析

  • 詞法分析是將 ( 字元流char stream ) 轉換為 ( 記號流token stream )
  • token 是不可分割的最小單元,是一個一維陣列
  • ( 詞法分析 ) 也稱之為 ( 掃描scanner )
  • ( 詞法分析器 ) 裡的每一個 ( 關鍵字,識別符號,操作符,標點符號,字串,數字,布林值,註釋符,空白字元,空格,換行符 ) 等都是一個token
  • token陣列中,每一個物件包含 ( type ) 和 ( value )
  • type
  • value
    • 常見的 ( type ) 如下:
    • Keyword (關鍵詞)
    • Identifier (識別符號)
    • Punctuator (標點符號)
    • Numeric(數字)
    • String (字串)
    • Boolean(布林)
    • Null(空值)
  • 最終程式碼被分割進一個tokens列表,即一維陣列
原始碼1:
const add = (a, b) => {
	return a + b
}

tokens:
[
  { "type": "Keyword", "value": "const" },
  { "type": "Identifier", "value": "add" },
  { "type": "Punctuator", "value": "=" },
  { "type": "Punctuator", "value": "(" },
  { "type": "Identifier", "value": "a" },
  { "type": "Punctuator", "value": "," },
  { "type": "Identifier", "value": "b" },
  { "type": "Punctuator", "value": ")" },
  { "type": "Punctuator", "value": "=>" },
  { "type": "Punctuator", "value": "{" },
  { "type": "Keyword", "value": "return" },
  { "type": "Identifier", "value": "a" },
  { "type": "Punctuator", "value": "+" },
  { "type": "Identifier", "value": "b" },
  { "type": "Punctuator", "value": "}" }
]


---
原始碼2:
const a = 1;

tokens:
[
    { "type": "Keyword","value": "const" },
    { "type": "Identifier","value": "a" },
    { "type": "Punctuator","value": "=" },
    { "type": "Numeric","value": "1" },
    { "type": "Punctuator","value": ";" }
]


說明:
(1) tokens是具有type,value屬性的物件組成的陣列
(2) token是詞法分析的最小單元,不能再分解
(3) 常見的type
- keyword關鍵字
- identfier識別符號
- punctuator標點符號
- Numeric:數字
複製程式碼

語法分析

  • ( 語法分析 ) 會將詞法分析得出的token轉化成 ( 有語法含義 ) 的 ( 抽象語法樹 ) 結構
  • 同時 ( 驗證語法 ),有語法錯誤則丟擲語法錯誤
  • 屬性
    • 最外層包含:
      • ( type )
      • ( sourceType )
      • ( start )
      • ( end ) 等
      • ( body )
        • body:是一個 ( 陣列 ) ,包含多個 ( 內容塊物件 statement ),每個內容塊包含
          • type
          • start
          • end
          • kind
          • declarations:乘裝變數內容的塊,這個塊也是一個陣列,因為變數宣告可能宣告多個
            • type
            • start
            • end
            • id
              • type
              • start
              • end
              • name
  • ( statement - body陣列中的物件 ) 有很多型別,比如說變數宣告,函式定義,if語句,while迴圈,等都是一個statement
    • VariableDeclaration:變數宣告
    • FunctionDeclaration:函式定義
    • IfStatement:if語句
    • WhileStatement:while迴圈
原始碼:
var a = 1;


AST
{
  "type": "Program",
  "start": 0,
  "end": 12,
  "body": [ // ---------------------------------------------- body表示程式碼具體的內容
    { // ---------------------------------------------------- statement內容塊物件,一個body可能包含多個statement
      "type": "VariableDeclaration", // --------------------- 變數宣告
      "start": 0,
      "end": 10,
      "declarations": [
        { 
          "type": "VariableDeclarator", // ------------------ 變數宣告
          "start": 4,
          "end": 9,
          "id": {
            "type": "Identifier", // ------------------------- 識別符號
            "start": 4,
            "end": 5,
            "name": "a"
          },
          "init": {
            "type": "Literal",
            "start": 8,
            "end": 9,
            "value": 1,
            "raw": "1"
          }
        }
      ],
      "kind": "var" // --------------------------------------- 變數型別
    }
  ],
  "sourceType": "module"
}


說明:
(1) 最外層屬性:type,start,end,body[],sourceType
- body:表示程式碼的具體內容
    - 內容塊:body中可能包含多個內容塊,每個內容塊用一個物件表示
    - 內容塊包含:
        - type
        - start
        - end
        - kind
        - declarations:乘裝變數內容的塊,這個塊也是一個陣列,因為變數宣告可能生命多個
            - type
            - start
            - end
            - id
                - type
                - start
                - end
                - name 
- sourceType:表示語言的種類


(2) body是一個陣列,成員是statement內容塊物件,因為body可以包含多個statement內容塊
- statement 有很多型別,比如說變數宣告,函式定義,if語句,while迴圈,等都是一個statement
    - VariableDeclaration:變數宣告
    - FunctionDeclaration:函式定義
    - IfStatement:if語句
    - WhileStatement:while迴圈
複製程式碼

[原始碼-webpack01-前置知識] AST抽象語法樹

[原始碼-webpack01-前置知識] AST抽象語法樹

[原始碼-webpack01-前置知識] AST抽象語法樹

Babel原理

  • babel編譯的過程:解析parse -> 轉換transform -> 生成generate
  • 解析 parse
    • @babel/parser:將字串轉換成AST,Babylon 是 Babel 中使用的 JavaScript 解析器。
    • 解析過程分為兩個階段
      • 語法分析:字元流 -> token流
      • 詞法分析:token流 -> AST
    • @babel/parser
  • 轉換 transform
    • @babel/traverse:允許你瀏覽、分析和修改抽象語法樹
      • Babel接收解析得到的AST並通過 ( babel-traverse ) 對其進行 ( 深度優先遍歷 )
      • 在此過程中對節點進行 ( 新增 )、( 更新 ) 及 ( 移除 ) 操作
      • traverse:是遍歷的意思
    • @babel/traverse
  • 生成 generate
    • @babel/generator:來將轉換後的抽象語法樹轉化為Javascript 字串
      • 將經過轉換的AST通過babel-generator再轉換為js程式碼
      • 過程及時深度遍歷整個AST,然後構建轉換後的程式碼字串。
    • @babel/generator

[原始碼-webpack01-前置知識] AST抽象語法樹

[原始碼-webpack01-前置知識] AST抽象語法樹

@babel/parser

babelParser.parse(code, [options]) ------------------------------------ 解析所有程式碼
babelParser.parseExpression(code, [options]) -------------------------- 解析單個表示式

引數:
- code:表示原始碼字串
- options:配置物件,可選
    - allowImportExportEverywhere:預設import和export宣告只能出現在頂部,當此選項為true則可以出現在任何地方
    - ...
複製程式碼

@babel/traverse

  • 因為 ( @babel/parser解析 ) 和 ( @babel/generator生成 ) 基本不會變化,所以重點是 ( @babel/traverse轉換 )
import * as babylon from "babylon";
import traverse from "babel-traverse";

// 原始碼string
const code = `function square(n) {
  return n * n;
}`;

// 解析 parse:string -> ast
const ast = babylon.parse(code);

// 轉換 transform:ast -> modified ast
traverse(ast, {
  enter(path) {
    if (
      path.node.type === "Identifier" &&
      path.node.name === "n"
    ) {
      path.node.name = "x"; // ---------------- 如果是識別符號並且識別符號的名字是n,就把n改為x
    }
  }
複製程式碼

@babel/generator

import {parse} from '@babel/parser';
import generate from '@babel/generator';

const code = 'class Example {}';
const ast = parse(code);

const output = generate(ast, { /* options */ }, code);
複製程式碼

babel轉化程式碼案例

  • 需求:將小寫變數轉換成大寫
// 輸入
const numberFive = 5;

// 輸出
const NUMBERFIVE = 5;
複製程式碼
  • 實現過程
安裝 

@babel/core ----------------------- babel核心模組
@babel/parser --------------------- 字元流 -> token流 -> AST
@babel/traverse ------------------- AST -> modified AST
@babel/generator ------------------ modified AST -> 字元流

npm install @babel/core @babel/parser @babel/traverse @babel/generator -S
複製程式碼
程式碼

const parser = require('@babel/parser');
const traverse = require('@babel/traverse').default;
const generator = require('@babel/generator').default;

// 原始碼字串
const code = `
  const nubmerFive = 5
`;

// 解析
let AST = parser.parse(code)

// 轉換
traverse(AST, {
  enter(path) {
    console.log(path.node.type, 'path.node.type')
    if (path.node.type === 'Identifier') { // 如果node型別是識別符號,就將name轉成大寫形式
      path.node.name = path.node.name.toUpperCase()
    }
  }
})

// 生成
const outputObj = generator(AST)
const outputStr = outputObj.code;

console.log(outputStr, 'outputStr')
複製程式碼

資料

AST babel-AST相關工具 juejin.im/post/5dca1e…
AST 從babel講到AST juejin.im/post/5ab35c…
AST 99%的人不瞭解的AST segmentfault.com/a/119000001…
AST 抽象語法樹-圖形 juejin.im/post/5bff94…
AST 具體細節:segmentfault.com/a/119000001…
AST cheogo.github.io/learn-javas…
AST詳細 www.codercto.com/a/88752.htm…

babel轉換 juejin.im/post/5dca1e…
babel轉換案例 cloud.tencent.com/developer/a…

相關文章