// clone clones a template Node. func clone(n parse.Node) parse.Node { switch t := n.(type) { case *parse.ActionNode: return cloneAction(t) case *parse.IfNode: b := new(parse.IfNode) copyBranch(&b.BranchNode, &t.BranchNode) return b case *parse.ListNode: return cloneList(t) case *parse.RangeNode: b := new(parse.RangeNode) copyBranch(&b.BranchNode, &t.BranchNode) return b case *parse.TemplateNode: return cloneTemplate(t) case *parse.TextNode: return cloneText(t) case *parse.WithNode: b := new(parse.WithNode) copyBranch(&b.BranchNode, &t.BranchNode) return b } panic("cloning " + n.String() + " is unimplemented") }
// Walk functions step through the major pieces of the template structure, // generating output as they go. func (s *state) walk(dot reflect.Value, node parse.Node) { s.at(node) switch node := node.(type) { case *parse.ActionNode: // Do not pop variables so they persist until next end. // Also, if the action declares variables, don't print the result. val := s.evalPipeline(dot, node.Pipe) if len(node.Pipe.Decl) == 0 { s.visitValue(node, val) } case *parse.IfNode: s.walkIfOrWith(parse.NodeIf, dot, node.Pipe, node.List, node.ElseList) case *parse.ListNode: for _, node := range node.Nodes { s.walk(dot, node) } case *parse.RangeNode: s.walkRange(dot, node) case *parse.TemplateNode: s.walkTemplate(dot, node) case *parse.TextNode: s.v.Visit(node.String()) case *parse.WithNode: s.walkIfOrWith(parse.NodeWith, dot, node.Pipe, node.List, node.ElseList) default: s.errorf("unknown node: %s", node) } }
// escape escapes a template node. func (e *escaper) escape(c context, n parse.Node) context { switch n := n.(type) { case *parse.ActionNode: return e.escapeAction(c, n) case *parse.IfNode: return e.escapeBranch(c, &n.BranchNode, "if") case *parse.ListNode: return e.escapeList(c, n) case *parse.RangeNode: return e.escapeBranch(c, &n.BranchNode, "range") case *parse.TemplateNode: return e.escapeTemplate(c, n) case *parse.TextNode: return e.escapeText(c, n) case *parse.WithNode: return e.escapeBranch(c, &n.BranchNode, "with") } panic("escaping " + n.String() + " is unimplemented") }
// IsPseudoFunction returns true iff n is a *parse.ActionNode // consisting solely of a fn call without any arguments. func IsPseudoFunction(n parse.Node, fn string) bool { return n.Type() == parse.NodeAction && n.String() == leftDelim+fn+rightDelim }
func DataNode(root *template.Template, node parse.Node, data *Dotted, vars map[string][]string, prefixwords []string) { if true { switch node.Type() { case parse.NodeWith: withNode := node.(*parse.WithNode) arg0 := withNode.Pipe.Cmds[0].Args[0].String() var withv string if len(arg0) > 0 && arg0[0] == '.' { if decl := withNode.Pipe.Decl; len(decl) > 0 { // just {{with $ := ...}} cases withv = decl[0].Ident[0] words := strings.Split(arg0[1:], ".") vars[withv] = words data.Append(words, prefixwords) } } if withNode.List != nil { for _, n := range withNode.List.Nodes { DataNode(root, n, data, vars, prefixwords) } } if withNode.ElseList != nil { for _, n := range withNode.ElseList.Nodes { DataNode(root, n, data, vars, prefixwords) } } if withv != "" { delete(vars, withv) } case parse.NodeTemplate: tnode := node.(*parse.TemplateNode) var tawords []string for _, arg := range tnode.Pipe.Cmds[0].Args { s := arg.String() if len(s) > 1 && s[0] == '.' { tawords = strings.Split(s[1:], ".") data.Append(tawords, prefixwords) break // just one argument (pipeline) to "{{template}}" allowed anyway } } if sub := root.Lookup(tnode.Name); sub != nil && sub.Tree != nil && sub.Tree.Root != nil { for _, n := range sub.Tree.Root.Nodes { pw := prefixwords if tawords != nil { pw = append(prefixwords, tawords...) } DataNode(root, n, data, vars, pw) } } case parse.NodeAction: actionNode := node.(*parse.ActionNode) decl := actionNode.Pipe.Decl for _, cmd := range actionNode.Pipe.Cmds { if cmd.NodeType != parse.NodeCommand { continue } for _, arg := range cmd.Args { var ident []string switch arg.Type() { case parse.NodeChain: chain := arg.(*parse.ChainNode) if chain.Node.Type() == parse.NodePipe { pipe := chain.Node.(*parse.PipeNode) for _, arg := range pipe.Cmds[0].Args { if arg.Type() == parse.NodeField { w := arg.String() if len(w) > 0 && w[0] == '.' { data.Append(strings.Split(w[1:], "."), prefixwords) } } } } case parse.NodeField: ident = arg.(*parse.FieldNode).Ident if len(decl) > 0 && len(decl[0].Ident) > 0 { vars[decl[0].Ident[0]] = ident } data.Append(ident, prefixwords) case parse.NodeVariable: ident = arg.(*parse.VariableNode).Ident if words, ok := vars[ident[0]]; ok { words := append(words, ident[1:]...) data.Append(words, prefixwords) if len(decl) > 0 && len(decl[0].Ident) > 0 { vars[decl[0].Ident[0]] = words } } } } } case parse.NodeRange: rangeNode := node.(*parse.RangeNode) decl := rangeNode.Pipe.Decl[len(rangeNode.Pipe.Decl)-1].String() keys := []string{} for _, ifnode := range rangeNode.List.Nodes { switch ifnode.Type() { case parse.NodeAction: keys = append(keys, getKeys(decl, ifnode)...) case parse.NodeIf: for _, z := range ifnode.(*parse.IfNode).List.Nodes { if z.Type() == parse.NodeAction { keys = append(keys, getKeys(decl, z)...) } } case parse.NodeTemplate: // DataNode(root, ifnode, data, vars, prefixwords) arg0 := ifnode.(*parse.TemplateNode).Pipe.Cmds[0].Args[0] if arg0.Type() == parse.NodePipe { cmd0 := arg0.(*parse.PipeNode).Cmds[0] if cmd0.Type() == parse.NodeCommand { for _, a := range cmd0.Args { if s, prefix := a.String(), decl+"."; strings.HasPrefix(s, prefix) { keys = append(keys, strings.Split(strings.TrimPrefix(s, prefix), ".")...) } } } } else if arg0.Type() == parse.NodeVariable { keys = append(keys, arg0.(*parse.VariableNode).Ident[1:]...) } } } // fml arg0 := rangeNode.Pipe.Cmds[0].Args[0].String() words, ok := vars[arg0] if !ok && len(arg0) > 0 && arg0[0] == '.' { words = strings.Split(arg0[1:], ".") data.Append(words, prefixwords) ok = true } if ok { if leaf := data.Find(words); leaf != nil { leaf.Ranged = true leaf.Keys = append(leaf.Keys, keys...) leaf.Decl = decl // redefined $ } } } } }
func templatePosition(name string, text string, n parse.Node) *token.Position { return &token.Position{ Filename: name, Line: strings.Count(text[:int(n.Position())], "\n") + 1, } }