| | |
| | |
| | |
| |
|
| | |
| |
|
| | package parse |
| |
|
| | import ( |
| | "fmt" |
| | "strconv" |
| | "strings" |
| | ) |
| |
|
| | var textFormat = "%s" |
| |
|
| | |
| | |
| | |
| | type Node interface { |
| | Type() NodeType |
| | String() string |
| | |
| | |
| | |
| | Copy() Node |
| | Position() Pos |
| | |
| | |
| | tree() *Tree |
| | |
| | writeTo(*strings.Builder) |
| | } |
| |
|
| | |
| | type NodeType int |
| |
|
| | |
| | |
| | type Pos int |
| |
|
| | func (p Pos) Position() Pos { |
| | return p |
| | } |
| |
|
| | |
| | |
| | func (t NodeType) Type() NodeType { |
| | return t |
| | } |
| |
|
| | const ( |
| | NodeText NodeType = iota |
| | NodeAction |
| | NodeBool |
| | NodeChain |
| | NodeCommand |
| | NodeDot |
| | nodeElse |
| | nodeEnd |
| | NodeField |
| | NodeIdentifier |
| | NodeIf |
| | NodeList |
| | NodeNil |
| | NodeNumber |
| | NodePipe |
| | NodeRange |
| | NodeString |
| | NodeTemplate |
| | NodeVariable |
| | NodeWith |
| | NodeComment |
| | NodeBreak |
| | NodeContinue |
| | ) |
| |
|
| | |
| |
|
| | |
| | type ListNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Nodes []Node |
| | } |
| |
|
| | func (t *Tree) newList(pos Pos) *ListNode { |
| | return &ListNode{tr: t, NodeType: NodeList, Pos: pos} |
| | } |
| |
|
| | func (l *ListNode) append(n Node) { |
| | l.Nodes = append(l.Nodes, n) |
| | } |
| |
|
| | func (l *ListNode) tree() *Tree { |
| | return l.tr |
| | } |
| |
|
| | func (l *ListNode) String() string { |
| | var sb strings.Builder |
| | l.writeTo(&sb) |
| | return sb.String() |
| | } |
| |
|
| | func (l *ListNode) writeTo(sb *strings.Builder) { |
| | for _, n := range l.Nodes { |
| | n.writeTo(sb) |
| | } |
| | } |
| |
|
| | func (l *ListNode) CopyList() *ListNode { |
| | if l == nil { |
| | return l |
| | } |
| | n := l.tr.newList(l.Pos) |
| | for _, elem := range l.Nodes { |
| | n.append(elem.Copy()) |
| | } |
| | return n |
| | } |
| |
|
| | func (l *ListNode) Copy() Node { |
| | return l.CopyList() |
| | } |
| |
|
| | |
| | type TextNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Text []byte |
| | } |
| |
|
| | func (t *Tree) newText(pos Pos, text string) *TextNode { |
| | return &TextNode{tr: t, NodeType: NodeText, Pos: pos, Text: []byte(text)} |
| | } |
| |
|
| | func (t *TextNode) String() string { |
| | return fmt.Sprintf(textFormat, t.Text) |
| | } |
| |
|
| | func (t *TextNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString(t.String()) |
| | } |
| |
|
| | func (t *TextNode) tree() *Tree { |
| | return t.tr |
| | } |
| |
|
| | func (t *TextNode) Copy() Node { |
| | return &TextNode{tr: t.tr, NodeType: NodeText, Pos: t.Pos, Text: append([]byte{}, t.Text...)} |
| | } |
| |
|
| | |
| | type CommentNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Text string |
| | } |
| |
|
| | func (t *Tree) newComment(pos Pos, text string) *CommentNode { |
| | return &CommentNode{tr: t, NodeType: NodeComment, Pos: pos, Text: text} |
| | } |
| |
|
| | func (c *CommentNode) String() string { |
| | var sb strings.Builder |
| | c.writeTo(&sb) |
| | return sb.String() |
| | } |
| |
|
| | func (c *CommentNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString("{{") |
| | sb.WriteString(c.Text) |
| | sb.WriteString("}}") |
| | } |
| |
|
| | func (c *CommentNode) tree() *Tree { |
| | return c.tr |
| | } |
| |
|
| | func (c *CommentNode) Copy() Node { |
| | return &CommentNode{tr: c.tr, NodeType: NodeComment, Pos: c.Pos, Text: c.Text} |
| | } |
| |
|
| | |
| | type PipeNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Line int |
| | IsAssign bool |
| | Decl []*VariableNode |
| | Cmds []*CommandNode |
| | } |
| |
|
| | func (t *Tree) newPipeline(pos Pos, line int, vars []*VariableNode) *PipeNode { |
| | return &PipeNode{tr: t, NodeType: NodePipe, Pos: pos, Line: line, Decl: vars} |
| | } |
| |
|
| | func (p *PipeNode) append(command *CommandNode) { |
| | p.Cmds = append(p.Cmds, command) |
| | } |
| |
|
| | func (p *PipeNode) String() string { |
| | var sb strings.Builder |
| | p.writeTo(&sb) |
| | return sb.String() |
| | } |
| |
|
| | func (p *PipeNode) writeTo(sb *strings.Builder) { |
| | if len(p.Decl) > 0 { |
| | for i, v := range p.Decl { |
| | if i > 0 { |
| | sb.WriteString(", ") |
| | } |
| | v.writeTo(sb) |
| | } |
| | if p.IsAssign { |
| | sb.WriteString(" = ") |
| | } else { |
| | sb.WriteString(" := ") |
| | } |
| | } |
| | for i, c := range p.Cmds { |
| | if i > 0 { |
| | sb.WriteString(" | ") |
| | } |
| | c.writeTo(sb) |
| | } |
| | } |
| |
|
| | func (p *PipeNode) tree() *Tree { |
| | return p.tr |
| | } |
| |
|
| | func (p *PipeNode) CopyPipe() *PipeNode { |
| | if p == nil { |
| | return p |
| | } |
| | vars := make([]*VariableNode, len(p.Decl)) |
| | for i, d := range p.Decl { |
| | vars[i] = d.Copy().(*VariableNode) |
| | } |
| | n := p.tr.newPipeline(p.Pos, p.Line, vars) |
| | n.IsAssign = p.IsAssign |
| | for _, c := range p.Cmds { |
| | n.append(c.Copy().(*CommandNode)) |
| | } |
| | return n |
| | } |
| |
|
| | func (p *PipeNode) Copy() Node { |
| | return p.CopyPipe() |
| | } |
| |
|
| | |
| | |
| | |
| | type ActionNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Line int |
| | Pipe *PipeNode |
| | } |
| |
|
| | func (t *Tree) newAction(pos Pos, line int, pipe *PipeNode) *ActionNode { |
| | return &ActionNode{tr: t, NodeType: NodeAction, Pos: pos, Line: line, Pipe: pipe} |
| | } |
| |
|
| | func (a *ActionNode) String() string { |
| | var sb strings.Builder |
| | a.writeTo(&sb) |
| | return sb.String() |
| | } |
| |
|
| | func (a *ActionNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString("{{") |
| | a.Pipe.writeTo(sb) |
| | sb.WriteString("}}") |
| | } |
| |
|
| | func (a *ActionNode) tree() *Tree { |
| | return a.tr |
| | } |
| |
|
| | func (a *ActionNode) Copy() Node { |
| | return a.tr.newAction(a.Pos, a.Line, a.Pipe.CopyPipe()) |
| | } |
| |
|
| | |
| | type CommandNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Args []Node |
| | } |
| |
|
| | func (t *Tree) newCommand(pos Pos) *CommandNode { |
| | return &CommandNode{tr: t, NodeType: NodeCommand, Pos: pos} |
| | } |
| |
|
| | func (c *CommandNode) append(arg Node) { |
| | c.Args = append(c.Args, arg) |
| | } |
| |
|
| | func (c *CommandNode) String() string { |
| | var sb strings.Builder |
| | c.writeTo(&sb) |
| | return sb.String() |
| | } |
| |
|
| | func (c *CommandNode) writeTo(sb *strings.Builder) { |
| | for i, arg := range c.Args { |
| | if i > 0 { |
| | sb.WriteByte(' ') |
| | } |
| | if arg, ok := arg.(*PipeNode); ok { |
| | sb.WriteByte('(') |
| | arg.writeTo(sb) |
| | sb.WriteByte(')') |
| | continue |
| | } |
| | arg.writeTo(sb) |
| | } |
| | } |
| |
|
| | func (c *CommandNode) tree() *Tree { |
| | return c.tr |
| | } |
| |
|
| | func (c *CommandNode) Copy() Node { |
| | if c == nil { |
| | return c |
| | } |
| | n := c.tr.newCommand(c.Pos) |
| | for _, c := range c.Args { |
| | n.append(c.Copy()) |
| | } |
| | return n |
| | } |
| |
|
| | |
| | type IdentifierNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Ident string |
| | } |
| |
|
| | |
| | func NewIdentifier(ident string) *IdentifierNode { |
| | return &IdentifierNode{NodeType: NodeIdentifier, Ident: ident} |
| | } |
| |
|
| | |
| | |
| | |
| | func (i *IdentifierNode) SetPos(pos Pos) *IdentifierNode { |
| | i.Pos = pos |
| | return i |
| | } |
| |
|
| | |
| | |
| | |
| | func (i *IdentifierNode) SetTree(t *Tree) *IdentifierNode { |
| | i.tr = t |
| | return i |
| | } |
| |
|
| | func (i *IdentifierNode) String() string { |
| | return i.Ident |
| | } |
| |
|
| | func (i *IdentifierNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString(i.String()) |
| | } |
| |
|
| | func (i *IdentifierNode) tree() *Tree { |
| | return i.tr |
| | } |
| |
|
| | func (i *IdentifierNode) Copy() Node { |
| | return NewIdentifier(i.Ident).SetTree(i.tr).SetPos(i.Pos) |
| | } |
| |
|
| | |
| | |
| | type VariableNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Ident []string |
| | } |
| |
|
| | func (t *Tree) newVariable(pos Pos, ident string) *VariableNode { |
| | return &VariableNode{tr: t, NodeType: NodeVariable, Pos: pos, Ident: strings.Split(ident, ".")} |
| | } |
| |
|
| | func (v *VariableNode) String() string { |
| | var sb strings.Builder |
| | v.writeTo(&sb) |
| | return sb.String() |
| | } |
| |
|
| | func (v *VariableNode) writeTo(sb *strings.Builder) { |
| | for i, id := range v.Ident { |
| | if i > 0 { |
| | sb.WriteByte('.') |
| | } |
| | sb.WriteString(id) |
| | } |
| | } |
| |
|
| | func (v *VariableNode) tree() *Tree { |
| | return v.tr |
| | } |
| |
|
| | func (v *VariableNode) Copy() Node { |
| | return &VariableNode{tr: v.tr, NodeType: NodeVariable, Pos: v.Pos, Ident: append([]string{}, v.Ident...)} |
| | } |
| |
|
| | |
| | type DotNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | } |
| |
|
| | func (t *Tree) newDot(pos Pos) *DotNode { |
| | return &DotNode{tr: t, NodeType: NodeDot, Pos: pos} |
| | } |
| |
|
| | func (d *DotNode) Type() NodeType { |
| | |
| | |
| | |
| | return NodeDot |
| | } |
| |
|
| | func (d *DotNode) String() string { |
| | return "." |
| | } |
| |
|
| | func (d *DotNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString(d.String()) |
| | } |
| |
|
| | func (d *DotNode) tree() *Tree { |
| | return d.tr |
| | } |
| |
|
| | func (d *DotNode) Copy() Node { |
| | return d.tr.newDot(d.Pos) |
| | } |
| |
|
| | |
| | type NilNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | } |
| |
|
| | func (t *Tree) newNil(pos Pos) *NilNode { |
| | return &NilNode{tr: t, NodeType: NodeNil, Pos: pos} |
| | } |
| |
|
| | func (n *NilNode) Type() NodeType { |
| | |
| | |
| | |
| | return NodeNil |
| | } |
| |
|
| | func (n *NilNode) String() string { |
| | return "nil" |
| | } |
| |
|
| | func (n *NilNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString(n.String()) |
| | } |
| |
|
| | func (n *NilNode) tree() *Tree { |
| | return n.tr |
| | } |
| |
|
| | func (n *NilNode) Copy() Node { |
| | return n.tr.newNil(n.Pos) |
| | } |
| |
|
| | |
| | |
| | |
| | type FieldNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Ident []string |
| | } |
| |
|
| | func (t *Tree) newField(pos Pos, ident string) *FieldNode { |
| | return &FieldNode{tr: t, NodeType: NodeField, Pos: pos, Ident: strings.Split(ident[1:], ".")} |
| | } |
| |
|
| | func (f *FieldNode) String() string { |
| | var sb strings.Builder |
| | f.writeTo(&sb) |
| | return sb.String() |
| | } |
| |
|
| | func (f *FieldNode) writeTo(sb *strings.Builder) { |
| | for _, id := range f.Ident { |
| | sb.WriteByte('.') |
| | sb.WriteString(id) |
| | } |
| | } |
| |
|
| | func (f *FieldNode) tree() *Tree { |
| | return f.tr |
| | } |
| |
|
| | func (f *FieldNode) Copy() Node { |
| | return &FieldNode{tr: f.tr, NodeType: NodeField, Pos: f.Pos, Ident: append([]string{}, f.Ident...)} |
| | } |
| |
|
| | |
| | |
| | |
| | type ChainNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Node Node |
| | Field []string |
| | } |
| |
|
| | func (t *Tree) newChain(pos Pos, node Node) *ChainNode { |
| | return &ChainNode{tr: t, NodeType: NodeChain, Pos: pos, Node: node} |
| | } |
| |
|
| | |
| | func (c *ChainNode) Add(field string) { |
| | if len(field) == 0 || field[0] != '.' { |
| | panic("no dot in field") |
| | } |
| | field = field[1:] |
| | if field == "" { |
| | panic("empty field") |
| | } |
| | c.Field = append(c.Field, field) |
| | } |
| |
|
| | func (c *ChainNode) String() string { |
| | var sb strings.Builder |
| | c.writeTo(&sb) |
| | return sb.String() |
| | } |
| |
|
| | func (c *ChainNode) writeTo(sb *strings.Builder) { |
| | if _, ok := c.Node.(*PipeNode); ok { |
| | sb.WriteByte('(') |
| | c.Node.writeTo(sb) |
| | sb.WriteByte(')') |
| | } else { |
| | c.Node.writeTo(sb) |
| | } |
| | for _, field := range c.Field { |
| | sb.WriteByte('.') |
| | sb.WriteString(field) |
| | } |
| | } |
| |
|
| | func (c *ChainNode) tree() *Tree { |
| | return c.tr |
| | } |
| |
|
| | func (c *ChainNode) Copy() Node { |
| | return &ChainNode{tr: c.tr, NodeType: NodeChain, Pos: c.Pos, Node: c.Node, Field: append([]string{}, c.Field...)} |
| | } |
| |
|
| | |
| | type BoolNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | True bool |
| | } |
| |
|
| | func (t *Tree) newBool(pos Pos, true bool) *BoolNode { |
| | return &BoolNode{tr: t, NodeType: NodeBool, Pos: pos, True: true} |
| | } |
| |
|
| | func (b *BoolNode) String() string { |
| | if b.True { |
| | return "true" |
| | } |
| | return "false" |
| | } |
| |
|
| | func (b *BoolNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString(b.String()) |
| | } |
| |
|
| | func (b *BoolNode) tree() *Tree { |
| | return b.tr |
| | } |
| |
|
| | func (b *BoolNode) Copy() Node { |
| | return b.tr.newBool(b.Pos, b.True) |
| | } |
| |
|
| | |
| | |
| | |
| | type NumberNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | IsInt bool |
| | IsUint bool |
| | IsFloat bool |
| | IsComplex bool |
| | Int64 int64 |
| | Uint64 uint64 |
| | Float64 float64 |
| | Complex128 complex128 |
| | Text string |
| | } |
| |
|
| | func (t *Tree) newNumber(pos Pos, text string, typ itemType) (*NumberNode, error) { |
| | n := &NumberNode{tr: t, NodeType: NodeNumber, Pos: pos, Text: text} |
| | switch typ { |
| | case itemCharConstant: |
| | rune, _, tail, err := strconv.UnquoteChar(text[1:], text[0]) |
| | if err != nil { |
| | return nil, err |
| | } |
| | if tail != "'" { |
| | return nil, fmt.Errorf("malformed character constant: %s", text) |
| | } |
| | n.Int64 = int64(rune) |
| | n.IsInt = true |
| | n.Uint64 = uint64(rune) |
| | n.IsUint = true |
| | n.Float64 = float64(rune) |
| | n.IsFloat = true |
| | return n, nil |
| | case itemComplex: |
| | |
| | if _, err := fmt.Sscan(text, &n.Complex128); err != nil { |
| | return nil, err |
| | } |
| | n.IsComplex = true |
| | n.simplifyComplex() |
| | return n, nil |
| | } |
| | |
| | if len(text) > 0 && text[len(text)-1] == 'i' { |
| | f, err := strconv.ParseFloat(text[:len(text)-1], 64) |
| | if err == nil { |
| | n.IsComplex = true |
| | n.Complex128 = complex(0, f) |
| | n.simplifyComplex() |
| | return n, nil |
| | } |
| | } |
| | |
| | u, err := strconv.ParseUint(text, 0, 64) |
| | if err == nil { |
| | n.IsUint = true |
| | n.Uint64 = u |
| | } |
| | i, err := strconv.ParseInt(text, 0, 64) |
| | if err == nil { |
| | n.IsInt = true |
| | n.Int64 = i |
| | if i == 0 { |
| | n.IsUint = true |
| | n.Uint64 = u |
| | } |
| | } |
| | |
| | if n.IsInt { |
| | n.IsFloat = true |
| | n.Float64 = float64(n.Int64) |
| | } else if n.IsUint { |
| | n.IsFloat = true |
| | n.Float64 = float64(n.Uint64) |
| | } else { |
| | f, err := strconv.ParseFloat(text, 64) |
| | if err == nil { |
| | |
| | |
| | if !strings.ContainsAny(text, ".eEpP") { |
| | return nil, fmt.Errorf("integer overflow: %q", text) |
| | } |
| | n.IsFloat = true |
| | n.Float64 = f |
| | |
| | if !n.IsInt && float64(int64(f)) == f { |
| | n.IsInt = true |
| | n.Int64 = int64(f) |
| | } |
| | if !n.IsUint && float64(uint64(f)) == f { |
| | n.IsUint = true |
| | n.Uint64 = uint64(f) |
| | } |
| | } |
| | } |
| | if !n.IsInt && !n.IsUint && !n.IsFloat { |
| | return nil, fmt.Errorf("illegal number syntax: %q", text) |
| | } |
| | return n, nil |
| | } |
| |
|
| | |
| | |
| | func (n *NumberNode) simplifyComplex() { |
| | n.IsFloat = imag(n.Complex128) == 0 |
| | if n.IsFloat { |
| | n.Float64 = real(n.Complex128) |
| | n.IsInt = float64(int64(n.Float64)) == n.Float64 |
| | if n.IsInt { |
| | n.Int64 = int64(n.Float64) |
| | } |
| | n.IsUint = float64(uint64(n.Float64)) == n.Float64 |
| | if n.IsUint { |
| | n.Uint64 = uint64(n.Float64) |
| | } |
| | } |
| | } |
| |
|
| | func (n *NumberNode) String() string { |
| | return n.Text |
| | } |
| |
|
| | func (n *NumberNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString(n.String()) |
| | } |
| |
|
| | func (n *NumberNode) tree() *Tree { |
| | return n.tr |
| | } |
| |
|
| | func (n *NumberNode) Copy() Node { |
| | nn := new(NumberNode) |
| | *nn = *n |
| | return nn |
| | } |
| |
|
| | |
| | type StringNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Quoted string |
| | Text string |
| | } |
| |
|
| | func (t *Tree) newString(pos Pos, orig, text string) *StringNode { |
| | return &StringNode{tr: t, NodeType: NodeString, Pos: pos, Quoted: orig, Text: text} |
| | } |
| |
|
| | func (s *StringNode) String() string { |
| | return s.Quoted |
| | } |
| |
|
| | func (s *StringNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString(s.String()) |
| | } |
| |
|
| | func (s *StringNode) tree() *Tree { |
| | return s.tr |
| | } |
| |
|
| | func (s *StringNode) Copy() Node { |
| | return s.tr.newString(s.Pos, s.Quoted, s.Text) |
| | } |
| |
|
| | |
| | |
| | type endNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | } |
| |
|
| | func (t *Tree) newEnd(pos Pos) *endNode { |
| | return &endNode{tr: t, NodeType: nodeEnd, Pos: pos} |
| | } |
| |
|
| | func (e *endNode) String() string { |
| | return "{{end}}" |
| | } |
| |
|
| | func (e *endNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString(e.String()) |
| | } |
| |
|
| | func (e *endNode) tree() *Tree { |
| | return e.tr |
| | } |
| |
|
| | func (e *endNode) Copy() Node { |
| | return e.tr.newEnd(e.Pos) |
| | } |
| |
|
| | |
| | type elseNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Line int |
| | } |
| |
|
| | func (t *Tree) newElse(pos Pos, line int) *elseNode { |
| | return &elseNode{tr: t, NodeType: nodeElse, Pos: pos, Line: line} |
| | } |
| |
|
| | func (e *elseNode) Type() NodeType { |
| | return nodeElse |
| | } |
| |
|
| | func (e *elseNode) String() string { |
| | return "{{else}}" |
| | } |
| |
|
| | func (e *elseNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString(e.String()) |
| | } |
| |
|
| | func (e *elseNode) tree() *Tree { |
| | return e.tr |
| | } |
| |
|
| | func (e *elseNode) Copy() Node { |
| | return e.tr.newElse(e.Pos, e.Line) |
| | } |
| |
|
| | |
| | type BranchNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Line int |
| | Pipe *PipeNode |
| | List *ListNode |
| | ElseList *ListNode |
| | } |
| |
|
| | func (b *BranchNode) String() string { |
| | var sb strings.Builder |
| | b.writeTo(&sb) |
| | return sb.String() |
| | } |
| |
|
| | func (b *BranchNode) writeTo(sb *strings.Builder) { |
| | name := "" |
| | switch b.NodeType { |
| | case NodeIf: |
| | name = "if" |
| | case NodeRange: |
| | name = "range" |
| | case NodeWith: |
| | name = "with" |
| | default: |
| | panic("unknown branch type") |
| | } |
| | sb.WriteString("{{") |
| | sb.WriteString(name) |
| | sb.WriteByte(' ') |
| | b.Pipe.writeTo(sb) |
| | sb.WriteString("}}") |
| | b.List.writeTo(sb) |
| | if b.ElseList != nil { |
| | sb.WriteString("{{else}}") |
| | b.ElseList.writeTo(sb) |
| | } |
| | sb.WriteString("{{end}}") |
| | } |
| |
|
| | func (b *BranchNode) tree() *Tree { |
| | return b.tr |
| | } |
| |
|
| | func (b *BranchNode) Copy() Node { |
| | switch b.NodeType { |
| | case NodeIf: |
| | return b.tr.newIf(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) |
| | case NodeRange: |
| | return b.tr.newRange(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) |
| | case NodeWith: |
| | return b.tr.newWith(b.Pos, b.Line, b.Pipe, b.List, b.ElseList) |
| | default: |
| | panic("unknown branch type") |
| | } |
| | } |
| |
|
| | |
| | type IfNode struct { |
| | BranchNode |
| | } |
| |
|
| | func (t *Tree) newIf(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *IfNode { |
| | return &IfNode{BranchNode{tr: t, NodeType: NodeIf, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} |
| | } |
| |
|
| | func (i *IfNode) Copy() Node { |
| | return i.tr.newIf(i.Pos, i.Line, i.Pipe.CopyPipe(), i.List.CopyList(), i.ElseList.CopyList()) |
| | } |
| |
|
| | |
| | type BreakNode struct { |
| | tr *Tree |
| | NodeType |
| | Pos |
| | Line int |
| | } |
| |
|
| | func (t *Tree) newBreak(pos Pos, line int) *BreakNode { |
| | return &BreakNode{tr: t, NodeType: NodeBreak, Pos: pos, Line: line} |
| | } |
| |
|
| | func (b *BreakNode) Copy() Node { return b.tr.newBreak(b.Pos, b.Line) } |
| | func (b *BreakNode) String() string { return "{{break}}" } |
| | func (b *BreakNode) tree() *Tree { return b.tr } |
| | func (b *BreakNode) writeTo(sb *strings.Builder) { sb.WriteString("{{break}}") } |
| |
|
| | |
| | type ContinueNode struct { |
| | tr *Tree |
| | NodeType |
| | Pos |
| | Line int |
| | } |
| |
|
| | func (t *Tree) newContinue(pos Pos, line int) *ContinueNode { |
| | return &ContinueNode{tr: t, NodeType: NodeContinue, Pos: pos, Line: line} |
| | } |
| |
|
| | func (c *ContinueNode) Copy() Node { return c.tr.newContinue(c.Pos, c.Line) } |
| | func (c *ContinueNode) String() string { return "{{continue}}" } |
| | func (c *ContinueNode) tree() *Tree { return c.tr } |
| | func (c *ContinueNode) writeTo(sb *strings.Builder) { sb.WriteString("{{continue}}") } |
| |
|
| | |
| | type RangeNode struct { |
| | BranchNode |
| | } |
| |
|
| | func (t *Tree) newRange(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *RangeNode { |
| | return &RangeNode{BranchNode{tr: t, NodeType: NodeRange, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} |
| | } |
| |
|
| | func (r *RangeNode) Copy() Node { |
| | return r.tr.newRange(r.Pos, r.Line, r.Pipe.CopyPipe(), r.List.CopyList(), r.ElseList.CopyList()) |
| | } |
| |
|
| | |
| | type WithNode struct { |
| | BranchNode |
| | } |
| |
|
| | func (t *Tree) newWith(pos Pos, line int, pipe *PipeNode, list, elseList *ListNode) *WithNode { |
| | return &WithNode{BranchNode{tr: t, NodeType: NodeWith, Pos: pos, Line: line, Pipe: pipe, List: list, ElseList: elseList}} |
| | } |
| |
|
| | func (w *WithNode) Copy() Node { |
| | return w.tr.newWith(w.Pos, w.Line, w.Pipe.CopyPipe(), w.List.CopyList(), w.ElseList.CopyList()) |
| | } |
| |
|
| | |
| | type TemplateNode struct { |
| | NodeType |
| | Pos |
| | tr *Tree |
| | Line int |
| | Name string |
| | Pipe *PipeNode |
| | } |
| |
|
| | func (t *Tree) newTemplate(pos Pos, line int, name string, pipe *PipeNode) *TemplateNode { |
| | return &TemplateNode{tr: t, NodeType: NodeTemplate, Pos: pos, Line: line, Name: name, Pipe: pipe} |
| | } |
| |
|
| | func (t *TemplateNode) String() string { |
| | var sb strings.Builder |
| | t.writeTo(&sb) |
| | return sb.String() |
| | } |
| |
|
| | func (t *TemplateNode) writeTo(sb *strings.Builder) { |
| | sb.WriteString("{{template ") |
| | sb.WriteString(strconv.Quote(t.Name)) |
| | if t.Pipe != nil { |
| | sb.WriteByte(' ') |
| | t.Pipe.writeTo(sb) |
| | } |
| | sb.WriteString("}}") |
| | } |
| |
|
| | func (t *TemplateNode) tree() *Tree { |
| | return t.tr |
| | } |
| |
|
| | func (t *TemplateNode) Copy() Node { |
| | return t.tr.newTemplate(t.Pos, t.Line, t.Name, t.Pipe.CopyPipe()) |
| | } |
| |
|