Skip to content

Commit

Permalink
support merge tag (#597)
Browse files Browse the repository at this point in the history
  • Loading branch information
goccy authored Dec 18, 2024
1 parent 61bc6c1 commit 7d56fe2
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 12 deletions.
87 changes: 87 additions & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,7 @@ type Node interface {
// MapKeyNode type for map key node
type MapKeyNode interface {
Node
IsMergeKey() bool
// String node to text without comment
stringWithoutComment() string
}
Expand Down Expand Up @@ -633,6 +634,11 @@ func (n *NullNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *NullNode) IsMergeKey() bool {
return false
}

// IntegerNode type of integer node
type IntegerNode struct {
*BaseNode
Expand Down Expand Up @@ -680,6 +686,11 @@ func (n *IntegerNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *IntegerNode) IsMergeKey() bool {
return false
}

// FloatNode type of float node
type FloatNode struct {
*BaseNode
Expand Down Expand Up @@ -728,6 +739,11 @@ func (n *FloatNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *FloatNode) IsMergeKey() bool {
return false
}

// StringNode type of string node
type StringNode struct {
*BaseNode
Expand Down Expand Up @@ -758,6 +774,11 @@ func (n *StringNode) GetValue() interface{} {
return n.Value
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *StringNode) IsMergeKey() bool {
return false
}

// escapeSingleQuote escapes s to a single quoted scalar.
// https://yaml.org/spec/1.2.2/#732-single-quoted-style
func escapeSingleQuote(s string) string {
Expand Down Expand Up @@ -895,6 +916,11 @@ func (n *LiteralNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *LiteralNode) IsMergeKey() bool {
return false
}

// MergeKeyNode type of merge key node
type MergeKeyNode struct {
*BaseNode
Expand Down Expand Up @@ -938,6 +964,11 @@ func (n *MergeKeyNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *MergeKeyNode) IsMergeKey() bool {
return true
}

// BoolNode type of boolean node
type BoolNode struct {
*BaseNode
Expand Down Expand Up @@ -985,6 +1016,11 @@ func (n *BoolNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *BoolNode) IsMergeKey() bool {
return false
}

// InfinityNode type of infinity node
type InfinityNode struct {
*BaseNode
Expand Down Expand Up @@ -1032,6 +1068,11 @@ func (n *InfinityNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *InfinityNode) IsMergeKey() bool {
return false
}

// NanNode type of nan node
type NanNode struct {
*BaseNode
Expand Down Expand Up @@ -1078,6 +1119,11 @@ func (n *NanNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *NanNode) IsMergeKey() bool {
return false
}

// MapNode interface of MappingValueNode / MappingNode
type MapNode interface {
MapRange() *MapNodeIter
Expand Down Expand Up @@ -1281,6 +1327,18 @@ func (n *MappingKeyNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *MappingKeyNode) IsMergeKey() bool {
if n.Value == nil {
return false
}
key, ok := n.Value.(MapKeyNode)
if !ok {
return false
}
return key.IsMergeKey()
}

// MappingValueNode type of mapping value
type MappingValueNode struct {
*BaseNode
Expand Down Expand Up @@ -1665,6 +1723,18 @@ func (n *AnchorNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *AnchorNode) IsMergeKey() bool {
if n.Value == nil {
return false
}
key, ok := n.Value.(MapKeyNode)
if !ok {
return false
}
return key.IsMergeKey()
}

// AliasNode type of alias node
type AliasNode struct {
*BaseNode
Expand Down Expand Up @@ -1723,6 +1793,11 @@ func (n *AliasNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *AliasNode) IsMergeKey() bool {
return false
}

// DirectiveNode type of directive node
type DirectiveNode struct {
*BaseNode
Expand Down Expand Up @@ -1822,6 +1897,18 @@ func (n *TagNode) MarshalYAML() ([]byte, error) {
return []byte(n.String()), nil
}

// IsMergeKey returns whether it is a MergeKey node.
func (n *TagNode) IsMergeKey() bool {
if n.Value == nil {
return false
}
key, ok := n.Value.(MapKeyNode)
if !ok {
return false
}
return key.IsMergeKey()
}

// CommentNode type of comment node
type CommentNode struct {
*BaseNode
Expand Down
18 changes: 9 additions & 9 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ func (d *Decoder) setToMapValue(node ast.Node, m map[string]interface{}) error {
d.setPathToCommentMap(node)
switch n := node.(type) {
case *ast.MappingValueNode:
if n.Key.Type() == ast.MergeKeyType {
if n.Key.IsMergeKey() {
if err := d.setToMapValue(d.mergeValueNode(n.Value), m); err != nil {
return err
}
Expand Down Expand Up @@ -185,7 +185,7 @@ func (d *Decoder) setToOrderedMapValue(node ast.Node, m *MapSlice) error {
d.setPathToCommentMap(node)
switch n := node.(type) {
case *ast.MappingValueNode:
if n.Key.Type() == ast.MergeKeyType {
if n.Key.IsMergeKey() {
if err := d.setToOrderedMapValue(d.mergeValueNode(n.Value), m); err != nil {
return err
}
Expand Down Expand Up @@ -464,7 +464,7 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) {
case *ast.MappingKeyNode:
return d.nodeToValue(n.Value)
case *ast.MappingValueNode:
if n.Key.Type() == ast.MergeKeyType {
if n.Key.IsMergeKey() {
value := d.mergeValueNode(n.Value)
if d.useOrderedMap {
m := MapSlice{}
Expand Down Expand Up @@ -555,7 +555,7 @@ func (d *Decoder) resolveAlias(node ast.Node) (ast.Node, error) {
}
n.Value = value
case *ast.MappingValueNode:
if n.Key.Type() == ast.MergeKeyType && n.Value.Type() == ast.AliasType {
if n.Key.IsMergeKey() && n.Value.Type() == ast.AliasType {
value, err := d.resolveAlias(n.Value)
if err != nil {
return nil, err
Expand Down Expand Up @@ -1186,7 +1186,7 @@ func (d *Decoder) keyToNodeMap(node ast.Node, ignoreMergeKey bool, getKeyOrValue
mapIter := mapNode.MapRange()
for mapIter.Next() {
keyNode := mapIter.Key()
if keyNode.Type() == ast.MergeKeyType {
if keyNode.IsMergeKey() {
if ignoreMergeKey {
continue
}
Expand Down Expand Up @@ -1349,7 +1349,7 @@ func (d *Decoder) getMergeAliasName(src ast.Node) string {
for mapIter.Next() {
key := mapIter.Key()
value := mapIter.Value()
if key.Type() == ast.MergeKeyType && value.Type() == ast.AliasType {
if key.IsMergeKey() && value.Type() == ast.AliasType {
return value.(*ast.AliasNode).Value.GetToken().Value
}
}
Expand Down Expand Up @@ -1639,7 +1639,7 @@ func (d *Decoder) decodeMapItem(ctx context.Context, dst *MapItem, src ast.Node)
}
key := mapIter.Key()
value := mapIter.Value()
if key.Type() == ast.MergeKeyType {
if key.IsMergeKey() {
if err := d.decodeMapItem(ctx, dst, value); err != nil {
return err
}
Expand Down Expand Up @@ -1691,7 +1691,7 @@ func (d *Decoder) decodeMapSlice(ctx context.Context, dst *MapSlice, src ast.Nod
for mapIter.Next() {
key := mapIter.Key()
value := mapIter.Value()
if key.Type() == ast.MergeKeyType {
if key.IsMergeKey() {
var m MapSlice
if err := d.decodeMapSlice(ctx, &m, value); err != nil {
return err
Expand Down Expand Up @@ -1745,7 +1745,7 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node
for mapIter.Next() {
key := mapIter.Key()
value := mapIter.Value()
if key.Type() == ast.MergeKeyType {
if key.IsMergeKey() {
if err := d.decodeMap(ctx, dst, value); err != nil {
return err
}
Expand Down
7 changes: 7 additions & 0 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,13 @@ func TestDecoder(t *testing.T) {
"v: !!bool False",
map[string]bool{"v": false},
},
{
`
!!merge <<: { a: 1, b: 2 }
c: 3
`,
map[string]any{"a": 1, "b": 2, "c": 3},
},

// Flow sequence
{
Expand Down
32 changes: 29 additions & 3 deletions parser/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,10 @@ func createGroupedTokens(tokens token.Tokens) ([]*Token, error) {
if err != nil {
return nil, err
}
tks = createScalarTagTokenGroups(tks)
tks, err = createScalarTagTokenGroups(tks)
if err != nil {
return nil, err
}
tks, err = createAnchorWithScalarTagTokenGroups(tks)
if err != nil {
return nil, err
Expand Down Expand Up @@ -348,7 +351,7 @@ func createAnchorAndAliasTokenGroups(tokens []*Token) ([]*Token, error) {
return ret, nil
}

func createScalarTagTokenGroups(tokens []*Token) []*Token {
func createScalarTagTokenGroups(tokens []*Token) ([]*Token, error) {
ret := make([]*Token, 0, len(tokens))
for i := 0; i < len(tokens); i++ {
tk := tokens[i]
Expand Down Expand Up @@ -384,6 +387,29 @@ func createScalarTagTokenGroups(tokens []*Token) []*Token {
} else {
ret = append(ret, tk)
}
case token.MergeTag:
if len(tokens) <= i+1 {
ret = append(ret, tk)
continue
}
if tk.Line() != tokens[i+1].Line() {
ret = append(ret, tk)
continue
}
if tokens[i+1].GroupType() == TokenGroupAnchorName {
ret = append(ret, tk)
continue
}
if tokens[i+1].Type() != token.MergeKeyType {
return nil, errors.ErrSyntax("could not find merge key", tokens[i+1].RawToken())
}
ret = append(ret, &Token{
Group: &TokenGroup{
Type: TokenGroupScalarTag,
Tokens: []*Token{tk, tokens[i+1]},
},
})
i++
default:
ret = append(ret, tk)
}
Expand All @@ -409,7 +435,7 @@ func createScalarTagTokenGroups(tokens []*Token) []*Token {
i++
}
}
return ret
return ret, nil
}

func createAnchorWithScalarTagTokenGroups(tokens []*Token) ([]*Token, error) {
Expand Down
12 changes: 12 additions & 0 deletions token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -405,6 +405,8 @@ const (
TimestampTag ReservedTagKeyword = "!!timestamp"
// BooleanTag `!!bool` tag
BooleanTag ReservedTagKeyword = "!!bool"
// MergeTag `!!merge` tag
MergeTag ReservedTagKeyword = "!!merge"
)

var (
Expand Down Expand Up @@ -520,6 +522,16 @@ var (
Position: pos,
}
},
MergeTag: func(value, org string, pos *Position) *Token {
return &Token{
Type: TagType,
CharacterType: CharacterTypeIndicator,
Indicator: NodePropertyIndicator,
Value: value,
Origin: org,
Position: pos,
}
},
}
)

Expand Down

0 comments on commit 7d56fe2

Please sign in to comment.