Skip to content

Commit

Permalink
Improve empty document handling.
Browse files Browse the repository at this point in the history
  • Loading branch information
felixfontein committed Jun 1, 2022
1 parent f6f7691 commit 21e0769
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 3 deletions.
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

0 comments on commit 21e0769

Please sign in to comment.