如何比较两个源代码文件/ ast 树?

我正在使用模板包生成一些源代码(有没有更好的方法?)和测试的一部分,我需要检查输出是否与预期的源代码匹配。


我尝试了字符串比较,但由于模板包生成的额外空格/新行而失败。我也试过format.Source没有成功。( 失败)

我试图解析两个源的 ast(见下文),但即使除了新行/空格之外,代码基本相同,但 ast 也不匹配。(失败)


包主


import (

    "fmt"

    "go/parser"

    "go/token"

    "reflect"

)


func main() {

    stub1 := `package main

     func myfunc(s string) error {

        return nil  

    }`

    stub2 := `package main


     func myfunc(s string) error {


        return nil


    }`

    fset := token.NewFileSet()

    r1, err := parser.ParseFile(fset, "", stub1, parser.AllErrors)

    if err != nil {

        panic(err)

    }

    fset = token.NewFileSet()

    r2, err := parser.ParseFile(fset, "", stub2, parser.AllErrors)

    if err != nil {

        panic(err)

    }

    if !reflect.DeepEqual(r1, r2) {

        fmt.Printf("e %v, r %s, ", r1, r2)

    }

}


皈依舞
浏览 268回答 2
2回答

有只小跳蛙

好吧,实现这一点的一种简单方法是使用go/printer库,它可以让您更好地控制输出格式,并且基本上就像gofmt在源代码上运行一样,规范化两个树:package mainimport (    "fmt"    "go/parser"    "go/token"    "go/printer"    //"reflect"    "bytes")func main() {    stub1 := `package main     func myfunc(s string) error {        return nil      }`    stub2 := `package main     func myfunc(s string) error {        return nil    }`    fset1 := token.NewFileSet()    r1, err := parser.ParseFile(fset1, "", stub1, parser.AllErrors)    if err != nil {        panic(err)    }    fset2 := token.NewFileSet()    r2, err := parser.ParseFile(fset1, "", stub2, parser.AllErrors)    if err != nil {        panic(err)    }    // we create two output buffers for each source tree    out1 := bytes.NewBuffer(nil)    out2 := bytes.NewBuffer(nil)    // we use the same printer config for both    conf := &printer.Config{Mode: printer.TabIndent | printer.UseSpaces, Tabwidth: 8}    // print to both outputs    if err := conf.Fprint(out1, fset1, r1); err != nil {        panic(err)    }    if err := conf.Fprint(out2, fset2, r2); err != nil {        panic(err)    }    // they should be identical!    if string(out1.Bytes()) != string(out2.Bytes()) {        panic(string(out1.Bytes()) +"\n" + string(out2.Bytes()))    } else {        fmt.Println("A-OKAY!")    }}当然,这段代码需要重构才能看起来不那么愚蠢。另一种方法是不使用 DeepEqual,而是自己创建一个树比较函数,跳过不相关的节点。

红颜莎娜

这比我想象的要容易。我所要做的就是删除空的新行(格式化后)。下面是代码。    package main    import (        "fmt"        "go/format"        "strings"    )    func main() {        a, err := fmtSource(stub1)        if err != nil {            panic(err)        }        b, err := fmtSource(stub2)        if err != nil {            panic(err)        }        if a != b {            fmt.Printf("a %v, \n b %v", a, b)        }    }func fmtSource(source string) (string, error) {    if !strings.Contains(source, "package") {        source = "package main\n" + source    }    b, err := format.Source([]byte(source))    if err != nil {        return "", err    }    // cleanLine replaces double space with one space    cleanLine := func(s string)string{        sa := strings.Fields(s)        return strings.Join(sa, " ")    }    lines := strings.Split(string(b), "\n")    n := 0    var startLn *int    for _, line := range lines {        if line != "" {            line = cleanLine(line)            lines[n] = line            if startLn == nil {                x := n                startLn = &x            }            n++        }    }    lines = lines[*startLn:n]    // Add final "" entry to get trailing newline from Join.    if n > 0 && lines[n-1] != "" {        lines = append(lines, "")    }    // Make it pretty     b, err = format.Source([]byte(strings.Join(lines, "\n")))    if err != nil {        return "", err    }    return string(b), nil}
打开App,查看更多内容
随时随地看视频慕课网APP

相关分类

Go