英文:
Concise way to do multiple interface conversions in Golang?
问题
我正在尝试使用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) {
// 保存包装函数名
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
有没有一种简洁的方法来进行这些连续的转换,并且在找不到这个层次结构时能够优雅地处理错误?
英文:
I'm trying to parse function calls from a code using go/ast
package.
To do that, I first find all function calls like:
ast.Inspect(f, func(n ast.Node) bool {
switch x := n.(type) {
case *ast.FuncDecl:
processFunction(x)
}
return true
})
And then, processFunction() looks like this:
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)
}
}
The problem with this code is that if that particular hierarchy is not found in AST, the program panics. I know that we can do interface conversions gracefully via
x, ok := x.(type)
But, if I do each conversion like this, my code will be huge. Trying to use that in this fails of course.
pkg, ok := expression.(*ast.ExprStmt).X.(*ast.CallExpr).Fun.(*ast.SelectorExpr).X.(*ast.Ident).Name
if !ok {
continue
}
Error:
./parser.go:41: assignment count mismatch: 2 = 1
Is there a concise way to do these series of conversions and also fail gracefully if this hierarchy is not found?
答案1
得分: 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) {
// 保存包装函数名称
f := e.Name.Name
for _, expression := range e.Body.List {
logrus.Printf("当前语句: %#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("耶。找到了我的函数调用: ", fcall)
}
}
希望对您有所帮助!
英文:
There is no way, that I know of, to chain type assertions. But you can simplify this specific example by extracting duplicate code into its own function.
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
}
Then you can simplify your processFunction
like this.
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)
}
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论