I can only agree, traversing or navigating Go AST is cumbersome for at least two reasons:
1. each type has its own fields - no uniform API is available
2. you often need type assertions on fields because they have interface type instead of concrete type
You may find it helps by providing an uniform API and removing the need for type assertions, but it still requires compile-time code,
not CSS-style strings that can be created at runtime.
A quick example: this uses plain go/ast
```
package main
import (
"fmt"
"go/ast"
"go/parser"
)
func main() {
x, _ := parser.ParseExpr("foo[1 + 2 * 3]")
// we want to extract the "3"
var three string = x.(*ast.IndexExpr).Index.(*ast.BinaryExpr).Y.(*ast.BinaryExpr).Y.(*ast.BasicLit).Value
fmt.Printf("%s\n", three) // prints 3
}
```
```
package main
import (
"fmt"
"go/ast"
"go/parser"
func main() {
x, _ := parser.ParseExpr("foo[1 + 2 * 3]")
y := ast2.AnyToAst(x, nil) // wrap the existing AST. does not allocate memory.
ythree := y.Get(1).Get(1).Get(1) // extract the "3" as ast2.Node
// now unwrap it, getting the *ast.BasicLit and the Value inside it
var three string = ast2.ToBasicLit(ythree).Value
fmt.Printf("%s\n", three) // prints 3
}
```