// Copyright (c) 2016, Daniel Martí <mvdan@mvdan.cc>
// See LICENSE for licensing information

package syntax

import (
	
	
	
)

func walkStmts( []*Stmt,  []Comment,  func(Node) bool) {
	for ,  := range  {
		Walk(, )
	}
	for ,  := range  {
		Walk(&, )
	}
}

func walkWords( []*Word,  func(Node) bool) {
	for ,  := range  {
		Walk(, )
	}
}

// Walk traverses a syntax tree in depth-first order: It starts by calling
// f(node); node must not be nil. If f returns true, Walk invokes f
// recursively for each of the non-nil children of node, followed by
// f(nil).
func ( Node,  func(Node) bool) {
	if !() {
		return
	}

	switch x := .(type) {
	case *File:
		walkStmts(.Stmts, .Last, )
	case *Comment:
	case *Stmt:
		for ,  := range .Comments {
			if !.End().After(.Pos()) {
				defer (&, )
				break
			}
			(&, )
		}
		if .Cmd != nil {
			(.Cmd, )
		}
		for ,  := range .Redirs {
			(, )
		}
	case *Assign:
		if .Name != nil {
			(.Name, )
		}
		if .Value != nil {
			(.Value, )
		}
		if .Index != nil {
			(.Index, )
		}
		if .Array != nil {
			(.Array, )
		}
	case *Redirect:
		if .N != nil {
			(.N, )
		}
		(.Word, )
		if .Hdoc != nil {
			(.Hdoc, )
		}
	case *CallExpr:
		for ,  := range .Assigns {
			(, )
		}
		walkWords(.Args, )
	case *Subshell:
		walkStmts(.Stmts, .Last, )
	case *Block:
		walkStmts(.Stmts, .Last, )
	case *IfClause:
		walkStmts(.Cond, .CondLast, )
		walkStmts(.Then, .ThenLast, )
		if .Else != nil {
			(.Else, )
		}
	case *WhileClause:
		walkStmts(.Cond, .CondLast, )
		walkStmts(.Do, .DoLast, )
	case *ForClause:
		(.Loop, )
		walkStmts(.Do, .DoLast, )
	case *WordIter:
		(.Name, )
		walkWords(.Items, )
	case *CStyleLoop:
		if .Init != nil {
			(.Init, )
		}
		if .Cond != nil {
			(.Cond, )
		}
		if .Post != nil {
			(.Post, )
		}
	case *BinaryCmd:
		(.X, )
		(.Y, )
	case *FuncDecl:
		(.Name, )
		(.Body, )
	case *Word:
		for ,  := range .Parts {
			(, )
		}
	case *Lit:
	case *SglQuoted:
	case *DblQuoted:
		for ,  := range .Parts {
			(, )
		}
	case *CmdSubst:
		walkStmts(.Stmts, .Last, )
	case *ParamExp:
		(.Param, )
		if .Index != nil {
			(.Index, )
		}
		if .Repl != nil {
			if .Repl.Orig != nil {
				(.Repl.Orig, )
			}
			if .Repl.With != nil {
				(.Repl.With, )
			}
		}
		if .Exp != nil && .Exp.Word != nil {
			(.Exp.Word, )
		}
	case *ArithmExp:
		(.X, )
	case *ArithmCmd:
		(.X, )
	case *BinaryArithm:
		(.X, )
		(.Y, )
	case *BinaryTest:
		(.X, )
		(.Y, )
	case *UnaryArithm:
		(.X, )
	case *UnaryTest:
		(.X, )
	case *ParenArithm:
		(.X, )
	case *ParenTest:
		(.X, )
	case *CaseClause:
		(.Word, )
		for ,  := range .Items {
			(, )
		}
		for ,  := range .Last {
			(&, )
		}
	case *CaseItem:
		for ,  := range .Comments {
			if .Pos().After(.Pos()) {
				defer (&, )
				break
			}
			(&, )
		}
		walkWords(.Patterns, )
		walkStmts(.Stmts, .Last, )
	case *TestClause:
		(.X, )
	case *DeclClause:
		for ,  := range .Args {
			(, )
		}
	case *ArrayExpr:
		for ,  := range .Elems {
			(, )
		}
		for ,  := range .Last {
			(&, )
		}
	case *ArrayElem:
		for ,  := range .Comments {
			if .Pos().After(.Pos()) {
				defer (&, )
				break
			}
			(&, )
		}
		if .Index != nil {
			(.Index, )
		}
		if .Value != nil {
			(.Value, )
		}
	case *ExtGlob:
		(.Pattern, )
	case *ProcSubst:
		walkStmts(.Stmts, .Last, )
	case *TimeClause:
		if .Stmt != nil {
			(.Stmt, )
		}
	case *CoprocClause:
		if .Name != nil {
			(.Name, )
		}
		(.Stmt, )
	case *LetClause:
		for ,  := range .Exprs {
			(, )
		}
	case *TestDecl:
		(.Description, )
		(.Body, )
	default:
		panic(fmt.Sprintf("syntax.Walk: unexpected node type %T", ))
	}

	(nil)
}

// DebugPrint prints the provided syntax tree, spanning multiple lines and with
// indentation. Can be useful to investigate the content of a syntax tree.
func ( io.Writer,  Node) error {
	 := debugPrinter{out: }
	.print(reflect.ValueOf())
	return .err
}

type debugPrinter struct {
	out   io.Writer
	level int
	err   error
}

func ( *debugPrinter) ( string,  ...any) {
	,  := fmt.Fprintf(.out, , ...)
	if  != nil && .err == nil {
		.err = 
	}
}

func ( *debugPrinter) () {
	.printf("\n")
	for  := 0;  < .level; ++ {
		.printf(".  ")
	}
}

func ( *debugPrinter) ( reflect.Value) {
	switch .Kind() {
	case reflect.Interface:
		if .IsNil() {
			.printf("nil")
			return
		}
		.(.Elem())
	case reflect.Ptr:
		if .IsNil() {
			.printf("nil")
			return
		}
		.printf("*")
		.(.Elem())
	case reflect.Slice:
		.printf("%s (len = %d) {", .Type(), .Len())
		if .Len() > 0 {
			.level++
			.newline()
			for  := 0;  < .Len(); ++ {
				.printf("%d: ", )
				.(.Index())
				if  == .Len()-1 {
					.level--
				}
				.newline()
			}
		}
		.printf("}")

	case reflect.Struct:
		if ,  := .Interface().(Pos);  {
			.printf("%v:%v", .Line(), .Col())
			return
		}
		 := .Type()
		.printf("%s {", )
		.level++
		.newline()
		for  := 0;  < .NumField(); ++ {
			.printf("%s: ", .Field().Name)
			.(.Field())
			if  == .NumField()-1 {
				.level--
			}
			.newline()
		}
		.printf("}")
	default:
		.printf("%#v", .Interface())
	}
}