sq/libsq/ast/walker.go

77 lines
1.7 KiB
Go
Raw Normal View History

2016-10-17 07:14:01 +03:00
package ast
2020-08-06 20:58:47 +03:00
import (
"reflect"
)
2016-10-17 07:14:01 +03:00
2020-08-06 20:58:47 +03:00
// nodeVisitorFn is a visitor function that the walker invokes for each node it visits.
type nodeVisitorFn func(*Walker, Node) error
2020-08-06 20:58:47 +03:00
// Walker traverses a node tree (the AST, or a subset thereof).
2016-10-17 07:14:01 +03:00
type Walker struct {
root Node
2020-08-06 20:58:47 +03:00
visitors map[reflect.Type][]nodeVisitorFn
2016-10-17 07:14:01 +03:00
// state is a generic field to hold any data that a visitor function
// might need to stash on the walker.
2022-12-17 02:34:33 +03:00
state any
2016-10-17 07:14:01 +03:00
}
// NewWalker returns a new Walker instance.
func NewWalker(node Node) *Walker {
w := &Walker{root: node}
2020-08-06 20:58:47 +03:00
w.visitors = map[reflect.Type][]nodeVisitorFn{}
2016-10-17 07:14:01 +03:00
return w
}
// AddVisitor adds a visitor function for any node that is assignable
// to typ.
2020-08-06 20:58:47 +03:00
func (w *Walker) AddVisitor(typ reflect.Type, visitor nodeVisitorFn) *Walker {
2016-10-17 07:14:01 +03:00
funcs := w.visitors[typ]
if funcs == nil {
2020-08-06 20:58:47 +03:00
funcs = []nodeVisitorFn{}
2016-10-17 07:14:01 +03:00
}
funcs = append(funcs, visitor)
w.visitors[typ] = funcs
return w
}
// Walk starts the walking process.
func (w *Walker) Walk() error {
return w.visit(w.root)
}
func (w *Walker) visit(node Node) error {
var visitFns []nodeVisitorFn
nodeType := reflect.TypeOf(node)
for fnType, fns := range w.visitors {
if nodeType.AssignableTo(fnType) {
visitFns = append(visitFns, fns...)
}
}
for _, visitFn := range visitFns {
if err := visitFn(w, node); err != nil {
return err
2016-10-17 07:14:01 +03:00
}
}
return w.visitChildren(node)
}
func (w *Walker) visitChildren(node Node) error {
for _, child := range node.Children() {
err := w.visit(child)
if err != nil {
return err
}
}
return nil
}
2020-08-06 20:58:47 +03:00
// walkWith is a convenience function for using Walker.
func walkWith(ast *AST, typ reflect.Type, fn nodeVisitorFn) error {
return NewWalker(ast).AddVisitor(typ, fn).Walk()
}