github.com/joomcode/cue@v0.4.4-0.20221111115225-539fe3512047/cue/ast/astutil/walk.go (about)

     1  // Copyright 2018 The CUE Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package astutil
    16  
    17  import (
    18  	"fmt"
    19  
    20  	"github.com/joomcode/cue/cue/ast"
    21  )
    22  
    23  // TODO: use ast.Walk or adopt that version to allow visitors.
    24  
    25  // A visitor's before method is invoked for each node encountered by Walk.
    26  // If the result visitor w is not nil, Walk visits each of the children
    27  // of node with the visitor w, followed by a call of w.After.
    28  type visitor interface {
    29  	Before(node ast.Node) (w visitor)
    30  	After(node ast.Node)
    31  }
    32  
    33  // Helper functions for common node lists. They may be empty.
    34  
    35  func walkExprList(v visitor, list []ast.Expr) {
    36  	for _, x := range list {
    37  		walk(v, x)
    38  	}
    39  }
    40  
    41  func walkDeclList(v visitor, list []ast.Decl) {
    42  	for _, x := range list {
    43  		walk(v, x)
    44  	}
    45  }
    46  
    47  // walk traverses an AST in depth-first order: It starts by calling
    48  // v.Visit(node); node must not be nil. If the visitor w returned by
    49  // v.Visit(node) is not nil, walk is invoked recursively with visitor
    50  // w for each of the non-nil children of node, followed by a call of
    51  // w.Visit(nil).
    52  //
    53  func walk(v visitor, node ast.Node) {
    54  	if v = v.Before(node); v == nil {
    55  		return
    56  	}
    57  
    58  	// TODO: record the comment groups and interleave with the values like for
    59  	// parsing and printing?
    60  	for _, c := range node.Comments() {
    61  		walk(v, c)
    62  	}
    63  
    64  	// walk children
    65  	// (the order of the cases matches the order
    66  	// of the corresponding node types in go)
    67  	switch n := node.(type) {
    68  	// Comments and fields
    69  	case *ast.Comment:
    70  		// nothing to do
    71  
    72  	case *ast.CommentGroup:
    73  		for _, c := range n.List {
    74  			walk(v, c)
    75  		}
    76  
    77  	case *ast.Attribute:
    78  		// nothing to do
    79  
    80  	case *ast.Field:
    81  		walk(v, n.Label)
    82  		if n.Value != nil {
    83  			walk(v, n.Value)
    84  		}
    85  		for _, a := range n.Attrs {
    86  			walk(v, a)
    87  		}
    88  
    89  	case *ast.StructLit:
    90  		for _, f := range n.Elts {
    91  			walk(v, f)
    92  		}
    93  
    94  	// Expressions
    95  	case *ast.BottomLit, *ast.BadExpr, *ast.Ident, *ast.BasicLit:
    96  		// nothing to do
    97  
    98  	case *ast.Interpolation:
    99  		for _, e := range n.Elts {
   100  			walk(v, e)
   101  		}
   102  
   103  	case *ast.ListLit:
   104  		walkExprList(v, n.Elts)
   105  
   106  	case *ast.Ellipsis:
   107  		if n.Type != nil {
   108  			walk(v, n.Type)
   109  		}
   110  
   111  	case *ast.ParenExpr:
   112  		walk(v, n.X)
   113  
   114  	case *ast.SelectorExpr:
   115  		walk(v, n.X)
   116  		walk(v, n.Sel)
   117  
   118  	case *ast.IndexExpr:
   119  		walk(v, n.X)
   120  		walk(v, n.Index)
   121  
   122  	case *ast.SliceExpr:
   123  		walk(v, n.X)
   124  		if n.Low != nil {
   125  			walk(v, n.Low)
   126  		}
   127  		if n.High != nil {
   128  			walk(v, n.High)
   129  		}
   130  
   131  	case *ast.CallExpr:
   132  		walk(v, n.Fun)
   133  		walkExprList(v, n.Args)
   134  
   135  	case *ast.UnaryExpr:
   136  		walk(v, n.X)
   137  
   138  	case *ast.BinaryExpr:
   139  		walk(v, n.X)
   140  		walk(v, n.Y)
   141  
   142  	// Declarations
   143  	case *ast.ImportSpec:
   144  		if n.Name != nil {
   145  			walk(v, n.Name)
   146  		}
   147  		walk(v, n.Path)
   148  
   149  	case *ast.BadDecl:
   150  		// nothing to do
   151  
   152  	case *ast.ImportDecl:
   153  		for _, s := range n.Specs {
   154  			walk(v, s)
   155  		}
   156  
   157  	case *ast.EmbedDecl:
   158  		walk(v, n.Expr)
   159  
   160  	case *ast.Alias:
   161  		walk(v, n.Ident)
   162  		walk(v, n.Expr)
   163  
   164  	case *ast.Comprehension:
   165  		for _, c := range n.Clauses {
   166  			walk(v, c)
   167  		}
   168  		walk(v, n.Value)
   169  
   170  	// Files and packages
   171  	case *ast.File:
   172  		walkDeclList(v, n.Decls)
   173  
   174  	case *ast.Package:
   175  		// The package identifier isn't really an identifier. Skip it.
   176  
   177  	case *ast.LetClause:
   178  		walk(v, n.Ident)
   179  		walk(v, n.Expr)
   180  
   181  	case *ast.ForClause:
   182  		if n.Key != nil {
   183  			walk(v, n.Key)
   184  		}
   185  		walk(v, n.Value)
   186  		walk(v, n.Source)
   187  
   188  	case *ast.IfClause:
   189  		walk(v, n.Condition)
   190  
   191  	default:
   192  		panic(fmt.Sprintf("Walk: unexpected node type %T", n))
   193  	}
   194  
   195  	v.After(node)
   196  }