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 ())
}
The pages are generated with Golds v0.7.0-preview . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .