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



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

  • 我尝试了一个字符串比较,但由于模板软件包生成的额外空间/新线路而失败。我也尝试过格式。源没有成功。(失败)
  • 我试图解析两个源的AST(请参阅Bellow),但是即使代码基本相同,除了新的线/空格,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)
        }
    }
    
  • 游乐场

    好吧,实现此目的的一种简单方法是使用go/printer库,它使您可以更好地控制输出格式,并且基本上就像在源上运行gofmt一样,将这两个树正常化:

    package main
    import (
        "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!")
        }
    }
    

    当然,需要重构此代码以使其看起来不那么愚蠢。另一种方法是自己创建树的比较功能,而是跳过无关的节点。

    这比我想象的要容易。我要做的就是删除空的新线条(格式化后)。以下是代码。

        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 mainn" + 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
    }
    

    相关内容

    • 没有找到相关文章

    最新更新