From 950afad7c71f95d18b736f41ca7a0382d350771d Mon Sep 17 00:00:00 2001 From: Masaaki Goshima Date: Wed, 30 Oct 2024 19:25:18 +0900 Subject: [PATCH] support recursive anchor (#489) --- decode.go | 14 +++++++++++++- decode_test.go | 16 +++++++++++++++- lexer/lexer_test.go | 2 +- 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/decode.go b/decode.go index 95f7217a..9ff9f3eb 100644 --- a/decode.go +++ b/decode.go @@ -26,6 +26,7 @@ type Decoder struct { reader io.Reader referenceReaders []io.Reader anchorNodeMap map[string]ast.Node + aliasValueMap map[*ast.AliasNode]any anchorValueMap map[string]reflect.Value customUnmarshalerMap map[reflect.Type]func(interface{}, []byte) error toCommentMap CommentMap @@ -48,6 +49,7 @@ func NewDecoder(r io.Reader, opts ...DecodeOption) *Decoder { return &Decoder{ reader: r, anchorNodeMap: map[string]ast.Node{}, + aliasValueMap: make(map[*ast.AliasNode]any), anchorValueMap: map[string]reflect.Value{}, customUnmarshalerMap: map[reflect.Type]func(interface{}, []byte) error{}, opts: opts, @@ -301,9 +303,19 @@ func (d *Decoder) nodeToValue(node ast.Node) interface{} { d.anchorNodeMap[anchorName] = n.Value return anchorValue case *ast.AliasNode: + if v, exists := d.aliasValueMap[n]; exists { + return v + } + // To handle the case where alias is processed recursively, the result of alias can be set to nil in advance. + d.aliasValueMap[n] = nil + aliasName := n.Value.GetToken().Value node := d.anchorNodeMap[aliasName] - return d.nodeToValue(node) + aliasValue := d.nodeToValue(node) + + // once the correct alias value is obtained, overwrite with that value. + d.aliasValueMap[n] = aliasValue + return aliasValue case *ast.LiteralNode: return n.Value.GetValue() case *ast.MappingKeyNode: diff --git a/decode_test.go b/decode_test.go index 26a7901e..36dd3a07 100644 --- a/decode_test.go +++ b/decode_test.go @@ -948,7 +948,21 @@ func TestDecoder(t *testing.T) { "a: &a [1, 2]\nb: *a\n", struct{ B []int }{[]int{1, 2}}, }, - + { + "&0: *0\n*0:\n*0:", + map[string]any{"null": nil}, + }, + { + "key1: &anchor\n subkey: *anchor\nkey2: *anchor\n", + map[string]any{ + "key1": map[string]any{ + "subkey": nil, + }, + "key2": map[string]any{ + "subkey": nil, + }, + }, + }, { "tags:\n- hello-world\na: foo", struct { diff --git a/lexer/lexer_test.go b/lexer/lexer_test.go index b6cc87a7..3c9428f4 100644 --- a/lexer/lexer_test.go +++ b/lexer/lexer_test.go @@ -2400,7 +2400,7 @@ func TestInvalid(t *testing.T) { src string }{ { - name: "literal opt", + name: "literal opt with content", src: ` a: |invalid foo`,