3B.5 Unit Test及AST可视化

在了解了语法树的数据结构与基本解析思路之后,就没有必要继续钻入各个代码角落去学习解析细节了。此时更好的方式是反向学习:通过 UT 来调用解析函数,查看各种代码块的解析结果。

语法分析器很容易进行单元测试,因为其本质上只需要一段代码作为输入,对运行环境没有额外的要求,因此我们不需要做太多的初始化工作。测试文件 parsertest.go 中已经包含了很多测试用例可供参考,可以直接运行。但在运行 UT 或者构建自己的 UT 之前,我们先来看一下如何更好的查看语法树的内容,毕竟其内嵌结构可能很深。

文件 dumper.go 中的函数 Fdump 可以将一个 Node 的结构 dump 出来,而包括 File 在内,所有前面介绍过得结构体都实现了 Node 接口,所以该函数是我们可视化语法树的好工具。

下面我们通过一些 UT 来进一步学习一下语法解析的功能。

parser_test.go 内创建如下 UT

func TestParser(t *testing.T) {
    code := `
package main

import (
"fmt"
"net/http"
)

const (a, b)                    
var names []string = make([]int, 10)

func iterate[T any](list []T, f func(T))  {
    for _, t  := range list {
        f(t)
    }
}
`

    ast, _ := Parse(NewFileBase("dump_source.go"), bytes.NewBuffer([]byte(code)), func(err error) {
        fmt.Printf("Parsing Error: %v\n", err)
    }, nil, CheckBranches|AllowGenerics) // 开启泛型支持,以便解析出类型参数

    if ast != nil {
        Fdump(os.Stdout, ast)
    }
}

执行该 UT 得到输出结果:

但仔细查看我们所解析的代码,会发现其存在如下问题:

  1. import 的库未使用

  2. 常量 a, b 没有初始化

  3. 变量 names 声明的类型与 make 中指定的类型不一致

  4. iterate 声明中不需要返回值,但该函数却返回了一个字符串

但解析器依然成功完成了解析,这是因为此时解析器只是负责做语法分析,只要程序符合语言的文法规范,解析器就能够顺利完成;而上述问题是语义问题,或者是代码规范问题(import 的库未使用),不属于语法解析器的管理范围。想要处理这些问题,编译器还需要将语法树继续往后传递并进行进一步处理。

最后更新于

这有帮助吗?