2017-04-23 116 views
3

我想解析函數調用從代碼使用go/ast包。在Golang中進行多重界面轉換的簡明方法?

爲了做到這一點,我首先找到所有的函數調用,如:

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 

有一個簡潔的方式做這些一系列的轉換,如果沒有找到這個層次還不能正常?

+0

這就是那些情況下使用一個單子的時候是可行的(不是簡單地看爽):實現一個'Either'單子和你的表情會轉換成'PKG:= Either.Of(表達).Bind(GetExpStmt ).Bind(的getX).Bind(...)'。它會返回你想要的值,或者返回一個錯誤/ null /一個帶有額外數據的結構,你可以正常處理。 – zerkms

回答

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) 
    } 
}