Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

第 149 题:babel 怎么把字符串解析成 AST,是怎么进行词法/语法分析的? #315

Open
yygmind opened this issue Nov 12, 2019 · 5 comments

Comments

@yygmind
Copy link
Contributor

yygmind commented Nov 12, 2019

No description provided.

@caiyongmin
Copy link

caiyongmin commented Nov 12, 2019

大致分为下面四步:

  • Babel
    • input => tokenizer => tokens,先对输入代码进行分词,根据最小有效语法单元,对字符串进行切割。
    • tokens => parser => AST,然后进行语法分析,会涉及到读取、暂存、回溯、暂存点销毁等操作。
    • AST => transformer => newAST,然后转换生成新的 AST。
    • newAST => codeGenerator => output,最后根据新生成的 AST 输出目标代码。

具体见下方链接文件的代码:

@GeekQiaQia
Copy link

GeekQiaQia commented Nov 12, 2019

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 (Abstract Syntax Tree)是抽象语法树英文的缩写,AST语法树每一层都拥有相同的结构,这样的每一层结构也被叫做节点(Node)。
  • AST 是源代码的抽象语法结构树状表现形式,Webpack、ESLint、JSX、TypeScript 的编译和模块化规则之间的转化都是通过 AST 来实现对代码的检查、分析以及编译等操作。
  • 一个 AST 可以由单一的节点或是成百上千个节点构成。 它们组合在一起可以描述用于静态分析的程序语法。

Javascript 语法的AST(抽象语法树)

  • javascript 语句要想知道抽象语法树之后的代码是什么,里面的字段都代表什么含义以及遍历的规则,可以通过javascript语法转换AST工具来实现javascript语法的在线转换;
    例如:

  • esprima、estraverse 和 escodegen 模块是操作 AST 的三个重要模块,也是实现 babel 的核心依赖。

    例如:语法转换插件需要借助 babel-core 和 babel-types 两个模块,就是依赖 esprima、estraverse 和 escodegen

转换的抽象语法树:var answer= 6 * 7

每一个含有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

查看遍历过程:

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 抽象语法树的核心依赖;让我们有了更加深刻地认识;

参考整合:https://juejin.im/post/5dca1eb86fb9a04a76025292

@pagemarks
Copy link

编译原理(第2版) 值得一读
image

@front-end-yu
Copy link

@shinelp100
Copy link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants