| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172 |
- package goyaml
- /* #include "helpers.c" */
- import "C"
- import (
- "unsafe"
- "reflect"
- "strconv"
- )
- type decoder struct {
- parser *C.yaml_parser_t
- event *C.yaml_event_t
- }
- func newDecoder(b []byte) *decoder {
- if len(b) == 0 {
- panic("Can't handle empty buffers yet") // XXX Fix this.
- }
- d := decoder{}
- d.event = &C.yaml_event_t{}
- d.parser = &C.yaml_parser_t{}
- C.yaml_parser_initialize(d.parser)
- // How unsafe is this really? Will this break if the GC becomes compacting?
- // Probably not, otherwise that would likely break &parse below as well.
- input := (*C.uchar)(unsafe.Pointer(&b[0]))
- C.yaml_parser_set_input_string(d.parser, input, (C.size_t)(len(b)))
- d.next()
- if d.event._type != C.YAML_STREAM_START_EVENT {
- panic("Expected stream start event, got " +
- strconv.Itoa(int(d.event._type)))
- }
- d.next()
- return &d
- }
- func (d *decoder) destroy() {
- if d.event._type != C.YAML_NO_EVENT {
- C.yaml_event_delete(d.event)
- }
- C.yaml_parser_delete(d.parser)
- }
- func (d *decoder) next() {
- if d.event._type != C.YAML_NO_EVENT {
- if d.event._type == C.YAML_STREAM_END_EVENT {
- panic("Attempted to go past the end of stream. Corrupted value?")
- }
- C.yaml_event_delete(d.event)
- }
- if C.yaml_parser_parse(d.parser, d.event) == 0 {
- panic("Parsing failed.") // XXX Need better error handling here.
- }
- }
- func (d *decoder) skip(_type C.yaml_event_type_t) {
- for d.event._type != _type {
- d.next()
- }
- d.next()
- }
- func (d *decoder) unmarshal(out reflect.Value) bool {
- switch d.event._type {
- case C.YAML_SCALAR_EVENT:
- return d.scalar(out)
- case C.YAML_MAPPING_START_EVENT:
- return d.mapping(out)
- case C.YAML_SEQUENCE_START_EVENT:
- return d.sequence(out)
- case C.YAML_DOCUMENT_START_EVENT:
- return d.document(out)
- default:
- panic("Attempted to unmarshal unexpected event: " +
- strconv.Itoa(int(d.event._type)))
- }
- return true
- }
- func (d *decoder) document(out reflect.Value) bool {
- d.next()
- result := d.unmarshal(out)
- if d.event._type != C.YAML_DOCUMENT_END_EVENT {
- panic("Expected end of document event but got " +
- strconv.Itoa(int(d.event._type)))
- }
- d.next()
- return result
- }
- func (d *decoder) scalar(out reflect.Value) (ok bool) {
- scalar := C.event_scalar(d.event)
- str := GoYString(scalar.value)
- resolved, _ := resolve(str)
- switch out := out.(type) {
- case *reflect.StringValue:
- out.Set(str)
- ok = true
- case *reflect.InterfaceValue:
- out.Set(reflect.NewValue(resolved))
- ok = true
- case *reflect.IntValue:
- switch resolved := resolved.(type) {
- case int:
- out.Set(int64(resolved))
- ok = true
- case int64:
- out.Set(resolved)
- // ok = true // XXX TEST ME
- }
- default:
- panic("Can't handle scalar type yet: " + out.Type().String())
- }
- d.next()
- return ok
- }
- func (d *decoder) sequence(out reflect.Value) bool {
- sv, ok := out.(*reflect.SliceValue)
- if !ok {
- d.skip(C.YAML_SEQUENCE_END_EVENT)
- return false
- }
- st := sv.Type().(*reflect.SliceType)
- et := st.Elem()
- d.next()
- for d.event._type != C.YAML_SEQUENCE_END_EVENT {
- e := reflect.MakeZero(et)
- if ok := d.unmarshal(e); ok {
- sv.SetValue(reflect.Append(sv, e))
- }
- }
- d.next()
- return true
- }
- func (d *decoder) mapping(out reflect.Value) bool {
- //if iface, ok := out.(*reflect.InterfaceValue); ok {
- // XXX What if it's an interface{}?
- mv, ok := out.(*reflect.MapValue)
- if !ok {
- d.skip(C.YAML_MAPPING_END_EVENT)
- return false
- }
- mt := mv.Type().(*reflect.MapType)
- kt := mt.Key()
- et := mt.Elem()
- d.next()
- for d.event._type != C.YAML_MAPPING_END_EVENT {
- k := reflect.MakeZero(kt)
- kok := d.unmarshal(k)
- e := reflect.MakeZero(et)
- eok := d.unmarshal(e)
- if kok && eok {
- mv.SetElem(k, e)
- }
- }
- d.next()
- return false
- }
- func GoYString(s *C.yaml_char_t) string {
- return C.GoString((*C.char)(unsafe.Pointer(s)))
- }
|