diff --git a/decode.go b/decode.go index afc005c..06690bc 100644 --- a/decode.go +++ b/decode.go @@ -13,6 +13,7 @@ import ( "reflect" "sort" "strconv" + "strings" "time" "github.com/goccy/go-yaml/ast" @@ -332,10 +333,29 @@ func (d *Decoder) nodeToValue(node ast.Node) (any, error) { } b, _ := base64.StdEncoding.DecodeString(v.(string)) return b, nil + case token.BooleanTag: + v, err := d.nodeToValue(n.Value) + if err != nil { + return nil, err + } + str := strings.ToLower(fmt.Sprint(v)) + b, err := strconv.ParseBool(str) + if err == nil { + return b, nil + } + switch str { + case "yes": + return true, nil + case "no": + return false, nil + } + return nil, errors.ErrSyntax(fmt.Sprintf("cannot convert %q to boolean", fmt.Sprint(v)), n.Value.GetToken()) case token.StringTag: return d.nodeToValue(n.Value) case token.MappingTag: return d.nodeToValue(n.Value) + default: + return d.nodeToValue(n.Value) } case *ast.AnchorNode: anchorName := n.Name.GetToken().Value diff --git a/decode_test.go b/decode_test.go index cac2b81..a442a40 100644 --- a/decode_test.go +++ b/decode_test.go @@ -582,6 +582,14 @@ func TestDecoder(t *testing.T) { "v: !!timestamp 2015-01-01", map[string]time.Time{"v": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)}, }, + { + "v: !!bool yes", + map[string]bool{"v": true}, + }, + { + "v: !!bool False", + map[string]bool{"v": false}, + }, // Flow sequence { diff --git a/parser/parser.go b/parser/parser.go index 5776a08..71c99a8 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -241,6 +241,7 @@ func (p *parser) parseTag(ctx *context) (*ast.TagNode, error) { token.StringTag, token.BinaryTag, token.TimestampTag, + token.BooleanTag, token.NullTag: typ := p.currentToken().Type if typ == token.LiteralType || typ == token.FoldedType { @@ -252,7 +253,11 @@ func (p *parser) parseTag(ctx *context) (*ast.TagNode, error) { token.SetTag: err = errors.ErrSyntax(fmt.Sprintf("sorry, currently not supported %s tag", tagToken.Value), tagToken) default: - err = errors.ErrSyntax(fmt.Sprintf("unknown tag name %q specified", tagToken.Value), tagToken) + if strings.HasPrefix(tagToken.Value, "!!") { + err = errors.ErrSyntax(fmt.Sprintf("unknown secondary tag name %q specified", tagToken.Value), tagToken) + } else { + value, err = p.parseToken(ctx, p.currentToken()) + } } if err != nil { return nil, err diff --git a/parser/parser_test.go b/parser/parser_test.go index f1961cf..f9218b7 100644 --- a/parser/parser_test.go +++ b/parser/parser_test.go @@ -114,6 +114,20 @@ func TestParser(t *testing.T) { a: { b: c }} +`, + ` +- !tag + a: b + c: d +`, + ` +a: !tag + b: c +`, + ` +a: !tag + b: c + d: e `, } for _, src := range sources { @@ -958,41 +972,12 @@ func TestSyntaxError(t *testing.T) { expect string }{ { + `v: !!int64 2`, ` -- !tag - a: b - c: d -`, - ` -[2:3] unknown tag name "!tag" specified -> 2 | - !tag - ^ - 3 | a: b - 4 | c: d`, - }, - { - ` -a: !tag - b: c -`, - ` -[2:4] unknown tag name "!tag" specified -> 2 | a: !tag +[1:4] unknown secondary tag name "!!int64" specified +> 1 | v: !!int64 2 ^ - 3 | b: c`, - }, - { - ` -a: !tag - b: c - d: e `, - ` -[2:4] unknown tag name "!tag" specified -> 2 | a: !tag - ^ - 3 | b: c - 4 | d: e`, }, { ` diff --git a/token/token.go b/token/token.go index 8eaf974..a90d0df 100644 --- a/token/token.go +++ b/token/token.go @@ -401,6 +401,8 @@ const ( SetTag ReservedTagKeyword = "!!set" // TimestampTag `!!timestamp` tag TimestampTag ReservedTagKeyword = "!!timestamp" + // BooleanTag `!!bool` tag + BooleanTag ReservedTagKeyword = "!!bool" ) var ( @@ -506,6 +508,16 @@ var ( Position: pos, } }, + BooleanTag: func(value, org string, pos *Position) *Token { + return &Token{ + Type: TagType, + CharacterType: CharacterTypeIndicator, + Indicator: NodePropertyIndicator, + Value: value, + Origin: org, + Position: pos, + } + }, } )