阅读 8527

AST抽象语法树


1.概念

抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示。这里特指编程语言的源代码。

树上的每个节点都表示源代码中的一种结构,之所以说是抽象的,是因为抽象语法树并不会表示出真实语法出现的每一个细节,比如说,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现。

抽象语法树并不依赖于源语言的语法,也就是说语法分析阶段所采用的上下文无关文法,因为在写文法时,经常会对文法进行等价的转换(消除左递归,回溯,二义性等),这样会给文法分析引入一些多余的成分,对后续阶段造成不利影响,甚至会使各个阶段变得混乱。因此,很多编译器经常要独立地构造语法分析树,为前端,后端建立一个清晰的接口。

抽象语法树在很多领域有广泛的应用,比如浏览器,智能编辑器,编译器等。


2.为何需要抽象语法树(抽象语法树作用)

编程语言太多,需要一个统一的结构让计算机识别。

作用:比如typescript的类型检查,IDE的语法高亮,代码检查,转译等等,都是需要先将代码转化成AST在进行后续的操作。


3.抽象语法树的生成过程(编译)

js为例:

词法分析(lexical analysis):进行词法分析的程序或者函数叫作词法分析器(Lexical analyzer,简称Lexer),也叫扫描器(Scanner,例如typescript源码中的scanner.ts),字符流转换成对应的Token流

tokenize:tokenize就是按照一定的规则,例如token令牌(通常代表关键字,变量名,语法符号等),将代码分割为一个个的“串”,也就是语法单元)。涉及到词法解析的时候,常会用到tokennize。

语法分析(parse analysis)是编译过程的一个逻辑阶段。语法分析的任务是在词法分析的基础上将单词序列组合成语法树,如“程序”,“语句”,“表达式”等等.语法分析程序判断源程序在结构上是否正确。源程序的结构由上下文无关文法描述。

例如:对

const a = 1;
const b = a + 1;
复制代码

的编译过程。


图片地址:www.processon.com/view/link/5…

词法解析过程:一边扫描源代码一边进行分类,例如扫描到第一行const a = 1,首先扫描到const,会生成一个语法单元说这是关键字const,接着扫描到a,这是变量名a,接着操作符=,接着常量1,等等,构成一个个token流。

语法分析过程:将token流转化为一个有元素层级嵌套所组成的代表程序语法结构的树,这个树被叫做抽象语法树AST。

4.扩展测试:如何将const a = 1转化成var a = 1

   1. 新建一个testAst的工程

  mkdir testAst复制代码

     testAst下新建test.js文件

  touch test.js复制代码

  • testAst下安装esprima的npm模块,得到AST

  npm i  esprima --save复制代码


   test.js写入代码

const esprima = require('esprima');

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

console.log(ast);
复制代码

  2.运行test.js

node test.js复制代码

  3.得到生成的AST


也可通过esprima.org/demo/parse.…,输入代码,在线查看AST

  • testAst下安装estraverse的npm模块,遍历更新AST

npm i estraverse  --save
复制代码

  

 4.修改代码如下:

const esprima = require('esprima');
const estraverse = require('estraverse');

let code = 'const a = 1';
const ast = esprima.parseScript(code);
estraverse.traverse(ast, {
    enter: function (node) {
        node.kind = "var";
    }
});

console.log(ast);复制代码

 5.运行test.js,得到更新过后的AST


  • testAst下安装escodegen的npm模块,得到转译后的代码
npm i escodegen  --save复制代码

6.修改代码如下:

const esprima = require('esprima');
const estraverse = require('estraverse');
const escodegen = require('escodegen');

let code = 'const a = 1';
const ast = esprima.parseScript(code);
estraverse.traverse(ast, {
    enter: function (node) {
        node.kind = "var";
    }
});
const transformCode = escodegen.generate(ast);

console.log(transformCode);
复制代码

7.运行test.js,得到转译后的代码



参考文档:

  1. https://segmentfault.com/a/1190000012943992

  2. https://baike.baidu.com/item/语法分析/8853407?fr=aladdin
  3. baike.baidu.com/item/词法分析
  4. blog.csdn.net/feng98ren/a…
  5. https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md#toc-asts



文章分类
前端
文章标签