package parse
import (
)
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 ( Pos) () Pos {
return
}
func ( NodeType) () NodeType {
return
}
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 ( *Tree) ( Pos) *ListNode {
return &ListNode{tr: , NodeType: NodeList, Pos: }
}
func ( *ListNode) ( Node) {
.Nodes = append(.Nodes, )
}
func ( *ListNode) () *Tree {
return .tr
}
func ( *ListNode) () string {
var strings.Builder
.writeTo(&)
return .String()
}
func ( *ListNode) ( *strings.Builder) {
for , := range .Nodes {
.writeTo()
}
}
func ( *ListNode) () *ListNode {
if == nil {
return
}
:= .tr.newList(.Pos)
for , := range .Nodes {
.append(.Copy())
}
return
}
func ( *ListNode) () Node {
return .CopyList()
}
type TextNode struct {
NodeType
Pos
tr *Tree
Text []byte
}
func ( *Tree) ( Pos, string) *TextNode {
return &TextNode{tr: , NodeType: NodeText, Pos: , Text: []byte()}
}
func ( *TextNode) () string {
return fmt.Sprintf(textFormat, .Text)
}
func ( *TextNode) ( *strings.Builder) {
.WriteString(.String())
}
func ( *TextNode) () *Tree {
return .tr
}
func ( *TextNode) () Node {
return &TextNode{tr: .tr, NodeType: NodeText, Pos: .Pos, Text: append([]byte{}, .Text...)}
}
type CommentNode struct {
NodeType
Pos
tr *Tree
Text string
}
func ( *Tree) ( Pos, string) *CommentNode {
return &CommentNode{tr: , NodeType: NodeComment, Pos: , Text: }
}
func ( *CommentNode) () string {
var strings.Builder
.writeTo(&)
return .String()
}
func ( *CommentNode) ( *strings.Builder) {
.WriteString("{{")
.WriteString(.Text)
.WriteString("}}")
}
func ( *CommentNode) () *Tree {
return .tr
}
func ( *CommentNode) () Node {
return &CommentNode{tr: .tr, NodeType: NodeComment, Pos: .Pos, Text: .Text}
}
type PipeNode struct {
NodeType
Pos
tr *Tree
Line int
IsAssign bool
Decl []*VariableNode
Cmds []*CommandNode
}
func ( *Tree) ( Pos, int, []*VariableNode) *PipeNode {
return &PipeNode{tr: , NodeType: NodePipe, Pos: , Line: , Decl: }
}
func ( *PipeNode) ( *CommandNode) {
.Cmds = append(.Cmds, )
}
func ( *PipeNode) () string {
var strings.Builder
.writeTo(&)
return .String()
}
func ( *PipeNode) ( *strings.Builder) {
if len(.Decl) > 0 {
for , := range .Decl {
if > 0 {
.WriteString(", ")
}
.writeTo()
}
.WriteString(" := ")
}
for , := range .Cmds {
if > 0 {
.WriteString(" | ")
}
.writeTo()
}
}
func ( *PipeNode) () *Tree {
return .tr
}
func ( *PipeNode) () *PipeNode {
if == nil {
return
}
:= make([]*VariableNode, len(.Decl))
for , := range .Decl {
[] = .Copy().(*VariableNode)
}
:= .tr.newPipeline(.Pos, .Line, )
.IsAssign = .IsAssign
for , := range .Cmds {
.append(.Copy().(*CommandNode))
}
return
}
func ( *PipeNode) () Node {
return .CopyPipe()
}
type ActionNode struct {
NodeType
Pos
tr *Tree
Line int
Pipe *PipeNode
}
func ( *Tree) ( Pos, int, *PipeNode) *ActionNode {
return &ActionNode{tr: , NodeType: NodeAction, Pos: , Line: , Pipe: }
}
func ( *ActionNode) () string {
var strings.Builder
.writeTo(&)
return .String()
}
func ( *ActionNode) ( *strings.Builder) {
.WriteString("{{")
.Pipe.writeTo()
.WriteString("}}")
}
func ( *ActionNode) () *Tree {
return .tr
}
func ( *ActionNode) () Node {
return .tr.newAction(.Pos, .Line, .Pipe.CopyPipe())
}
type CommandNode struct {
NodeType
Pos
tr *Tree
Args []Node
}
func ( *Tree) ( Pos) *CommandNode {
return &CommandNode{tr: , NodeType: NodeCommand, Pos: }
}
func ( *CommandNode) ( Node) {
.Args = append(.Args, )
}
func ( *CommandNode) () string {
var strings.Builder
.writeTo(&)
return .String()
}
func ( *CommandNode) ( *strings.Builder) {
for , := range .Args {
if > 0 {
.WriteByte(' ')
}
if , := .(*PipeNode); {
.WriteByte('(')
.writeTo()
.WriteByte(')')
continue
}
.writeTo()
}
}
func ( *CommandNode) () *Tree {
return .tr
}
func ( *CommandNode) () Node {
if == nil {
return
}
:= .tr.newCommand(.Pos)
for , := range .Args {
.append(.Copy())
}
return
}
type IdentifierNode struct {
NodeType
Pos
tr *Tree
Ident string
}
func ( string) *IdentifierNode {
return &IdentifierNode{NodeType: NodeIdentifier, Ident: }
}
func ( *IdentifierNode) ( Pos) *IdentifierNode {
.Pos =
return
}
func ( *IdentifierNode) ( *Tree) *IdentifierNode {
.tr =
return
}
func ( *IdentifierNode) () string {
return .Ident
}
func ( *IdentifierNode) ( *strings.Builder) {
.WriteString(.String())
}
func ( *IdentifierNode) () *Tree {
return .tr
}
func ( *IdentifierNode) () Node {
return NewIdentifier(.Ident).SetTree(.tr).SetPos(.Pos)
}
type VariableNode struct {
NodeType
Pos
tr *Tree
Ident []string
}
func ( *Tree) ( Pos, string) *VariableNode {
return &VariableNode{tr: , NodeType: NodeVariable, Pos: , Ident: strings.Split(, ".")}
}
func ( *VariableNode) () string {
var strings.Builder
.writeTo(&)
return .String()
}
func ( *VariableNode) ( *strings.Builder) {
for , := range .Ident {
if > 0 {
.WriteByte('.')
}
.WriteString()
}
}
func ( *VariableNode) () *Tree {
return .tr
}
func ( *VariableNode) () Node {
return &VariableNode{tr: .tr, NodeType: NodeVariable, Pos: .Pos, Ident: append([]string{}, .Ident...)}
}
type DotNode struct {
NodeType
Pos
tr *Tree
}
func ( *Tree) ( Pos) *DotNode {
return &DotNode{tr: , NodeType: NodeDot, Pos: }
}
func ( *DotNode) () NodeType {
return NodeDot
}
func ( *DotNode) () string {
return "."
}
func ( *DotNode) ( *strings.Builder) {
.WriteString(.String())
}
func ( *DotNode) () *Tree {
return .tr
}
func ( *DotNode) () Node {
return .tr.newDot(.Pos)
}
type NilNode struct {
NodeType
Pos
tr *Tree
}
func ( *Tree) ( Pos) *NilNode {
return &NilNode{tr: , NodeType: NodeNil, Pos: }
}
func ( *NilNode) () NodeType {
return NodeNil
}
func ( *NilNode) () string {
return "nil"
}
func ( *NilNode) ( *strings.Builder) {
.WriteString(.String())
}
func ( *NilNode) () *Tree {
return .tr
}
func ( *NilNode) () Node {
return .tr.newNil(.Pos)
}
type FieldNode struct {
NodeType
Pos
tr *Tree
Ident []string
}
func ( *Tree) ( Pos, string) *FieldNode {
return &FieldNode{tr: , NodeType: NodeField, Pos: , Ident: strings.Split([1:], ".")}
}
func ( *FieldNode) () string {
var strings.Builder
.writeTo(&)
return .String()
}
func ( *FieldNode) ( *strings.Builder) {
for , := range .Ident {
.WriteByte('.')
.WriteString()
}
}
func ( *FieldNode) () *Tree {
return .tr
}
func ( *FieldNode) () Node {
return &FieldNode{tr: .tr, NodeType: NodeField, Pos: .Pos, Ident: append([]string{}, .Ident...)}
}
type ChainNode struct {
NodeType
Pos
tr *Tree
Node Node
Field []string
}
func ( *Tree) ( Pos, Node) *ChainNode {
return &ChainNode{tr: , NodeType: NodeChain, Pos: , Node: }
}
func ( *ChainNode) ( string) {
if len() == 0 || [0] != '.' {
panic("no dot in field")
}
= [1:]
if == "" {
panic("empty field")
}
.Field = append(.Field, )
}
func ( *ChainNode) () string {
var strings.Builder
.writeTo(&)
return .String()
}
func ( *ChainNode) ( *strings.Builder) {
if , := .Node.(*PipeNode); {
.WriteByte('(')
.Node.writeTo()
.WriteByte(')')
} else {
.Node.writeTo()
}
for , := range .Field {
.WriteByte('.')
.WriteString()
}
}
func ( *ChainNode) () *Tree {
return .tr
}
func ( *ChainNode) () Node {
return &ChainNode{tr: .tr, NodeType: NodeChain, Pos: .Pos, Node: .Node, Field: append([]string{}, .Field...)}
}
type BoolNode struct {
NodeType
Pos
tr *Tree
True bool
}
func ( *Tree) ( Pos, bool) *BoolNode {
return &BoolNode{tr: , NodeType: NodeBool, Pos: , True: }
}
func ( *BoolNode) () string {
if .True {
return "true"
}
return "false"
}
func ( *BoolNode) ( *strings.Builder) {
.WriteString(.String())
}
func ( *BoolNode) () *Tree {
return .tr
}
func ( *BoolNode) () Node {
return .tr.newBool(.Pos, .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 ( *Tree) ( Pos, string, itemType) (*NumberNode, error) {
:= &NumberNode{tr: , NodeType: NodeNumber, Pos: , Text: }
switch {
case itemCharConstant:
, , , := strconv.UnquoteChar([1:], [0])
if != nil {
return nil,
}
if != "'" {
return nil, fmt.Errorf("malformed character constant: %s", )
}
.Int64 = int64()
.IsInt = true
.Uint64 = uint64()
.IsUint = true
.Float64 = float64()
.IsFloat = true
return , nil
case itemComplex:
if , := fmt.Sscan(, &.Complex128); != nil {
return nil,
}
.IsComplex = true
.simplifyComplex()
return , nil
}
if len() > 0 && [len()-1] == 'i' {
, := strconv.ParseFloat([:len()-1], 64)
if == nil {
.IsComplex = true
.Complex128 = complex(0, )
.simplifyComplex()
return , nil
}
}
, := strconv.ParseUint(, 0, 64)
if == nil {
.IsUint = true
.Uint64 =
}
, := strconv.ParseInt(, 0, 64)
if == nil {
.IsInt = true
.Int64 =
if == 0 {
.IsUint = true
.Uint64 =
}
}
if .IsInt {
.IsFloat = true
.Float64 = float64(.Int64)
} else if .IsUint {
.IsFloat = true
.Float64 = float64(.Uint64)
} else {
, := strconv.ParseFloat(, 64)
if == nil {
if !strings.ContainsAny(, ".eEpP") {
return nil, fmt.Errorf("integer overflow: %q", )
}
.IsFloat = true
.Float64 =
if !.IsInt && float64(int64()) == {
.IsInt = true
.Int64 = int64()
}
if !.IsUint && float64(uint64()) == {
.IsUint = true
.Uint64 = uint64()
}
}
}
if !.IsInt && !.IsUint && !.IsFloat {
return nil, fmt.Errorf("illegal number syntax: %q", )
}
return , nil
}
func ( *NumberNode) () {
.IsFloat = imag(.Complex128) == 0
if .IsFloat {
.Float64 = real(.Complex128)
.IsInt = float64(int64(.Float64)) == .Float64
if .IsInt {
.Int64 = int64(.Float64)
}
.IsUint = float64(uint64(.Float64)) == .Float64
if .IsUint {
.Uint64 = uint64(.Float64)
}
}
}
func ( *NumberNode) () string {
return .Text
}
func ( *NumberNode) ( *strings.Builder) {
.WriteString(.String())
}
func ( *NumberNode) () *Tree {
return .tr
}
func ( *NumberNode) () Node {
:= new(NumberNode)
* = *
return
}
type StringNode struct {
NodeType
Pos
tr *Tree
Quoted string
Text string
}
func ( *Tree) ( Pos, , string) *StringNode {
return &StringNode{tr: , NodeType: NodeString, Pos: , Quoted: , Text: }
}
func ( *StringNode) () string {
return .Quoted
}
func ( *StringNode) ( *strings.Builder) {
.WriteString(.String())
}
func ( *StringNode) () *Tree {
return .tr
}
func ( *StringNode) () Node {
return .tr.newString(.Pos, .Quoted, .Text)
}
type endNode struct {
NodeType
Pos
tr *Tree
}
func ( *Tree) ( Pos) *endNode {
return &endNode{tr: , NodeType: nodeEnd, Pos: }
}
func ( *endNode) () string {
return "{{end}}"
}
func ( *endNode) ( *strings.Builder) {
.WriteString(.String())
}
func ( *endNode) () *Tree {
return .tr
}
func ( *endNode) () Node {
return .tr.newEnd(.Pos)
}
type elseNode struct {
NodeType
Pos
tr *Tree
Line int
}
func ( *Tree) ( Pos, int) *elseNode {
return &elseNode{tr: , NodeType: nodeElse, Pos: , Line: }
}
func ( *elseNode) () NodeType {
return nodeElse
}
func ( *elseNode) () string {
return "{{else}}"
}
func ( *elseNode) ( *strings.Builder) {
.WriteString(.String())
}
func ( *elseNode) () *Tree {
return .tr
}
func ( *elseNode) () Node {
return .tr.newElse(.Pos, .Line)
}
type BranchNode struct {
NodeType
Pos
tr *Tree
Line int
Pipe *PipeNode
List *ListNode
ElseList *ListNode
}
func ( *BranchNode) () string {
var strings.Builder
.writeTo(&)
return .String()
}
func ( *BranchNode) ( *strings.Builder) {
:= ""
switch .NodeType {
case NodeIf:
= "if"
case NodeRange:
= "range"
case NodeWith:
= "with"
default:
panic("unknown branch type")
}
.WriteString("{{")
.WriteString()
.WriteByte(' ')
.Pipe.writeTo()
.WriteString("}}")
.List.writeTo()
if .ElseList != nil {
.WriteString("{{else}}")
.ElseList.writeTo()
}
.WriteString("{{end}}")
}
func ( *BranchNode) () *Tree {
return .tr
}
func ( *BranchNode) () Node {
switch .NodeType {
case NodeIf:
return .tr.newIf(.Pos, .Line, .Pipe, .List, .ElseList)
case NodeRange:
return .tr.newRange(.Pos, .Line, .Pipe, .List, .ElseList)
case NodeWith:
return .tr.newWith(.Pos, .Line, .Pipe, .List, .ElseList)
default:
panic("unknown branch type")
}
}
type IfNode struct {
BranchNode
}
func ( *Tree) ( Pos, int, *PipeNode, , *ListNode) *IfNode {
return &IfNode{BranchNode{tr: , NodeType: NodeIf, Pos: , Line: , Pipe: , List: , ElseList: }}
}
func ( *IfNode) () Node {
return .tr.newIf(.Pos, .Line, .Pipe.CopyPipe(), .List.CopyList(), .ElseList.CopyList())
}
type BreakNode struct {
tr *Tree
NodeType
Pos
Line int
}
func ( *Tree) ( Pos, int) *BreakNode {
return &BreakNode{tr: , NodeType: NodeBreak, Pos: , Line: }
}
func ( *BreakNode) () Node { return .tr.newBreak(.Pos, .Line) }
func ( *BreakNode) () string { return "{{break}}" }
func ( *BreakNode) () *Tree { return .tr }
func ( *BreakNode) ( *strings.Builder) { .WriteString("{{break}}") }
type ContinueNode struct {
tr *Tree
NodeType
Pos
Line int
}
func ( *Tree) ( Pos, int) *ContinueNode {
return &ContinueNode{tr: , NodeType: NodeContinue, Pos: , Line: }
}
func ( *ContinueNode) () Node { return .tr.newContinue(.Pos, .Line) }
func ( *ContinueNode) () string { return "{{continue}}" }
func ( *ContinueNode) () *Tree { return .tr }
func ( *ContinueNode) ( *strings.Builder) { .WriteString("{{continue}}") }
type RangeNode struct {
BranchNode
}
func ( *Tree) ( Pos, int, *PipeNode, , *ListNode) *RangeNode {
return &RangeNode{BranchNode{tr: , NodeType: NodeRange, Pos: , Line: , Pipe: , List: , ElseList: }}
}
func ( *RangeNode) () Node {
return .tr.newRange(.Pos, .Line, .Pipe.CopyPipe(), .List.CopyList(), .ElseList.CopyList())
}
type WithNode struct {
BranchNode
}
func ( *Tree) ( Pos, int, *PipeNode, , *ListNode) *WithNode {
return &WithNode{BranchNode{tr: , NodeType: NodeWith, Pos: , Line: , Pipe: , List: , ElseList: }}
}
func ( *WithNode) () Node {
return .tr.newWith(.Pos, .Line, .Pipe.CopyPipe(), .List.CopyList(), .ElseList.CopyList())
}
type TemplateNode struct {
NodeType
Pos
tr *Tree
Line int
Name string
Pipe *PipeNode
}
func ( *Tree) ( Pos, int, string, *PipeNode) *TemplateNode {
return &TemplateNode{tr: , NodeType: NodeTemplate, Pos: , Line: , Name: , Pipe: }
}
func ( *TemplateNode) () string {
var strings.Builder
.writeTo(&)
return .String()
}
func ( *TemplateNode) ( *strings.Builder) {
.WriteString("{{template ")
.WriteString(strconv.Quote(.Name))
if .Pipe != nil {
.WriteByte(' ')
.Pipe.writeTo()
}
.WriteString("}}")
}
func ( *TemplateNode) () *Tree {
return .tr
}
func ( *TemplateNode) () Node {
return .tr.newTemplate(.Pos, .Line, .Name, .Pipe.CopyPipe())
}