在Golang中进行多个接口转换的简明方法?

在Golang中进行多个接口转换的简明方法?,go,abstract-syntax-tree,Go,Abstract Syntax Tree,我试图使用go/ast包解析代码中的函数调用 为此,我首先查找所有函数调用,如: ast.Inspect(f, func(n ast.Node) bool { switch x := n.(type) { case *ast.FuncDecl: processFunction(x) } return true }) 然后,processFunction()如下所示: func processFunction(e *ast.FuncDecl) {

我试图使用
go/ast
包解析代码中的函数调用

为此,我首先查找所有函数调用,如:

ast.Inspect(f, func(n ast.Node) bool {
    switch x := n.(type) {
    case *ast.FuncDecl:
        processFunction(x)
    }
    return true
})
然后,processFunction()如下所示:

func processFunction(e *ast.FuncDecl) {
    // Save wrapper function name
    f := e.Name.Name

    for _, expression := range e.Body.List {
        logrus.Printf("Current stmt: %#v", expression)
        pkg := expression.(*ast.ExprStmt).X.(*ast.CallExpr).Fun.(*ast.SelectorExpr).X.(*ast.Ident).Name
        fn := expression.(*ast.ExprStmt).X.(*ast.CallExpr).Fun.(*ast.SelectorExpr).Sel.Name
        fcall := fmt.Sprintf("%s.%s", pkg, fn)
        logrus.Printf("Yay. found my function call: ", fcall)
    }

}
这段代码的问题是,如果在AST中找不到特定的层次结构,程序就会崩溃。我知道我们可以通过

x, ok := x.(type)
但是,如果我这样做每一个转换,我的代码将是巨大的。当然,在这种情况下尝试使用它是失败的

    pkg, ok := expression.(*ast.ExprStmt).X.(*ast.CallExpr).Fun.(*ast.SelectorExpr).X.(*ast.Ident).Name
    if !ok {
        continue
    }
错误:

./parser.go:41: assignment count mismatch: 2 = 1

有没有一种简洁的方法可以完成这一系列的转换,并且在没有找到这个层次结构的情况下也会优雅地失败?

据我所知,没有任何方法可以链接类型断言。但您可以通过将重复代码提取到其自身的函数中来简化此特定示例

func fnSelExpr(s ast.Stmt) (*ast.SelectorExpr, bool) {
    if xs, ok := s.(*ast.ExprStmt); ok {
        if cx, ok := xs.X.(*ast.CallExpr); ok {
            return cx.Fun.(*ast.SelectorExpr)
        }
    }
    return nil, false
}
然后你可以像这样简化你的
processFunction

func processFunction(e *ast.FuncDecl) {
    // Save wrapper function name
    f := e.Name.Name

    for _, expression := range e.Body.List {
        logrus.Printf("Current stmt: %#v", expression)

        sx, ok := fnSelExpr(expression)
        if !ok {
            continue
        }

        var pkg string
        if id, ok := sx.X.(*ast.Ident); ok {
            pkg = id.Name
        }
        fn := sx.Sel.Name

        fcall := fmt.Sprintf("%s.%s", pkg, fn)
        logrus.Printf("Yay. found my function call: ", fcall)
    }
}

这是使用monad实用的情况之一(不是简单地看起来很酷):实现一个
other
monad,您的表达式将转换为
pkg:=other.of(expression).Bind(GetExpStmt).Bind(GetX).Bind(…)
。它将返回一个值,该值可以是您想要的值,也可以是一个error/null/a结构和其他数据,您可以优雅地处理该值。