github.com/cilki/sh@v2.6.4+incompatible/syntax/walk.go (about)

     1  // Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
     2  // See LICENSE for licensing information
     3  
     4  package syntax
     5  
     6  import (
     7  	"fmt"
     8  	"io"
     9  	"reflect"
    10  )
    11  
    12  func walkStmts(sl StmtList, f func(Node) bool) {
    13  	for _, s := range sl.Stmts {
    14  		Walk(s, f)
    15  	}
    16  	for _, c := range sl.Last {
    17  		Walk(&c, f)
    18  	}
    19  }
    20  
    21  func walkWords(words []*Word, f func(Node) bool) {
    22  	for _, w := range words {
    23  		Walk(w, f)
    24  	}
    25  }
    26  
    27  // Walk traverses a syntax tree in depth-first order: It starts by calling
    28  // f(node); node must not be nil. If f returns true, Walk invokes f
    29  // recursively for each of the non-nil children of node, followed by
    30  // f(nil).
    31  func Walk(node Node, f func(Node) bool) {
    32  	if !f(node) {
    33  		return
    34  	}
    35  
    36  	switch x := node.(type) {
    37  	case *File:
    38  		walkStmts(x.StmtList, f)
    39  	case *Comment:
    40  	case *Stmt:
    41  		for _, c := range x.Comments {
    42  			if !x.End().After(c.Pos()) {
    43  				defer Walk(&c, f)
    44  				break
    45  			}
    46  			Walk(&c, f)
    47  		}
    48  		if x.Cmd != nil {
    49  			Walk(x.Cmd, f)
    50  		}
    51  		for _, r := range x.Redirs {
    52  			Walk(r, f)
    53  		}
    54  	case *Assign:
    55  		if x.Name != nil {
    56  			Walk(x.Name, f)
    57  		}
    58  		if x.Value != nil {
    59  			Walk(x.Value, f)
    60  		}
    61  		if x.Index != nil {
    62  			Walk(x.Index, f)
    63  		}
    64  		if x.Array != nil {
    65  			Walk(x.Array, f)
    66  		}
    67  	case *Redirect:
    68  		if x.N != nil {
    69  			Walk(x.N, f)
    70  		}
    71  		Walk(x.Word, f)
    72  		if x.Hdoc != nil {
    73  			Walk(x.Hdoc, f)
    74  		}
    75  	case *CallExpr:
    76  		for _, a := range x.Assigns {
    77  			Walk(a, f)
    78  		}
    79  		walkWords(x.Args, f)
    80  	case *Subshell:
    81  		walkStmts(x.StmtList, f)
    82  	case *Block:
    83  		walkStmts(x.StmtList, f)
    84  	case *IfClause:
    85  		walkStmts(x.Cond, f)
    86  		walkStmts(x.Then, f)
    87  		walkStmts(x.Else, f)
    88  	case *WhileClause:
    89  		walkStmts(x.Cond, f)
    90  		walkStmts(x.Do, f)
    91  	case *ForClause:
    92  		Walk(x.Loop, f)
    93  		walkStmts(x.Do, f)
    94  	case *WordIter:
    95  		Walk(x.Name, f)
    96  		walkWords(x.Items, f)
    97  	case *CStyleLoop:
    98  		if x.Init != nil {
    99  			Walk(x.Init, f)
   100  		}
   101  		if x.Cond != nil {
   102  			Walk(x.Cond, f)
   103  		}
   104  		if x.Post != nil {
   105  			Walk(x.Post, f)
   106  		}
   107  	case *BinaryCmd:
   108  		Walk(x.X, f)
   109  		Walk(x.Y, f)
   110  	case *FuncDecl:
   111  		Walk(x.Name, f)
   112  		Walk(x.Body, f)
   113  	case *Word:
   114  		for _, wp := range x.Parts {
   115  			Walk(wp, f)
   116  		}
   117  	case *Lit:
   118  	case *SglQuoted:
   119  	case *DblQuoted:
   120  		for _, wp := range x.Parts {
   121  			Walk(wp, f)
   122  		}
   123  	case *CmdSubst:
   124  		walkStmts(x.StmtList, f)
   125  	case *ParamExp:
   126  		Walk(x.Param, f)
   127  		if x.Index != nil {
   128  			Walk(x.Index, f)
   129  		}
   130  		if x.Repl != nil {
   131  			if x.Repl.Orig != nil {
   132  				Walk(x.Repl.Orig, f)
   133  			}
   134  			if x.Repl.With != nil {
   135  				Walk(x.Repl.With, f)
   136  			}
   137  		}
   138  		if x.Exp != nil && x.Exp.Word != nil {
   139  			Walk(x.Exp.Word, f)
   140  		}
   141  	case *ArithmExp:
   142  		Walk(x.X, f)
   143  	case *ArithmCmd:
   144  		Walk(x.X, f)
   145  	case *BinaryArithm:
   146  		Walk(x.X, f)
   147  		Walk(x.Y, f)
   148  	case *BinaryTest:
   149  		Walk(x.X, f)
   150  		Walk(x.Y, f)
   151  	case *UnaryArithm:
   152  		Walk(x.X, f)
   153  	case *UnaryTest:
   154  		Walk(x.X, f)
   155  	case *ParenArithm:
   156  		Walk(x.X, f)
   157  	case *ParenTest:
   158  		Walk(x.X, f)
   159  	case *CaseClause:
   160  		Walk(x.Word, f)
   161  		for _, ci := range x.Items {
   162  			Walk(ci, f)
   163  		}
   164  		for _, c := range x.Last {
   165  			Walk(&c, f)
   166  		}
   167  	case *CaseItem:
   168  		for _, c := range x.Comments {
   169  			if c.Pos().After(x.Pos()) {
   170  				defer Walk(&c, f)
   171  				break
   172  			}
   173  			Walk(&c, f)
   174  		}
   175  		walkWords(x.Patterns, f)
   176  		walkStmts(x.StmtList, f)
   177  	case *TestClause:
   178  		Walk(x.X, f)
   179  	case *DeclClause:
   180  		walkWords(x.Opts, f)
   181  		for _, a := range x.Assigns {
   182  			Walk(a, f)
   183  		}
   184  	case *ArrayExpr:
   185  		for _, el := range x.Elems {
   186  			Walk(el, f)
   187  		}
   188  		for _, c := range x.Last {
   189  			Walk(&c, f)
   190  		}
   191  	case *ArrayElem:
   192  		for _, c := range x.Comments {
   193  			if c.Pos().After(x.Pos()) {
   194  				defer Walk(&c, f)
   195  				break
   196  			}
   197  			Walk(&c, f)
   198  		}
   199  		if x.Index != nil {
   200  			Walk(x.Index, f)
   201  		}
   202  		if x.Value != nil {
   203  			Walk(x.Value, f)
   204  		}
   205  	case *ExtGlob:
   206  		Walk(x.Pattern, f)
   207  	case *ProcSubst:
   208  		walkStmts(x.StmtList, f)
   209  	case *TimeClause:
   210  		if x.Stmt != nil {
   211  			Walk(x.Stmt, f)
   212  		}
   213  	case *CoprocClause:
   214  		if x.Name != nil {
   215  			Walk(x.Name, f)
   216  		}
   217  		Walk(x.Stmt, f)
   218  	case *LetClause:
   219  		for _, expr := range x.Exprs {
   220  			Walk(expr, f)
   221  		}
   222  	default:
   223  		panic(fmt.Sprintf("syntax.Walk: unexpected node type %T", x))
   224  	}
   225  
   226  	f(nil)
   227  }
   228  
   229  // DebugPrint prints the provided syntax tree, spanning multiple lines and with
   230  // indentation. Can be useful to investigate the content of a syntax tree.
   231  func DebugPrint(w io.Writer, node Node) error {
   232  	p := debugPrinter{out: w}
   233  	p.print(reflect.ValueOf(node))
   234  	return p.err
   235  }
   236  
   237  type debugPrinter struct {
   238  	out   io.Writer
   239  	level int
   240  	err   error
   241  }
   242  
   243  func (p *debugPrinter) printf(format string, args ...interface{}) {
   244  	_, err := fmt.Fprintf(p.out, format, args...)
   245  	if err != nil && p.err == nil {
   246  		p.err = err
   247  	}
   248  }
   249  
   250  func (p *debugPrinter) newline() {
   251  	p.printf("\n")
   252  	for i := 0; i < p.level; i++ {
   253  		p.printf(".  ")
   254  	}
   255  }
   256  
   257  func (p *debugPrinter) print(x reflect.Value) {
   258  	switch x.Kind() {
   259  	case reflect.Interface:
   260  		if x.IsNil() {
   261  			p.printf("nil")
   262  			return
   263  		}
   264  		p.print(x.Elem())
   265  	case reflect.Ptr:
   266  		if x.IsNil() {
   267  			p.printf("nil")
   268  			return
   269  		}
   270  		p.printf("*")
   271  		p.print(x.Elem())
   272  	case reflect.Slice:
   273  		p.printf("%s (len = %d) {", x.Type(), x.Len())
   274  		if x.Len() > 0 {
   275  			p.level++
   276  			p.newline()
   277  			for i := 0; i < x.Len(); i++ {
   278  				p.printf("%d: ", i)
   279  				p.print(x.Index(i))
   280  				if i == x.Len()-1 {
   281  					p.level--
   282  				}
   283  				p.newline()
   284  			}
   285  		}
   286  		p.printf("}")
   287  
   288  	case reflect.Struct:
   289  		if v, ok := x.Interface().(Pos); ok {
   290  			p.printf("%v:%v", v.Line(), v.Col())
   291  			return
   292  		}
   293  		t := x.Type()
   294  		p.printf("%s {", t)
   295  		p.level++
   296  		p.newline()
   297  		for i := 0; i < t.NumField(); i++ {
   298  			p.printf("%s: ", t.Field(i).Name)
   299  			p.print(x.Field(i))
   300  			if i == x.NumField()-1 {
   301  				p.level--
   302  			}
   303  			p.newline()
   304  		}
   305  		p.printf("}")
   306  	default:
   307  		p.printf("%#v", x.Interface())
   308  	}
   309  }