diff options
Diffstat (limited to 'go/ast/inspector/inspector.go')
-rw-r--r-- | go/ast/inspector/inspector.go | 68 |
1 files changed, 50 insertions, 18 deletions
diff --git a/go/ast/inspector/inspector.go b/go/ast/inspector/inspector.go index af5e17fee..3fbfebf36 100644 --- a/go/ast/inspector/inspector.go +++ b/go/ast/inspector/inspector.go @@ -53,10 +53,13 @@ func New(files []*ast.File) *Inspector { // of an ast.Node during a traversal. type event struct { node ast.Node - typ uint64 // typeOf(node) - index int // 1 + index of corresponding pop event, or 0 if this is a pop + typ uint64 // typeOf(node) on push event, or union of typ strictly between push and pop events on pop events + index int // index of corresponding push or pop event } +// TODO: Experiment with storing only the second word of event.node (unsafe.Pointer). +// Type can be recovered from the sole bit in typ. + // Preorder visits all the nodes of the files supplied to New in // depth-first order. It calls f(n) for each node n before it visits // n's children. @@ -72,10 +75,17 @@ func (in *Inspector) Preorder(types []ast.Node, f func(ast.Node)) { mask := maskOf(types) for i := 0; i < len(in.events); { ev := in.events[i] - if ev.typ&mask != 0 { - if ev.index > 0 { + if ev.index > i { + // push + if ev.typ&mask != 0 { f(ev.node) } + pop := ev.index + if in.events[pop].typ&mask == 0 { + // Subtrees do not contain types: skip them and pop. + i = pop + 1 + continue + } } i++ } @@ -94,15 +104,24 @@ func (in *Inspector) Nodes(types []ast.Node, f func(n ast.Node, push bool) (proc mask := maskOf(types) for i := 0; i < len(in.events); { ev := in.events[i] - if ev.typ&mask != 0 { - if ev.index > 0 { - // push + if ev.index > i { + // push + pop := ev.index + if ev.typ&mask != 0 { if !f(ev.node, true) { - i = ev.index // jump to corresponding pop + 1 + i = pop + 1 // jump to corresponding pop + 1 continue } - } else { - // pop + } + if in.events[pop].typ&mask == 0 { + // Subtrees do not contain types: skip them. + i = pop + continue + } + } else { + // pop + push := ev.index + if in.events[push].typ&mask != 0 { f(ev.node, false) } } @@ -119,19 +138,26 @@ func (in *Inspector) WithStack(types []ast.Node, f func(n ast.Node, push bool, s var stack []ast.Node for i := 0; i < len(in.events); { ev := in.events[i] - if ev.index > 0 { + if ev.index > i { // push + pop := ev.index stack = append(stack, ev.node) if ev.typ&mask != 0 { if !f(ev.node, true, stack) { - i = ev.index + i = pop + 1 stack = stack[:len(stack)-1] continue } } + if in.events[pop].typ&mask == 0 { + // Subtrees does not contain types: skip them. + i = pop + continue + } } else { // pop - if ev.typ&mask != 0 { + push := ev.index + if in.events[push].typ&mask != 0 { f(ev.node, false, stack) } stack = stack[:len(stack)-1] @@ -157,25 +183,31 @@ func traverse(files []*ast.File) []event { events := make([]event, 0, capacity) var stack []event + stack = append(stack, event{}) // include an extra event so file nodes have a parent for _, f := range files { ast.Inspect(f, func(n ast.Node) bool { if n != nil { // push ev := event{ node: n, - typ: typeOf(n), + typ: 0, // temporarily used to accumulate type bits of subtree index: len(events), // push event temporarily holds own index } stack = append(stack, ev) events = append(events, ev) } else { // pop - ev := stack[len(stack)-1] - stack = stack[:len(stack)-1] + top := len(stack) - 1 + ev := stack[top] + typ := typeOf(ev.node) + push := ev.index + parent := top - 1 - events[ev.index].index = len(events) + 1 // make push refer to pop + events[push].typ = typ // set type of push + stack[parent].typ |= typ | ev.typ // parent's typ contains push and pop's typs. + events[push].index = len(events) // make push refer to pop - ev.index = 0 // turn ev into a pop event + stack = stack[:top] events = append(events, ev) } return true |