package yaml import ( "encoding/base64" "fmt" "reflect" "strconv" "time" ) const ( documentNode = 1 << iota mappingNode sequenceNode scalarNode aliasNode ) type node struct { kind int line, column int tag string value string implicit bool children []*node anchors map[string]*node } // ---------------------------------------------------------------------------- // Parser, produces a node tree out of a libyaml event stream. type parser struct { parser yaml_parser_t event yaml_event_t doc *node } 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) p.skip() if p.event.typ != yaml_STREAM_START_EVENT { panic("Expected stream start event, got " + strconv.Itoa(int(p.event.typ))) } p.skip() return &p } func (p *parser) destroy() { if p.event.typ != yaml_NO_EVENT { yaml_event_delete(&p.event) } yaml_parser_delete(&p.parser) } func (p *parser) skip() { if p.event.typ != yaml_NO_EVENT { if p.event.typ == yaml_STREAM_END_EVENT { fail("Attempted to go past the end of stream. Corrupted value?") } yaml_event_delete(&p.event) } if !yaml_parser_parse(&p.parser, &p.event) { p.fail() } } func (p *parser) fail() { var where string var line int if p.parser.problem_mark.line != 0 { line = p.parser.problem_mark.line } else if p.parser.context_mark.line != 0 { line = p.parser.context_mark.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" } fail(where + msg) } func (p *parser) anchor(n *node, anchor []byte) { if anchor != nil { p.doc.anchors[string(anchor)] = n } } func (p *parser) parse() *node { switch p.event.typ { 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: // Happens when attempting to decode an empty buffer. return nil default: panic("Attempted to parse unknown event: " + strconv.Itoa(int(p.event.typ))) } panic("unreachable") } func (p *parser) node(kind int) *node { return &node{ kind: kind, line: p.event.start_mark.line, column: p.event.start_mark.column, } } func (p *parser) document() *node { n := p.node(documentNode) n.anchors = make(map[string]*node) p.doc = n p.skip() n.children = append(n.children, p.parse()) if p.event.typ != yaml_DOCUMENT_END_EVENT { panic("Expected end of document event but got " + strconv.Itoa(int(p.event.typ))) } p.skip() return n } func (p *parser) alias() *node { n := p.node(aliasNode) n.value = string(p.event.anchor) p.skip() return n } func (p *parser) scalar() *node { n := p.node(scalarNode) n.value = string(p.event.value) n.tag = string(p.event.tag) n.implicit = p.event.implicit p.anchor(n, p.event.anchor) p.skip() return n } func (p *parser) sequence() *node { n := p.node(sequenceNode) p.anchor(n, p.event.anchor) p.skip() for p.event.typ != yaml_SEQUENCE_END_EVENT { n.children = append(n.children, p.parse()) } p.skip() return n } func (p *parser) mapping() *node { n := p.node(mappingNode) p.anchor(n, p.event.anchor) p.skip() for p.event.typ != yaml_MAPPING_END_EVENT { n.children = append(n.children, p.parse(), p.parse()) } p.skip() return n } // ---------------------------------------------------------------------------- // Decoder, unmarshals a node into a provided value. type decoder struct { doc *node aliases map[string]bool } func newDecoder() *decoder { d := &decoder{} d.aliases = make(map[string]bool) return d } // d.setter deals with setters and pointer dereferencing and initialization. // // It's a slightly convoluted case to handle properly: // // - nil pointers should be initialized, unless being set to nil // - we don't know at this point yet what's the value to SetYAML() with. // - we can't separate pointer deref/init and setter checking, because // a setter may be found while going down a pointer chain. // // Thus, here is how it takes care of it: // // - out is provided as a pointer, so that it can be replaced. // - when looking at a non-setter ptr, *out=ptr.Elem(), unless tag=!!null // - when a setter is found, *out=interface{}, and a set() function is // returned to call SetYAML() with the value of *out once it's defined. // func (d *decoder) setter(tag string, out *reflect.Value, good *bool) (set func()) { if (*out).Kind() != reflect.Ptr && (*out).CanAddr() { setter, _ := (*out).Addr().Interface().(Setter) if setter != nil { var arg interface{} *out = reflect.ValueOf(&arg).Elem() return func() { *good = setter.SetYAML(shortTag(tag), arg) } } } again := true for again { again = false setter, _ := (*out).Interface().(Setter) if tag != yaml_NULL_TAG || setter != nil { if pv := (*out); pv.Kind() == reflect.Ptr { if pv.IsNil() { *out = reflect.New(pv.Type().Elem()).Elem() pv.Set((*out).Addr()) } else { *out = pv.Elem() } setter, _ = pv.Interface().(Setter) again = true } } if setter != nil { var arg interface{} *out = reflect.ValueOf(&arg).Elem() return func() { *good = setter.SetYAML(shortTag(tag), arg) } } } return nil } func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) { switch n.kind { case documentNode: good = d.document(n, out) case scalarNode: good = d.scalar(n, out) case aliasNode: good = d.alias(n, out) case mappingNode: good = d.mapping(n, out) case sequenceNode: good = d.sequence(n, out) default: panic("Internal error: unknown node kind: " + strconv.Itoa(n.kind)) } return } func (d *decoder) document(n *node, out reflect.Value) (good bool) { if len(n.children) == 1 { d.doc = n d.unmarshal(n.children[0], out) return true } return false } func (d *decoder) alias(n *node, out reflect.Value) (good bool) { an, ok := d.doc.anchors[n.value] if !ok { fail("Unknown anchor '" + n.value + "' referenced") } if d.aliases[n.value] { fail("Anchor '" + n.value + "' value contains itself") } d.aliases[n.value] = true good = d.unmarshal(an, out) delete(d.aliases, n.value) return good } var zeroValue reflect.Value func resetMap(out reflect.Value) { for _, k := range out.MapKeys() { out.SetMapIndex(k, zeroValue) } } var durationType = reflect.TypeOf(time.Duration(0)) func (d *decoder) scalar(n *node, out reflect.Value) (good bool) { var tag string var resolved interface{} if n.tag == "" && !n.implicit { tag = yaml_STR_TAG resolved = n.value } else { tag, resolved = resolve(n.tag, n.value) if tag == yaml_BINARY_TAG { data, err := base64.StdEncoding.DecodeString(resolved.(string)) if err != nil { fail("!!binary value contains invalid base64 data") } resolved = string(data) } } if set := d.setter(tag, &out, &good); set != nil { defer set() } if resolved == nil { if out.Kind() == reflect.Map && !out.CanAddr() { resetMap(out) } else { out.Set(reflect.Zero(out.Type())) } good = true return } switch out.Kind() { case reflect.String: if tag == yaml_BINARY_TAG { out.SetString(resolved.(string)) good = true } else if resolved != nil { out.SetString(n.value) good = true } case reflect.Interface: if resolved == nil { out.Set(reflect.Zero(out.Type())) } else { out.Set(reflect.ValueOf(resolved)) } good = true case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: switch resolved := resolved.(type) { case int: if !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) good = true } case int64: if !out.OverflowInt(resolved) { out.SetInt(resolved) good = true } case float64: if resolved < 1<<63-1 && !out.OverflowInt(int64(resolved)) { out.SetInt(int64(resolved)) good = true } case string: if out.Type() == durationType { d, err := time.ParseDuration(resolved) if err == nil { out.SetInt(int64(d)) good = true } } } case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: switch resolved := resolved.(type) { case int: if resolved >= 0 { out.SetUint(uint64(resolved)) good = true } case int64: if resolved >= 0 { out.SetUint(uint64(resolved)) good = true } case float64: if resolved < 1<<64-1 && !out.OverflowUint(uint64(resolved)) { out.SetUint(uint64(resolved)) good = true } } case reflect.Bool: switch resolved := resolved.(type) { case bool: out.SetBool(resolved) good = true } case reflect.Float32, reflect.Float64: switch resolved := resolved.(type) { case int: out.SetFloat(float64(resolved)) good = true case int64: out.SetFloat(float64(resolved)) good = true case float64: out.SetFloat(resolved) good = true } case reflect.Ptr: if out.Type().Elem() == reflect.TypeOf(resolved) { elem := reflect.New(out.Type().Elem()) elem.Elem().Set(reflect.ValueOf(resolved)) out.Set(elem) good = true } } return good } 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) { if set := d.setter(yaml_SEQ_TAG, &out, &good); set != nil { defer set() } var iface reflect.Value if out.Kind() == reflect.Interface { // No type hints. Will have to use a generic sequence. iface = out out = settableValueOf(make([]interface{}, 0)) } if out.Kind() != reflect.Slice { return false } et := out.Type().Elem() l := len(n.children) for i := 0; i < l; i++ { e := reflect.New(et).Elem() if ok := d.unmarshal(n.children[i], e); ok { out.Set(reflect.Append(out, e)) } } if iface.IsValid() { iface.Set(out) } return true } func (d *decoder) mapping(n *node, out reflect.Value) (good bool) { if set := d.setter(yaml_MAP_TAG, &out, &good); set != nil { defer set() } if out.Kind() == reflect.Struct { return d.mappingStruct(n, out) } if out.Kind() == reflect.Interface { // No type hints. Will have to use a generic map. iface := out out = settableValueOf(make(map[interface{}]interface{})) iface.Set(out) } if out.Kind() != reflect.Map { return false } outt := out.Type() kt := outt.Key() et := outt.Elem() if out.IsNil() { out.Set(reflect.MakeMap(outt)) } l := len(n.children) for i := 0; i < l; i += 2 { if isMerge(n.children[i]) { d.merge(n.children[i+1], out) continue } k := reflect.New(kt).Elem() if d.unmarshal(n.children[i], k) { kkind := k.Kind() if kkind == reflect.Interface { kkind = k.Elem().Kind() } if kkind == reflect.Map || kkind == reflect.Slice { fail(fmt.Sprintf("invalid map key: %#v", k.Interface())) } e := reflect.New(et).Elem() if d.unmarshal(n.children[i+1], e) { out.SetMapIndex(k, e) } } } return true } func (d *decoder) mappingStruct(n *node, out reflect.Value) (good bool) { sinfo, err := getStructInfo(out.Type()) if err != nil { panic(err) } name := settableValueOf("") l := len(n.children) for i := 0; i < l; i += 2 { ni := n.children[i] if isMerge(ni) { d.merge(n.children[i+1], out) continue } if !d.unmarshal(ni, name) { continue } if info, ok := sinfo.FieldsMap[name.String()]; ok { var field reflect.Value if info.Inline == nil { field = out.Field(info.Num) } else { field = out.FieldByIndex(info.Inline) } d.unmarshal(n.children[i+1], field) } } return true } func (d *decoder) merge(n *node, out reflect.Value) { const wantMap = "map merge requires map or sequence of maps as the value" switch n.kind { case mappingNode: d.unmarshal(n, out) case aliasNode: an, ok := d.doc.anchors[n.value] if ok && an.kind != mappingNode { fail(wantMap) } d.unmarshal(n, out) case sequenceNode: // Step backwards as earlier nodes take precedence. for i := len(n.children) - 1; i >= 0; i-- { ni := n.children[i] if ni.kind == aliasNode { an, ok := d.doc.anchors[ni.value] if ok && an.kind != mappingNode { fail(wantMap) } } else if ni.kind != mappingNode { fail(wantMap) } d.unmarshal(ni, out) } default: fail(wantMap) } } func isMerge(n *node) bool { return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG) }