package yaml
import (
"encoding"
"encoding/base64"
"fmt"
"io"
"math"
"reflect"
"strconv"
"time"
)
type parser struct {
parser yaml_parser_t
event yaml_event_t
doc *Node
anchors map [string ]*Node
doneInit bool
textless bool
}
func newParser(b []byte ) *parser {
p := parser {}
if !yaml_parser_initialize (&p .parser ) {
panic ("failed to initialize YAML emitter" )
}
if len (b ) == 0 {
b = []byte {'\n' }
}
yaml_parser_set_input_string (&p .parser , b )
return &p
}
func newParserFromReader(r io .Reader ) *parser {
p := parser {}
if !yaml_parser_initialize (&p .parser ) {
panic ("failed to initialize YAML emitter" )
}
yaml_parser_set_input_reader (&p .parser , r )
return &p
}
func (p *parser ) init () {
if p .doneInit {
return
}
p .anchors = make (map [string ]*Node )
p .expect (yaml_STREAM_START_EVENT )
p .doneInit = true
}
func (p *parser ) destroy () {
if p .event .typ != yaml_NO_EVENT {
yaml_event_delete (&p .event )
}
yaml_parser_delete (&p .parser )
}
func (p *parser ) expect (e yaml_event_type_t ) {
if p .event .typ == yaml_NO_EVENT {
if !yaml_parser_parse (&p .parser , &p .event ) {
p .fail ()
}
}
if p .event .typ == yaml_STREAM_END_EVENT {
failf ("attempted to go past the end of stream; corrupted value?" )
}
if p .event .typ != e {
p .parser .problem = fmt .Sprintf ("expected %s event but got %s" , e , p .event .typ )
p .fail ()
}
yaml_event_delete (&p .event )
p .event .typ = yaml_NO_EVENT
}
func (p *parser ) peek () yaml_event_type_t {
if p .event .typ != yaml_NO_EVENT {
return p .event .typ
}
if !yaml_parser_parse (&p .parser , &p .event ) || p .parser .error != yaml_NO_ERROR {
p .fail ()
}
return p .event .typ
}
func (p *parser ) fail () {
var where string
var line int
if p .parser .context_mark .line != 0 {
line = p .parser .context_mark .line
if p .parser .error == yaml_SCANNER_ERROR {
line ++
}
} else if p .parser .problem_mark .line != 0 {
line = p .parser .problem_mark .line
if p .parser .error == yaml_SCANNER_ERROR {
line ++
}
}
if line != 0 {
where = "line " + strconv .Itoa (line ) + ": "
}
var msg string
if len (p .parser .problem ) > 0 {
msg = p .parser .problem
} else {
msg = "unknown problem parsing YAML content"
}
failf ("%s%s" , where , msg )
}
func (p *parser ) anchor (n *Node , anchor []byte ) {
if anchor != nil {
n .Anchor = string (anchor )
p .anchors [n .Anchor ] = n
}
}
func (p *parser ) parse () *Node {
p .init ()
switch p .peek () {
case yaml_SCALAR_EVENT :
return p .scalar ()
case yaml_ALIAS_EVENT :
return p .alias ()
case yaml_MAPPING_START_EVENT :
return p .mapping ()
case yaml_SEQUENCE_START_EVENT :
return p .sequence ()
case yaml_DOCUMENT_START_EVENT :
return p .document ()
case yaml_STREAM_END_EVENT :
return nil
case yaml_TAIL_COMMENT_EVENT :
panic ("internal error: unexpected tail comment event (please report)" )
default :
panic ("internal error: attempted to parse unknown event (please report): " + p .event .typ .String ())
}
}
func (p *parser ) node (kind Kind , defaultTag , tag , value string ) *Node {
var style Style
if tag != "" && tag != "!" {
tag = shortTag (tag )
style = TaggedStyle
} else if defaultTag != "" {
tag = defaultTag
} else if kind == ScalarNode {
tag , _ = resolve ("" , value )
}
n := &Node {
Kind : kind ,
Tag : tag ,
Value : value ,
Style : style ,
}
if !p .textless {
n .Line = p .event .start_mark .line + 1
n .Column = p .event .start_mark .column + 1
n .HeadComment = string (p .event .head_comment )
n .LineComment = string (p .event .line_comment )
n .FootComment = string (p .event .foot_comment )
}
return n
}
func (p *parser ) parseChild (parent *Node ) *Node {
child := p .parse ()
parent .Content = append (parent .Content , child )
return child
}
func (p *parser ) document () *Node {
n := p .node (DocumentNode , "" , "" , "" )
p .doc = n
p .expect (yaml_DOCUMENT_START_EVENT )
p .parseChild (n )
if p .peek () == yaml_DOCUMENT_END_EVENT {
n .FootComment = string (p .event .foot_comment )
}
p .expect (yaml_DOCUMENT_END_EVENT )
return n
}
func (p *parser ) alias () *Node {
n := p .node (AliasNode , "" , "" , string (p .event .anchor ))
n .Alias = p .anchors [n .Value ]
if n .Alias == nil {
failf ("unknown anchor '%s' referenced" , n .Value )
}
p .expect (yaml_ALIAS_EVENT )
return n
}
func (p *parser ) scalar () *Node {
var parsedStyle = p .event .scalar_style ()
var nodeStyle Style
switch {
case parsedStyle &yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0 :
nodeStyle = DoubleQuotedStyle
case parsedStyle &yaml_SINGLE_QUOTED_SCALAR_STYLE != 0 :
nodeStyle = SingleQuotedStyle
case parsedStyle &yaml_LITERAL_SCALAR_STYLE != 0 :
nodeStyle = LiteralStyle
case parsedStyle &yaml_FOLDED_SCALAR_STYLE != 0 :
nodeStyle = FoldedStyle
}
var nodeValue = string (p .event .value )
var nodeTag = string (p .event .tag )
var defaultTag string
if nodeStyle == 0 {
if nodeValue == "<<" {
defaultTag = mergeTag
}
} else {
defaultTag = strTag
}
n := p .node (ScalarNode , defaultTag , nodeTag , nodeValue )
n .Style |= nodeStyle
p .anchor (n , p .event .anchor )
p .expect (yaml_SCALAR_EVENT )
return n
}
func (p *parser ) sequence () *Node {
n := p .node (SequenceNode , seqTag , string (p .event .tag ), "" )
if p .event .sequence_style ()&yaml_FLOW_SEQUENCE_STYLE != 0 {
n .Style |= FlowStyle
}
p .anchor (n , p .event .anchor )
p .expect (yaml_SEQUENCE_START_EVENT )
for p .peek () != yaml_SEQUENCE_END_EVENT {
p .parseChild (n )
}
n .LineComment = string (p .event .line_comment )
n .FootComment = string (p .event .foot_comment )
p .expect (yaml_SEQUENCE_END_EVENT )
return n
}
func (p *parser ) mapping () *Node {
n := p .node (MappingNode , mapTag , string (p .event .tag ), "" )
block := true
if p .event .mapping_style ()&yaml_FLOW_MAPPING_STYLE != 0 {
block = false
n .Style |= FlowStyle
}
p .anchor (n , p .event .anchor )
p .expect (yaml_MAPPING_START_EVENT )
for p .peek () != yaml_MAPPING_END_EVENT {
k := p .parseChild (n )
if block && k .FootComment != "" {
if len (n .Content ) > 2 {
n .Content [len (n .Content )-3 ].FootComment = k .FootComment
k .FootComment = ""
}
}
v := p .parseChild (n )
if k .FootComment == "" && v .FootComment != "" {
k .FootComment = v .FootComment
v .FootComment = ""
}
if p .peek () == yaml_TAIL_COMMENT_EVENT {
if k .FootComment == "" {
k .FootComment = string (p .event .foot_comment )
}
p .expect (yaml_TAIL_COMMENT_EVENT )
}
}
n .LineComment = string (p .event .line_comment )
n .FootComment = string (p .event .foot_comment )
if n .Style &FlowStyle == 0 && n .FootComment != "" && len (n .Content ) > 1 {
n .Content [len (n .Content )-2 ].FootComment = n .FootComment
n .FootComment = ""
}
p .expect (yaml_MAPPING_END_EVENT )
return n
}
type decoder struct {
doc *Node
aliases map [*Node ]bool
terrors []string
stringMapType reflect .Type
generalMapType reflect .Type
knownFields bool
uniqueKeys bool
decodeCount int
aliasCount int
aliasDepth int
mergedFields map [interface {}]bool
}
var (
nodeType = reflect .TypeOf (Node {})
durationType = reflect .TypeOf (time .Duration (0 ))
stringMapType = reflect .TypeOf (map [string ]interface {}{})
generalMapType = reflect .TypeOf (map [interface {}]interface {}{})
ifaceType = generalMapType .Elem ()
timeType = reflect .TypeOf (time .Time {})
ptrTimeType = reflect .TypeOf (&time .Time {})
)
func newDecoder() *decoder {
d := &decoder {
stringMapType : stringMapType ,
generalMapType : generalMapType ,
uniqueKeys : true ,
}
d .aliases = make (map [*Node ]bool )
return d
}
func (d *decoder ) terror (n *Node , tag string , out reflect .Value ) {
if n .Tag != "" {
tag = n .Tag
}
value := n .Value
if tag != seqTag && tag != mapTag {
if len (value ) > 10 {
value = " `" + value [:7 ] + "...`"
} else {
value = " `" + value + "`"
}
}
d .terrors = append (d .terrors , fmt .Sprintf ("line %d: cannot unmarshal %s%s into %s" , n .Line , shortTag (tag ), value , out .Type ()))
}
func (d *decoder ) callUnmarshaler (n *Node , u Unmarshaler ) (good bool ) {
err := u .UnmarshalYAML (n )
if e , ok := err .(*TypeError ); ok {
d .terrors = append (d .terrors , e .Errors ...)
return false
}
if err != nil {
fail (err )
}
return true
}
func (d *decoder ) callObsoleteUnmarshaler (n *Node , u obsoleteUnmarshaler ) (good bool ) {
terrlen := len (d .terrors )
err := u .UnmarshalYAML (func (v interface {}) (err error ) {
defer handleErr (&err )
d .unmarshal (n , reflect .ValueOf (v ))
if len (d .terrors ) > terrlen {
issues := d .terrors [terrlen :]
d .terrors = d .terrors [:terrlen ]
return &TypeError {issues }
}
return nil
})
if e , ok := err .(*TypeError ); ok {
d .terrors = append (d .terrors , e .Errors ...)
return false
}
if err != nil {
fail (err )
}
return true
}
func (d *decoder ) prepare (n *Node , out reflect .Value ) (newout reflect .Value , unmarshaled , good bool ) {
if n .ShortTag () == nullTag {
return out , false , false
}
again := true
for again {
again = false
if out .Kind () == reflect .Ptr {
if out .IsNil () {
out .Set (reflect .New (out .Type ().Elem ()))
}
out = out .Elem ()
again = true
}
if out .CanAddr () {
outi := out .Addr ().Interface ()
if u , ok := outi .(Unmarshaler ); ok {
good = d .callUnmarshaler (n , u )
return out , true , good
}
if u , ok := outi .(obsoleteUnmarshaler ); ok {
good = d .callObsoleteUnmarshaler (n , u )
return out , true , good
}
}
}
return out , false , false
}
func (d *decoder ) fieldByIndex (n *Node , v reflect .Value , index []int ) (field reflect .Value ) {
if n .ShortTag () == nullTag {
return reflect .Value {}
}
for _ , num := range index {
for {
if v .Kind () == reflect .Ptr {
if v .IsNil () {
v .Set (reflect .New (v .Type ().Elem ()))
}
v = v .Elem ()
continue
}
break
}
v = v .Field (num )
}
return v
}
const (
alias_ratio_range_low = 400000
alias_ratio_range_high = 4000000
alias_ratio_range = float64 (alias_ratio_range_high - alias_ratio_range_low )
)
func allowedAliasRatio(decodeCount int ) float64 {
switch {
case decodeCount <= alias_ratio_range_low :
return 0.99
case decodeCount >= alias_ratio_range_high :
return 0.10
default :
return 0.99 - 0.89 *(float64 (decodeCount -alias_ratio_range_low )/alias_ratio_range )
}
}
func (d *decoder ) unmarshal (n *Node , out reflect .Value ) (good bool ) {
d .decodeCount ++
if d .aliasDepth > 0 {
d .aliasCount ++
}
if d .aliasCount > 100 && d .decodeCount > 1000 && float64 (d .aliasCount )/float64 (d .decodeCount ) > allowedAliasRatio (d .decodeCount ) {
failf ("document contains excessive aliasing" )
}
if out .Type () == nodeType {
out .Set (reflect .ValueOf (n ).Elem ())
return true
}
switch n .Kind {
case DocumentNode :
return d .document (n , out )
case AliasNode :
return d .alias (n , out )
}
out , unmarshaled , good := d .prepare (n , out )
if unmarshaled {
return good
}
switch n .Kind {
case ScalarNode :
good = d .scalar (n , out )
case MappingNode :
good = d .mapping (n , out )
case SequenceNode :
good = d .sequence (n , out )
case 0 :
if n .IsZero () {
return d .null (out )
}
fallthrough
default :
failf ("cannot decode node with unknown kind %d" , n .Kind )
}
return good
}
func (d *decoder ) document (n *Node , out reflect .Value ) (good bool ) {
if len (n .Content ) == 1 {
d .doc = n
d .unmarshal (n .Content [0 ], out )
return true
}
return false
}
func (d *decoder ) alias (n *Node , out reflect .Value ) (good bool ) {
if d .aliases [n ] {
failf ("anchor '%s' value contains itself" , n .Value )
}
d .aliases [n ] = true
d .aliasDepth ++
good = d .unmarshal (n .Alias , out )
d .aliasDepth --
delete (d .aliases , n )
return good
}
var zeroValue reflect .Value
func resetMap(out reflect .Value ) {
for _ , k := range out .MapKeys () {
out .SetMapIndex (k , zeroValue )
}
}
func (d *decoder ) null (out reflect .Value ) bool {
if out .CanAddr () {
switch out .Kind () {
case reflect .Interface , reflect .Ptr , reflect .Map , reflect .Slice :
out .Set (reflect .Zero (out .Type ()))
return true
}
}
return false
}
func (d *decoder ) scalar (n *Node , out reflect .Value ) bool {
var tag string
var resolved interface {}
if n .indicatedString () {
tag = strTag
resolved = n .Value
} else {
tag , resolved = resolve (n .Tag , n .Value )
if tag == binaryTag {
data , err := base64 .StdEncoding .DecodeString (resolved .(string ))
if err != nil {
failf ("!!binary value contains invalid base64 data" )
}
resolved = string (data )
}
}
if resolved == nil {
return d .null (out )
}
if resolvedv := reflect .ValueOf (resolved ); out .Type () == resolvedv .Type () {
out .Set (resolvedv )
return true
}
if out .CanAddr () {
u , ok := out .Addr ().Interface ().(encoding .TextUnmarshaler )
if ok {
var text []byte
if tag == binaryTag {
text = []byte (resolved .(string ))
} else {
text = []byte (n .Value )
}
err := u .UnmarshalText (text )
if err != nil {
fail (err )
}
return true
}
}
switch out .Kind () {
case reflect .String :
if tag == binaryTag {
out .SetString (resolved .(string ))
return true
}
out .SetString (n .Value )
return true
case reflect .Interface :
out .Set (reflect .ValueOf (resolved ))
return true
case reflect .Int , reflect .Int8 , reflect .Int16 , reflect .Int32 , reflect .Int64 :
isDuration := out .Type () == durationType
switch resolved := resolved .(type ) {
case int :
if !isDuration && !out .OverflowInt (int64 (resolved )) {
out .SetInt (int64 (resolved ))
return true
}
case int64 :
if !isDuration && !out .OverflowInt (resolved ) {
out .SetInt (resolved )
return true
}
case uint64 :
if !isDuration && resolved <= math .MaxInt64 && !out .OverflowInt (int64 (resolved )) {
out .SetInt (int64 (resolved ))
return true
}
case float64 :
if !isDuration && resolved <= math .MaxInt64 && !out .OverflowInt (int64 (resolved )) {
out .SetInt (int64 (resolved ))
return true
}
case string :
if out .Type () == durationType {
d , err := time .ParseDuration (resolved )
if err == nil {
out .SetInt (int64 (d ))
return true
}
}
}
case reflect .Uint , reflect .Uint8 , reflect .Uint16 , reflect .Uint32 , reflect .Uint64 , reflect .Uintptr :
switch resolved := resolved .(type ) {
case int :
if resolved >= 0 && !out .OverflowUint (uint64 (resolved )) {
out .SetUint (uint64 (resolved ))
return true
}
case int64 :
if resolved >= 0 && !out .OverflowUint (uint64 (resolved )) {
out .SetUint (uint64 (resolved ))
return true
}
case uint64 :
if !out .OverflowUint (uint64 (resolved )) {
out .SetUint (uint64 (resolved ))
return true
}
case float64 :
if resolved <= math .MaxUint64 && !out .OverflowUint (uint64 (resolved )) {
out .SetUint (uint64 (resolved ))
return true
}
}
case reflect .Bool :
switch resolved := resolved .(type ) {
case bool :
out .SetBool (resolved )
return true
case string :
switch resolved {
case "y" , "Y" , "yes" , "Yes" , "YES" , "on" , "On" , "ON" :
out .SetBool (true )
return true
case "n" , "N" , "no" , "No" , "NO" , "off" , "Off" , "OFF" :
out .SetBool (false )
return true
}
}
case reflect .Float32 , reflect .Float64 :
switch resolved := resolved .(type ) {
case int :
out .SetFloat (float64 (resolved ))
return true
case int64 :
out .SetFloat (float64 (resolved ))
return true
case uint64 :
out .SetFloat (float64 (resolved ))
return true
case float64 :
out .SetFloat (resolved )
return true
}
case reflect .Struct :
if resolvedv := reflect .ValueOf (resolved ); out .Type () == resolvedv .Type () {
out .Set (resolvedv )
return true
}
case reflect .Ptr :
panic ("yaml internal error: please report the issue" )
}
d .terror (n , tag , out )
return false
}
func settableValueOf(i interface {}) reflect .Value {
v := reflect .ValueOf (i )
sv := reflect .New (v .Type ()).Elem ()
sv .Set (v )
return sv
}
func (d *decoder ) sequence (n *Node , out reflect .Value ) (good bool ) {
l := len (n .Content )
var iface reflect .Value
switch out .Kind () {
case reflect .Slice :
out .Set (reflect .MakeSlice (out .Type (), l , l ))
case reflect .Array :
if l != out .Len () {
failf ("invalid array: want %d elements but got %d" , out .Len (), l )
}
case reflect .Interface :
iface = out
out = settableValueOf (make ([]interface {}, l ))
default :
d .terror (n , seqTag , out )
return false
}
et := out .Type ().Elem ()
j := 0
for i := 0 ; i < l ; i ++ {
e := reflect .New (et ).Elem ()
if ok := d .unmarshal (n .Content [i ], e ); ok {
out .Index (j ).Set (e )
j ++
}
}
if out .Kind () != reflect .Array {
out .Set (out .Slice (0 , j ))
}
if iface .IsValid () {
iface .Set (out )
}
return true
}
func (d *decoder ) mapping (n *Node , out reflect .Value ) (good bool ) {
l := len (n .Content )
if d .uniqueKeys {
nerrs := len (d .terrors )
for i := 0 ; i < l ; i += 2 {
ni := n .Content [i ]
for j := i + 2 ; j < l ; j += 2 {
nj := n .Content [j ]
if ni .Kind == nj .Kind && ni .Value == nj .Value {
d .terrors = append (d .terrors , fmt .Sprintf ("line %d: mapping key %#v already defined at line %d" , nj .Line , nj .Value , ni .Line ))
}
}
}
if len (d .terrors ) > nerrs {
return false
}
}
switch out .Kind () {
case reflect .Struct :
return d .mappingStruct (n , out )
case reflect .Map :
case reflect .Interface :
iface := out
if isStringMap (n ) {
out = reflect .MakeMap (d .stringMapType )
} else {
out = reflect .MakeMap (d .generalMapType )
}
iface .Set (out )
default :
d .terror (n , mapTag , out )
return false
}
outt := out .Type ()
kt := outt .Key ()
et := outt .Elem ()
stringMapType := d .stringMapType
generalMapType := d .generalMapType
if outt .Elem () == ifaceType {
if outt .Key ().Kind () == reflect .String {
d .stringMapType = outt
} else if outt .Key () == ifaceType {
d .generalMapType = outt
}
}
mergedFields := d .mergedFields
d .mergedFields = nil
var mergeNode *Node
mapIsNew := false
if out .IsNil () {
out .Set (reflect .MakeMap (outt ))
mapIsNew = true
}
for i := 0 ; i < l ; i += 2 {
if isMerge (n .Content [i ]) {
mergeNode = n .Content [i +1 ]
continue
}
k := reflect .New (kt ).Elem ()
if d .unmarshal (n .Content [i ], k ) {
if mergedFields != nil {
ki := k .Interface ()
if mergedFields [ki ] {
continue
}
mergedFields [ki ] = true
}
kkind := k .Kind ()
if kkind == reflect .Interface {
kkind = k .Elem ().Kind ()
}
if kkind == reflect .Map || kkind == reflect .Slice {
failf ("invalid map key: %#v" , k .Interface ())
}
e := reflect .New (et ).Elem ()
if d .unmarshal (n .Content [i +1 ], e ) || n .Content [i +1 ].ShortTag () == nullTag && (mapIsNew || !out .MapIndex (k ).IsValid ()) {
out .SetMapIndex (k , e )
}
}
}
d .mergedFields = mergedFields
if mergeNode != nil {
d .merge (n , mergeNode , out )
}
d .stringMapType = stringMapType
d .generalMapType = generalMapType
return true
}
func isStringMap(n *Node ) bool {
if n .Kind != MappingNode {
return false
}
l := len (n .Content )
for i := 0 ; i < l ; i += 2 {
shortTag := n .Content [i ].ShortTag ()
if shortTag != strTag && shortTag != mergeTag {
return false
}
}
return true
}
func (d *decoder ) mappingStruct (n *Node , out reflect .Value ) (good bool ) {
sinfo , err := getStructInfo (out .Type ())
if err != nil {
panic (err )
}
var inlineMap reflect .Value
var elemType reflect .Type
if sinfo .InlineMap != -1 {
inlineMap = out .Field (sinfo .InlineMap )
elemType = inlineMap .Type ().Elem ()
}
for _ , index := range sinfo .InlineUnmarshalers {
field := d .fieldByIndex (n , out , index )
d .prepare (n , field )
}
mergedFields := d .mergedFields
d .mergedFields = nil
var mergeNode *Node
var doneFields []bool
if d .uniqueKeys {
doneFields = make ([]bool , len (sinfo .FieldsList ))
}
name := settableValueOf ("" )
l := len (n .Content )
for i := 0 ; i < l ; i += 2 {
ni := n .Content [i ]
if isMerge (ni ) {
mergeNode = n .Content [i +1 ]
continue
}
if !d .unmarshal (ni , name ) {
continue
}
sname := name .String ()
if mergedFields != nil {
if mergedFields [sname ] {
continue
}
mergedFields [sname ] = true
}
if info , ok := sinfo .FieldsMap [sname ]; ok {
if d .uniqueKeys {
if doneFields [info .Id ] {
d .terrors = append (d .terrors , fmt .Sprintf ("line %d: field %s already set in type %s" , ni .Line , name .String (), out .Type ()))
continue
}
doneFields [info .Id ] = true
}
var field reflect .Value
if info .Inline == nil {
field = out .Field (info .Num )
} else {
field = d .fieldByIndex (n , out , info .Inline )
}
d .unmarshal (n .Content [i +1 ], field )
} else if sinfo .InlineMap != -1 {
if inlineMap .IsNil () {
inlineMap .Set (reflect .MakeMap (inlineMap .Type ()))
}
value := reflect .New (elemType ).Elem ()
d .unmarshal (n .Content [i +1 ], value )
inlineMap .SetMapIndex (name , value )
} else if d .knownFields {
d .terrors = append (d .terrors , fmt .Sprintf ("line %d: field %s not found in type %s" , ni .Line , name .String (), out .Type ()))
}
}
d .mergedFields = mergedFields
if mergeNode != nil {
d .merge (n , mergeNode , out )
}
return true
}
func failWantMap() {
failf ("map merge requires map or sequence of maps as the value" )
}
func (d *decoder ) merge (parent *Node , merge *Node , out reflect .Value ) {
mergedFields := d .mergedFields
if mergedFields == nil {
d .mergedFields = make (map [interface {}]bool )
for i := 0 ; i < len (parent .Content ); i += 2 {
k := reflect .New (ifaceType ).Elem ()
if d .unmarshal (parent .Content [i ], k ) {
d .mergedFields [k .Interface ()] = true
}
}
}
switch merge .Kind {
case MappingNode :
d .unmarshal (merge , out )
case AliasNode :
if merge .Alias != nil && merge .Alias .Kind != MappingNode {
failWantMap ()
}
d .unmarshal (merge , out )
case SequenceNode :
for i := 0 ; i < len (merge .Content ); i ++ {
ni := merge .Content [i ]
if ni .Kind == AliasNode {
if ni .Alias != nil && ni .Alias .Kind != MappingNode {
failWantMap ()
}
} else if ni .Kind != MappingNode {
failWantMap ()
}
d .unmarshal (ni , out )
}
default :
failWantMap ()
}
d .mergedFields = mergedFields
}
func isMerge(n *Node ) bool {
return n .Kind == ScalarNode && n .Value == "<<" && (n .Tag == "" || n .Tag == "!" || shortTag (n .Tag ) == mergeTag )
}
The pages are generated with Golds v0.8.2 . (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 .