Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v3] Improve empty document handling #690

Open
wants to merge 1 commit into
base: v3
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions decode.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,13 @@ func (p *parser) parse() *Node {
return p.document()
case yaml_STREAM_END_EVENT:
// Happens when attempting to decode an empty buffer.
if len(p.event.head_comment) > 0 {
// The buffer is empty, but there is a comment.
// Create a document node which contains this comment.
n := p.node(DocumentNode, "", "", "")
p.event.head_comment = nil
return n
}
return nil
case yaml_TAIL_COMMENT_EVENT:
panic("internal error: unexpected tail comment event (please report)")
Expand Down Expand Up @@ -202,6 +209,13 @@ func (p *parser) parseChild(parent *Node) *Node {
func (p *parser) document() *Node {
n := p.node(DocumentNode, "", "", "")
p.doc = n
if !p.event.implicit && len(p.event.head_comment) > 0 {
// This comment belongs to **before** the document event
n.Line = 1
n.Column = 1
p.event.head_comment = nil
return n
}
p.expect(yaml_DOCUMENT_START_EVENT)
p.parseChild(n)
if p.peek() == yaml_DOCUMENT_END_EVENT {
Expand Down
159 changes: 159 additions & 0 deletions decode_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,165 @@ func (s *S) TestDecoder(c *C) {
}
}

var decoderNodeTests = []struct {
data string
nodes []*yaml.Node
}{
{
"# foo\n---\nkey: value\n",
[]*yaml.Node{
{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
HeadComment: "# foo",
}, {
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 3,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "key",
Tag: "!!str",
Line: 3,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Value: "value",
Tag: "!!str",
Line: 3,
Column: 6,
}},
}},
},
},
},
{
"# foo\n---\n# bar\nkey: value\n",
[]*yaml.Node{
{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
HeadComment: "# foo",
}, {
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 4,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "key",
Tag: "!!str",
Line: 4,
Column: 1,
HeadComment: "# bar",
}, {
Kind: yaml.ScalarNode,
Value: "value",
Tag: "!!str",
Line: 4,
Column: 6,
}},
}},
},
},
},
{
"key: value\n\n# foo\n---\nkey: value\n",
[]*yaml.Node{
{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
FootComment: "# foo",
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "key",
Tag: "!!str",
Line: 1,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Value: "value",
Tag: "!!str",
Line: 1,
Column: 6,
}},
}},
}, {
Kind: yaml.DocumentNode,
Line: 4,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 5,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Value: "key",
Tag: "!!str",
Line: 5,
Column: 1,
}, {
Kind: yaml.ScalarNode,
Value: "value",
Tag: "!!str",
Line: 5,
Column: 6,
}},
}},
},
},
},
}

/*
func walkTree(indent int, node *yaml.Node) {
fmt.Printf("%s{%d %d %#v:%#v anchor:%#v head:%#v line:%#v foot:%#v %d:%d}\n", strings.Repeat(" ", indent), node.Kind, node.Style, node.Tag, node.Value, node.Anchor, node.HeadComment, node.LineComment, node.FootComment, node.Line, node.Column)
for _, item := range node.Content {
walkTree(indent + 1, item)
}
if node.Alias != nil {
walkTree(indent + 1, node.Alias)
}
}
*/

func (s *S) TestDecoderNodes(c *C) {
for i, item := range decoderNodeTests {
c.Logf("test %d: %q", i, item.data)
var nodes []*yaml.Node
d := yaml.NewDecoder(bytes.NewReader([]byte(item.data)))
for true {
node := &yaml.Node{}
err := d.Decode(node)
if err == io.EOF {
break
}
c.Assert(err, IsNil)
nodes = append(nodes, node)
// walkTree(0, node)
}
c.Assert(nodes, DeepEquals, item.nodes)
}
}

type errReader struct{}

func (errReader) Read([]byte) (int, error) {
Expand Down
8 changes: 7 additions & 1 deletion emitterc.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,6 +359,9 @@ func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t
func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {

if event.typ == yaml_DOCUMENT_START_EVENT {
// The next element has been parsed because yaml_emitter_need_more_events() demands
// one more element for yaml_DOCUMENT_START_EVENT (accumulate == 1)
isEmpty := emitter.events[emitter.events_head + 1].typ == yaml_DOCUMENT_END_EVENT

if event.version_directive != nil {
if !yaml_emitter_analyze_version_directive(emitter, event.version_directive) {
Expand Down Expand Up @@ -450,12 +453,15 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event
if !yaml_emitter_process_head_comment(emitter) {
return false
}
if !put_break(emitter) {
if !isEmpty && !put_break(emitter) {
return false
}
}

emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE
if isEmpty {
emitter.state = yaml_EMIT_DOCUMENT_END_STATE
}
return true
}

Expand Down
89 changes: 87 additions & 2 deletions node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,41 @@ var nodeTests = []struct {
}},
},
}, {
"[encode]null\n",
yaml.Node{},
"null\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!null",
Value: "null",
Line: 1,
Column: 1,
}},
},
}, {
"[decode]\n",
yaml.Node{
Kind: 0,
Line: 0,
Column: 0,
Content: []*yaml.Node(nil),
},
}, {
"[decode]---\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 1,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.ScalarNode,
Tag: "!!null",
Value: "",
Line: 2,
Column: 1,
}},
},
}, {
"foo\n",
yaml.Node{
Expand Down Expand Up @@ -2550,6 +2583,58 @@ var nodeTests = []struct {
},
}},
},
}, {
"# foo\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
HeadComment: "# foo",
Content: []*yaml.Node(nil),
},
}, {
"# beginning\na:\n ## foo\n ##\n b:\n",
yaml.Node{
Kind: yaml.DocumentNode,
Line: 2,
Column: 1,
Content: []*yaml.Node{{
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 2,
Column: 1,
Content: []*yaml.Node{
{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 2,
Column: 1,
Value: "a",
HeadComment: "# beginning",
}, {
Kind: yaml.MappingNode,
Tag: "!!map",
Line: 5,
Column: 3,
Content: []*yaml.Node{
{
Kind: yaml.ScalarNode,
Tag: "!!str",
Line: 5,
Column: 3,
Value: "b",
HeadComment: "## foo\n##",
}, {
Kind: yaml.ScalarNode,
Tag: "!!null",
Line: 5,
Column: 5,
},
},
},
},
}},
},
},
}

Expand Down
3 changes: 3 additions & 0 deletions parserc.go
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,7 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t
typ: yaml_DOCUMENT_START_EVENT,
start_mark: token.start_mark,
end_mark: token.end_mark,
implicit: true,

head_comment: head_comment,
}
Expand Down Expand Up @@ -338,6 +339,7 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t
tag_directives: tag_directives,
implicit: false,
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)

} else {
Expand All @@ -348,6 +350,7 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t
start_mark: token.start_mark,
end_mark: token.end_mark,
}
yaml_parser_set_event_comments(parser, event)
skip_token(parser)
}

Expand Down