github.com/neugram/ng@v0.0.0-20180309130942-d472ff93d872/syntax/walk.go (about)

     1  // Copyright 2017 The Neugram Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package syntax
     6  
     7  import (
     8  	"fmt"
     9  	"reflect"
    10  
    11  	"neugram.io/ng/syntax/expr"
    12  	"neugram.io/ng/syntax/stmt"
    13  )
    14  
    15  // Walk traverses a syntax tree, calling preFn and postFn for each node.
    16  //
    17  // If a preFn is provided it is called for each node before its children
    18  // are traversed. If preFn returns false no children are traversed.
    19  //
    20  // If a postFn is provided it is called for each node after its children
    21  // are traversed. TODO: If a postFn returns false traversal ends.
    22  func Walk(root Node, preFn, postFn WalkFunc) (result Node) {
    23  	type rootNode struct {
    24  		Node
    25  	}
    26  	parent := &rootNode{Node: root}
    27  
    28  	w := walker{preFn: preFn, postFn: postFn}
    29  	w.walk(parent, root, "Node", nil)
    30  
    31  	return root
    32  }
    33  
    34  // A WalkFunc is invoked by Walk when traversing nodes in a syntax tree.
    35  //
    36  // The return value determines how traversal will proceed, it is
    37  // described in detail in the Walk function documentation.
    38  type WalkFunc func(*Cursor) bool
    39  
    40  // A Cursor describes a Node during a syntax tree traversal.
    41  type Cursor struct {
    42  	Node   Node   // the current Node
    43  	Parent Node   // the parent of the current Node
    44  	Name   string // name of the parent field containing the current Node
    45  
    46  	iter *iterator
    47  }
    48  
    49  // TODO: Replace(Node), InsertAfter(Node), InsertBefore(Node), Delete()
    50  
    51  type iterator struct {
    52  	index int
    53  }
    54  
    55  type walker struct {
    56  	preFn  WalkFunc
    57  	postFn WalkFunc
    58  	c      Cursor   // reusable Cursor
    59  	iter   iterator // reusable iterator
    60  }
    61  
    62  func (w *walker) walk(parent, node Node, fieldName string, iter *iterator) {
    63  	// typed nil -> untyped nil
    64  	if v := reflect.ValueOf(node); v.Kind() == reflect.Ptr && v.IsNil() {
    65  		node = nil
    66  	}
    67  
    68  	oldCursor := w.c
    69  	w.c = Cursor{
    70  		Node:   node,
    71  		Parent: parent,
    72  		Name:   fieldName,
    73  		iter:   iter,
    74  	}
    75  	defer func() { w.c = oldCursor }()
    76  
    77  	if w.preFn != nil && !w.preFn(&w.c) {
    78  		return
    79  	}
    80  
    81  	switch node := node.(type) {
    82  	case nil:
    83  		// done
    84  
    85  	case *File:
    86  		w.walkSlice(node, "Stmts")
    87  
    88  	case *stmt.Import:
    89  		// done
    90  
    91  	case *stmt.ImportSet:
    92  		w.walkSlice(node, "Imports")
    93  
    94  	case *stmt.TypeDecl:
    95  		// done
    96  
    97  	case *stmt.TypeDeclSet:
    98  		w.walkSlice(node, "TypeDecls")
    99  
   100  	case *stmt.MethodikDecl:
   101  		w.walkSlice(node, "Methods")
   102  
   103  	case *stmt.Const:
   104  		w.walkSlice(node, "Values")
   105  
   106  	case *stmt.ConstSet:
   107  		w.walkSlice(node, "Consts")
   108  
   109  	case *stmt.Var:
   110  		w.walkSlice(node, "Values")
   111  
   112  	case *stmt.VarSet:
   113  		w.walkSlice(node, "Vars")
   114  
   115  	case *stmt.Assign:
   116  		w.walkSlice(node, "Left")
   117  		w.walkSlice(node, "Right")
   118  
   119  	case *stmt.Block:
   120  		w.walkSlice(node, "Stmts")
   121  
   122  	case *stmt.If:
   123  		w.walk(node, node.Init, "Init", nil)
   124  		w.walk(node, node.Cond, "Cond", nil)
   125  		w.walk(node, node.Body, "Body", nil)
   126  		w.walk(node, node.Else, "Else", nil)
   127  
   128  	case *stmt.For:
   129  		w.walk(node, node.Init, "Init", nil)
   130  		w.walk(node, node.Cond, "Cond", nil)
   131  		w.walk(node, node.Post, "Post", nil)
   132  		w.walk(node, node.Body, "Body", nil)
   133  
   134  	case *stmt.Switch:
   135  		w.walk(node, node.Init, "Init", nil)
   136  		w.walk(node, node.Cond, "Cond", nil)
   137  		w.walkSlice(node, "Cases")
   138  
   139  	case stmt.SwitchCase:
   140  		w.walkSlice(node, "Conds")
   141  		w.walk(node, node.Body, "Body", nil)
   142  
   143  	case *stmt.TypeSwitch:
   144  		w.walk(node, node.Init, "Init", nil)
   145  		w.walk(node, node.Assign, "Assign", nil)
   146  		w.walkSlice(node, "Cases")
   147  
   148  	case stmt.TypeSwitchCase:
   149  		w.walk(node, node.Body, "Body", nil)
   150  
   151  	case *stmt.Go:
   152  		w.walk(node, node.Call, "Call", nil)
   153  
   154  	case *stmt.Range:
   155  		w.walk(node, node.Key, "Key", nil)
   156  		w.walk(node, node.Val, "Val", nil)
   157  		w.walk(node, node.Expr, "Expr", nil)
   158  		w.walk(node, node.Body, "Body", nil)
   159  
   160  	case *stmt.Return:
   161  		w.walkSlice(node, "Exprs")
   162  
   163  	case *stmt.Defer:
   164  		w.walk(node, node.Expr, "Expr", nil)
   165  
   166  	case *stmt.Simple:
   167  		w.walk(node, node.Expr, "Expr", nil)
   168  
   169  	case *stmt.Send:
   170  		w.walk(node, node.Chan, "Chan", nil)
   171  		w.walk(node, node.Value, "Value", nil)
   172  
   173  	case *stmt.Branch:
   174  
   175  	case *stmt.Labeled:
   176  		w.walk(node, node.Stmt, "Stmt", nil)
   177  
   178  	case *stmt.Select:
   179  		w.walkSlice(node, "Cases")
   180  
   181  	case stmt.SelectCase:
   182  		w.walk(node, node.Stmt, "Stmt", nil)
   183  		w.walk(node, node.Body, "Body", nil)
   184  
   185  	case *stmt.Bad:
   186  
   187  	case *expr.Binary:
   188  		w.walk(node, node.Left, "Left", nil)
   189  		w.walk(node, node.Right, "Right", nil)
   190  
   191  	case *expr.Unary:
   192  		w.walk(node, node.Expr, "Expr", nil)
   193  
   194  	case *expr.Bad:
   195  
   196  	case *expr.Selector:
   197  		w.walk(node, node.Left, "Left", nil)
   198  		w.walk(node, node.Right, "Right", nil)
   199  
   200  	case *expr.Slice:
   201  		w.walk(node, node.Low, "Low", nil)
   202  		w.walk(node, node.High, "High", nil)
   203  		w.walk(node, node.Max, "Max", nil)
   204  
   205  	case *expr.Index:
   206  		w.walk(node, node.Left, "Left", nil)
   207  		w.walkSlice(node, "Indicies")
   208  
   209  	case *expr.TypeAssert:
   210  		w.walk(node, node.Left, "Left", nil)
   211  
   212  	case *expr.BasicLiteral:
   213  
   214  	case *expr.FuncLiteral:
   215  		if body, isStmt := node.Body.(*stmt.Block); isStmt {
   216  			w.walk(node, body, "Body", nil)
   217  		}
   218  
   219  	case *expr.CompLiteral:
   220  		w.walkSlice(node, "Keys")
   221  		w.walkSlice(node, "Values")
   222  
   223  	case *expr.MapLiteral:
   224  		w.walkSlice(node, "Keys")
   225  		w.walkSlice(node, "Values")
   226  
   227  	case *expr.ArrayLiteral:
   228  		w.walkSlice(node, "Keys")
   229  		w.walkSlice(node, "Values")
   230  
   231  	case *expr.SliceLiteral:
   232  		w.walkSlice(node, "Keys")
   233  		w.walkSlice(node, "Values")
   234  
   235  	case *expr.TableLiteral:
   236  		w.walkSlice(node, "ColNames")
   237  		// TODO: handle rows
   238  
   239  	case *expr.Type:
   240  
   241  	case *expr.Ident:
   242  
   243  	case *expr.Call:
   244  		w.walk(node, node.Func, "Func", nil)
   245  		w.walkSlice(node, "Args")
   246  
   247  	case *expr.Range:
   248  		w.walk(node, node.Start, "Start", nil)
   249  		w.walk(node, node.End, "End", nil)
   250  		w.walk(node, node.Exact, "Exact", nil)
   251  
   252  	case *expr.ShellList:
   253  		w.walkSlice(node, "AndOr")
   254  
   255  	case *expr.ShellAndOr:
   256  		w.walkSlice(node, "Pipeline")
   257  
   258  	case *expr.ShellPipeline:
   259  		w.walkSlice(node, "Cmd")
   260  
   261  	case *expr.ShellCmd:
   262  		w.walk(node, node.SimpleCmd, "SimpleCmd", nil)
   263  		w.walk(node, node.Subshell, "Subshell", nil)
   264  
   265  	case *expr.ShellSimpleCmd:
   266  		w.walkSlice(node, "Redirect")
   267  		w.walkSlice(node, "Assign")
   268  
   269  	case *expr.ShellRedirect:
   270  
   271  	case expr.ShellAssign:
   272  
   273  	case *expr.Shell:
   274  		w.walkSlice(node, "Cmds")
   275  
   276  	default:
   277  		panic(fmt.Sprintf("syntax.Walk: unknown node (type %T)", node))
   278  	}
   279  
   280  	if w.postFn != nil && !w.postFn(&w.c) {
   281  		// TODO: implement abort
   282  	}
   283  }
   284  
   285  func (w *walker) walkSlice(parent Node, fieldName string) {
   286  	oldIter := w.iter
   287  	defer func() { w.iter = oldIter }()
   288  
   289  	w.iter.index = 0
   290  
   291  	for {
   292  		v := reflect.Indirect(reflect.ValueOf(parent)).FieldByName(fieldName)
   293  		if w.iter.index >= v.Len() {
   294  			break
   295  		}
   296  
   297  		var node Node
   298  		if e := v.Index(w.iter.index); e.IsValid() {
   299  			node = e.Interface().(Node)
   300  		}
   301  
   302  		w.walk(parent, node, fieldName, &w.iter)
   303  		w.iter.index++
   304  	}
   305  }