Skip to content

proposal: text/template/parse: traversal utility  #56404

@tylermmorton

Description

@tylermmorton

When using the package text/template/parse to analyze a template ParseTree, it is common to want to visit all Nodes in a tree, even when they are nested in other nodes.

I've implemented a Traverse function that works recursively from a tree root:

package traverse

import (
	"text/template/parse"
)

func Traverse(cur parse.Node, visitor func(parse.Node)) {
	switch node := cur.(type) {
	case *parse.ActionNode:
		if node.Pipe != nil {
			Traverse(node.Pipe, visitor)
		}
	case *parse.BoolNode:
	case *parse.BranchNode:
		if node.Pipe != nil {
			Traverse(node.Pipe, visitor)
		}
		if node.List != nil {
			Traverse(node.List, visitor)
		}
		if node.ElseList != nil {
			Traverse(node.ElseList, visitor)
		}
	case *parse.BreakNode:
	case *parse.ChainNode:
	case *parse.CommandNode:
		if node.Args != nil {
			for _, arg := range node.Args {
				Traverse(arg, visitor)
			}
		}
	case *parse.CommentNode:
	case *parse.ContinueNode:
	case *parse.DotNode:
	case *parse.FieldNode:
	case *parse.IdentifierNode:
	case *parse.IfNode:
		Traverse(&node.BranchNode, visitor)
	case *parse.ListNode:
		if node.Nodes != nil {
			for _, child := range node.Nodes {
				Traverse(child, visitor)
			}
		}
	case *parse.NilNode:
	case *parse.NumberNode:
	case *parse.PipeNode:
		if node.Cmds != nil {
			for _, cmd := range node.Cmds {
				Traverse(cmd, visitor)
			}
		}
		if node.Decl != nil {
			for _, decl := range node.Decl {
				Traverse(decl, visitor)
			}
		}
	case *parse.RangeNode:
		Traverse(&node.BranchNode, visitor)
	case *parse.StringNode:
	case *parse.TemplateNode:
		if node.Pipe != nil {
			Traverse(node.Pipe, visitor)
		}
	case *parse.TextNode:
	case *parse.VariableNode:
	case *parse.WithNode:
		Traverse(&node.BranchNode, visitor)
	}
	visitor(cur)
}

Is this something that could be added to text/template/parse as a utility function? I believe that a cleaned up version of this or something similar to the ast.Walk interface could increase the utility of the parse package and open the door for static analysis tooling around go templates.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Incoming

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions