package syntax_test

import (
	"bytes"
	"fmt"
	"log"
	"reflect"
	"strings"
	"testing"

	"go.starlark.net/syntax"
)

func TestWalk(t *testing.T) {
	const src = `
for x in y:
  if x:
    pass
  else:
    f([2*x for x in "abc"])
`
	// TODO(adonovan): test that it finds all syntax.Nodes
	// (compare against a reflect-based implementation).
	// TODO(adonovan): test that the result of f is used to prune
	// the descent.
	f, err := syntax.Parse("hello.go", src, 0)
	if err != nil {
		t.Fatal(err)
	}

	var buf bytes.Buffer
	var depth int
	syntax.Walk(f, func(n syntax.Node) bool {
		if n == nil {
			depth--
			return true
		}
		fmt.Fprintf(&buf, "%s%s\n",
			strings.Repeat("  ", depth),
			strings.TrimPrefix(reflect.TypeOf(n).String(), "*syntax."))
		depth++
		return true
	})
	got := buf.String()
	want := `
File
  ForStmt
    Ident
    Ident
    IfStmt
      Ident
      BranchStmt
      ExprStmt
        CallExpr
          Ident
          Comprehension
            BinaryExpr
              Literal
              Ident
            ForClause
              Ident
              Literal`
	got = strings.TrimSpace(got)
	want = strings.TrimSpace(want)
	if got != want {
		t.Errorf("got %s, want %s", got, want)
	}
}

// ExampleWalk demonstrates the use of Walk to
// enumerate the identifiers in a Starlark source file
// containing a nonsense program with varied grammar.
func ExampleWalk() {
	const src = `
load("library", "a")

def b(c, *, d=e):
    f += {g: h}
    i = -(j)
    return k.l[m + n]

for o in [p for q, r in s if t]:
    u(lambda: v, w[x:y:z])
`
	f, err := syntax.Parse("hello.star", src, 0)
	if err != nil {
		log.Fatal(err)
	}

	var idents []string
	syntax.Walk(f, func(n syntax.Node) bool {
		if id, ok := n.(*syntax.Ident); ok {
			idents = append(idents, id.Name)
		}
		return true
	})
	fmt.Println(strings.Join(idents, " "))

	// The identifer 'a' appears in both LoadStmt.From[0] and LoadStmt.To[0].

	// Output:
	// a a b c d e f g h i j k l m n o p q r s t u v w x y z
}
