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

抽象语法树AST #59

Open
kekobin opened this issue Oct 30, 2019 · 0 comments
Open

抽象语法树AST #59

kekobin opened this issue Oct 30, 2019 · 0 comments

Comments

@kekobin
Copy link
Owner

kekobin commented Oct 30, 2019

AST是什么呢?

在计算机科学中,抽象语法和抽象语法树其实是源代码的抽象语法结构的树状表现形式。常用的浏览器就是通过将js代码转化为抽象语法树来进行下一步的分析等其他操作。所以将js转化为抽象语法树更利于程序的分析。

AST用途

代码语法的检查,代码风格的检查,代码的格式化,代码的高亮,代码错误提示,代码自动补全等等

如:JSLint、JSHint 对代码错误或风格的检查,发现一些潜在的错误 IDE的错误提示,格式化,高亮,自动补全等等 代码的混淆压缩 如:UglifyJS2等

优化变更代码,改变代码结构达到想要的结构

代码打包工具webpack,rollup等等 CommonJS、AMD、CMD、UMD等代码规范之间的转化 CoffeeScript、TypeScript、JSX等转化为原生Javascript

众多的引擎

chrome有v8,firefix有spidermonkey.还有一些常用的引擎有:

  • esprima
  • acron
  • Traceur
  • UglifyJS2
  • shift
  • babylon(大名鼎鼎的babel使用的引擎)

AST的三板斧

  • 通过esprima生成AST
  • 通过estraverse遍历和更新AST
  • 通过escodegen将AST重新生成源码

示例:

npm i esprima estraverse escodegen --save

新建文件test.js:

const esprima = require('esprima');
let code = 'const a = 1';
const ast = esprima.parseScript(code);
console.log(ast);

得到结果:

Script {
  type: 'Program',
  body:
   [ VariableDeclaration {
       type: 'VariableDeclaration',
       declarations: [Array],
       kind: 'const' } ],
  sourceType: 'script' }

继续添加代码:

const estraverse = require('estraverse');

estraverse.traverse(ast, {
    enter: function (node) {
        node.kind = "var";
    }
});

console.log(ast);

注:这里的leave是转换过程中的vistor,具体对于vistor解释参见super-tiny-compiler

得到结果:

Script {
  type: 'Program',
  body:
   [ VariableDeclaration {
       type: 'VariableDeclaration',
       declarations: [Array],
       kind: 'var' } ],
  sourceType: 'script' }

继续添加代码:

const escodegen = require("escodegen");
const transformCode = escodegen.generate(ast)

console.log(transformCode);

得到:

var a = 1;

babylon

babel是一个javascript编译器,它使用的解析引擎是babylon。和上面解释的一样,也是分3个阶段运行代码:解析(parsing),转译(transforming),生成(generation)。

特别注意的是,当我们开发babel-plugin的时候,我们只需要描述转化你AST的节点“visitors”就可以了。

image

babel的AST示例:

// babel 核心库,用来实现核心的转换引擎
const babel = require('babel-core');
// 实现类型转化 生成AST节点
const types = require('babel-types');
let code = 'let sum = (a,b) => a+b;';
let es5Code = function (a,b) {
    return a+b;
};

// babel 转化采用的是访问者模式Visitor 对于某个对象或者一组对象,不同的访问者,产生的结果不同,执行操作也不同

// 这个访问者可以对特定的类型的节点进行处理
let visitor = {
    ArrowFunctionExpression(path) {
        // 如果这个节点是箭头函数的节点的话,我们在这里进行处理替换工作
        // 不是简单的字符串替换,而是对象级别的替换
        console.log(path.node.body);
        let params = path.node.params;
        let blockStatement = types.blockStatement([types.returnStatement(path.node.body)])
        let func = types.functionExpression(null, params, blockStatement, false,false);
        path.replaceWith(func)
    }
};

let arrayPlugin = {visitor};

// babel内部先把代码转化成AST,然后进行遍历

let result = babel.transform(code, {
    plugins: [
        arrayPlugin
    ]
});

console.log(result.code);
// let sum = function (a, b) {
//     return a + b;
// };

jscodeshift

相当于babylon的一个超集,提供了非常多使用的工具,实际用途大于上面的基础工具,具体参见
jscodeshift基础

参考

AST explorer 神器,阅读和书写AST操作全靠它
Babel Plugin Handbook从这里找到了一些API
Babylon和babel-traverse详解其实也称不上详解,不过看到的比较早,算是篇启蒙文章吧
Babel types生成代码时会大量用到
前端码农之蜕变 — AST(抽象语法树)

@kekobin kekobin changed the title 抽象语法书AST 抽象语法数AST Oct 31, 2019
@kekobin kekobin changed the title 抽象语法数AST 抽象语法树AST Oct 31, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant