Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
tylermmorton opened this issue Oct 24, 2022 · 1 comment
Open

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

tylermmorton opened this issue Oct 24, 2022 · 1 comment
Labels
Milestone

Comments

@tylermmorton
Copy link

tylermmorton commented Oct 24, 2022

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.

@gopherbot gopherbot added this to the Proposal milestone Oct 24, 2022
@seankhliao
Copy link
Member

cc @robpike

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: Incoming
Development

No branches or pull requests

3 participants