AST抽象语法树

AST抽象语法树

介绍

抽象语法树用途

webpackLint等很多的工具和库的核心都是通过Abstract Syntax Tree抽象语法树这个概念来实现对代码的检查、分析等操作的

  • 代码语法的检查、代码风格的检查、代码的格式化、代码的高亮、代码错误提示、代码自动补全等等
    • 如 JSLint、JSHint 对代码错误或风格的检查,发现一些潜在的错误
    • IDE 的错误提示、格式化、高亮、自动补全等等
  • 代码混淆压缩
    • UglifyJS2 等
  • 优化变更代码,改变代码结构使达到想要的结构
    • 代码打包工具 webpack、rollup 等等
    • CommonJS、AMD、CMD、UMD 等代码规范之间的转化
    • CoffeeScript、TypeScript、JSX 等转化为原生 Javascript

抽象语法树定义

上面这些工具的原理都是通过JavaScript Parser把代码转化为一颗抽象语法树(AST),这颗树定义了代码的结构,通过操纵这颗树,我们可以精准的定位到声明语句、赋值语句、运算语句等等,实现对代码的分析、优化、变更等操作

在这里插入图片描述

  • 词法分析:编译器会先将一连串字符打断成(对于语言来说)有意义的片段,称为 token(记号),例如 var a = 2;。这段程序很可能会被打断成如下 token:var,a,=,2,和 ;

  • 语法分析:编译器将一个 token 的流(数组)转换为一个“抽象语法树”,它表示了程序的语法结构


一棵真实的抽象语法树

我们可以访问 https://astexplorer.net,来将代码转换为抽象语法树。

比如,我们将 function ast(){} 转换为抽象语法树

{
  "type": "Program",
  "start": 0,
  "end": 16,
  "body": [
    {
      "type": "FunctionDeclaration",
      "start": 0,
      "end": 16,
      "id": {
        "type": "Identifier",
        "start": 9,
        "end": 12,
        "name": "ast"
      },
      "expression": false,
      "generator": false,
      "async": false,
      "params": [],
      "body": {
        "type": "BlockStatement",
        "start": 14,
        "end": 16,
        "body": []
      }
    }
  ],
  "sourceType": "module"
}

在这里插入图片描述


AST 结构的一些基本定义

在这里插入图片描述


JavaScript Parser

  • JavaScript Parser是把JavaScript源码转化为抽象语法树的解析器。
  • 浏览器会把JavaScript源码通过解析器转为抽象语法树,再进一步转化为字节码或直接生成机器码。
  • 一般来说每个JavaScript引擎都会有自己的抽象语法树格式,Chrome 的 v8 引擎,firefox 的 SpiderMonkey 引擎等等,MDN 提供了详细SpiderMonkey AST format 的详细说明,算是业界的标准。

常用的 JavaScript Parser

  • esprima
  • traceur
  • acorn
  • shift

在js代码中得到AST

安装:

npm i esprima estraverse- S

示例代码:
这里我们将function ast(){}转换为AST

let esprima = require('esprima');//把JS源代码转成AST语法树
let estraverse = require('estraverse');///遍历语法树,修改树上的节点
let escodegen = require('escodegen');//把AST语法树重新转换成代码
let code = `function ast(){}`;
let ast = esprima.parse(code);
let indent = 0;
const padding = ()=>" ".repeat(indent);
estraverse.traverse(ast,{
    enter(node){
        console.log(padding()+node.type+'进入');
        if(node.type === 'FunctionDeclaration'){
            node.id.name = 'newAst';
        }
        indent+=2;
    },
    leave(node){
        indent-=2;
        console.log(padding()+node.type+'离开');
    }
});

输出:

Program进入
  FunctionDeclaration进入
    Identifier进入
    Identifier离开
    BlockStatement进入
    BlockStatement离开
  FunctionDeclaration离开
Program离开

babel的工作原理

  • Babel 能够转译 ECMAScript 2015+ 的代码,使它在旧的浏览器或者环境中也能够运行
  • 工作过程分为三个部人
    • Parse(解析) 将源代码转换成抽象语法树,树上有很多的estree节点
    • Transform(转换) 对抽象语法树进行转换
    • Generate(代码生成) 将上一步经过转换过的抽象语法树生成新的代码

在这里插入图片描述


AST遍历

  • AST是深度优先遍历
  • 访问者模式 Visitor 对于某个对象或者一组对象,不同的访问者,产生的结果不同,执行操作也不同
  • Visitor 的对象定义了用于 AST 中获取具体节点的方法
  • Visitor 上挂载以节点 type 命名的方法,当遍历 AST 的时候,如果匹配上 type,就会执行对应的方法

解析过程

分为两个步骤

  • 词法分析:将整个代码字符串分割成语法单元数组
  • 语法分析:建立分析语法单元之间的关系

语法单元

JavaScript代码中的语法单元主要包括以下几种:

  • 关键字:const,let,var等
  • 标识符:可能是一个变量,也可能是if,else这些关键字,又或者是true,false这些常量
  • 运算符
  • 数字
  • 空格
  • 注释

词法分析

源码1const 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": "}" }
]


---
源码2const 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:数字

语法分析

  • 语义分析则是将得到的词汇进行一个立体的组合,确定词语之间的关系
  • 简单来说语法分析是对语句和表达式的识别,是一个递归的过程

AST属性分析

  • 最外层包含:

    • ( 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循环

一个demo

源码:
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抽象语法树

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序媛小y

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值