package semantic
import "fmt"
type Visitor interface {
Visit(n Node) (w Visitor)
}
func Walk(v Visitor, node Node) {
if v = v.Visit(node); v == nil {
return
}
switch n := node.(type) {
case *Unit:
// n.Main is part of the functions that will be visited, don't visit it
// separately here.
for _, f := range n.Files {
Walk(v, f)
}
case *File:
for _, vv := range n.Vars {
Walk(v, vv)
}
for _, fn := range n.Fns {
Walk(v, fn)
}
for _, str := range n.Structs {
Walk(v, str)
}
case *Fn:
for _, attr := range n.Attrs {
Walk(v, attr)
}
for _, p := range n.Params {
Walk(v, p)
}
if n.ReturnExpr != nil {
Walk(v, n.ReturnExpr)
}
if n.Body != nil {
Walk(v, n.Body)
}
// n.MethodOf is a reference to a struct that exists elsewhere, don't visit
// it separately here.
case *Var:
if n.TypeExpr != nil {
Walk(v, n.TypeExpr)
}
if n.Value != nil {
Walk(v, n.Value)
}
case *Struct:
for _, vv := range n.Vars {
Walk(v, vv)
}
for _, fn := range n.Fns {
Walk(v, fn)
}
for _, str := range n.Structs {
Walk(v, str)
}
case *Block:
for _, stmt := range n.Stmts {
Walk(v, stmt)
}
case *Return:
if n.Value != nil {
Walk(v, n.Value)
}
case *Assign:
if n.Left != nil {
Walk(v, n.Left)
}
if n.Right != nil {
Walk(v, n.Right)
}
case *ExprStmt:
if n.Value != nil {
Walk(v, n.Value)
}
case *If:
for _, cond := range n.Conds {
Walk(v, cond)
}
if n.Body != nil {
Walk(v, n.Body)
}
if n.Else != nil {
Walk(v, n.Else)
}
case *Guard:
for _, cond := range n.Conds {
Walk(v, cond)
}
if n.Else != nil {
Walk(v, n.Else)
}
case *FnTypeExpr:
for _, p := range n.Params {
Walk(v, p)
}
if n.Return != nil {
Walk(v, n.Return)
}
case *TupleTypeExpr:
for _, field := range n.Fields {
Walk(v, field)
}
case *TupleVal:
for _, val := range n.Values {
Walk(v, val)
}
case *Binary:
if n.Left != nil {
Walk(v, n.Left)
}
if n.Right != nil {
Walk(v, n.Right)
}
case *Unary:
if n.Right != nil {
Walk(v, n.Right)
}
case *Paren:
if n.Value != nil {
Walk(v, n.Value)
}
case *Call:
if n.Fun != nil {
Walk(v, n.Fun)
}
for _, arg := range n.Args {
Walk(v, arg)
}
case *Selector:
if n.Left != nil {
Walk(v, n.Left)
}
if n.Sel != nil {
Walk(v, n.Sel)
}
case *ImplicitConv:
if n.Value != nil {
Walk(v, n.Value)
}
case *LitString, *LitInt, *Ident:
// nothing to visit
default:
panic(fmt.Sprintf("Walk: unexpected node type %T", n))
}
v.Visit(nil)
}
type inspector func(Node) bool
func (f inspector) Visit(node Node) Visitor {
if f(node) {
return f
}
return nil
}
// Inspect traverses an AST in depth-first order: It starts by calling
// f(node); node must not be nil. If f returns true, Inspect invokes f
// recursively for each of the non-nil children of node, followed by a
// call of f(nil).
func Inspect(node Node, f func(Node) bool) {
Walk(inspector(f), node)
}