You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2163 lines
50 KiB
2163 lines
50 KiB
3 years ago
|
package ast
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"math"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
|
||
|
"github.com/goccy/go-yaml/token"
|
||
|
"golang.org/x/xerrors"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
ErrInvalidTokenType = xerrors.New("invalid token type")
|
||
|
ErrInvalidAnchorName = xerrors.New("invalid anchor name")
|
||
|
ErrInvalidAliasName = xerrors.New("invalid alias name")
|
||
|
)
|
||
|
|
||
|
// NodeType type identifier of node
|
||
|
type NodeType int
|
||
|
|
||
|
const (
|
||
|
// UnknownNodeType type identifier for default
|
||
|
UnknownNodeType NodeType = iota
|
||
|
// DocumentType type identifier for document node
|
||
|
DocumentType
|
||
|
// NullType type identifier for null node
|
||
|
NullType
|
||
|
// BoolType type identifier for boolean node
|
||
|
BoolType
|
||
|
// IntegerType type identifier for integer node
|
||
|
IntegerType
|
||
|
// FloatType type identifier for float node
|
||
|
FloatType
|
||
|
// InfinityType type identifier for infinity node
|
||
|
InfinityType
|
||
|
// NanType type identifier for nan node
|
||
|
NanType
|
||
|
// StringType type identifier for string node
|
||
|
StringType
|
||
|
// MergeKeyType type identifier for merge key node
|
||
|
MergeKeyType
|
||
|
// LiteralType type identifier for literal node
|
||
|
LiteralType
|
||
|
// MappingType type identifier for mapping node
|
||
|
MappingType
|
||
|
// MappingKeyType type identifier for mapping key node
|
||
|
MappingKeyType
|
||
|
// MappingValueType type identifier for mapping value node
|
||
|
MappingValueType
|
||
|
// SequenceType type identifier for sequence node
|
||
|
SequenceType
|
||
|
// AnchorType type identifier for anchor node
|
||
|
AnchorType
|
||
|
// AliasType type identifier for alias node
|
||
|
AliasType
|
||
|
// DirectiveType type identifier for directive node
|
||
|
DirectiveType
|
||
|
// TagType type identifier for tag node
|
||
|
TagType
|
||
|
// CommentType type identifier for comment node
|
||
|
CommentType
|
||
|
// CommentGroupType type identifier for comment group node
|
||
|
CommentGroupType
|
||
|
)
|
||
|
|
||
|
// String node type identifier to text
|
||
|
func (t NodeType) String() string {
|
||
|
switch t {
|
||
|
case UnknownNodeType:
|
||
|
return "UnknownNode"
|
||
|
case DocumentType:
|
||
|
return "Document"
|
||
|
case NullType:
|
||
|
return "Null"
|
||
|
case BoolType:
|
||
|
return "Bool"
|
||
|
case IntegerType:
|
||
|
return "Integer"
|
||
|
case FloatType:
|
||
|
return "Float"
|
||
|
case InfinityType:
|
||
|
return "Infinity"
|
||
|
case NanType:
|
||
|
return "Nan"
|
||
|
case StringType:
|
||
|
return "String"
|
||
|
case MergeKeyType:
|
||
|
return "MergeKey"
|
||
|
case LiteralType:
|
||
|
return "Literal"
|
||
|
case MappingType:
|
||
|
return "Mapping"
|
||
|
case MappingKeyType:
|
||
|
return "MappingKey"
|
||
|
case MappingValueType:
|
||
|
return "MappingValue"
|
||
|
case SequenceType:
|
||
|
return "Sequence"
|
||
|
case AnchorType:
|
||
|
return "Anchor"
|
||
|
case AliasType:
|
||
|
return "Alias"
|
||
|
case DirectiveType:
|
||
|
return "Directive"
|
||
|
case TagType:
|
||
|
return "Tag"
|
||
|
case CommentType:
|
||
|
return "Comment"
|
||
|
case CommentGroupType:
|
||
|
return "CommentGroup"
|
||
|
}
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// String node type identifier to YAML Structure name
|
||
|
// based on https://yaml.org/spec/1.2/spec.html
|
||
|
func (t NodeType) YAMLName() string {
|
||
|
switch t {
|
||
|
case UnknownNodeType:
|
||
|
return "unknown"
|
||
|
case DocumentType:
|
||
|
return "document"
|
||
|
case NullType:
|
||
|
return "null"
|
||
|
case BoolType:
|
||
|
return "boolean"
|
||
|
case IntegerType:
|
||
|
return "int"
|
||
|
case FloatType:
|
||
|
return "float"
|
||
|
case InfinityType:
|
||
|
return "inf"
|
||
|
case NanType:
|
||
|
return "nan"
|
||
|
case StringType:
|
||
|
return "string"
|
||
|
case MergeKeyType:
|
||
|
return "merge key"
|
||
|
case LiteralType:
|
||
|
return "scalar"
|
||
|
case MappingType:
|
||
|
return "mapping"
|
||
|
case MappingKeyType:
|
||
|
return "key"
|
||
|
case MappingValueType:
|
||
|
return "value"
|
||
|
case SequenceType:
|
||
|
return "sequence"
|
||
|
case AnchorType:
|
||
|
return "anchor"
|
||
|
case AliasType:
|
||
|
return "alias"
|
||
|
case DirectiveType:
|
||
|
return "directive"
|
||
|
case TagType:
|
||
|
return "tag"
|
||
|
case CommentType:
|
||
|
return "comment"
|
||
|
case CommentGroupType:
|
||
|
return "comment"
|
||
|
}
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// Node type of node
|
||
|
type Node interface {
|
||
|
io.Reader
|
||
|
// String node to text
|
||
|
String() string
|
||
|
// GetToken returns token instance
|
||
|
GetToken() *token.Token
|
||
|
// Type returns type of node
|
||
|
Type() NodeType
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
AddColumn(int)
|
||
|
// SetComment set comment token to node
|
||
|
SetComment(*CommentGroupNode) error
|
||
|
// Comment returns comment token instance
|
||
|
GetComment() *CommentGroupNode
|
||
|
// GetPath returns YAMLPath for the current node
|
||
|
GetPath() string
|
||
|
// SetPath set YAMLPath for the current node
|
||
|
SetPath(string)
|
||
|
// MarshalYAML
|
||
|
MarshalYAML() ([]byte, error)
|
||
|
// already read length
|
||
|
readLen() int
|
||
|
// append read length
|
||
|
addReadLen(int)
|
||
|
// clean read length
|
||
|
clearLen()
|
||
|
// String node to text without comment
|
||
|
stringWithoutComment() string
|
||
|
}
|
||
|
|
||
|
// ScalarNode type for scalar node
|
||
|
type ScalarNode interface {
|
||
|
Node
|
||
|
GetValue() interface{}
|
||
|
}
|
||
|
|
||
|
type BaseNode struct {
|
||
|
Path string
|
||
|
Comment *CommentGroupNode
|
||
|
read int
|
||
|
}
|
||
|
|
||
|
func addCommentString(base string, node *CommentGroupNode) string {
|
||
|
return fmt.Sprintf("%s %s", base, node.String())
|
||
|
}
|
||
|
|
||
|
func (n *BaseNode) readLen() int {
|
||
|
return n.read
|
||
|
}
|
||
|
|
||
|
func (n *BaseNode) clearLen() {
|
||
|
n.read = 0
|
||
|
}
|
||
|
|
||
|
func (n *BaseNode) addReadLen(len int) {
|
||
|
n.read += len
|
||
|
}
|
||
|
|
||
|
// GetPath returns YAMLPath for the current node.
|
||
|
func (n *BaseNode) GetPath() string {
|
||
|
if n == nil {
|
||
|
return ""
|
||
|
}
|
||
|
return n.Path
|
||
|
}
|
||
|
|
||
|
// SetPath set YAMLPath for the current node.
|
||
|
func (n *BaseNode) SetPath(path string) {
|
||
|
if n == nil {
|
||
|
return
|
||
|
}
|
||
|
n.Path = path
|
||
|
}
|
||
|
|
||
|
// GetComment returns comment token instance
|
||
|
func (n *BaseNode) GetComment() *CommentGroupNode {
|
||
|
return n.Comment
|
||
|
}
|
||
|
|
||
|
// SetComment set comment token
|
||
|
func (n *BaseNode) SetComment(node *CommentGroupNode) error {
|
||
|
n.Comment = node
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func min(a, b int) int {
|
||
|
if a < b {
|
||
|
return a
|
||
|
}
|
||
|
return b
|
||
|
}
|
||
|
|
||
|
func readNode(p []byte, node Node) (int, error) {
|
||
|
s := node.String()
|
||
|
readLen := node.readLen()
|
||
|
remain := len(s) - readLen
|
||
|
if remain == 0 {
|
||
|
node.clearLen()
|
||
|
return 0, io.EOF
|
||
|
}
|
||
|
size := min(remain, len(p))
|
||
|
for idx, b := range s[readLen : readLen+size] {
|
||
|
p[idx] = byte(b)
|
||
|
}
|
||
|
node.addReadLen(size)
|
||
|
return size, nil
|
||
|
}
|
||
|
|
||
|
// Null create node for null value
|
||
|
func Null(tk *token.Token) Node {
|
||
|
return &NullNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Bool create node for boolean value
|
||
|
func Bool(tk *token.Token) Node {
|
||
|
b, _ := strconv.ParseBool(tk.Value)
|
||
|
return &BoolNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
Value: b,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Integer create node for integer value
|
||
|
func Integer(tk *token.Token) Node {
|
||
|
value := removeUnderScoreFromNumber(tk.Value)
|
||
|
switch tk.Type {
|
||
|
case token.BinaryIntegerType:
|
||
|
// skip two characters because binary token starts with '0b'
|
||
|
skipCharacterNum := 2
|
||
|
negativePrefix := ""
|
||
|
if value[0] == '-' {
|
||
|
skipCharacterNum++
|
||
|
negativePrefix = "-"
|
||
|
}
|
||
|
if len(negativePrefix) > 0 {
|
||
|
i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 2, 64)
|
||
|
return &IntegerNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
Value: i,
|
||
|
}
|
||
|
}
|
||
|
i, _ := strconv.ParseUint(negativePrefix+value[skipCharacterNum:], 2, 64)
|
||
|
return &IntegerNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
Value: i,
|
||
|
}
|
||
|
case token.OctetIntegerType:
|
||
|
// octet token starts with '0o' or '-0o' or '0' or '-0'
|
||
|
skipCharacterNum := 1
|
||
|
negativePrefix := ""
|
||
|
if value[0] == '-' {
|
||
|
skipCharacterNum++
|
||
|
if len(value) > 2 && value[2] == 'o' {
|
||
|
skipCharacterNum++
|
||
|
}
|
||
|
negativePrefix = "-"
|
||
|
} else {
|
||
|
if value[1] == 'o' {
|
||
|
skipCharacterNum++
|
||
|
}
|
||
|
}
|
||
|
if len(negativePrefix) > 0 {
|
||
|
i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 8, 64)
|
||
|
return &IntegerNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
Value: i,
|
||
|
}
|
||
|
}
|
||
|
i, _ := strconv.ParseUint(value[skipCharacterNum:], 8, 64)
|
||
|
return &IntegerNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
Value: i,
|
||
|
}
|
||
|
case token.HexIntegerType:
|
||
|
// hex token starts with '0x' or '-0x'
|
||
|
skipCharacterNum := 2
|
||
|
negativePrefix := ""
|
||
|
if value[0] == '-' {
|
||
|
skipCharacterNum++
|
||
|
negativePrefix = "-"
|
||
|
}
|
||
|
if len(negativePrefix) > 0 {
|
||
|
i, _ := strconv.ParseInt(negativePrefix+value[skipCharacterNum:], 16, 64)
|
||
|
return &IntegerNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
Value: i,
|
||
|
}
|
||
|
}
|
||
|
i, _ := strconv.ParseUint(value[skipCharacterNum:], 16, 64)
|
||
|
return &IntegerNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
Value: i,
|
||
|
}
|
||
|
}
|
||
|
if value[0] == '-' || value[0] == '+' {
|
||
|
i, _ := strconv.ParseInt(value, 10, 64)
|
||
|
return &IntegerNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
Value: i,
|
||
|
}
|
||
|
}
|
||
|
i, _ := strconv.ParseUint(value, 10, 64)
|
||
|
return &IntegerNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
Value: i,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Float create node for float value
|
||
|
func Float(tk *token.Token) Node {
|
||
|
f, _ := strconv.ParseFloat(removeUnderScoreFromNumber(tk.Value), 64)
|
||
|
return &FloatNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
Value: f,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Infinity create node for .inf or -.inf value
|
||
|
func Infinity(tk *token.Token) *InfinityNode {
|
||
|
node := &InfinityNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
}
|
||
|
switch tk.Value {
|
||
|
case ".inf", ".Inf", ".INF":
|
||
|
node.Value = math.Inf(0)
|
||
|
case "-.inf", "-.Inf", "-.INF":
|
||
|
node.Value = math.Inf(-1)
|
||
|
}
|
||
|
return node
|
||
|
}
|
||
|
|
||
|
// Nan create node for .nan value
|
||
|
func Nan(tk *token.Token) *NanNode {
|
||
|
return &NanNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String create node for string value
|
||
|
func String(tk *token.Token) *StringNode {
|
||
|
return &StringNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
Value: tk.Value,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Comment create node for comment
|
||
|
func Comment(tk *token.Token) *CommentNode {
|
||
|
return &CommentNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func CommentGroup(comments []*token.Token) *CommentGroupNode {
|
||
|
nodes := []*CommentNode{}
|
||
|
for _, comment := range comments {
|
||
|
nodes = append(nodes, Comment(comment))
|
||
|
}
|
||
|
return &CommentGroupNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Comments: nodes,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MergeKey create node for merge key ( << )
|
||
|
func MergeKey(tk *token.Token) *MergeKeyNode {
|
||
|
return &MergeKeyNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Token: tk,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Mapping create node for map
|
||
|
func Mapping(tk *token.Token, isFlowStyle bool, values ...*MappingValueNode) *MappingNode {
|
||
|
node := &MappingNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Start: tk,
|
||
|
IsFlowStyle: isFlowStyle,
|
||
|
Values: []*MappingValueNode{},
|
||
|
}
|
||
|
node.Values = append(node.Values, values...)
|
||
|
return node
|
||
|
}
|
||
|
|
||
|
// MappingValue create node for mapping value
|
||
|
func MappingValue(tk *token.Token, key Node, value Node) *MappingValueNode {
|
||
|
return &MappingValueNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Start: tk,
|
||
|
Key: key,
|
||
|
Value: value,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MappingKey create node for map key ( '?' ).
|
||
|
func MappingKey(tk *token.Token) *MappingKeyNode {
|
||
|
return &MappingKeyNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Start: tk,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Sequence create node for sequence
|
||
|
func Sequence(tk *token.Token, isFlowStyle bool) *SequenceNode {
|
||
|
return &SequenceNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Start: tk,
|
||
|
IsFlowStyle: isFlowStyle,
|
||
|
Values: []Node{},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func Anchor(tk *token.Token) *AnchorNode {
|
||
|
return &AnchorNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Start: tk,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func Alias(tk *token.Token) *AliasNode {
|
||
|
return &AliasNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Start: tk,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func Document(tk *token.Token, body Node) *DocumentNode {
|
||
|
return &DocumentNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Start: tk,
|
||
|
Body: body,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func Directive(tk *token.Token) *DirectiveNode {
|
||
|
return &DirectiveNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Start: tk,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func Literal(tk *token.Token) *LiteralNode {
|
||
|
return &LiteralNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Start: tk,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func Tag(tk *token.Token) *TagNode {
|
||
|
return &TagNode{
|
||
|
BaseNode: &BaseNode{},
|
||
|
Start: tk,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// File contains all documents in YAML file
|
||
|
type File struct {
|
||
|
Name string
|
||
|
Docs []*DocumentNode
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (f *File) Read(p []byte) (int, error) {
|
||
|
for _, doc := range f.Docs {
|
||
|
n, err := doc.Read(p)
|
||
|
if err == io.EOF {
|
||
|
continue
|
||
|
}
|
||
|
return n, nil
|
||
|
}
|
||
|
return 0, io.EOF
|
||
|
}
|
||
|
|
||
|
// String all documents to text
|
||
|
func (f *File) String() string {
|
||
|
docs := []string{}
|
||
|
for _, doc := range f.Docs {
|
||
|
docs = append(docs, doc.String())
|
||
|
}
|
||
|
return strings.Join(docs, "\n")
|
||
|
}
|
||
|
|
||
|
func (f *File) stringWithoutComment() string {
|
||
|
return f.String()
|
||
|
}
|
||
|
|
||
|
// DocumentNode type of Document
|
||
|
type DocumentNode struct {
|
||
|
*BaseNode
|
||
|
Start *token.Token // position of DocumentHeader ( `---` )
|
||
|
End *token.Token // position of DocumentEnd ( `...` )
|
||
|
Body Node
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (d *DocumentNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, d)
|
||
|
}
|
||
|
|
||
|
// Type returns DocumentNodeType
|
||
|
func (d *DocumentNode) Type() NodeType { return DocumentType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (d *DocumentNode) GetToken() *token.Token {
|
||
|
return d.Body.GetToken()
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (d *DocumentNode) AddColumn(col int) {
|
||
|
if d.Body != nil {
|
||
|
d.Body.AddColumn(col)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String document to text
|
||
|
func (d *DocumentNode) String() string {
|
||
|
doc := []string{}
|
||
|
if d.Start != nil {
|
||
|
doc = append(doc, d.Start.Value)
|
||
|
}
|
||
|
doc = append(doc, d.Body.String())
|
||
|
if d.End != nil {
|
||
|
doc = append(doc, d.End.Value)
|
||
|
}
|
||
|
return strings.Join(doc, "\n")
|
||
|
}
|
||
|
|
||
|
func (d *DocumentNode) stringWithoutComment() string {
|
||
|
return d.String()
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (d *DocumentNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(d.String()), nil
|
||
|
}
|
||
|
|
||
|
func removeUnderScoreFromNumber(num string) string {
|
||
|
return strings.ReplaceAll(num, "_", "")
|
||
|
}
|
||
|
|
||
|
// NullNode type of null node
|
||
|
type NullNode struct {
|
||
|
*BaseNode
|
||
|
Token *token.Token
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *NullNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns NullType
|
||
|
func (n *NullNode) Type() NodeType { return NullType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *NullNode) GetToken() *token.Token {
|
||
|
return n.Token
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *NullNode) AddColumn(col int) {
|
||
|
n.Token.AddColumn(col)
|
||
|
}
|
||
|
|
||
|
// GetValue returns nil value
|
||
|
func (n *NullNode) GetValue() interface{} {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// String returns `null` text
|
||
|
func (n *NullNode) String() string {
|
||
|
if n.Comment != nil {
|
||
|
return fmt.Sprintf("null %s", n.Comment.String())
|
||
|
}
|
||
|
return n.stringWithoutComment()
|
||
|
}
|
||
|
|
||
|
func (n *NullNode) stringWithoutComment() string {
|
||
|
return "null"
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *NullNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// IntegerNode type of integer node
|
||
|
type IntegerNode struct {
|
||
|
*BaseNode
|
||
|
Token *token.Token
|
||
|
Value interface{} // int64 or uint64 value
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *IntegerNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns IntegerType
|
||
|
func (n *IntegerNode) Type() NodeType { return IntegerType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *IntegerNode) GetToken() *token.Token {
|
||
|
return n.Token
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *IntegerNode) AddColumn(col int) {
|
||
|
n.Token.AddColumn(col)
|
||
|
}
|
||
|
|
||
|
// GetValue returns int64 value
|
||
|
func (n *IntegerNode) GetValue() interface{} {
|
||
|
return n.Value
|
||
|
}
|
||
|
|
||
|
// String int64 to text
|
||
|
func (n *IntegerNode) String() string {
|
||
|
if n.Comment != nil {
|
||
|
return addCommentString(n.Token.Value, n.Comment)
|
||
|
}
|
||
|
return n.stringWithoutComment()
|
||
|
}
|
||
|
|
||
|
func (n *IntegerNode) stringWithoutComment() string {
|
||
|
return n.Token.Value
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *IntegerNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// FloatNode type of float node
|
||
|
type FloatNode struct {
|
||
|
*BaseNode
|
||
|
Token *token.Token
|
||
|
Precision int
|
||
|
Value float64
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *FloatNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns FloatType
|
||
|
func (n *FloatNode) Type() NodeType { return FloatType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *FloatNode) GetToken() *token.Token {
|
||
|
return n.Token
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *FloatNode) AddColumn(col int) {
|
||
|
n.Token.AddColumn(col)
|
||
|
}
|
||
|
|
||
|
// GetValue returns float64 value
|
||
|
func (n *FloatNode) GetValue() interface{} {
|
||
|
return n.Value
|
||
|
}
|
||
|
|
||
|
// String float64 to text
|
||
|
func (n *FloatNode) String() string {
|
||
|
if n.Comment != nil {
|
||
|
return addCommentString(n.Token.Value, n.Comment)
|
||
|
}
|
||
|
return n.stringWithoutComment()
|
||
|
}
|
||
|
|
||
|
func (n *FloatNode) stringWithoutComment() string {
|
||
|
return n.Token.Value
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *FloatNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// StringNode type of string node
|
||
|
type StringNode struct {
|
||
|
*BaseNode
|
||
|
Token *token.Token
|
||
|
Value string
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *StringNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns StringType
|
||
|
func (n *StringNode) Type() NodeType { return StringType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *StringNode) GetToken() *token.Token {
|
||
|
return n.Token
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *StringNode) AddColumn(col int) {
|
||
|
n.Token.AddColumn(col)
|
||
|
}
|
||
|
|
||
|
// GetValue returns string value
|
||
|
func (n *StringNode) GetValue() interface{} {
|
||
|
return n.Value
|
||
|
}
|
||
|
|
||
|
// escapeSingleQuote escapes s to a single quoted scalar.
|
||
|
// https://yaml.org/spec/1.2.2/#732-single-quoted-style
|
||
|
func escapeSingleQuote(s string) string {
|
||
|
var sb strings.Builder
|
||
|
growLen := len(s) + // s includes also one ' from the doubled pair
|
||
|
2 + // opening and closing '
|
||
|
strings.Count(s, "'") // ' added by ReplaceAll
|
||
|
sb.Grow(growLen)
|
||
|
sb.WriteString("'")
|
||
|
sb.WriteString(strings.ReplaceAll(s, "'", "''"))
|
||
|
sb.WriteString("'")
|
||
|
return sb.String()
|
||
|
}
|
||
|
|
||
|
// String string value to text with quote or literal header if required
|
||
|
func (n *StringNode) String() string {
|
||
|
switch n.Token.Type {
|
||
|
case token.SingleQuoteType:
|
||
|
quoted := escapeSingleQuote(n.Value)
|
||
|
if n.Comment != nil {
|
||
|
return addCommentString(quoted, n.Comment)
|
||
|
}
|
||
|
return quoted
|
||
|
case token.DoubleQuoteType:
|
||
|
quoted := strconv.Quote(n.Value)
|
||
|
if n.Comment != nil {
|
||
|
return addCommentString(quoted, n.Comment)
|
||
|
}
|
||
|
return quoted
|
||
|
}
|
||
|
|
||
|
lbc := token.DetectLineBreakCharacter(n.Value)
|
||
|
if strings.Contains(n.Value, lbc) {
|
||
|
// This block assumes that the line breaks in this inside scalar content and the Outside scalar content are the same.
|
||
|
// It works mostly, but inconsistencies occur if line break characters are mixed.
|
||
|
header := token.LiteralBlockHeader(n.Value)
|
||
|
space := strings.Repeat(" ", n.Token.Position.Column-1)
|
||
|
values := []string{}
|
||
|
for _, v := range strings.Split(n.Value, lbc) {
|
||
|
values = append(values, fmt.Sprintf("%s %s", space, v))
|
||
|
}
|
||
|
block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space))
|
||
|
return fmt.Sprintf("%s%s%s", header, lbc, block)
|
||
|
} else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') {
|
||
|
return fmt.Sprintf(`'%s'`, n.Value)
|
||
|
}
|
||
|
if n.Comment != nil {
|
||
|
return addCommentString(n.Value, n.Comment)
|
||
|
}
|
||
|
return n.Value
|
||
|
}
|
||
|
|
||
|
func (n *StringNode) stringWithoutComment() string {
|
||
|
switch n.Token.Type {
|
||
|
case token.SingleQuoteType:
|
||
|
quoted := fmt.Sprintf(`'%s'`, n.Value)
|
||
|
return quoted
|
||
|
case token.DoubleQuoteType:
|
||
|
quoted := strconv.Quote(n.Value)
|
||
|
return quoted
|
||
|
}
|
||
|
|
||
|
lbc := token.DetectLineBreakCharacter(n.Value)
|
||
|
if strings.Contains(n.Value, lbc) {
|
||
|
// This block assumes that the line breaks in this inside scalar content and the Outside scalar content are the same.
|
||
|
// It works mostly, but inconsistencies occur if line break characters are mixed.
|
||
|
header := token.LiteralBlockHeader(n.Value)
|
||
|
space := strings.Repeat(" ", n.Token.Position.Column-1)
|
||
|
values := []string{}
|
||
|
for _, v := range strings.Split(n.Value, lbc) {
|
||
|
values = append(values, fmt.Sprintf("%s %s", space, v))
|
||
|
}
|
||
|
block := strings.TrimSuffix(strings.TrimSuffix(strings.Join(values, lbc), fmt.Sprintf("%s %s", lbc, space)), fmt.Sprintf(" %s", space))
|
||
|
return fmt.Sprintf("%s%s%s", header, lbc, block)
|
||
|
} else if len(n.Value) > 0 && (n.Value[0] == '{' || n.Value[0] == '[') {
|
||
|
return fmt.Sprintf(`'%s'`, n.Value)
|
||
|
}
|
||
|
return n.Value
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *StringNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// LiteralNode type of literal node
|
||
|
type LiteralNode struct {
|
||
|
*BaseNode
|
||
|
Start *token.Token
|
||
|
Value *StringNode
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *LiteralNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns LiteralType
|
||
|
func (n *LiteralNode) Type() NodeType { return LiteralType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *LiteralNode) GetToken() *token.Token {
|
||
|
return n.Start
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *LiteralNode) AddColumn(col int) {
|
||
|
n.Start.AddColumn(col)
|
||
|
if n.Value != nil {
|
||
|
n.Value.AddColumn(col)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// GetValue returns string value
|
||
|
func (n *LiteralNode) GetValue() interface{} {
|
||
|
return n.String()
|
||
|
}
|
||
|
|
||
|
// String literal to text
|
||
|
func (n *LiteralNode) String() string {
|
||
|
origin := n.Value.GetToken().Origin
|
||
|
lit := strings.TrimRight(strings.TrimRight(origin, " "), "\n")
|
||
|
if n.Comment != nil {
|
||
|
return fmt.Sprintf("%s %s\n%s", n.Start.Value, n.Comment.String(), lit)
|
||
|
}
|
||
|
return fmt.Sprintf("%s\n%s", n.Start.Value, lit)
|
||
|
}
|
||
|
|
||
|
func (n *LiteralNode) stringWithoutComment() string {
|
||
|
return n.String()
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *LiteralNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// MergeKeyNode type of merge key node
|
||
|
type MergeKeyNode struct {
|
||
|
*BaseNode
|
||
|
Token *token.Token
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *MergeKeyNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns MergeKeyType
|
||
|
func (n *MergeKeyNode) Type() NodeType { return MergeKeyType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *MergeKeyNode) GetToken() *token.Token {
|
||
|
return n.Token
|
||
|
}
|
||
|
|
||
|
// GetValue returns '<<' value
|
||
|
func (n *MergeKeyNode) GetValue() interface{} {
|
||
|
return n.Token.Value
|
||
|
}
|
||
|
|
||
|
// String returns '<<' value
|
||
|
func (n *MergeKeyNode) String() string {
|
||
|
return n.Token.Value
|
||
|
}
|
||
|
|
||
|
func (n *MergeKeyNode) stringWithoutComment() string {
|
||
|
return n.Token.Value
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *MergeKeyNode) AddColumn(col int) {
|
||
|
n.Token.AddColumn(col)
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *MergeKeyNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// BoolNode type of boolean node
|
||
|
type BoolNode struct {
|
||
|
*BaseNode
|
||
|
Token *token.Token
|
||
|
Value bool
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *BoolNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns BoolType
|
||
|
func (n *BoolNode) Type() NodeType { return BoolType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *BoolNode) GetToken() *token.Token {
|
||
|
return n.Token
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *BoolNode) AddColumn(col int) {
|
||
|
n.Token.AddColumn(col)
|
||
|
}
|
||
|
|
||
|
// GetValue returns boolean value
|
||
|
func (n *BoolNode) GetValue() interface{} {
|
||
|
return n.Value
|
||
|
}
|
||
|
|
||
|
// String boolean to text
|
||
|
func (n *BoolNode) String() string {
|
||
|
if n.Comment != nil {
|
||
|
return addCommentString(n.Token.Value, n.Comment)
|
||
|
}
|
||
|
return n.stringWithoutComment()
|
||
|
}
|
||
|
|
||
|
func (n *BoolNode) stringWithoutComment() string {
|
||
|
return n.Token.Value
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *BoolNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// InfinityNode type of infinity node
|
||
|
type InfinityNode struct {
|
||
|
*BaseNode
|
||
|
Token *token.Token
|
||
|
Value float64
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *InfinityNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns InfinityType
|
||
|
func (n *InfinityNode) Type() NodeType { return InfinityType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *InfinityNode) GetToken() *token.Token {
|
||
|
return n.Token
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *InfinityNode) AddColumn(col int) {
|
||
|
n.Token.AddColumn(col)
|
||
|
}
|
||
|
|
||
|
// GetValue returns math.Inf(0) or math.Inf(-1)
|
||
|
func (n *InfinityNode) GetValue() interface{} {
|
||
|
return n.Value
|
||
|
}
|
||
|
|
||
|
// String infinity to text
|
||
|
func (n *InfinityNode) String() string {
|
||
|
if n.Comment != nil {
|
||
|
return addCommentString(n.Token.Value, n.Comment)
|
||
|
}
|
||
|
return n.stringWithoutComment()
|
||
|
}
|
||
|
|
||
|
func (n *InfinityNode) stringWithoutComment() string {
|
||
|
return n.Token.Value
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *InfinityNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// NanNode type of nan node
|
||
|
type NanNode struct {
|
||
|
*BaseNode
|
||
|
Token *token.Token
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *NanNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns NanType
|
||
|
func (n *NanNode) Type() NodeType { return NanType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *NanNode) GetToken() *token.Token {
|
||
|
return n.Token
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *NanNode) AddColumn(col int) {
|
||
|
n.Token.AddColumn(col)
|
||
|
}
|
||
|
|
||
|
// GetValue returns math.NaN()
|
||
|
func (n *NanNode) GetValue() interface{} {
|
||
|
return math.NaN()
|
||
|
}
|
||
|
|
||
|
// String returns .nan
|
||
|
func (n *NanNode) String() string {
|
||
|
if n.Comment != nil {
|
||
|
return addCommentString(n.Token.Value, n.Comment)
|
||
|
}
|
||
|
return n.stringWithoutComment()
|
||
|
}
|
||
|
|
||
|
func (n *NanNode) stringWithoutComment() string {
|
||
|
return n.Token.Value
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *NanNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// MapNode interface of MappingValueNode / MappingNode
|
||
|
type MapNode interface {
|
||
|
MapRange() *MapNodeIter
|
||
|
}
|
||
|
|
||
|
// MapNodeIter is an iterator for ranging over a MapNode
|
||
|
type MapNodeIter struct {
|
||
|
values []*MappingValueNode
|
||
|
idx int
|
||
|
}
|
||
|
|
||
|
const (
|
||
|
startRangeIndex = -1
|
||
|
)
|
||
|
|
||
|
// Next advances the map iterator and reports whether there is another entry.
|
||
|
// It returns false when the iterator is exhausted.
|
||
|
func (m *MapNodeIter) Next() bool {
|
||
|
m.idx++
|
||
|
next := m.idx < len(m.values)
|
||
|
return next
|
||
|
}
|
||
|
|
||
|
// Key returns the key of the iterator's current map node entry.
|
||
|
func (m *MapNodeIter) Key() Node {
|
||
|
return m.values[m.idx].Key
|
||
|
}
|
||
|
|
||
|
// Value returns the value of the iterator's current map node entry.
|
||
|
func (m *MapNodeIter) Value() Node {
|
||
|
return m.values[m.idx].Value
|
||
|
}
|
||
|
|
||
|
// MappingNode type of mapping node
|
||
|
type MappingNode struct {
|
||
|
*BaseNode
|
||
|
Start *token.Token
|
||
|
End *token.Token
|
||
|
IsFlowStyle bool
|
||
|
Values []*MappingValueNode
|
||
|
}
|
||
|
|
||
|
func (n *MappingNode) startPos() *token.Position {
|
||
|
if len(n.Values) == 0 {
|
||
|
return n.Start.Position
|
||
|
}
|
||
|
return n.Values[0].Key.GetToken().Position
|
||
|
}
|
||
|
|
||
|
// Merge merge key/value of map.
|
||
|
func (n *MappingNode) Merge(target *MappingNode) {
|
||
|
keyToMapValueMap := map[string]*MappingValueNode{}
|
||
|
for _, value := range n.Values {
|
||
|
key := value.Key.String()
|
||
|
keyToMapValueMap[key] = value
|
||
|
}
|
||
|
column := n.startPos().Column - target.startPos().Column
|
||
|
target.AddColumn(column)
|
||
|
for _, value := range target.Values {
|
||
|
mapValue, exists := keyToMapValueMap[value.Key.String()]
|
||
|
if exists {
|
||
|
mapValue.Value = value.Value
|
||
|
} else {
|
||
|
n.Values = append(n.Values, value)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SetIsFlowStyle set value to IsFlowStyle field recursively.
|
||
|
func (n *MappingNode) SetIsFlowStyle(isFlow bool) {
|
||
|
n.IsFlowStyle = isFlow
|
||
|
for _, value := range n.Values {
|
||
|
value.SetIsFlowStyle(isFlow)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *MappingNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns MappingType
|
||
|
func (n *MappingNode) Type() NodeType { return MappingType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *MappingNode) GetToken() *token.Token {
|
||
|
return n.Start
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *MappingNode) AddColumn(col int) {
|
||
|
n.Start.AddColumn(col)
|
||
|
n.End.AddColumn(col)
|
||
|
for _, value := range n.Values {
|
||
|
value.AddColumn(col)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (n *MappingNode) flowStyleString(commentMode bool) string {
|
||
|
values := []string{}
|
||
|
for _, value := range n.Values {
|
||
|
values = append(values, strings.TrimLeft(value.String(), " "))
|
||
|
}
|
||
|
mapText := fmt.Sprintf("{%s}", strings.Join(values, ", "))
|
||
|
if commentMode && n.Comment != nil {
|
||
|
return addCommentString(mapText, n.Comment)
|
||
|
}
|
||
|
return mapText
|
||
|
}
|
||
|
|
||
|
func (n *MappingNode) blockStyleString(commentMode bool) string {
|
||
|
values := []string{}
|
||
|
for _, value := range n.Values {
|
||
|
values = append(values, value.String())
|
||
|
}
|
||
|
mapText := strings.Join(values, "\n")
|
||
|
if commentMode && n.Comment != nil {
|
||
|
value := values[0]
|
||
|
var spaceNum int
|
||
|
for i := 0; i < len(value); i++ {
|
||
|
if value[i] != ' ' {
|
||
|
break
|
||
|
}
|
||
|
spaceNum++
|
||
|
}
|
||
|
comment := n.Comment.StringWithSpace(spaceNum)
|
||
|
return fmt.Sprintf("%s\n%s", comment, mapText)
|
||
|
}
|
||
|
return mapText
|
||
|
}
|
||
|
|
||
|
// String mapping values to text
|
||
|
func (n *MappingNode) String() string {
|
||
|
if len(n.Values) == 0 {
|
||
|
if n.Comment != nil {
|
||
|
return addCommentString("{}", n.Comment)
|
||
|
}
|
||
|
return "{}"
|
||
|
}
|
||
|
|
||
|
commentMode := true
|
||
|
if n.IsFlowStyle || len(n.Values) == 0 {
|
||
|
return n.flowStyleString(commentMode)
|
||
|
}
|
||
|
return n.blockStyleString(commentMode)
|
||
|
}
|
||
|
|
||
|
func (n *MappingNode) stringWithoutComment() string {
|
||
|
commentMode := false
|
||
|
if n.IsFlowStyle || len(n.Values) == 0 {
|
||
|
return n.flowStyleString(commentMode)
|
||
|
}
|
||
|
return n.blockStyleString(commentMode)
|
||
|
}
|
||
|
|
||
|
// MapRange implements MapNode protocol
|
||
|
func (n *MappingNode) MapRange() *MapNodeIter {
|
||
|
return &MapNodeIter{
|
||
|
idx: startRangeIndex,
|
||
|
values: n.Values,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *MappingNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// MappingKeyNode type of tag node
|
||
|
type MappingKeyNode struct {
|
||
|
*BaseNode
|
||
|
Start *token.Token
|
||
|
Value Node
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *MappingKeyNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns MappingKeyType
|
||
|
func (n *MappingKeyNode) Type() NodeType { return MappingKeyType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *MappingKeyNode) GetToken() *token.Token {
|
||
|
return n.Start
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *MappingKeyNode) AddColumn(col int) {
|
||
|
n.Start.AddColumn(col)
|
||
|
if n.Value != nil {
|
||
|
n.Value.AddColumn(col)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String tag to text
|
||
|
func (n *MappingKeyNode) String() string {
|
||
|
return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String())
|
||
|
}
|
||
|
|
||
|
func (n *MappingKeyNode) stringWithoutComment() string {
|
||
|
return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String())
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *MappingKeyNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// MappingValueNode type of mapping value
|
||
|
type MappingValueNode struct {
|
||
|
*BaseNode
|
||
|
Start *token.Token
|
||
|
Key Node
|
||
|
Value Node
|
||
|
}
|
||
|
|
||
|
// Replace replace value node.
|
||
|
func (n *MappingValueNode) Replace(value Node) error {
|
||
|
column := n.Value.GetToken().Position.Column - value.GetToken().Position.Column
|
||
|
value.AddColumn(column)
|
||
|
n.Value = value
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *MappingValueNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns MappingValueType
|
||
|
func (n *MappingValueNode) Type() NodeType { return MappingValueType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *MappingValueNode) GetToken() *token.Token {
|
||
|
return n.Start
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *MappingValueNode) AddColumn(col int) {
|
||
|
n.Start.AddColumn(col)
|
||
|
if n.Key != nil {
|
||
|
n.Key.AddColumn(col)
|
||
|
}
|
||
|
if n.Value != nil {
|
||
|
n.Value.AddColumn(col)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SetIsFlowStyle set value to IsFlowStyle field recursively.
|
||
|
func (n *MappingValueNode) SetIsFlowStyle(isFlow bool) {
|
||
|
switch value := n.Value.(type) {
|
||
|
case *MappingNode:
|
||
|
value.SetIsFlowStyle(isFlow)
|
||
|
case *MappingValueNode:
|
||
|
value.SetIsFlowStyle(isFlow)
|
||
|
case *SequenceNode:
|
||
|
value.SetIsFlowStyle(isFlow)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String mapping value to text
|
||
|
func (n *MappingValueNode) String() string {
|
||
|
if n.Comment != nil {
|
||
|
return fmt.Sprintf(
|
||
|
"%s\n%s",
|
||
|
n.Comment.StringWithSpace(n.Key.GetToken().Position.Column-1),
|
||
|
n.toString(),
|
||
|
)
|
||
|
}
|
||
|
return n.toString()
|
||
|
}
|
||
|
|
||
|
func (n *MappingValueNode) toString() string {
|
||
|
space := strings.Repeat(" ", n.Key.GetToken().Position.Column-1)
|
||
|
keyIndentLevel := n.Key.GetToken().Position.IndentLevel
|
||
|
valueIndentLevel := n.Value.GetToken().Position.IndentLevel
|
||
|
keyComment := n.Key.GetComment()
|
||
|
if _, ok := n.Value.(ScalarNode); ok {
|
||
|
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
|
||
|
} else if keyIndentLevel < valueIndentLevel {
|
||
|
if keyComment != nil {
|
||
|
return fmt.Sprintf(
|
||
|
"%s%s: %s\n%s",
|
||
|
space,
|
||
|
n.Key.stringWithoutComment(),
|
||
|
keyComment.String(),
|
||
|
n.Value.String(),
|
||
|
)
|
||
|
}
|
||
|
return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String())
|
||
|
} else if m, ok := n.Value.(*MappingNode); ok && (m.IsFlowStyle || len(m.Values) == 0) {
|
||
|
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
|
||
|
} else if s, ok := n.Value.(*SequenceNode); ok && (s.IsFlowStyle || len(s.Values) == 0) {
|
||
|
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
|
||
|
} else if _, ok := n.Value.(*AnchorNode); ok {
|
||
|
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
|
||
|
} else if _, ok := n.Value.(*AliasNode); ok {
|
||
|
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
|
||
|
}
|
||
|
if keyComment != nil {
|
||
|
return fmt.Sprintf(
|
||
|
"%s%s: %s\n%s",
|
||
|
space,
|
||
|
n.Key.stringWithoutComment(),
|
||
|
keyComment.String(),
|
||
|
n.Value.String(),
|
||
|
)
|
||
|
}
|
||
|
if m, ok := n.Value.(*MappingNode); ok && m.Comment != nil {
|
||
|
return fmt.Sprintf(
|
||
|
"%s%s: %s",
|
||
|
space,
|
||
|
n.Key.String(),
|
||
|
strings.TrimLeft(n.Value.String(), " "),
|
||
|
)
|
||
|
}
|
||
|
return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String())
|
||
|
}
|
||
|
|
||
|
func (n *MappingValueNode) stringWithoutComment() string {
|
||
|
space := strings.Repeat(" ", n.Key.GetToken().Position.Column-1)
|
||
|
keyIndentLevel := n.Key.GetToken().Position.IndentLevel
|
||
|
valueIndentLevel := n.Value.GetToken().Position.IndentLevel
|
||
|
if _, ok := n.Value.(ScalarNode); ok {
|
||
|
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
|
||
|
} else if keyIndentLevel < valueIndentLevel {
|
||
|
return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String())
|
||
|
} else if m, ok := n.Value.(*MappingNode); ok && (m.IsFlowStyle || len(m.Values) == 0) {
|
||
|
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
|
||
|
} else if s, ok := n.Value.(*SequenceNode); ok && (s.IsFlowStyle || len(s.Values) == 0) {
|
||
|
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
|
||
|
} else if _, ok := n.Value.(*AnchorNode); ok {
|
||
|
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
|
||
|
} else if _, ok := n.Value.(*AliasNode); ok {
|
||
|
return fmt.Sprintf("%s%s: %s", space, n.Key.String(), n.Value.String())
|
||
|
}
|
||
|
return fmt.Sprintf("%s%s:\n%s", space, n.Key.String(), n.Value.String())
|
||
|
}
|
||
|
|
||
|
// MapRange implements MapNode protocol
|
||
|
func (n *MappingValueNode) MapRange() *MapNodeIter {
|
||
|
return &MapNodeIter{
|
||
|
idx: startRangeIndex,
|
||
|
values: []*MappingValueNode{n},
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *MappingValueNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// ArrayNode interface of SequenceNode
|
||
|
type ArrayNode interface {
|
||
|
ArrayRange() *ArrayNodeIter
|
||
|
}
|
||
|
|
||
|
// ArrayNodeIter is an iterator for ranging over a ArrayNode
|
||
|
type ArrayNodeIter struct {
|
||
|
values []Node
|
||
|
idx int
|
||
|
}
|
||
|
|
||
|
// Next advances the array iterator and reports whether there is another entry.
|
||
|
// It returns false when the iterator is exhausted.
|
||
|
func (m *ArrayNodeIter) Next() bool {
|
||
|
m.idx++
|
||
|
next := m.idx < len(m.values)
|
||
|
return next
|
||
|
}
|
||
|
|
||
|
// Value returns the value of the iterator's current array entry.
|
||
|
func (m *ArrayNodeIter) Value() Node {
|
||
|
return m.values[m.idx]
|
||
|
}
|
||
|
|
||
|
// Len returns length of array
|
||
|
func (m *ArrayNodeIter) Len() int {
|
||
|
return len(m.values)
|
||
|
}
|
||
|
|
||
|
// SequenceNode type of sequence node
|
||
|
type SequenceNode struct {
|
||
|
*BaseNode
|
||
|
Start *token.Token
|
||
|
End *token.Token
|
||
|
IsFlowStyle bool
|
||
|
Values []Node
|
||
|
ValueComments []*CommentGroupNode
|
||
|
}
|
||
|
|
||
|
// Replace replace value node.
|
||
|
func (n *SequenceNode) Replace(idx int, value Node) error {
|
||
|
if len(n.Values) <= idx {
|
||
|
return xerrors.Errorf(
|
||
|
"invalid index for sequence: sequence length is %d, but specified %d index",
|
||
|
len(n.Values), idx,
|
||
|
)
|
||
|
}
|
||
|
column := n.Values[idx].GetToken().Position.Column - value.GetToken().Position.Column
|
||
|
value.AddColumn(column)
|
||
|
n.Values[idx] = value
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Merge merge sequence value.
|
||
|
func (n *SequenceNode) Merge(target *SequenceNode) {
|
||
|
column := n.Start.Position.Column - target.Start.Position.Column
|
||
|
target.AddColumn(column)
|
||
|
for _, value := range target.Values {
|
||
|
n.Values = append(n.Values, value)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// SetIsFlowStyle set value to IsFlowStyle field recursively.
|
||
|
func (n *SequenceNode) SetIsFlowStyle(isFlow bool) {
|
||
|
n.IsFlowStyle = isFlow
|
||
|
for _, value := range n.Values {
|
||
|
switch value := value.(type) {
|
||
|
case *MappingNode:
|
||
|
value.SetIsFlowStyle(isFlow)
|
||
|
case *MappingValueNode:
|
||
|
value.SetIsFlowStyle(isFlow)
|
||
|
case *SequenceNode:
|
||
|
value.SetIsFlowStyle(isFlow)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *SequenceNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns SequenceType
|
||
|
func (n *SequenceNode) Type() NodeType { return SequenceType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *SequenceNode) GetToken() *token.Token {
|
||
|
return n.Start
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *SequenceNode) AddColumn(col int) {
|
||
|
n.Start.AddColumn(col)
|
||
|
n.End.AddColumn(col)
|
||
|
for _, value := range n.Values {
|
||
|
value.AddColumn(col)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func (n *SequenceNode) flowStyleString() string {
|
||
|
values := []string{}
|
||
|
for _, value := range n.Values {
|
||
|
values = append(values, value.String())
|
||
|
}
|
||
|
return fmt.Sprintf("[%s]", strings.Join(values, ", "))
|
||
|
}
|
||
|
|
||
|
func (n *SequenceNode) blockStyleString() string {
|
||
|
space := strings.Repeat(" ", n.Start.Position.Column-1)
|
||
|
values := []string{}
|
||
|
if n.Comment != nil {
|
||
|
values = append(values, n.Comment.StringWithSpace(n.Start.Position.Column-1))
|
||
|
}
|
||
|
|
||
|
for idx, value := range n.Values {
|
||
|
valueStr := value.String()
|
||
|
splittedValues := strings.Split(valueStr, "\n")
|
||
|
trimmedFirstValue := strings.TrimLeft(splittedValues[0], " ")
|
||
|
diffLength := len(splittedValues[0]) - len(trimmedFirstValue)
|
||
|
if len(splittedValues) > 1 && value.Type() == StringType || value.Type() == LiteralType {
|
||
|
// If multi-line string, the space characters for indent have already been added, so delete them.
|
||
|
for i := 1; i < len(splittedValues); i++ {
|
||
|
splittedValues[i] = strings.TrimLeft(splittedValues[i], " ")
|
||
|
}
|
||
|
}
|
||
|
newValues := []string{trimmedFirstValue}
|
||
|
for i := 1; i < len(splittedValues); i++ {
|
||
|
if len(splittedValues[i]) <= diffLength {
|
||
|
// this line is \n or white space only
|
||
|
newValues = append(newValues, "")
|
||
|
continue
|
||
|
}
|
||
|
trimmed := splittedValues[i][diffLength:]
|
||
|
newValues = append(newValues, fmt.Sprintf("%s %s", space, trimmed))
|
||
|
}
|
||
|
newValue := strings.Join(newValues, "\n")
|
||
|
if len(n.ValueComments) == len(n.Values) && n.ValueComments[idx] != nil {
|
||
|
values = append(values, n.ValueComments[idx].StringWithSpace(n.Start.Position.Column-1))
|
||
|
}
|
||
|
values = append(values, fmt.Sprintf("%s- %s", space, newValue))
|
||
|
}
|
||
|
return strings.Join(values, "\n")
|
||
|
}
|
||
|
|
||
|
// String sequence to text
|
||
|
func (n *SequenceNode) String() string {
|
||
|
if n.IsFlowStyle || len(n.Values) == 0 {
|
||
|
return n.flowStyleString()
|
||
|
}
|
||
|
return n.blockStyleString()
|
||
|
}
|
||
|
|
||
|
func (n *SequenceNode) stringWithoutComment() string {
|
||
|
if n.IsFlowStyle || len(n.Values) == 0 {
|
||
|
return n.flowStyleString()
|
||
|
}
|
||
|
return n.blockStyleString()
|
||
|
}
|
||
|
|
||
|
// ArrayRange implements ArrayNode protocol
|
||
|
func (n *SequenceNode) ArrayRange() *ArrayNodeIter {
|
||
|
return &ArrayNodeIter{
|
||
|
idx: startRangeIndex,
|
||
|
values: n.Values,
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *SequenceNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// AnchorNode type of anchor node
|
||
|
type AnchorNode struct {
|
||
|
*BaseNode
|
||
|
Start *token.Token
|
||
|
Name Node
|
||
|
Value Node
|
||
|
}
|
||
|
|
||
|
func (n *AnchorNode) SetName(name string) error {
|
||
|
if n.Name == nil {
|
||
|
return ErrInvalidAnchorName
|
||
|
}
|
||
|
s, ok := n.Name.(*StringNode)
|
||
|
if !ok {
|
||
|
return ErrInvalidAnchorName
|
||
|
}
|
||
|
s.Value = name
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *AnchorNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns AnchorType
|
||
|
func (n *AnchorNode) Type() NodeType { return AnchorType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *AnchorNode) GetToken() *token.Token {
|
||
|
return n.Start
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *AnchorNode) AddColumn(col int) {
|
||
|
n.Start.AddColumn(col)
|
||
|
if n.Name != nil {
|
||
|
n.Name.AddColumn(col)
|
||
|
}
|
||
|
if n.Value != nil {
|
||
|
n.Value.AddColumn(col)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String anchor to text
|
||
|
func (n *AnchorNode) String() string {
|
||
|
value := n.Value.String()
|
||
|
if len(strings.Split(value, "\n")) > 1 {
|
||
|
return fmt.Sprintf("&%s\n%s", n.Name.String(), value)
|
||
|
} else if s, ok := n.Value.(*SequenceNode); ok && !s.IsFlowStyle {
|
||
|
return fmt.Sprintf("&%s\n%s", n.Name.String(), value)
|
||
|
} else if m, ok := n.Value.(*MappingNode); ok && !m.IsFlowStyle {
|
||
|
return fmt.Sprintf("&%s\n%s", n.Name.String(), value)
|
||
|
}
|
||
|
return fmt.Sprintf("&%s %s", n.Name.String(), value)
|
||
|
}
|
||
|
|
||
|
func (n *AnchorNode) stringWithoutComment() string {
|
||
|
return n.String()
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *AnchorNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// AliasNode type of alias node
|
||
|
type AliasNode struct {
|
||
|
*BaseNode
|
||
|
Start *token.Token
|
||
|
Value Node
|
||
|
}
|
||
|
|
||
|
func (n *AliasNode) SetName(name string) error {
|
||
|
if n.Value == nil {
|
||
|
return ErrInvalidAliasName
|
||
|
}
|
||
|
s, ok := n.Value.(*StringNode)
|
||
|
if !ok {
|
||
|
return ErrInvalidAliasName
|
||
|
}
|
||
|
s.Value = name
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *AliasNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns AliasType
|
||
|
func (n *AliasNode) Type() NodeType { return AliasType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *AliasNode) GetToken() *token.Token {
|
||
|
return n.Start
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *AliasNode) AddColumn(col int) {
|
||
|
n.Start.AddColumn(col)
|
||
|
if n.Value != nil {
|
||
|
n.Value.AddColumn(col)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String alias to text
|
||
|
func (n *AliasNode) String() string {
|
||
|
return fmt.Sprintf("*%s", n.Value.String())
|
||
|
}
|
||
|
|
||
|
func (n *AliasNode) stringWithoutComment() string {
|
||
|
return fmt.Sprintf("*%s", n.Value.String())
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *AliasNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// DirectiveNode type of directive node
|
||
|
type DirectiveNode struct {
|
||
|
*BaseNode
|
||
|
Start *token.Token
|
||
|
Value Node
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *DirectiveNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns DirectiveType
|
||
|
func (n *DirectiveNode) Type() NodeType { return DirectiveType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *DirectiveNode) GetToken() *token.Token {
|
||
|
return n.Start
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *DirectiveNode) AddColumn(col int) {
|
||
|
if n.Value != nil {
|
||
|
n.Value.AddColumn(col)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String directive to text
|
||
|
func (n *DirectiveNode) String() string {
|
||
|
return fmt.Sprintf("%s%s", n.Start.Value, n.Value.String())
|
||
|
}
|
||
|
|
||
|
func (n *DirectiveNode) stringWithoutComment() string {
|
||
|
return fmt.Sprintf("%s%s", n.Start.Value, n.Value.String())
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *DirectiveNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// TagNode type of tag node
|
||
|
type TagNode struct {
|
||
|
*BaseNode
|
||
|
Start *token.Token
|
||
|
Value Node
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *TagNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns TagType
|
||
|
func (n *TagNode) Type() NodeType { return TagType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *TagNode) GetToken() *token.Token {
|
||
|
return n.Start
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *TagNode) AddColumn(col int) {
|
||
|
n.Start.AddColumn(col)
|
||
|
if n.Value != nil {
|
||
|
n.Value.AddColumn(col)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String tag to text
|
||
|
func (n *TagNode) String() string {
|
||
|
return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String())
|
||
|
}
|
||
|
|
||
|
func (n *TagNode) stringWithoutComment() string {
|
||
|
return fmt.Sprintf("%s %s", n.Start.Value, n.Value.String())
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *TagNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// CommentNode type of comment node
|
||
|
type CommentNode struct {
|
||
|
*BaseNode
|
||
|
Token *token.Token
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *CommentNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns TagType
|
||
|
func (n *CommentNode) Type() NodeType { return CommentType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *CommentNode) GetToken() *token.Token { return n.Token }
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *CommentNode) AddColumn(col int) {
|
||
|
if n.Token == nil {
|
||
|
return
|
||
|
}
|
||
|
n.Token.AddColumn(col)
|
||
|
}
|
||
|
|
||
|
// String comment to text
|
||
|
func (n *CommentNode) String() string {
|
||
|
return fmt.Sprintf("#%s", n.Token.Value)
|
||
|
}
|
||
|
|
||
|
func (n *CommentNode) stringWithoutComment() string {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *CommentNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// CommentGroupNode type of comment node
|
||
|
type CommentGroupNode struct {
|
||
|
*BaseNode
|
||
|
Comments []*CommentNode
|
||
|
}
|
||
|
|
||
|
// Read implements (io.Reader).Read
|
||
|
func (n *CommentGroupNode) Read(p []byte) (int, error) {
|
||
|
return readNode(p, n)
|
||
|
}
|
||
|
|
||
|
// Type returns TagType
|
||
|
func (n *CommentGroupNode) Type() NodeType { return CommentType }
|
||
|
|
||
|
// GetToken returns token instance
|
||
|
func (n *CommentGroupNode) GetToken() *token.Token {
|
||
|
if len(n.Comments) > 0 {
|
||
|
return n.Comments[0].Token
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// AddColumn add column number to child nodes recursively
|
||
|
func (n *CommentGroupNode) AddColumn(col int) {
|
||
|
for _, comment := range n.Comments {
|
||
|
comment.AddColumn(col)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// String comment to text
|
||
|
func (n *CommentGroupNode) String() string {
|
||
|
values := []string{}
|
||
|
for _, comment := range n.Comments {
|
||
|
values = append(values, comment.String())
|
||
|
}
|
||
|
return strings.Join(values, "\n")
|
||
|
}
|
||
|
|
||
|
func (n *CommentGroupNode) StringWithSpace(col int) string {
|
||
|
space := strings.Repeat(" ", col)
|
||
|
values := []string{}
|
||
|
for _, comment := range n.Comments {
|
||
|
values = append(values, space+comment.String())
|
||
|
}
|
||
|
return strings.Join(values, "\n")
|
||
|
|
||
|
}
|
||
|
|
||
|
func (n *CommentGroupNode) stringWithoutComment() string {
|
||
|
return ""
|
||
|
}
|
||
|
|
||
|
// MarshalYAML encodes to a YAML text
|
||
|
func (n *CommentGroupNode) MarshalYAML() ([]byte, error) {
|
||
|
return []byte(n.String()), nil
|
||
|
}
|
||
|
|
||
|
// Visitor has Visit method that is invokded for each node encountered by Walk.
|
||
|
// If the result visitor w is not nil, Walk visits each of the children of node with the visitor w,
|
||
|
// followed by a call of w.Visit(nil).
|
||
|
type Visitor interface {
|
||
|
Visit(Node) Visitor
|
||
|
}
|
||
|
|
||
|
// Walk traverses an AST in depth-first order: It starts by calling v.Visit(node); node must not be nil.
|
||
|
// If the visitor w returned by v.Visit(node) is not nil,
|
||
|
// Walk is invoked recursively with visitor w for each of the non-nil children of node,
|
||
|
// followed by a call of w.Visit(nil).
|
||
|
func Walk(v Visitor, node Node) {
|
||
|
if v = v.Visit(node); v == nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
switch n := node.(type) {
|
||
|
case *CommentNode:
|
||
|
case *NullNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
case *IntegerNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
case *FloatNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
case *StringNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
case *MergeKeyNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
case *BoolNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
case *InfinityNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
case *NanNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
case *LiteralNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
Walk(v, n.Value)
|
||
|
case *DirectiveNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
Walk(v, n.Value)
|
||
|
case *TagNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
Walk(v, n.Value)
|
||
|
case *DocumentNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
Walk(v, n.Body)
|
||
|
case *MappingNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
for _, value := range n.Values {
|
||
|
Walk(v, value)
|
||
|
}
|
||
|
case *MappingKeyNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
Walk(v, n.Value)
|
||
|
case *MappingValueNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
Walk(v, n.Key)
|
||
|
Walk(v, n.Value)
|
||
|
case *SequenceNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
for _, value := range n.Values {
|
||
|
Walk(v, value)
|
||
|
}
|
||
|
case *AnchorNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
Walk(v, n.Name)
|
||
|
Walk(v, n.Value)
|
||
|
case *AliasNode:
|
||
|
walkComment(v, n.BaseNode)
|
||
|
Walk(v, n.Value)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
func walkComment(v Visitor, base *BaseNode) {
|
||
|
if base == nil {
|
||
|
return
|
||
|
}
|
||
|
if base.Comment == nil {
|
||
|
return
|
||
|
}
|
||
|
Walk(v, base.Comment)
|
||
|
}
|
||
|
|
||
|
type filterWalker struct {
|
||
|
typ NodeType
|
||
|
results []Node
|
||
|
}
|
||
|
|
||
|
func (v *filterWalker) Visit(n Node) Visitor {
|
||
|
if v.typ == n.Type() {
|
||
|
v.results = append(v.results, n)
|
||
|
}
|
||
|
return v
|
||
|
}
|
||
|
|
||
|
type parentFinder struct {
|
||
|
target Node
|
||
|
}
|
||
|
|
||
|
func (f *parentFinder) walk(parent, node Node) Node {
|
||
|
if f.target == node {
|
||
|
return parent
|
||
|
}
|
||
|
switch n := node.(type) {
|
||
|
case *CommentNode:
|
||
|
return nil
|
||
|
case *NullNode:
|
||
|
return nil
|
||
|
case *IntegerNode:
|
||
|
return nil
|
||
|
case *FloatNode:
|
||
|
return nil
|
||
|
case *StringNode:
|
||
|
return nil
|
||
|
case *MergeKeyNode:
|
||
|
return nil
|
||
|
case *BoolNode:
|
||
|
return nil
|
||
|
case *InfinityNode:
|
||
|
return nil
|
||
|
case *NanNode:
|
||
|
return nil
|
||
|
case *LiteralNode:
|
||
|
return f.walk(node, n.Value)
|
||
|
case *DirectiveNode:
|
||
|
return f.walk(node, n.Value)
|
||
|
case *TagNode:
|
||
|
return f.walk(node, n.Value)
|
||
|
case *DocumentNode:
|
||
|
return f.walk(node, n.Body)
|
||
|
case *MappingNode:
|
||
|
for _, value := range n.Values {
|
||
|
if found := f.walk(node, value); found != nil {
|
||
|
return found
|
||
|
}
|
||
|
}
|
||
|
case *MappingKeyNode:
|
||
|
return f.walk(node, n.Value)
|
||
|
case *MappingValueNode:
|
||
|
if found := f.walk(node, n.Key); found != nil {
|
||
|
return found
|
||
|
}
|
||
|
return f.walk(node, n.Value)
|
||
|
case *SequenceNode:
|
||
|
for _, value := range n.Values {
|
||
|
if found := f.walk(node, value); found != nil {
|
||
|
return found
|
||
|
}
|
||
|
}
|
||
|
case *AnchorNode:
|
||
|
if found := f.walk(node, n.Name); found != nil {
|
||
|
return found
|
||
|
}
|
||
|
return f.walk(node, n.Value)
|
||
|
case *AliasNode:
|
||
|
return f.walk(node, n.Value)
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Parent get parent node from child node.
|
||
|
func Parent(root, child Node) Node {
|
||
|
finder := &parentFinder{target: child}
|
||
|
return finder.walk(root, root)
|
||
|
}
|
||
|
|
||
|
// Filter returns a list of nodes that match the given type.
|
||
|
func Filter(typ NodeType, node Node) []Node {
|
||
|
walker := &filterWalker{typ: typ}
|
||
|
Walk(walker, node)
|
||
|
return walker.results
|
||
|
}
|
||
|
|
||
|
// FilterFile returns a list of nodes that match the given type.
|
||
|
func FilterFile(typ NodeType, file *File) []Node {
|
||
|
results := []Node{}
|
||
|
for _, doc := range file.Docs {
|
||
|
walker := &filterWalker{typ: typ}
|
||
|
Walk(walker, doc)
|
||
|
results = append(results, walker.results...)
|
||
|
}
|
||
|
return results
|
||
|
}
|
||
|
|
||
|
type ErrInvalidMergeType struct {
|
||
|
dst Node
|
||
|
src Node
|
||
|
}
|
||
|
|
||
|
func (e *ErrInvalidMergeType) Error() string {
|
||
|
return fmt.Sprintf("cannot merge %s into %s", e.src.Type(), e.dst.Type())
|
||
|
}
|
||
|
|
||
|
// Merge merge document, map, sequence node.
|
||
|
func Merge(dst Node, src Node) error {
|
||
|
if doc, ok := src.(*DocumentNode); ok {
|
||
|
src = doc.Body
|
||
|
}
|
||
|
err := &ErrInvalidMergeType{dst: dst, src: src}
|
||
|
switch dst.Type() {
|
||
|
case DocumentType:
|
||
|
node := dst.(*DocumentNode)
|
||
|
return Merge(node.Body, src)
|
||
|
case MappingType:
|
||
|
node := dst.(*MappingNode)
|
||
|
target, ok := src.(*MappingNode)
|
||
|
if !ok {
|
||
|
return err
|
||
|
}
|
||
|
node.Merge(target)
|
||
|
return nil
|
||
|
case SequenceType:
|
||
|
node := dst.(*SequenceNode)
|
||
|
target, ok := src.(*SequenceNode)
|
||
|
if !ok {
|
||
|
return err
|
||
|
}
|
||
|
node.Merge(target)
|
||
|
return nil
|
||
|
}
|
||
|
return err
|
||
|
}
|