一、AST基础概念与入门必备

1.1 什么是AST(Abstract Syntax Tree)?

根据维基百科的介绍:在计算机科学中,抽象语法树(AST),或者仅仅是语法树,是用编程语言编写的源代码的抽象语法结构的树状表示。树的每个节点都表示源代码中出现的一个构造。

大多数编译器和解释器都使用AST作为源代码的内部表示,AST通常会省略语法树中的分号、换行字符、白空格、大括号、方括号和圆括号等。

1.2 生成AST的步骤?

下面我们来看看计算机是如何将一份代码文件转换成AST的。



词法分析器(Lexer)词法分析解析器ASTlexerparser

Lexer-又名词法分析器:词法分析器用来将字符序列转换为单词(Token)。词法分析主要是完成:

  1. 对源程序的代码进行从左到右的逐行扫描,识别出各个单词,从而确定单词的类型;
  2. 将识别出的单词转换为统一的机内表示——词法单元(Token)形式。

token是一种类型Map的key/value形式,它由<种别码,属性值>组成,种别码就是类型、属性值就是值。例如下述代码中:

转换为token之后:

=\n

Parser-语法分析器:语法分析器的作用是进行语法检查、并构建由输入的单词(Token)组成的数据结构(一般是语法分析树、抽象语法树等层次化的数据结构)。

语法分析的分析方法一般分为自顶向下和自底向上两种:

(这里的内容相对比较复杂,这里不做介绍)

通过上述的叙述中,我们知道通过“词法分析”和“语法分析”我们便可以得到AST,例如:

上述代码的AST为:

我们看到,即使非常简单的代码生成AST也非常复杂。那么我们得到了抽象语法树可以做什么呢?

1.3 AST的常见的几个用途

常见的几种用途:

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

二、Golang中的AST

2.1 Golang中的AST

golang官方提供的几个包,可以帮助我们进行AST分析:

通过上述的四个库,我们就可以实现golang代码的语法树分析。

go/ast/ast.go表达式和类型节点(Expressions and type nodes)语句节点(statement nodes)声明节点(declaration nodes)
ast.Node
ast.Node
  • ast.Expr - 代表表达式和类型的节点
  • ast.Stmt - 代表报表节点
  • ast.Decl - 代表声明节点



ast.Node

2.2 Golang的AST分析示例

我们将以下面这段代码为例子来介绍Golang的抽象语法树。

代码清单2-1:

  • 代码清单2-2:
go run main.go

2.2 Golang的AST分析

一棵树

代码清单2-3:

go/astInspect

代码清单2-4:

执行上述代码我们可以得到AST的全部节点,由于原生的AST太长,这里省略,给出AST的大体结构图:



*ast.File

*ast.Fileast.Node
ast.File包名导入声明函数声明

*ast.Ident-包名

hello

*ast.GenDecl-导入声明

ast.GenDeclimportconstvartype
Tok
import
ast.GenDecl*ast.ImportSpec

*ast.FuncDecl-函数声明

ast.FuncDeclast.Nodeast.FuncDecl*ast.Ident*ast.Object

如此多的类型我们不可能全部记住,因为官方文件已经帮我们全部记住了,我们只需按需查询即可:

下面这张表总结了AST常见的节点表,和源码做对比可快速使用。

总结

本文首先介绍了ast的一些基础概念以及生成步骤,以golang为例介绍了AST的详细实践。利用AST我们可以做很多的事情,例如语法检查、单测框架生成(gotests框架就是基于ast做的)等等。

博主在工作中正在使用ast对开源的单元测试框架进行修改,以便提供编写单元测试的效率,有什么问题欢迎交流~