-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
395 additions
and
11 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,172 @@ | ||
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))) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,14 @@ | ||
package goyaml | ||
|
||
import ( | ||
"reflect" | ||
"os" | ||
) | ||
|
||
|
||
func Unmarshal(in []byte, out interface{}) os.Error { | ||
d := newDecoder(in) | ||
defer d.destroy() | ||
d.unmarshal(reflect.NewValue(out)) | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
#include <yaml.h> | ||
|
||
|
||
__typeof__(((yaml_event_t *)0)->data.scalar) * // Sadness. | ||
event_scalar(yaml_event_t *event) | ||
{ | ||
return &event->data.scalar; | ||
} |
Oops, something went wrong.