Browse Source

使用v2版本

dcsunny 4 years ago
parent
commit
56ef2ad78f
23 changed files with 927 additions and 5601 deletions
  1. 37 0
      .gitignore
  2. 187 36
      LICENSE
  3. 31 0
      LICENSE.libyaml
  4. 29 32
      apic.go
  5. 212 357
      decode.go
  6. 104 413
      decode_test.go
  7. 39 374
      emitterc.go
  8. 88 268
      encode.go
  9. 61 151
      encode_test.go
  10. 0 15
      example_embedded_test.go
  11. 1 1
      go.mod
  12. 10 0
      go.sum
  13. 0 2886
      node_test.go
  14. 1 155
      parserc.go
  15. 0 22
      readerc.go
  16. 34 102
      resolve.go
  17. 18 345
      scannerc.go
  18. 1 22
      sorter.go
  19. 0 15
      suite_test.go
  20. 0 22
      writerc.go
  21. 65 284
      yaml.go
  22. 6 73
      yamlh.go
  23. 3 28
      yamlprivateh.go

+ 37 - 0
.gitignore

@@ -0,0 +1,37 @@
+# Reference https://github.com/github/gitignore/blob/master/Go.gitignore
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+vendor/
+
+# Compiled Object files, Static and Dynamic libs (Shared Objects)
+*.o
+*.a
+*.so
+
+# OS General
+Thumbs.db
+.DS_Store
+
+# project
+*.cert
+*.key
+*.log
+bin/
+
+# Develop tools
+.vscode/
+.idea/
+*.swp
+

+ 187 - 36
LICENSE

@@ -1,50 +1,201 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
 
-This project is covered by two different licenses: MIT and Apache.
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
 
-#### MIT License ####
+   1. Definitions.
 
-The following files were ported to Go from C files of libyaml, and thus
-are still covered by their original MIT license, with the additional
-copyright staring in 2011 when the project was ported over:
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
 
-    apic.go emitterc.go parserc.go readerc.go scannerc.go
-    writerc.go yamlh.go yamlprivateh.go
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
 
-Copyright (c) 2006-2010 Kirill Simonov
-Copyright (c) 2006-2011 Kirill Simonov
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
 
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
 
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
 
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
 
-### Apache License ###
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
 
-All the remaining project files are covered by the Apache license:
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
 
-Copyright (c) 2011-2019 Canonical Ltd
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
 
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
 
-    http://www.apache.org/licenses/LICENSE-2.0
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
 
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "{}"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright {yyyy} {name of copyright owner}
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.

+ 31 - 0
LICENSE.libyaml

@@ -0,0 +1,31 @@
+The following files were ported to Go from C files of libyaml, and thus
+are still covered by their original copyright and license:
+
+    apic.go
+    emitterc.go
+    parserc.go
+    readerc.go
+    scannerc.go
+    writerc.go
+    yamlh.go
+    yamlprivateh.go
+
+Copyright (c) 2006 Kirill Simonov
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
+of the Software, and to permit persons to whom the Software is furnished to do
+so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.

+ 29 - 32
apic.go

@@ -1,25 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-// Copyright (c) 2006-2010 Kirill Simonov
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-// of the Software, and to permit persons to whom the Software is furnished to do
-// so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
 package yaml
 
 import (
@@ -101,6 +79,8 @@ func yaml_parser_set_encoding(parser *yaml_parser_t, encoding yaml_encoding_t) {
 	parser.encoding = encoding
 }
 
+var disableLineWrapping = false
+
 // Create a new emitter object.
 func yaml_emitter_initialize(emitter *yaml_emitter_t) {
 	*emitter = yaml_emitter_t{
@@ -108,7 +88,9 @@ func yaml_emitter_initialize(emitter *yaml_emitter_t) {
 		raw_buffer: make([]byte, 0, output_raw_buffer_size),
 		states:     make([]yaml_emitter_state_t, 0, initial_stack_size),
 		events:     make([]yaml_event_t, 0, initial_queue_size),
-		best_width: -1,
+	}
+	if disableLineWrapping {
+		emitter.best_width = -1
 	}
 }
 
@@ -161,7 +143,7 @@ func yaml_emitter_set_canonical(emitter *yaml_emitter_t, canonical bool) {
 	emitter.canonical = canonical
 }
 
-// Set the indentation increment.
+//// Set the indentation increment.
 func yaml_emitter_set_indent(emitter *yaml_emitter_t, indent int) {
 	if indent < 2 || indent > 9 {
 		indent = 2
@@ -311,14 +293,29 @@ func yaml_document_end_event_initialize(event *yaml_event_t, implicit bool) {
 	}
 }
 
-// Create ALIAS.
-func yaml_alias_event_initialize(event *yaml_event_t, anchor []byte) bool {
-	*event = yaml_event_t{
-		typ:    yaml_ALIAS_EVENT,
-		anchor: anchor,
-	}
-	return true
-}
+///*
+// * Create ALIAS.
+// */
+//
+//YAML_DECLARE(int)
+//yaml_alias_event_initialize(event *yaml_event_t, anchor *yaml_char_t)
+//{
+//    mark yaml_mark_t = { 0, 0, 0 }
+//    anchor_copy *yaml_char_t = NULL
+//
+//    assert(event) // Non-NULL event object is expected.
+//    assert(anchor) // Non-NULL anchor is expected.
+//
+//    if (!yaml_check_utf8(anchor, strlen((char *)anchor))) return 0
+//
+//    anchor_copy = yaml_strdup(anchor)
+//    if (!anchor_copy)
+//        return 0
+//
+//    ALIAS_EVENT_INIT(*event, anchor_copy, mark, mark)
+//
+//    return 1
+//}
 
 // Create SCALAR.
 func yaml_scalar_event_initialize(event *yaml_event_t, anchor, tag, value []byte, plain_implicit, quoted_implicit bool, style yaml_scalar_style_t) bool {

+ 212 - 357
decode.go

@@ -1,18 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package yaml
 
 import (
@@ -28,16 +13,34 @@ import (
 	"google.golang.org/protobuf/types/known/durationpb"
 )
 
+const (
+	documentNode = 1 << iota
+	mappingNode
+	sequenceNode
+	scalarNode
+	aliasNode
+)
+
+type node struct {
+	kind         int
+	line, column int
+	tag          string
+	// For an alias node, alias holds the resolved alias.
+	alias    *node
+	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
-	anchors  map[string]*Node
+	doc      *node
 	doneInit bool
-	textless bool
 }
 
 func newParser(b []byte) *parser {
@@ -65,7 +68,6 @@ func (p *parser) init() {
 	if p.doneInit {
 		return
 	}
-	p.anchors = make(map[string]*Node)
 	p.expect(yaml_STREAM_START_EVENT)
 	p.doneInit = true
 }
@@ -111,18 +113,14 @@ func (p *parser) peek() yaml_event_type_t {
 func (p *parser) fail() {
 	var where string
 	var line int
-	if p.parser.context_mark.line != 0 {
-		line = p.parser.context_mark.line
-		// Scanner errors don't iterate line before returning error
-		if p.parser.error == yaml_SCANNER_ERROR {
-			line++
-		}
-	} else if p.parser.problem_mark.line != 0 {
+	if p.parser.problem_mark.line != 0 {
 		line = p.parser.problem_mark.line
 		// Scanner errors don't iterate line before returning error
 		if p.parser.error == yaml_SCANNER_ERROR {
 			line++
 		}
+	} else if p.parser.context_mark.line != 0 {
+		line = p.parser.context_mark.line
 	}
 	if line != 0 {
 		where = "line " + strconv.Itoa(line) + ": "
@@ -136,14 +134,13 @@ func (p *parser) fail() {
 	failf("%s%s", where, msg)
 }
 
-func (p *parser) anchor(n *Node, anchor []byte) {
+func (p *parser) anchor(n *node, anchor []byte) {
 	if anchor != nil {
-		n.Anchor = string(anchor)
-		p.anchors[n.Anchor] = n
+		p.doc.anchors[string(anchor)] = n
 	}
 }
 
-func (p *parser) parse() *Node {
+func (p *parser) parse() *node {
 	p.init()
 	switch p.peek() {
 	case yaml_SCALAR_EVENT:
@@ -159,148 +156,67 @@ func (p *parser) parse() *Node {
 	case yaml_STREAM_END_EVENT:
 		// Happens when attempting to decode an empty buffer.
 		return nil
-	case yaml_TAIL_COMMENT_EVENT:
-		panic("internal error: unexpected tail comment event (please report)")
 	default:
-		panic("internal error: attempted to parse unknown event (please report): " + p.event.typ.String())
+		panic("attempted to parse unknown event: " + p.event.typ.String())
 	}
 }
 
-func (p *parser) node(kind Kind, defaultTag, tag, value string) *Node {
-	var style Style
-	if tag != "" && tag != "!" {
-		tag = shortTag(tag)
-		style = TaggedStyle
-	} else if defaultTag != "" {
-		tag = defaultTag
-	} else if kind == ScalarNode {
-		tag, _ = resolve("", value)
-	}
-	n := &Node{
-		Kind:  kind,
-		Tag:   tag,
-		Value: value,
-		Style: style,
-	}
-	if !p.textless {
-		n.Line = p.event.start_mark.line + 1
-		n.Column = p.event.start_mark.column + 1
-		n.HeadComment = string(p.event.head_comment)
-		n.LineComment = string(p.event.line_comment)
-		n.FootComment = string(p.event.foot_comment)
+func (p *parser) node(kind int) *node {
+	return &node{
+		kind:   kind,
+		line:   p.event.start_mark.line,
+		column: p.event.start_mark.column,
 	}
-	return n
-}
-
-func (p *parser) parseChild(parent *Node) *Node {
-	child := p.parse()
-	parent.Content = append(parent.Content, child)
-	return child
 }
 
-func (p *parser) document() *Node {
-	n := p.node(DocumentNode, "", "", "")
+func (p *parser) document() *node {
+	n := p.node(documentNode)
+	n.anchors = make(map[string]*node)
 	p.doc = n
 	p.expect(yaml_DOCUMENT_START_EVENT)
-	p.parseChild(n)
-	if p.peek() == yaml_DOCUMENT_END_EVENT {
-		n.FootComment = string(p.event.foot_comment)
-	}
+	n.children = append(n.children, p.parse())
 	p.expect(yaml_DOCUMENT_END_EVENT)
 	return n
 }
 
-func (p *parser) alias() *Node {
-	n := p.node(AliasNode, "", "", string(p.event.anchor))
-	n.Alias = p.anchors[n.Value]
-	if n.Alias == nil {
-		failf("unknown anchor '%s' referenced", n.Value)
+func (p *parser) alias() *node {
+	n := p.node(aliasNode)
+	n.value = string(p.event.anchor)
+	n.alias = p.doc.anchors[n.value]
+	if n.alias == nil {
+		failf("unknown anchor '%s' referenced", n.value)
 	}
 	p.expect(yaml_ALIAS_EVENT)
 	return n
 }
 
-func (p *parser) scalar() *Node {
-	var parsedStyle = p.event.scalar_style()
-	var nodeStyle Style
-	switch {
-	case parsedStyle&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0:
-		nodeStyle = DoubleQuotedStyle
-	case parsedStyle&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0:
-		nodeStyle = SingleQuotedStyle
-	case parsedStyle&yaml_LITERAL_SCALAR_STYLE != 0:
-		nodeStyle = LiteralStyle
-	case parsedStyle&yaml_FOLDED_SCALAR_STYLE != 0:
-		nodeStyle = FoldedStyle
-	}
-	var nodeValue = string(p.event.value)
-	var nodeTag = string(p.event.tag)
-	var defaultTag string
-	if nodeStyle == 0 {
-		if nodeValue == "<<" {
-			defaultTag = mergeTag
-		}
-	} else {
-		defaultTag = strTag
-	}
-	n := p.node(ScalarNode, defaultTag, nodeTag, nodeValue)
-	n.Style |= nodeStyle
+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.expect(yaml_SCALAR_EVENT)
 	return n
 }
 
-func (p *parser) sequence() *Node {
-	n := p.node(SequenceNode, seqTag, string(p.event.tag), "")
-	if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 {
-		n.Style |= FlowStyle
-	}
+func (p *parser) sequence() *node {
+	n := p.node(sequenceNode)
 	p.anchor(n, p.event.anchor)
 	p.expect(yaml_SEQUENCE_START_EVENT)
 	for p.peek() != yaml_SEQUENCE_END_EVENT {
-		p.parseChild(n)
+		n.children = append(n.children, p.parse())
 	}
-	n.LineComment = string(p.event.line_comment)
-	n.FootComment = string(p.event.foot_comment)
 	p.expect(yaml_SEQUENCE_END_EVENT)
 	return n
 }
 
-func (p *parser) mapping() *Node {
-	n := p.node(MappingNode, mapTag, string(p.event.tag), "")
-	block := true
-	if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 {
-		block = false
-		n.Style |= FlowStyle
-	}
+func (p *parser) mapping() *node {
+	n := p.node(mappingNode)
 	p.anchor(n, p.event.anchor)
 	p.expect(yaml_MAPPING_START_EVENT)
 	for p.peek() != yaml_MAPPING_END_EVENT {
-		k := p.parseChild(n)
-		if block && k.FootComment != "" {
-			// Must be a foot comment for the prior value when being dedented.
-			if len(n.Content) > 2 {
-				n.Content[len(n.Content)-3].FootComment = k.FootComment
-				k.FootComment = ""
-			}
-		}
-		v := p.parseChild(n)
-		if k.FootComment == "" && v.FootComment != "" {
-			k.FootComment = v.FootComment
-			v.FootComment = ""
-		}
-		if p.peek() == yaml_TAIL_COMMENT_EVENT {
-			if k.FootComment == "" {
-				k.FootComment = string(p.event.foot_comment)
-			}
-			p.expect(yaml_TAIL_COMMENT_EVENT)
-		}
-	}
-	n.LineComment = string(p.event.line_comment)
-	n.FootComment = string(p.event.foot_comment)
-	if n.Style&FlowStyle == 0 && n.FootComment != "" && len(n.Content) > 1 {
-		n.Content[len(n.Content)-2].FootComment = n.FootComment
-		n.FootComment = ""
+		n.children = append(n.children, p.parse(), p.parse())
 	}
 	p.expect(yaml_MAPPING_END_EVENT)
 	return n
@@ -310,69 +226,49 @@ func (p *parser) mapping() *Node {
 // Decoder, unmarshals a node into a provided value.
 
 type decoder struct {
-	doc     *Node
-	aliases map[*Node]bool
+	doc     *node
+	aliases map[*node]bool
+	mapType reflect.Type
 	terrors []string
+	strict  bool
 
-	stringMapType  reflect.Type
-	generalMapType reflect.Type
-
-	knownFields bool
-	uniqueKeys  bool
 	decodeCount int
 	aliasCount  int
 	aliasDepth  int
 }
 
 var (
-	nodeType          = reflect.TypeOf(Node{})
+	mapItemType       = reflect.TypeOf(MapItem{})
 	durationType      = reflect.TypeOf(time.Duration(0))
 	protoDurationType = reflect.TypeOf(durationpb.Duration{})
-	stringMapType     = reflect.TypeOf(map[string]interface{}{})
-	generalMapType    = reflect.TypeOf(map[interface{}]interface{}{})
-	ifaceType         = generalMapType.Elem()
+	defaultMapType    = reflect.TypeOf(map[interface{}]interface{}{})
+	ifaceType         = defaultMapType.Elem()
 	timeType          = reflect.TypeOf(time.Time{})
 	ptrTimeType       = reflect.TypeOf(&time.Time{})
 )
 
-func newDecoder() *decoder {
-	d := &decoder{
-		stringMapType:  stringMapType,
-		generalMapType: generalMapType,
-		uniqueKeys:     true,
-	}
-	d.aliases = make(map[*Node]bool)
+func newDecoder(strict bool) *decoder {
+	d := &decoder{mapType: defaultMapType, strict: strict}
+	d.aliases = make(map[*node]bool)
 	return d
 }
 
-func (d *decoder) terror(n *Node, tag string, out reflect.Value) {
-	if n.Tag != "" {
-		tag = n.Tag
+func (d *decoder) terror(n *node, tag string, out reflect.Value) {
+	if n.tag != "" {
+		tag = n.tag
 	}
-	value := n.Value
-	if tag != seqTag && tag != mapTag {
+	value := n.value
+	if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG {
 		if len(value) > 10 {
 			value = " `" + value[:7] + "...`"
 		} else {
 			value = " `" + value + "`"
 		}
 	}
-	d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.Line, shortTag(tag), value, out.Type()))
+	d.terrors = append(d.terrors, fmt.Sprintf("line %d: cannot unmarshal %s%s into %s", n.line+1, shortTag(tag), value, out.Type()))
 }
 
-func (d *decoder) callUnmarshaler(n *Node, u Unmarshaler) (good bool) {
-	err := u.UnmarshalYAML(n)
-	if e, ok := err.(*TypeError); ok {
-		d.terrors = append(d.terrors, e.Errors...)
-		return false
-	}
-	if err != nil {
-		fail(err)
-	}
-	return true
-}
-
-func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good bool) {
+func (d *decoder) callUnmarshaler(n *node, u Unmarshaler) (good bool) {
 	terrlen := len(d.terrors)
 	err := u.UnmarshalYAML(func(v interface{}) (err error) {
 		defer handleErr(&err)
@@ -401,8 +297,8 @@ func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good
 // its types unmarshalled appropriately.
 //
 // If n holds a null value, prepare returns before doing anything.
-func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
-	if n.ShortTag() == nullTag {
+func (d *decoder) prepare(n *node, out reflect.Value) (newout reflect.Value, unmarshaled, good bool) {
+	if n.tag == yaml_NULL_TAG || n.kind == scalarNode && n.tag == "" && (n.value == "null" || n.value == "~" || n.value == "" && n.implicit) {
 		return out, false, false
 	}
 	again := true
@@ -416,40 +312,15 @@ func (d *decoder) prepare(n *Node, out reflect.Value) (newout reflect.Value, unm
 			again = true
 		}
 		if out.CanAddr() {
-			outi := out.Addr().Interface()
-			if u, ok := outi.(Unmarshaler); ok {
+			if u, ok := out.Addr().Interface().(Unmarshaler); ok {
 				good = d.callUnmarshaler(n, u)
 				return out, true, good
 			}
-			if u, ok := outi.(obsoleteUnmarshaler); ok {
-				good = d.callObsoleteUnmarshaler(n, u)
-				return out, true, good
-			}
 		}
 	}
 	return out, false, false
 }
 
-func (d *decoder) fieldByIndex(n *Node, v reflect.Value, index []int) (field reflect.Value) {
-	if n.ShortTag() == nullTag {
-		return reflect.Value{}
-	}
-	for _, num := range index {
-		for {
-			if v.Kind() == reflect.Ptr {
-				if v.IsNil() {
-					v.Set(reflect.New(v.Type().Elem()))
-				}
-				v = v.Elem()
-				continue
-			}
-			break
-		}
-		v = v.Field(num)
-	}
-	return v
-}
-
 const (
 	// 400,000 decode operations is ~500kb of dense object declarations, or
 	// ~5kb of dense object declarations with 10000% alias expansion
@@ -479,7 +350,7 @@ func allowedAliasRatio(decodeCount int) float64 {
 	}
 }
 
-func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) {
+func (d *decoder) unmarshal(n *node, out reflect.Value) (good bool) {
 	d.decodeCount++
 	if d.aliasDepth > 0 {
 		d.aliasCount++
@@ -487,55 +358,46 @@ func (d *decoder) unmarshal(n *Node, out reflect.Value) (good bool) {
 	if d.aliasCount > 100 && d.decodeCount > 1000 && float64(d.aliasCount)/float64(d.decodeCount) > allowedAliasRatio(d.decodeCount) {
 		failf("document contains excessive aliasing")
 	}
-	if out.Type() == nodeType {
-		out.Set(reflect.ValueOf(n).Elem())
-		return true
-	}
-	switch n.Kind {
-	case DocumentNode:
+	switch n.kind {
+	case documentNode:
 		return d.document(n, out)
-	case AliasNode:
+	case aliasNode:
 		return d.alias(n, out)
 	}
 	out, unmarshaled, good := d.prepare(n, out)
 	if unmarshaled {
 		return good
 	}
-	switch n.Kind {
-	case ScalarNode:
+	switch n.kind {
+	case scalarNode:
 		good = d.scalar(n, out)
-	case MappingNode:
+	case mappingNode:
 		good = d.mapping(n, out)
-	case SequenceNode:
+	case sequenceNode:
 		good = d.sequence(n, out)
-	case 0:
-		if n.IsZero() {
-			return d.null(out)
-		}
-		fallthrough
 	default:
-		failf("cannot decode node with unknown kind %d", n.Kind)
+		panic("internal error: unknown node kind: " + strconv.Itoa(n.kind))
 	}
 	return good
 }
 
-func (d *decoder) document(n *Node, out reflect.Value) (good bool) {
-	if len(n.Content) == 1 {
+func (d *decoder) document(n *node, out reflect.Value) (good bool) {
+	if len(n.children) == 1 {
 		d.doc = n
-		d.unmarshal(n.Content[0], out)
+		d.unmarshal(n.children[0], out)
 		return true
 	}
 	return false
 }
 
-func (d *decoder) alias(n *Node, out reflect.Value) (good bool) {
+func (d *decoder) alias(n *node, out reflect.Value) (good bool) {
 	if d.aliases[n] {
 		// TODO this could actually be allowed in some circumstances.
-		failf("anchor '%s' value contains itself", n.Value)
+		failf("anchor '%s' value contains itself", n.value)
 	}
 	d.aliases[n] = true
 	d.aliasDepth++
-	good = d.unmarshal(n.Alias, out)
+	good = d.unmarshal(n.alias, out)
 	d.aliasDepth--
 	delete(d.aliases, n)
 	return good
@@ -549,26 +411,15 @@ func resetMap(out reflect.Value) {
 	}
 }
 
-func (d *decoder) null(out reflect.Value) bool {
-	if out.CanAddr() {
-		switch out.Kind() {
-		case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
-			out.Set(reflect.Zero(out.Type()))
-			return true
-		}
-	}
-	return false
-}
-
-func (d *decoder) scalar(n *Node, out reflect.Value) bool {
+func (d *decoder) scalar(n *node, out reflect.Value) bool {
 	var tag string
 	var resolved interface{}
-	if n.indicatedString() {
-		tag = strTag
-		resolved = n.Value
+	if n.tag == "" && !n.implicit {
+		tag = yaml_STR_TAG
+		resolved = n.value
 	} else {
-		tag, resolved = resolve(n.Tag, n.Value)
-		if tag == binaryTag {
+		tag, resolved = resolve(n.tag, n.value)
+		if tag == yaml_BINARY_TAG {
 			data, err := base64.StdEncoding.DecodeString(resolved.(string))
 			if err != nil {
 				failf("!!binary value contains invalid base64 data")
@@ -577,7 +428,12 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 		}
 	}
 	if resolved == nil {
-		return d.null(out)
+		if out.Kind() == reflect.Map && !out.CanAddr() {
+			resetMap(out)
+		} else {
+			out.Set(reflect.Zero(out.Type()))
+		}
+		return true
 	}
 	if resolvedv := reflect.ValueOf(resolved); out.Type() == resolvedv.Type() {
 		// We've resolved to exactly the type we want, so use that.
@@ -590,13 +446,13 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 		u, ok := out.Addr().Interface().(encoding.TextUnmarshaler)
 		if ok {
 			var text []byte
-			if tag == binaryTag {
+			if tag == yaml_BINARY_TAG {
 				text = []byte(resolved.(string))
 			} else {
 				// We let any value be unmarshaled into TextUnmarshaler.
 				// That might be more lax than we'd like, but the
 				// TextUnmarshaler itself should bowl out any dubious values.
-				text = []byte(n.Value)
+				text = []byte(n.value)
 			}
 			err := u.UnmarshalText(text)
 			if err != nil {
@@ -607,36 +463,42 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 	}
 	switch out.Kind() {
 	case reflect.String:
-		if tag == binaryTag {
+		if tag == yaml_BINARY_TAG {
 			out.SetString(resolved.(string))
 			return true
 		}
-		out.SetString(n.Value)
-		return true
+
 	case reflect.Interface:
-		out.Set(reflect.ValueOf(resolved))
+		if tag == yaml_TIMESTAMP_TAG {
+			// It looks like a timestamp but for backward compatibility
+			// reasons we set it as a string, so that code that unmarshals
+			// timestamp-like values into interface{} will continue to
+			// see a string and not a time.Time.
+			// TODO(v3) Drop this.
+			out.Set(reflect.ValueOf(n.value))
+		} else {
+			out.Set(reflect.ValueOf(resolved))
+		}
 		return true
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		// This used to work in v2, but it's very unfriendly.
-		isDuration := out.Type() == durationType
 		switch resolved := resolved.(type) {
 		case int:
-			if !isDuration && !out.OverflowInt(int64(resolved)) {
+			if !out.OverflowInt(int64(resolved)) {
 				out.SetInt(int64(resolved))
 				return true
 			}
 		case int64:
-			if !isDuration && !out.OverflowInt(resolved) {
+			if !out.OverflowInt(resolved) {
 				out.SetInt(resolved)
 				return true
 			}
 		case uint64:
-			if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
+			if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
 				out.SetInt(int64(resolved))
 				return true
 			}
 		case float64:
-			if !isDuration && resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
+			if resolved <= math.MaxInt64 && !out.OverflowInt(int64(resolved)) {
 				out.SetInt(int64(resolved))
 				return true
 			}
@@ -647,12 +509,6 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 					out.SetInt(int64(d))
 					return true
 				}
-			} else if out.Type() == protoDurationType {
-				d, err := time.ParseDuration(resolved)
-				if err == nil {
-					out.SetInt(int64(d))
-					return true
-				}
 			}
 		}
 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
@@ -683,17 +539,6 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 		case bool:
 			out.SetBool(resolved)
 			return true
-		case string:
-			// This offers some compatibility with the 1.1 spec (https://yaml.org/type/bool.html).
-			// It only works if explicitly attempting to unmarshal into a typed bool value.
-			switch resolved {
-			case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON":
-				out.SetBool(true)
-				return true
-			case "n", "N", "no", "No", "NO", "off", "Off", "OFF":
-				out.SetBool(false)
-				return true
-			}
 		}
 	case reflect.Float32, reflect.Float64:
 		switch resolved := resolved.(type) {
@@ -746,7 +591,13 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 			}
 		}
 	case reflect.Ptr:
-		panic("yaml internal error: please report the issue")
+		if out.Type().Elem() == reflect.TypeOf(resolved) {
+			// TODO DOes this make sense? When is out a Ptr except when decoding a nil value?
+			elem := reflect.New(out.Type().Elem())
+			elem.Elem().Set(reflect.ValueOf(resolved))
+			out.Set(elem)
+			return true
+		}
 	}
 	d.terror(n, tag, out)
 	return false
@@ -759,8 +610,8 @@ func settableValueOf(i interface{}) reflect.Value {
 	return sv
 }
 
-func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) {
-	l := len(n.Content)
+func (d *decoder) sequence(n *node, out reflect.Value) (good bool) {
+	l := len(n.children)
 
 	var iface reflect.Value
 	switch out.Kind() {
@@ -775,7 +626,7 @@ func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) {
 		iface = out
 		out = settableValueOf(make([]interface{}, l))
 	default:
-		d.terror(n, seqTag, out)
+		d.terror(n, yaml_SEQ_TAG, out)
 		return false
 	}
 	et := out.Type().Elem()
@@ -783,7 +634,7 @@ func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) {
 	j := 0
 	for i := 0; i < l; i++ {
 		e := reflect.New(et).Elem()
-		if ok := d.unmarshal(n.Content[i], e); ok {
+		if ok := d.unmarshal(n.children[i], e); ok {
 			out.Index(j).Set(e)
 			j++
 		}
@@ -797,67 +648,51 @@ func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) {
 	return true
 }
 
-func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
-	l := len(n.Content)
-	if d.uniqueKeys {
-		nerrs := len(d.terrors)
-		for i := 0; i < l; i += 2 {
-			ni := n.Content[i]
-			for j := i + 2; j < l; j += 2 {
-				nj := n.Content[j]
-				if ni.Kind == nj.Kind && ni.Value == nj.Value {
-					d.terrors = append(d.terrors, fmt.Sprintf("line %d: mapping key %#v already defined at line %d", nj.Line, nj.Value, ni.Line))
-				}
-			}
-		}
-		if len(d.terrors) > nerrs {
-			return false
-		}
-	}
+func (d *decoder) mapping(n *node, out reflect.Value) (good bool) {
 	switch out.Kind() {
 	case reflect.Struct:
 		return d.mappingStruct(n, out)
+	case reflect.Slice:
+		return d.mappingSlice(n, out)
 	case reflect.Map:
 		// okay
 	case reflect.Interface:
-		iface := out
-		if isStringMap(n) {
-			out = reflect.MakeMap(d.stringMapType)
+		if d.mapType.Kind() == reflect.Map {
+			iface := out
+			out = reflect.MakeMap(d.mapType)
+			iface.Set(out)
 		} else {
-			out = reflect.MakeMap(d.generalMapType)
+			slicev := reflect.New(d.mapType).Elem()
+			if !d.mappingSlice(n, slicev) {
+				return false
+			}
+			out.Set(slicev)
+			return true
 		}
-		iface.Set(out)
 	default:
-		d.terror(n, mapTag, out)
+		d.terror(n, yaml_MAP_TAG, out)
 		return false
 	}
-
 	outt := out.Type()
 	kt := outt.Key()
 	et := outt.Elem()
 
-	stringMapType := d.stringMapType
-	generalMapType := d.generalMapType
-	if outt.Elem() == ifaceType {
-		if outt.Key().Kind() == reflect.String {
-			d.stringMapType = outt
-		} else if outt.Key() == ifaceType {
-			d.generalMapType = outt
-		}
+	mapType := d.mapType
+	if outt.Key() == ifaceType && outt.Elem() == ifaceType {
+		d.mapType = outt
 	}
 
-	mapIsNew := false
 	if out.IsNil() {
 		out.Set(reflect.MakeMap(outt))
-		mapIsNew = true
 	}
+	l := len(n.children)
 	for i := 0; i < l; i += 2 {
-		if isMerge(n.Content[i]) {
-			d.merge(n.Content[i+1], out)
+		if isMerge(n.children[i]) {
+			d.merge(n.children[i+1], out)
 			continue
 		}
 		k := reflect.New(kt).Elem()
-		if d.unmarshal(n.Content[i], k) {
+		if d.unmarshal(n.children[i], k) {
 			kkind := k.Kind()
 			if kkind == reflect.Interface {
 				kkind = k.Elem().Kind()
@@ -866,34 +701,61 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
 				failf("invalid map key: %#v", k.Interface())
 			}
 			e := reflect.New(et).Elem()
-			if d.unmarshal(n.Content[i+1], e) || n.Content[i+1].ShortTag() == nullTag && (mapIsNew || !out.MapIndex(k).IsValid()) {
-				out.SetMapIndex(k, e)
+			if d.unmarshal(n.children[i+1], e) {
+				d.setMapIndex(n.children[i+1], out, k, e)
 			}
 		}
 	}
-	d.stringMapType = stringMapType
-	d.generalMapType = generalMapType
+	d.mapType = mapType
 	return true
 }
 
-func isStringMap(n *Node) bool {
-	if n.Kind != MappingNode {
+func (d *decoder) setMapIndex(n *node, out, k, v reflect.Value) {
+	if d.strict && out.MapIndex(k) != zeroValue {
+		d.terrors = append(d.terrors, fmt.Sprintf("line %d: key %#v already set in map", n.line+1, k.Interface()))
+		return
+	}
+	out.SetMapIndex(k, v)
+}
+
+func (d *decoder) mappingSlice(n *node, out reflect.Value) (good bool) {
+	outt := out.Type()
+	if outt.Elem() != mapItemType {
+		d.terror(n, yaml_MAP_TAG, out)
 		return false
 	}
-	l := len(n.Content)
+
+	mapType := d.mapType
+	d.mapType = outt
+
+	var slice []MapItem
+	var l = len(n.children)
 	for i := 0; i < l; i += 2 {
-		if n.Content[i].ShortTag() != strTag {
-			return false
+		if isMerge(n.children[i]) {
+			d.merge(n.children[i+1], out)
+			continue
+		}
+		item := MapItem{}
+		k := reflect.ValueOf(&item.Key).Elem()
+		if d.unmarshal(n.children[i], k) {
+			v := reflect.ValueOf(&item.Value).Elem()
+			if d.unmarshal(n.children[i+1], v) {
+				slice = append(slice, item)
+			}
 		}
 	}
+	out.Set(reflect.ValueOf(slice))
+	d.mapType = mapType
 	return true
 }
 
-func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
+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)
 
 	var inlineMap reflect.Value
 	var elemType reflect.Type
@@ -903,30 +765,23 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
 		elemType = inlineMap.Type().Elem()
 	}
 
-	for _, index := range sinfo.InlineUnmarshalers {
-		field := d.fieldByIndex(n, out, index)
-		d.prepare(n, field)
-	}
-
 	var doneFields []bool
-	if d.uniqueKeys {
+	if d.strict {
 		doneFields = make([]bool, len(sinfo.FieldsList))
 	}
-	name := settableValueOf("")
-	l := len(n.Content)
 	for i := 0; i < l; i += 2 {
-		ni := n.Content[i]
+		ni := n.children[i]
 		if isMerge(ni) {
-			d.merge(n.Content[i+1], out)
+			d.merge(n.children[i+1], out)
 			continue
 		}
 		if !d.unmarshal(ni, name) {
 			continue
 		}
 		if info, ok := sinfo.FieldsMap[name.String()]; ok {
-			if d.uniqueKeys {
+			if d.strict {
 				if doneFields[info.Id] {
-					d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.Line, name.String(), out.Type()))
+					d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s already set in type %s", ni.line+1, name.String(), out.Type()))
 					continue
 				}
 				doneFields[info.Id] = true
@@ -935,18 +790,18 @@ func (d *decoder) mappingStruct(n *Node, out reflect.Value) (good bool) {
 			if info.Inline == nil {
 				field = out.Field(info.Num)
 			} else {
-				field = d.fieldByIndex(n, out, info.Inline)
+				field = out.FieldByIndex(info.Inline)
 			}
-			d.unmarshal(n.Content[i+1], field)
+			d.unmarshal(n.children[i+1], field)
 		} else if sinfo.InlineMap != -1 {
 			if inlineMap.IsNil() {
 				inlineMap.Set(reflect.MakeMap(inlineMap.Type()))
 			}
 			value := reflect.New(elemType).Elem()
-			d.unmarshal(n.Content[i+1], value)
-			inlineMap.SetMapIndex(name, value)
-		} else if d.knownFields {
-			d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.Line, name.String(), out.Type()))
+			d.unmarshal(n.children[i+1], value)
+			d.setMapIndex(n.children[i+1], inlineMap, name, value)
+		} else if d.strict {
+			d.terrors = append(d.terrors, fmt.Sprintf("line %d: field %s not found in type %s", ni.line+1, name.String(), out.Type()))
 		}
 	}
 	return true
@@ -956,24 +811,24 @@ func failWantMap() {
 	failf("map merge requires map or sequence of maps as the value")
 }
 
-func (d *decoder) merge(n *Node, out reflect.Value) {
-	switch n.Kind {
-	case MappingNode:
+func (d *decoder) merge(n *node, out reflect.Value) {
+	switch n.kind {
+	case mappingNode:
 		d.unmarshal(n, out)
-	case AliasNode:
-		if n.Alias != nil && n.Alias.Kind != MappingNode {
+	case aliasNode:
+		if n.alias != nil && n.alias.kind != mappingNode {
 			failWantMap()
 		}
 		d.unmarshal(n, out)
-	case SequenceNode:
+	case sequenceNode:
 		// Step backwards as earlier nodes take precedence.
-		for i := len(n.Content) - 1; i >= 0; i-- {
-			ni := n.Content[i]
-			if ni.Kind == AliasNode {
-				if ni.Alias != nil && ni.Alias.Kind != MappingNode {
+		for i := len(n.children) - 1; i >= 0; i-- {
+			ni := n.children[i]
+			if ni.kind == aliasNode {
+				if ni.alias != nil && ni.alias.kind != mappingNode {
 					failWantMap()
 				}
-			} else if ni.Kind != MappingNode {
+			} else if ni.kind != mappingNode {
 				failWantMap()
 			}
 			d.unmarshal(ni, out)
@@ -983,6 +838,6 @@ func (d *decoder) merge(n *Node, out reflect.Value) {
 	}
 }
 
-func isMerge(n *Node) bool {
-	return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag)
+func isMerge(n *node) bool {
+	return n.kind == scalarNode && n.value == "<<" && (n.implicit == true || n.tag == yaml_MERGE_TAG)
 }

+ 104 - 413
decode_test.go

@@ -1,24 +1,7 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package yaml_test
 
 import (
-	"bytes"
 	"errors"
-	"fmt"
 	"io"
 	"math"
 	"reflect"
@@ -110,42 +93,22 @@ var unmarshalTests = []struct {
 	//{"sexa: 190:20:30.15", map[string]interface{}{"sexa": 0}}, // Unsupported
 	//{"notanum: .NaN", map[string]interface{}{"notanum": math.NaN()}}, // Equality of NaN fails.
 
-	// Bools are per 1.2 spec.
+	// Bools from spec
 	{
-		"canonical: true",
+		"canonical: y",
 		map[string]interface{}{"canonical": true},
 	}, {
-		"canonical: false",
-		map[string]interface{}{"canonical": false},
-	}, {
-		"bool: True",
-		map[string]interface{}{"bool": true},
-	}, {
-		"bool: False",
-		map[string]interface{}{"bool": false},
+		"answer: NO",
+		map[string]interface{}{"answer": false},
 	}, {
-		"bool: TRUE",
-		map[string]interface{}{"bool": true},
+		"logical: True",
+		map[string]interface{}{"logical": true},
 	}, {
-		"bool: FALSE",
-		map[string]interface{}{"bool": false},
-	},
-	// For backwards compatibility with 1.1, decoding old strings into typed values still works.
-	{
 		"option: on",
-		map[string]bool{"option": true},
+		map[string]interface{}{"option": true},
 	}, {
-		"option: y",
+		"option: on",
 		map[string]bool{"option": true},
-	}, {
-		"option: Off",
-		map[string]bool{"option": false},
-	}, {
-		"option: No",
-		map[string]bool{"option": false},
-	}, {
-		"option: other",
-		map[string]bool{},
 	},
 	// Ints from spec
 	{
@@ -157,15 +120,6 @@ var unmarshalTests = []struct {
 	}, {
 		"octal: 02472256",
 		map[string]interface{}{"octal": 685230},
-	}, {
-		"octal: -02472256",
-		map[string]interface{}{"octal": -685230},
-	}, {
-		"octal: 0o2472256",
-		map[string]interface{}{"octal": 685230},
-	}, {
-		"octal: -0o2472256",
-		map[string]interface{}{"octal": -685230},
 	}, {
 		"hexa: 0x_0A_74_AE",
 		map[string]interface{}{"hexa": 685230},
@@ -253,12 +207,7 @@ var unmarshalTests = []struct {
 	// Map inside interface with no type hints.
 	{
 		"a: {b: c}",
-		map[interface{}]interface{}{"a": map[string]interface{}{"b": "c"}},
-	},
-	// Non-string map inside interface with no type hints.
-	{
-		"a: {b: c, 1: d}",
-		map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c", 1: "d"}},
+		map[interface{}]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
 	},
 
 	// Structs and type conversions.
@@ -271,9 +220,6 @@ var unmarshalTests = []struct {
 	}, {
 		"a: {b: c}",
 		&struct{ A *struct{ B string } }{&struct{ B string }{"c"}},
-	}, {
-		"a: 'null'",
-		&struct{ A *unmarshalerType }{&unmarshalerType{"null"}},
 	}, {
 		"a: {b: c}",
 		&struct{ A map[string]string }{map[string]string{"b": "c"}},
@@ -310,8 +256,7 @@ var unmarshalTests = []struct {
 			B int "a"
 		}{1},
 	}, {
-		// Some limited backwards compatibility with the 1.1 spec.
-		"a: YES",
+		"a: y",
 		&struct{ A bool }{true},
 	},
 
@@ -556,27 +501,6 @@ var unmarshalTests = []struct {
 		}{1, inlineB{2, inlineC{3}}},
 	},
 
-	// Struct inlining as a pointer.
-	{
-		"a: 1\nb: 2\nc: 3\n",
-		&struct {
-			A int
-			C *inlineB `yaml:",inline"`
-		}{1, &inlineB{2, inlineC{3}}},
-	}, {
-		"a: 1\n",
-		&struct {
-			A int
-			C *inlineB `yaml:",inline"`
-		}{1, nil},
-	}, {
-		"a: 1\nc: 3\nd: 4\n",
-		&struct {
-			A int
-			C *inlineD `yaml:",inline"`
-		}{1, &inlineD{&inlineC{3}, 4}},
-	},
-
 	// Map inlining
 	{
 		"a: 1\nb: 2\nc: 3\n",
@@ -603,7 +527,7 @@ var unmarshalTests = []struct {
 	// issue #295 (allow scalars with colons in flow mappings and sequences)
 	{
 		"a: {b: https://github.com/go-yaml/yaml}",
-		map[string]interface{}{"a": map[string]interface{}{
+		map[string]interface{}{"a": map[interface{}]interface{}{
 			"b": "https://github.com/go-yaml/yaml",
 		}},
 	},
@@ -642,10 +566,16 @@ var unmarshalTests = []struct {
 		map[string]string{"a": strings.Repeat("\x00", 52)},
 	},
 
+	// Ordered maps.
+	{
+		"{b: 2, a: 1, d: 4, c: 3, sub: {e: 5}}",
+		&yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}},
+	},
+
 	// Issue #39.
 	{
 		"a:\n b:\n  c: d\n",
-		map[string]struct{ B interface{} }{"a": {map[string]interface{}{"c": "d"}}},
+		map[string]struct{ B interface{} }{"a": {map[interface{}]interface{}{"c": "d"}}},
 	},
 
 	// Custom map type.
@@ -725,12 +655,12 @@ var unmarshalTests = []struct {
 	{
 		// explicit timestamp tag into interface.
 		"a: !!timestamp \"2015-01-01\"",
-		map[string]interface{}{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)},
+		map[string]interface{}{"a": "2015-01-01"},
 	},
 	{
 		// implicit timestamp tag into interface.
 		"a: 2015-01-01",
-		map[string]interface{}{"a": time.Date(2015, 1, 1, 0, 0, 0, 0, time.UTC)},
+		map[string]interface{}{"a": "2015-01-01"},
 	},
 
 	// Encode empty lists as zero-length slices.
@@ -772,11 +702,11 @@ var unmarshalTests = []struct {
 	// yaml-test-suite 3GZX: Spec Example 7.1. Alias Nodes
 	{
 		"First occurrence: &anchor Foo\nSecond occurrence: *anchor\nOverride anchor: &anchor Bar\nReuse anchor: *anchor\n",
-		map[string]interface{}{
+		map[interface{}]interface{}{
+			"Reuse anchor":      "Bar",
 			"First occurrence":  "Foo",
 			"Second occurrence": "Foo",
 			"Override anchor":   "Bar",
-			"Reuse anchor":      "Bar",
 		},
 	},
 	// Single document with garbage following it.
@@ -784,27 +714,29 @@ var unmarshalTests = []struct {
 		"---\nhello\n...\n}not yaml",
 		"hello",
 	},
-
-	// Comment scan exhausting the input buffer (issue #469).
 	{
-		"true\n#" + strings.Repeat(" ", 512*3),
-		"true",
-	}, {
-		"true #" + strings.Repeat(" ", 512*3),
-		"true",
+		"a: 5\n",
+		&struct{ A jsonNumberT }{"5"},
 	},
-
-	// CRLF
 	{
-		"a: b\r\nc:\r\n- d\r\n- e\r\n",
-		map[string]interface{}{
-			"a": "b",
-			"c": []interface{}{"d", "e"},
-		},
+		"a: 5.5\n",
+		&struct{ A jsonNumberT }{"5.5"},
+	},
+	{
+		`
+a:
+  b
+b:
+  ? a
+  : a`,
+		&M{"a": "b",
+			"b": M{
+				"a": "a",
+			}},
 	},
 }
 
-type M map[string]interface{}
+type M map[interface{}]interface{}
 
 type inlineB struct {
 	B       int
@@ -815,11 +747,6 @@ type inlineC struct {
 	C int
 }
 
-type inlineD struct {
-	C *inlineC `yaml:",inline"`
-	D int
-}
-
 func (s *S) TestUnmarshal(c *C) {
 	for i, item := range unmarshalTests {
 		c.Logf("test %d: %q", i, item.data)
@@ -833,15 +760,16 @@ func (s *S) TestUnmarshal(c *C) {
 	}
 }
 
+// TODO(v3): This test should also work when unmarshaling onto an interface{}.
 func (s *S) TestUnmarshalFullTimestamp(c *C) {
 	// Full timestamp in same format as encoded. This is confirmed to be
 	// properly decoded by Python as a timestamp as well.
 	var str = "2015-02-24T18:19:39.123456789-03:00"
-	var t interface{}
+	var t time.Time
 	err := yaml.Unmarshal([]byte(str), &t)
 	c.Assert(err, IsNil)
-	c.Assert(t, Equals, time.Date(2015, 2, 24, 18, 19, 39, 123456789, t.(time.Time).Location()))
-	c.Assert(t.(time.Time).In(time.UTC), Equals, time.Date(2015, 2, 24, 21, 19, 39, 123456789, time.UTC))
+	c.Assert(t, Equals, time.Date(2015, 2, 24, 18, 19, 39, 123456789, t.Location()))
+	c.Assert(t.In(time.UTC), Equals, time.Date(2015, 2, 24, 21, 19, 39, 123456789, time.UTC))
 }
 
 func (s *S) TestDecoderSingleDocument(c *C) {
@@ -872,12 +800,12 @@ var decoderTests = []struct {
 }, {
 	"a: b",
 	[]interface{}{
-		map[string]interface{}{"a": "b"},
+		map[interface{}]interface{}{"a": "b"},
 	},
 }, {
 	"---\na: b\n...\n",
 	[]interface{}{
-		map[string]interface{}{"a": "b"},
+		map[interface{}]interface{}{"a": "b"},
 	},
 }, {
 	"---\n'hello'\n...\n---\ngoodbye\n...\n",
@@ -923,13 +851,6 @@ func (s *S) TestUnmarshalNaN(c *C) {
 	c.Assert(math.IsNaN(value["notanum"].(float64)), Equals, true)
 }
 
-func (s *S) TestUnmarshalDurationInt(c *C) {
-	// Don't accept plain ints as durations as it's unclear (issue #200).
-	var d time.Duration
-	err := yaml.Unmarshal([]byte("123"), &d)
-	c.Assert(err, ErrorMatches, "(?s).* line 1: cannot unmarshal !!int `123` into time.Duration")
-}
-
 var unmarshalErrorTests = []struct {
 	data, error string
 }{
@@ -939,14 +860,14 @@ var unmarshalErrorTests = []struct {
 	{"a:\n- b: *,", "yaml: line 2: did not find expected alphabetic or numeric character"},
 	{"a: *b\n", "yaml: unknown anchor 'b' referenced"},
 	{"a: &a\n  b: *a\n", "yaml: anchor 'a' value contains itself"},
+	{"a: &x null\n<<:\n- *x\nb: &x {}\n", `yaml: map merge requires map or sequence of maps as the value`}, // Issue #529.
 	{"value: -", "yaml: block sequence entries are not allowed in this context"},
 	{"a: !!binary ==", "yaml: !!binary value contains invalid base64 data"},
 	{"{[.]}", `yaml: invalid map key: \[\]interface \{\}\{"\."\}`},
-	{"{{.}}", `yaml: invalid map key: map\[string]interface \{\}\{".":interface \{\}\(nil\)\}`},
+	{"{{.}}", `yaml: invalid map key: map\[interface\ \{\}\]interface \{\}\{".":interface \{\}\(nil\)\}`},
 	{"b: *a\na: &a {c: 1}", `yaml: unknown anchor 'a' referenced`},
 	{"%TAG !%79! tag:yaml.org,2002:\n---\nv: !%79!int '1'", "yaml: did not find expected whitespace"},
 	{"a:\n  1:\nb\n  2:", ".*could not find expected ':'"},
-	{"a: 1\nb: 2\nc 2\nd: 3\n", "^yaml: line 3: could not find expected ':'$"},
 	{
 		"a: &a [00,00,00,00,00,00,00,00,00]\n" +
 			"b: &b [*a,*a,*a,*a,*a,*a,*a,*a,*a]\n" +
@@ -967,6 +888,13 @@ func (s *S) TestUnmarshalErrors(c *C) {
 		var value interface{}
 		err := yaml.Unmarshal([]byte(item.data), &value)
 		c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value))
+
+		if strings.Contains(item.data, ":") {
+			// Repeat test with typed value.
+			var value map[string]interface{}
+			err := yaml.Unmarshal([]byte(item.data), &value)
+			c.Assert(err, ErrorMatches, item.error, Commentf("Partial unmarshal: %#v", value))
+		}
 	}
 }
 
@@ -982,7 +910,7 @@ var unmarshalerTests = []struct {
 	data, tag string
 	value     interface{}
 }{
-	{"_: {hi: there}", "!!map", map[string]interface{}{"hi": "there"}},
+	{"_: {hi: there}", "!!map", map[interface{}]interface{}{"hi": "there"}},
 	{"_: [1,A]", "!!seq", []interface{}{1, "A"}},
 	{"_: 10", "!!int", 10},
 	{"_: null", "!!null", nil},
@@ -998,8 +926,8 @@ type unmarshalerType struct {
 	value interface{}
 }
 
-func (o *unmarshalerType) UnmarshalYAML(value *yaml.Node) error {
-	if err := value.Decode(&o.value); err != nil {
+func (o *unmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error {
+	if err := unmarshal(&o.value); err != nil {
 		return err
 	}
 	if i, ok := o.value.(int); ok {
@@ -1018,39 +946,6 @@ type unmarshalerValue struct {
 	Field unmarshalerType "_"
 }
 
-type unmarshalerInlined struct {
-	Field   *unmarshalerType "_"
-	Inlined unmarshalerType  `yaml:",inline"`
-}
-
-type unmarshalerInlinedTwice struct {
-	InlinedTwice unmarshalerInlined `yaml:",inline"`
-}
-
-type obsoleteUnmarshalerType struct {
-	value interface{}
-}
-
-func (o *obsoleteUnmarshalerType) UnmarshalYAML(unmarshal func(v interface{}) error) error {
-	if err := unmarshal(&o.value); err != nil {
-		return err
-	}
-	if i, ok := o.value.(int); ok {
-		if result, ok := unmarshalerResult[i]; ok {
-			return result
-		}
-	}
-	return nil
-}
-
-type obsoleteUnmarshalerPointer struct {
-	Field *obsoleteUnmarshalerType "_"
-}
-
-type obsoleteUnmarshalerValue struct {
-	Field obsoleteUnmarshalerType "_"
-}
-
 func (s *S) TestUnmarshalerPointerField(c *C) {
 	for _, item := range unmarshalerTests {
 		obj := &unmarshalerPointer{}
@@ -1063,22 +958,11 @@ func (s *S) TestUnmarshalerPointerField(c *C) {
 			c.Assert(obj.Field.value, DeepEquals, item.value)
 		}
 	}
-	for _, item := range unmarshalerTests {
-		obj := &obsoleteUnmarshalerPointer{}
-		err := yaml.Unmarshal([]byte(item.data), obj)
-		c.Assert(err, IsNil)
-		if item.value == nil {
-			c.Assert(obj.Field, IsNil)
-		} else {
-			c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
-			c.Assert(obj.Field.value, DeepEquals, item.value)
-		}
-	}
 }
 
 func (s *S) TestUnmarshalerValueField(c *C) {
 	for _, item := range unmarshalerTests {
-		obj := &obsoleteUnmarshalerValue{}
+		obj := &unmarshalerValue{}
 		err := yaml.Unmarshal([]byte(item.data), obj)
 		c.Assert(err, IsNil)
 		c.Assert(obj.Field, NotNil, Commentf("Pointer not initialized (%#v)", item.value))
@@ -1086,25 +970,11 @@ func (s *S) TestUnmarshalerValueField(c *C) {
 	}
 }
 
-func (s *S) TestUnmarshalerInlinedField(c *C) {
-	obj := &unmarshalerInlined{}
-	err := yaml.Unmarshal([]byte("_: a\ninlined: b\n"), obj)
-	c.Assert(err, IsNil)
-	c.Assert(obj.Field, DeepEquals, &unmarshalerType{"a"})
-	c.Assert(obj.Inlined, DeepEquals, unmarshalerType{map[string]interface{}{"_": "a", "inlined": "b"}})
-
-	twc := &unmarshalerInlinedTwice{}
-	err = yaml.Unmarshal([]byte("_: a\ninlined: b\n"), twc)
-	c.Assert(err, IsNil)
-	c.Assert(twc.InlinedTwice.Field, DeepEquals, &unmarshalerType{"a"})
-	c.Assert(twc.InlinedTwice.Inlined, DeepEquals, unmarshalerType{map[string]interface{}{"_": "a", "inlined": "b"}})
-}
-
 func (s *S) TestUnmarshalerWholeDocument(c *C) {
-	obj := &obsoleteUnmarshalerType{}
+	obj := &unmarshalerType{}
 	err := yaml.Unmarshal([]byte(unmarshalerTests[0].data), obj)
 	c.Assert(err, IsNil)
-	value, ok := obj.value.(map[string]interface{})
+	value, ok := obj.value.(map[interface{}]interface{})
 	c.Assert(ok, Equals, true, Commentf("value: %#v", obj.value))
 	c.Assert(value["_"], DeepEquals, unmarshalerTests[0].value)
 }
@@ -1140,78 +1010,9 @@ func (s *S) TestUnmarshalerTypeError(c *C) {
 	c.Assert(v.M["ghi"].value, Equals, 3)
 }
 
-func (s *S) TestObsoleteUnmarshalerTypeError(c *C) {
-	unmarshalerResult[2] = &yaml.TypeError{[]string{"foo"}}
-	unmarshalerResult[4] = &yaml.TypeError{[]string{"bar"}}
-	defer func() {
-		delete(unmarshalerResult, 2)
-		delete(unmarshalerResult, 4)
-	}()
-
-	type T struct {
-		Before int
-		After  int
-		M      map[string]*obsoleteUnmarshalerType
-	}
-	var v T
-	data := `{before: A, m: {abc: 1, def: 2, ghi: 3, jkl: 4}, after: B}`
-	err := yaml.Unmarshal([]byte(data), &v)
-	c.Assert(err, ErrorMatches, ""+
-		"yaml: unmarshal errors:\n"+
-		"  line 1: cannot unmarshal !!str `A` into int\n"+
-		"  foo\n"+
-		"  bar\n"+
-		"  line 1: cannot unmarshal !!str `B` into int")
-	c.Assert(v.M["abc"], NotNil)
-	c.Assert(v.M["def"], IsNil)
-	c.Assert(v.M["ghi"], NotNil)
-	c.Assert(v.M["jkl"], IsNil)
-
-	c.Assert(v.M["abc"].value, Equals, 1)
-	c.Assert(v.M["ghi"].value, Equals, 3)
-}
-
 type proxyTypeError struct{}
 
-func (v *proxyTypeError) UnmarshalYAML(node *yaml.Node) error {
-	var s string
-	var a int32
-	var b int64
-	if err := node.Decode(&s); err != nil {
-		panic(err)
-	}
-	if s == "a" {
-		if err := node.Decode(&b); err == nil {
-			panic("should have failed")
-		}
-		return node.Decode(&a)
-	}
-	if err := node.Decode(&a); err == nil {
-		panic("should have failed")
-	}
-	return node.Decode(&b)
-}
-
-func (s *S) TestUnmarshalerTypeErrorProxying(c *C) {
-	type T struct {
-		Before int
-		After  int
-		M      map[string]*proxyTypeError
-	}
-	var v T
-	data := `{before: A, m: {abc: a, def: b}, after: B}`
-	err := yaml.Unmarshal([]byte(data), &v)
-	c.Assert(err, ErrorMatches, ""+
-		"yaml: unmarshal errors:\n"+
-		"  line 1: cannot unmarshal !!str `A` into int\n"+
-		"  line 1: cannot unmarshal !!str `a` into int32\n"+
-		"  line 1: cannot unmarshal !!str `b` into int64\n"+
-		"  line 1: cannot unmarshal !!str `B` into int")
-}
-
-type obsoleteProxyTypeError struct{}
-
-func (v *obsoleteProxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error {
+func (v *proxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	var s string
 	var a int32
 	var b int64
@@ -1230,11 +1031,11 @@ func (v *obsoleteProxyTypeError) UnmarshalYAML(unmarshal func(interface{}) error
 	return unmarshal(&b)
 }
 
-func (s *S) TestObsoleteUnmarshalerTypeErrorProxying(c *C) {
+func (s *S) TestUnmarshalerTypeErrorProxying(c *C) {
 	type T struct {
 		Before int
 		After  int
-		M      map[string]*obsoleteProxyTypeError
+		M      map[string]*proxyTypeError
 	}
 	var v T
 	data := `{before: A, m: {abc: a, def: b}, after: B}`
@@ -1247,11 +1048,11 @@ func (s *S) TestObsoleteUnmarshalerTypeErrorProxying(c *C) {
 		"  line 1: cannot unmarshal !!str `B` into int")
 }
 
-var failingErr = errors.New("failingErr")
-
 type failingUnmarshaler struct{}
 
-func (ft *failingUnmarshaler) UnmarshalYAML(node *yaml.Node) error {
+var failingErr = errors.New("failingErr")
+
+func (ft *failingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	return failingErr
 }
 
@@ -1260,29 +1061,18 @@ func (s *S) TestUnmarshalerError(c *C) {
 	c.Assert(err, Equals, failingErr)
 }
 
-type obsoleteFailingUnmarshaler struct{}
-
-func (ft *obsoleteFailingUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
-	return failingErr
-}
-
-func (s *S) TestObsoleteUnmarshalerError(c *C) {
-	err := yaml.Unmarshal([]byte("a: b"), &obsoleteFailingUnmarshaler{})
-	c.Assert(err, Equals, failingErr)
-}
-
 type sliceUnmarshaler []int
 
-func (su *sliceUnmarshaler) UnmarshalYAML(node *yaml.Node) error {
+func (su *sliceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
 	var slice []int
-	err := node.Decode(&slice)
+	err := unmarshal(&slice)
 	if err == nil {
 		*su = slice
 		return nil
 	}
 
 	var intVal int
-	err = node.Decode(&intVal)
+	err = unmarshal(&intVal)
 	if err == nil {
 		*su = []int{intVal}
 		return nil
@@ -1302,37 +1092,6 @@ func (s *S) TestUnmarshalerRetry(c *C) {
 	c.Assert(su, DeepEquals, sliceUnmarshaler([]int{1}))
 }
 
-type obsoleteSliceUnmarshaler []int
-
-func (su *obsoleteSliceUnmarshaler) UnmarshalYAML(unmarshal func(interface{}) error) error {
-	var slice []int
-	err := unmarshal(&slice)
-	if err == nil {
-		*su = slice
-		return nil
-	}
-
-	var intVal int
-	err = unmarshal(&intVal)
-	if err == nil {
-		*su = []int{intVal}
-		return nil
-	}
-
-	return err
-}
-
-func (s *S) TestObsoleteUnmarshalerRetry(c *C) {
-	var su obsoleteSliceUnmarshaler
-	err := yaml.Unmarshal([]byte("[1, 2, 3]"), &su)
-	c.Assert(err, IsNil)
-	c.Assert(su, DeepEquals, obsoleteSliceUnmarshaler([]int{1, 2, 3}))
-
-	err = yaml.Unmarshal([]byte("1"), &su)
-	c.Assert(err, IsNil)
-	c.Assert(su, DeepEquals, obsoleteSliceUnmarshaler([]int{1}))
-}
-
 // From http://yaml.org/type/merge.html
 var mergeTests = `
 anchors:
@@ -1397,11 +1156,6 @@ func (s *S) TestMerge(c *C) {
 		"label": "center/big",
 	}
 
-	wantStringMap := make(map[string]interface{})
-	for k, v := range want {
-		wantStringMap[fmt.Sprintf("%v", k)] = v
-	}
-
 	var m map[interface{}]interface{}
 	err := yaml.Unmarshal([]byte(mergeTests), &m)
 	c.Assert(err, IsNil)
@@ -1409,10 +1163,6 @@ func (s *S) TestMerge(c *C) {
 		if name == "anchors" {
 			continue
 		}
-		if name == "plain" {
-			c.Assert(test, DeepEquals, wantStringMap, Commentf("test %q failed", name))
-			continue
-		}
 		c.Assert(test, DeepEquals, want, Commentf("test %q failed", name))
 	}
 }
@@ -1435,75 +1185,28 @@ func (s *S) TestMergeStruct(c *C) {
 	}
 }
 
-var unmarshalNullTests = []struct {
-	input              string
-	pristine, expected func() interface{}
-}{{
-	"null",
+var unmarshalNullTests = []func() interface{}{
 	func() interface{} { var v interface{}; v = "v"; return &v },
-	func() interface{} { var v interface{}; v = nil; return &v },
-}, {
-	"null",
 	func() interface{} { var s = "s"; return &s },
-	func() interface{} { var s = "s"; return &s },
-}, {
-	"null",
 	func() interface{} { var s = "s"; sptr := &s; return &sptr },
-	func() interface{} { var sptr *string; return &sptr },
-}, {
-	"null",
-	func() interface{} { var i = 1; return &i },
 	func() interface{} { var i = 1; return &i },
-}, {
-	"null",
 	func() interface{} { var i = 1; iptr := &i; return &iptr },
-	func() interface{} { var iptr *int; return &iptr },
-}, {
-	"null",
-	func() interface{} { var m = map[string]int{"s": 1}; return &m },
-	func() interface{} { var m map[string]int; return &m },
-}, {
-	"null",
-	func() interface{} { var m = map[string]int{"s": 1}; return m },
-	func() interface{} { var m = map[string]int{"s": 1}; return m },
-}, {
-	"s2: null\ns3: null",
-	func() interface{} { var m = map[string]int{"s1": 1, "s2": 2}; return m },
-	func() interface{} { var m = map[string]int{"s1": 1, "s2": 2, "s3": 0}; return m },
-}, {
-	"s2: null\ns3: null",
-	func() interface{} { var m = map[string]interface{}{"s1": 1, "s2": 2}; return m },
-	func() interface{} { var m = map[string]interface{}{"s1": 1, "s2": nil, "s3": nil}; return m },
-}}
+	func() interface{} { m := map[string]int{"s": 1}; return &m },
+	func() interface{} { m := map[string]int{"s": 1}; return m },
+}
 
 func (s *S) TestUnmarshalNull(c *C) {
 	for _, test := range unmarshalNullTests {
-		pristine := test.pristine()
-		expected := test.expected()
-		err := yaml.Unmarshal([]byte(test.input), pristine)
+		item := test()
+		zero := reflect.Zero(reflect.TypeOf(item).Elem()).Interface()
+		err := yaml.Unmarshal([]byte("null"), item)
 		c.Assert(err, IsNil)
-		c.Assert(pristine, DeepEquals, expected)
-	}
-}
-
-func (s *S) TestUnmarshalPreservesData(c *C) {
-	var v struct {
-		A, B int
-		C    int `yaml:"-"`
+		if reflect.TypeOf(item).Kind() == reflect.Map {
+			c.Assert(reflect.ValueOf(item).Interface(), DeepEquals, reflect.MakeMap(reflect.TypeOf(item)).Interface())
+		} else {
+			c.Assert(reflect.ValueOf(item).Elem().Interface(), DeepEquals, zero)
+		}
 	}
-	v.A = 42
-	v.C = 88
-	err := yaml.Unmarshal([]byte("---"), &v)
-	c.Assert(err, IsNil)
-	c.Assert(v.A, Equals, 42)
-	c.Assert(v.B, Equals, 0)
-	c.Assert(v.C, Equals, 88)
-
-	err = yaml.Unmarshal([]byte("b: 21\nc: 99"), &v)
-	c.Assert(err, IsNil)
-	c.Assert(v.A, Equals, 42)
-	c.Assert(v.B, Equals, 21)
-	c.Assert(v.C, Equals, 88)
 }
 
 func (s *S) TestUnmarshalSliceOnPreset(c *C) {
@@ -1514,24 +1217,19 @@ func (s *S) TestUnmarshalSliceOnPreset(c *C) {
 }
 
 var unmarshalStrictTests = []struct {
-	known  bool
-	unique bool
-	data   string
-	value  interface{}
-	error  string
+	data  string
+	value interface{}
+	error string
 }{{
-	known: true,
 	data:  "a: 1\nc: 2\n",
 	value: struct{ A, B int }{A: 1},
 	error: `yaml: unmarshal errors:\n  line 2: field c not found in type struct { A int; B int }`,
 }, {
-	unique: true,
-	data:   "a: 1\nb: 2\na: 3\n",
-	value:  struct{ A, B int }{A: 3, B: 2},
-	error:  `yaml: unmarshal errors:\n  line 3: mapping key "a" already defined at line 1`,
+	data:  "a: 1\nb: 2\na: 3\n",
+	value: struct{ A, B int }{A: 3, B: 2},
+	error: `yaml: unmarshal errors:\n  line 3: field a already set in type struct { A int; B int }`,
 }, {
-	unique: true,
-	data:   "c: 3\na: 1\nb: 2\nc: 4\n",
+	data: "c: 3\na: 1\nb: 2\nc: 4\n",
 	value: struct {
 		A       int
 		inlineB `yaml:",inline"`
@@ -1544,10 +1242,9 @@ var unmarshalStrictTests = []struct {
 			},
 		},
 	},
-	error: `yaml: unmarshal errors:\n  line 4: mapping key "c" already defined at line 1`,
+	error: `yaml: unmarshal errors:\n  line 4: field c already set in type struct { A int; yaml_test.inlineB "yaml:\\",inline\\"" }`,
 }, {
-	unique: true,
-	data:   "c: 0\na: 1\nb: 2\nc: 1\n",
+	data: "c: 0\na: 1\nb: 2\nc: 1\n",
 	value: struct {
 		A       int
 		inlineB `yaml:",inline"`
@@ -1560,10 +1257,9 @@ var unmarshalStrictTests = []struct {
 			},
 		},
 	},
-	error: `yaml: unmarshal errors:\n  line 4: mapping key "c" already defined at line 1`,
+	error: `yaml: unmarshal errors:\n  line 4: field c already set in type struct { A int; yaml_test.inlineB "yaml:\\",inline\\"" }`,
 }, {
-	unique: true,
-	data:   "c: 1\na: 1\nb: 2\nc: 3\n",
+	data: "c: 1\na: 1\nb: 2\nc: 3\n",
 	value: struct {
 		A int
 		M map[string]interface{} `yaml:",inline"`
@@ -1574,36 +1270,31 @@ var unmarshalStrictTests = []struct {
 			"c": 3,
 		},
 	},
-	error: `yaml: unmarshal errors:\n  line 4: mapping key "c" already defined at line 1`,
+	error: `yaml: unmarshal errors:\n  line 4: key "c" already set in map`,
 }, {
-	unique: true,
-	data:   "a: 1\n9: 2\nnull: 3\n9: 4",
+	data: "a: 1\n9: 2\nnull: 3\n9: 4",
 	value: map[interface{}]interface{}{
 		"a": 1,
 		nil: 3,
 		9:   4,
 	},
-	error: `yaml: unmarshal errors:\n  line 4: mapping key "9" already defined at line 2`,
+	error: `yaml: unmarshal errors:\n  line 4: key 9 already set in map`,
 }}
 
-func (s *S) TestUnmarshalKnownFields(c *C) {
+func (s *S) TestUnmarshalStrict(c *C) {
 	for i, item := range unmarshalStrictTests {
 		c.Logf("test %d: %q", i, item.data)
 		// First test that normal Unmarshal unmarshals to the expected value.
-		if !item.unique {
-			t := reflect.ValueOf(item.value).Type()
-			value := reflect.New(t)
-			err := yaml.Unmarshal([]byte(item.data), value.Interface())
-			c.Assert(err, Equals, nil)
-			c.Assert(value.Elem().Interface(), DeepEquals, item.value)
-		}
-
-		// Then test that it fails on the same thing with KnownFields on.
 		t := reflect.ValueOf(item.value).Type()
 		value := reflect.New(t)
-		dec := yaml.NewDecoder(bytes.NewBuffer([]byte(item.data)))
-		dec.KnownFields(item.known)
-		err := dec.Decode(value.Interface())
+		err := yaml.Unmarshal([]byte(item.data), value.Interface())
+		c.Assert(err, Equals, nil)
+		c.Assert(value.Elem().Interface(), DeepEquals, item.value)
+
+		// Then test that UnmarshalStrict fails on the same thing.
+		t = reflect.ValueOf(item.value).Type()
+		value = reflect.New(t)
+		err = yaml.UnmarshalStrict([]byte(item.data), value.Interface())
 		c.Assert(err, ErrorMatches, item.error)
 	}
 }

+ 39 - 374
emitterc.go

@@ -1,25 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-// Copyright (c) 2006-2010 Kirill Simonov
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-// of the Software, and to permit persons to whom the Software is furnished to do
-// so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
 package yaml
 
 import (
@@ -65,13 +43,8 @@ func put_break(emitter *yaml_emitter_t) bool {
 	default:
 		panic("unknown line break setting")
 	}
-	if emitter.column == 0 {
-		emitter.space_above = true
-	}
 	emitter.column = 0
 	emitter.line++
-	// [Go] Do this here and below and drop from everywhere else (see commented lines).
-	emitter.indention = true
 	return true
 }
 
@@ -124,13 +97,8 @@ func write_break(emitter *yaml_emitter_t, s []byte, i *int) bool {
 		if !write(emitter, s, i) {
 			return false
 		}
-		if emitter.column == 0 {
-			emitter.space_above = true
-		}
 		emitter.column = 0
 		emitter.line++
-		// [Go] Do this here and above and drop from everywhere else (see commented lines).
-		emitter.indention = true
 	}
 	return true
 }
@@ -235,14 +203,7 @@ func yaml_emitter_increase_indent(emitter *yaml_emitter_t, flow, indentless bool
 			emitter.indent = 0
 		}
 	} else if !indentless {
-		// [Go] This was changed so that indentations are more regular.
-		if emitter.states[len(emitter.states)-1] == yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE {
-			// The first indent inside a sequence will just skip the "- " indicator.
-			emitter.indent += 2
-		} else {
-			// Everything else aligns to the chosen indentation.
-			emitter.indent = emitter.best_indent * ((emitter.indent + emitter.best_indent) / emitter.best_indent)
-		}
+		emitter.indent += emitter.best_indent
 	}
 	return true
 }
@@ -267,22 +228,16 @@ func yaml_emitter_state_machine(emitter *yaml_emitter_t, event *yaml_event_t) bo
 		return yaml_emitter_emit_document_end(emitter, event)
 
 	case yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE:
-		return yaml_emitter_emit_flow_sequence_item(emitter, event, true, false)
-
-	case yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE:
-		return yaml_emitter_emit_flow_sequence_item(emitter, event, false, true)
+		return yaml_emitter_emit_flow_sequence_item(emitter, event, true)
 
 	case yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE:
-		return yaml_emitter_emit_flow_sequence_item(emitter, event, false, false)
+		return yaml_emitter_emit_flow_sequence_item(emitter, event, false)
 
 	case yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE:
-		return yaml_emitter_emit_flow_mapping_key(emitter, event, true, false)
-
-	case yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE:
-		return yaml_emitter_emit_flow_mapping_key(emitter, event, false, true)
+		return yaml_emitter_emit_flow_mapping_key(emitter, event, true)
 
 	case yaml_EMIT_FLOW_MAPPING_KEY_STATE:
-		return yaml_emitter_emit_flow_mapping_key(emitter, event, false, false)
+		return yaml_emitter_emit_flow_mapping_key(emitter, event, false)
 
 	case yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE:
 		return yaml_emitter_emit_flow_mapping_value(emitter, event, true)
@@ -343,8 +298,6 @@ func yaml_emitter_emit_stream_start(emitter *yaml_emitter_t, event *yaml_event_t
 	emitter.column = 0
 	emitter.whitespace = true
 	emitter.indention = true
-	emitter.space_above = true
-	emitter.foot_indent = -1
 
 	if emitter.encoding != yaml_UTF8_ENCODING {
 		if !yaml_emitter_write_bom(emitter) {
@@ -439,22 +392,13 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event
 			if !yaml_emitter_write_indicator(emitter, []byte("---"), true, false, false) {
 				return false
 			}
-			if emitter.canonical || true {
+			if emitter.canonical {
 				if !yaml_emitter_write_indent(emitter) {
 					return false
 				}
 			}
 		}
 
-		if len(emitter.head_comment) > 0 {
-			if !yaml_emitter_process_head_comment(emitter) {
-				return false
-			}
-			if !put_break(emitter) {
-				return false
-			}
-		}
-
 		emitter.state = yaml_EMIT_DOCUMENT_CONTENT_STATE
 		return true
 	}
@@ -481,20 +425,7 @@ func yaml_emitter_emit_document_start(emitter *yaml_emitter_t, event *yaml_event
 // Expect the root node.
 func yaml_emitter_emit_document_content(emitter *yaml_emitter_t, event *yaml_event_t) bool {
 	emitter.states = append(emitter.states, yaml_EMIT_DOCUMENT_END_STATE)
-
-	if !yaml_emitter_process_head_comment(emitter) {
-		return false
-	}
-	if !yaml_emitter_emit_node(emitter, event, true, false, false, false) {
-		return false
-	}
-	if !yaml_emitter_process_line_comment(emitter) {
-		return false
-	}
-	if !yaml_emitter_process_foot_comment(emitter) {
-		return false
-	}
-	return true
+	return yaml_emitter_emit_node(emitter, event, true, false, false, false)
 }
 
 // Expect DOCUMENT-END.
@@ -502,12 +433,6 @@ func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t
 	if event.typ != yaml_DOCUMENT_END_EVENT {
 		return yaml_emitter_set_emitter_error(emitter, "expected DOCUMENT-END")
 	}
-	// [Go] Force document foot separation.
-	emitter.foot_indent = 0
-	if !yaml_emitter_process_foot_comment(emitter) {
-		return false
-	}
-	emitter.foot_indent = -1
 	if !yaml_emitter_write_indent(emitter) {
 		return false
 	}
@@ -529,7 +454,7 @@ func yaml_emitter_emit_document_end(emitter *yaml_emitter_t, event *yaml_event_t
 }
 
 // Expect a flow item node.
-func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool {
+func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
 	if first {
 		if !yaml_emitter_write_indicator(emitter, []byte{'['}, true, true, false) {
 			return false
@@ -541,15 +466,13 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e
 	}
 
 	if event.typ == yaml_SEQUENCE_END_EVENT {
-		if emitter.canonical && !first && !trail {
-			if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
-				return false
-			}
-		}
 		emitter.flow_level--
 		emitter.indent = emitter.indents[len(emitter.indents)-1]
 		emitter.indents = emitter.indents[:len(emitter.indents)-1]
-		if emitter.column == 0 || emitter.canonical && !first {
+		if emitter.canonical && !first {
+			if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+				return false
+			}
 			if !yaml_emitter_write_indent(emitter) {
 				return false
 			}
@@ -557,62 +480,29 @@ func yaml_emitter_emit_flow_sequence_item(emitter *yaml_emitter_t, event *yaml_e
 		if !yaml_emitter_write_indicator(emitter, []byte{']'}, false, false, false) {
 			return false
 		}
-		if !yaml_emitter_process_line_comment(emitter) {
-			return false
-		}
-		if !yaml_emitter_process_foot_comment(emitter) {
-			return false
-		}
 		emitter.state = emitter.states[len(emitter.states)-1]
 		emitter.states = emitter.states[:len(emitter.states)-1]
 
 		return true
 	}
 
-	if !first && !trail {
+	if !first {
 		if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
 			return false
 		}
 	}
 
-	if !yaml_emitter_process_head_comment(emitter) {
-		return false
-	}
-	if emitter.column == 0 {
-		if !yaml_emitter_write_indent(emitter) {
-			return false
-		}
-	}
-
 	if emitter.canonical || emitter.column > emitter.best_width {
 		if !yaml_emitter_write_indent(emitter) {
 			return false
 		}
 	}
-	if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
-		emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE)
-	} else {
-		emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE)
-	}
-	if !yaml_emitter_emit_node(emitter, event, false, true, false, false) {
-		return false
-	}
-	if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
-		if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
-			return false
-		}
-	}
-	if !yaml_emitter_process_line_comment(emitter) {
-		return false
-	}
-	if !yaml_emitter_process_foot_comment(emitter) {
-		return false
-	}
-	return true
+	emitter.states = append(emitter.states, yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE)
+	return yaml_emitter_emit_node(emitter, event, false, true, false, false)
 }
 
 // Expect a flow key node.
-func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first, trail bool) bool {
+func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
 	if first {
 		if !yaml_emitter_write_indicator(emitter, []byte{'{'}, true, true, false) {
 			return false
@@ -624,18 +514,13 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve
 	}
 
 	if event.typ == yaml_MAPPING_END_EVENT {
-		if (emitter.canonical || len(emitter.head_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0) && !first && !trail {
-			if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
-				return false
-			}
-		}
-		if !yaml_emitter_process_head_comment(emitter) {
-			return false
-		}
 		emitter.flow_level--
 		emitter.indent = emitter.indents[len(emitter.indents)-1]
 		emitter.indents = emitter.indents[:len(emitter.indents)-1]
 		if emitter.canonical && !first {
+			if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
+				return false
+			}
 			if !yaml_emitter_write_indent(emitter) {
 				return false
 			}
@@ -643,33 +528,16 @@ func yaml_emitter_emit_flow_mapping_key(emitter *yaml_emitter_t, event *yaml_eve
 		if !yaml_emitter_write_indicator(emitter, []byte{'}'}, false, false, false) {
 			return false
 		}
-		if !yaml_emitter_process_line_comment(emitter) {
-			return false
-		}
-		if !yaml_emitter_process_foot_comment(emitter) {
-			return false
-		}
 		emitter.state = emitter.states[len(emitter.states)-1]
 		emitter.states = emitter.states[:len(emitter.states)-1]
 		return true
 	}
 
-	if !first && !trail {
+	if !first {
 		if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
 			return false
 		}
 	}
-
-	if !yaml_emitter_process_head_comment(emitter) {
-		return false
-	}
-
-	if emitter.column == 0 {
-		if !yaml_emitter_write_indent(emitter) {
-			return false
-		}
-	}
-
 	if emitter.canonical || emitter.column > emitter.best_width {
 		if !yaml_emitter_write_indent(emitter) {
 			return false
@@ -703,32 +571,14 @@ func yaml_emitter_emit_flow_mapping_value(emitter *yaml_emitter_t, event *yaml_e
 			return false
 		}
 	}
-	if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
-		emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE)
-	} else {
-		emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE)
-	}
-	if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
-		return false
-	}
-	if len(emitter.line_comment)+len(emitter.foot_comment)+len(emitter.tail_comment) > 0 {
-		if !yaml_emitter_write_indicator(emitter, []byte{','}, false, false, false) {
-			return false
-		}
-	}
-	if !yaml_emitter_process_line_comment(emitter) {
-		return false
-	}
-	if !yaml_emitter_process_foot_comment(emitter) {
-		return false
-	}
-	return true
+	emitter.states = append(emitter.states, yaml_EMIT_FLOW_MAPPING_KEY_STATE)
+	return yaml_emitter_emit_node(emitter, event, false, false, true, false)
 }
 
 // Expect a block item node.
 func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_event_t, first bool) bool {
 	if first {
-		if !yaml_emitter_increase_indent(emitter, false, false) {
+		if !yaml_emitter_increase_indent(emitter, false, emitter.mapping_context && !emitter.indention) {
 			return false
 		}
 	}
@@ -739,9 +589,6 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_
 		emitter.states = emitter.states[:len(emitter.states)-1]
 		return true
 	}
-	if !yaml_emitter_process_head_comment(emitter) {
-		return false
-	}
 	if !yaml_emitter_write_indent(emitter) {
 		return false
 	}
@@ -749,16 +596,7 @@ func yaml_emitter_emit_block_sequence_item(emitter *yaml_emitter_t, event *yaml_
 		return false
 	}
 	emitter.states = append(emitter.states, yaml_EMIT_BLOCK_SEQUENCE_ITEM_STATE)
-	if !yaml_emitter_emit_node(emitter, event, false, true, false, false) {
-		return false
-	}
-	if !yaml_emitter_process_line_comment(emitter) {
-		return false
-	}
-	if !yaml_emitter_process_foot_comment(emitter) {
-		return false
-	}
-	return true
+	return yaml_emitter_emit_node(emitter, event, false, true, false, false)
 }
 
 // Expect a block key node.
@@ -768,9 +606,6 @@ func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_ev
 			return false
 		}
 	}
-	if !yaml_emitter_process_head_comment(emitter) {
-		return false
-	}
 	if event.typ == yaml_MAPPING_END_EVENT {
 		emitter.indent = emitter.indents[len(emitter.indents)-1]
 		emitter.indents = emitter.indents[:len(emitter.indents)-1]
@@ -781,13 +616,6 @@ func yaml_emitter_emit_block_mapping_key(emitter *yaml_emitter_t, event *yaml_ev
 	if !yaml_emitter_write_indent(emitter) {
 		return false
 	}
-	if len(emitter.line_comment) > 0 {
-		// [Go] A line comment was provided for the key. That's unusual as the
-		//      scanner associates line comments with the value. Either way,
-		//      save the line comment and render it appropriately later.
-		emitter.key_line_comment = emitter.line_comment
-		emitter.line_comment = nil
-	}
 	if yaml_emitter_check_simple_key(emitter) {
 		emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_SIMPLE_VALUE_STATE)
 		return yaml_emitter_emit_node(emitter, event, false, false, true, true)
@@ -813,42 +641,8 @@ func yaml_emitter_emit_block_mapping_value(emitter *yaml_emitter_t, event *yaml_
 			return false
 		}
 	}
-	if len(emitter.key_line_comment) > 0 {
-		// [Go] Line comments are generally associated with the value, but when there's
-		//      no value on the same line as a mapping key they end up attached to the
-		//      key itself.
-		if event.typ == yaml_SCALAR_EVENT {
-			if len(emitter.line_comment) == 0 {
-				// A scalar is coming and it has no line comments by itself yet,
-				// so just let it handle the line comment as usual. If it has a
-				// line comment, we can't have both so the one from the key is lost.
-				emitter.line_comment = emitter.key_line_comment
-				emitter.key_line_comment = nil
-			}
-		} else if event.sequence_style() != yaml_FLOW_SEQUENCE_STYLE && (event.typ == yaml_MAPPING_START_EVENT || event.typ == yaml_SEQUENCE_START_EVENT) {
-			// An indented block follows, so write the comment right now.
-			emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment
-			if !yaml_emitter_process_line_comment(emitter) {
-				return false
-			}
-			emitter.line_comment, emitter.key_line_comment = emitter.key_line_comment, emitter.line_comment
-		}
-	}
 	emitter.states = append(emitter.states, yaml_EMIT_BLOCK_MAPPING_KEY_STATE)
-	if !yaml_emitter_emit_node(emitter, event, false, false, true, false) {
-		return false
-	}
-	if !yaml_emitter_process_line_comment(emitter) {
-		return false
-	}
-	if !yaml_emitter_process_foot_comment(emitter) {
-		return false
-	}
-	return true
-}
-
-func yaml_emitter_silent_nil_event(emitter *yaml_emitter_t, event *yaml_event_t) bool {
-	return event.typ == yaml_SCALAR_EVENT && event.implicit && !emitter.canonical && len(emitter.scalar_data.value) == 0
+	return yaml_emitter_emit_node(emitter, event, false, false, true, false)
 }
 
 // Expect a node.
@@ -1114,71 +908,6 @@ func yaml_emitter_process_scalar(emitter *yaml_emitter_t) bool {
 	panic("unknown scalar style")
 }
 
-// Write a head comment.
-func yaml_emitter_process_head_comment(emitter *yaml_emitter_t) bool {
-	if len(emitter.tail_comment) > 0 {
-		if !yaml_emitter_write_indent(emitter) {
-			return false
-		}
-		if !yaml_emitter_write_comment(emitter, emitter.tail_comment) {
-			return false
-		}
-		emitter.tail_comment = emitter.tail_comment[:0]
-		emitter.foot_indent = emitter.indent
-		if emitter.foot_indent < 0 {
-			emitter.foot_indent = 0
-		}
-	}
-
-	if len(emitter.head_comment) == 0 {
-		return true
-	}
-	if !yaml_emitter_write_indent(emitter) {
-		return false
-	}
-	if !yaml_emitter_write_comment(emitter, emitter.head_comment) {
-		return false
-	}
-	emitter.head_comment = emitter.head_comment[:0]
-	return true
-}
-
-// Write an line comment.
-func yaml_emitter_process_line_comment(emitter *yaml_emitter_t) bool {
-	if len(emitter.line_comment) == 0 {
-		return true
-	}
-	if !emitter.whitespace {
-		if !put(emitter, ' ') {
-			return false
-		}
-	}
-	if !yaml_emitter_write_comment(emitter, emitter.line_comment) {
-		return false
-	}
-	emitter.line_comment = emitter.line_comment[:0]
-	return true
-}
-
-// Write a foot comment.
-func yaml_emitter_process_foot_comment(emitter *yaml_emitter_t) bool {
-	if len(emitter.foot_comment) == 0 {
-		return true
-	}
-	if !yaml_emitter_write_indent(emitter) {
-		return false
-	}
-	if !yaml_emitter_write_comment(emitter, emitter.foot_comment) {
-		return false
-	}
-	emitter.foot_comment = emitter.foot_comment[:0]
-	emitter.foot_indent = emitter.indent
-	if emitter.foot_indent < 0 {
-		emitter.foot_indent = 0
-	}
-	return true
-}
-
 // Check if a %YAML directive is valid.
 func yaml_emitter_analyze_version_directive(emitter *yaml_emitter_t, version_directive *yaml_version_directive_t) bool {
 	if version_directive.major != 1 || version_directive.minor != 1 {
@@ -1258,7 +987,6 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
 		flow_indicators    = false
 		line_breaks        = false
 		special_characters = false
-		tab_characters     = false
 
 		leading_space  = false
 		leading_break  = false
@@ -1327,9 +1055,7 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
 			}
 		}
 
-		if value[i] == '\t' {
-			tab_characters = true
-		} else if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode {
+		if !is_printable(value, i) || !is_ascii(value, i) && !emitter.unicode {
 			special_characters = true
 		}
 		if is_space(value, i) {
@@ -1384,12 +1110,10 @@ func yaml_emitter_analyze_scalar(emitter *yaml_emitter_t, value []byte) bool {
 		emitter.scalar_data.block_plain_allowed = false
 		emitter.scalar_data.single_quoted_allowed = false
 	}
-	if space_break || tab_characters || special_characters {
+	if space_break || special_characters {
 		emitter.scalar_data.flow_plain_allowed = false
 		emitter.scalar_data.block_plain_allowed = false
 		emitter.scalar_data.single_quoted_allowed = false
-	}
-	if space_break || special_characters {
 		emitter.scalar_data.block_allowed = false
 	}
 	if line_breaks {
@@ -1413,19 +1137,6 @@ func yaml_emitter_analyze_event(emitter *yaml_emitter_t, event *yaml_event_t) bo
 	emitter.tag_data.suffix = nil
 	emitter.scalar_data.value = nil
 
-	if len(event.head_comment) > 0 {
-		emitter.head_comment = event.head_comment
-	}
-	if len(event.line_comment) > 0 {
-		emitter.line_comment = event.line_comment
-	}
-	if len(event.foot_comment) > 0 {
-		emitter.foot_comment = event.foot_comment
-	}
-	if len(event.tail_comment) > 0 {
-		emitter.tail_comment = event.tail_comment
-	}
-
 	switch event.typ {
 	case yaml_ALIAS_EVENT:
 		if !yaml_emitter_analyze_anchor(emitter, event.anchor, true) {
@@ -1497,20 +1208,13 @@ func yaml_emitter_write_indent(emitter *yaml_emitter_t) bool {
 			return false
 		}
 	}
-	if emitter.foot_indent == indent {
-		if !put_break(emitter) {
-			return false
-		}
-	}
 	for emitter.column < indent {
 		if !put(emitter, ' ') {
 			return false
 		}
 	}
 	emitter.whitespace = true
-	//emitter.indention = true
-	emitter.space_above = false
-	emitter.foot_indent = -1
+	emitter.indention = true
 	return true
 }
 
@@ -1607,7 +1311,7 @@ func yaml_emitter_write_tag_content(emitter *yaml_emitter_t, value []byte, need_
 }
 
 func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allow_breaks bool) bool {
-	if len(value) > 0 && !emitter.whitespace {
+	if !emitter.whitespace {
 		if !put(emitter, ' ') {
 			return false
 		}
@@ -1637,7 +1341,7 @@ func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allo
 			if !write_break(emitter, value, &i) {
 				return false
 			}
-			//emitter.indention = true
+			emitter.indention = true
 			breaks = true
 		} else {
 			if breaks {
@@ -1654,9 +1358,7 @@ func yaml_emitter_write_plain_scalar(emitter *yaml_emitter_t, value []byte, allo
 		}
 	}
 
-	if len(value) > 0 {
-		emitter.whitespace = false
-	}
+	emitter.whitespace = false
 	emitter.indention = false
 	if emitter.root_context {
 		emitter.open_ended = true
@@ -1695,7 +1397,7 @@ func yaml_emitter_write_single_quoted_scalar(emitter *yaml_emitter_t, value []by
 			if !write_break(emitter, value, &i) {
 				return false
 			}
-			//emitter.indention = true
+			emitter.indention = true
 			breaks = true
 		} else {
 			if breaks {
@@ -1894,10 +1596,10 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo
 	if !yaml_emitter_write_block_scalar_hints(emitter, value) {
 		return false
 	}
-	if !yaml_emitter_process_line_comment(emitter) {
+	if !put_break(emitter) {
 		return false
 	}
-	//emitter.indention = true
+	emitter.indention = true
 	emitter.whitespace = true
 	breaks := true
 	for i := 0; i < len(value); {
@@ -1905,7 +1607,7 @@ func yaml_emitter_write_literal_scalar(emitter *yaml_emitter_t, value []byte) bo
 			if !write_break(emitter, value, &i) {
 				return false
 			}
-			//emitter.indention = true
+			emitter.indention = true
 			breaks = true
 		} else {
 			if breaks {
@@ -1931,11 +1633,11 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
 	if !yaml_emitter_write_block_scalar_hints(emitter, value) {
 		return false
 	}
-	if !yaml_emitter_process_line_comment(emitter) {
+
+	if !put_break(emitter) {
 		return false
 	}
-
-	//emitter.indention = true
+	emitter.indention = true
 	emitter.whitespace = true
 
 	breaks := true
@@ -1956,7 +1658,7 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
 			if !write_break(emitter, value, &i) {
 				return false
 			}
-			//emitter.indention = true
+			emitter.indention = true
 			breaks = true
 		} else {
 			if breaks {
@@ -1981,40 +1683,3 @@ func yaml_emitter_write_folded_scalar(emitter *yaml_emitter_t, value []byte) boo
 	}
 	return true
 }
-
-func yaml_emitter_write_comment(emitter *yaml_emitter_t, comment []byte) bool {
-	breaks := false
-	pound := false
-	for i := 0; i < len(comment); {
-		if is_break(comment, i) {
-			if !write_break(emitter, comment, &i) {
-				return false
-			}
-			//emitter.indention = true
-			breaks = true
-			pound = false
-		} else {
-			if breaks && !yaml_emitter_write_indent(emitter) {
-				return false
-			}
-			if !pound {
-				if comment[i] != '#' && (!put(emitter, '#') || !put(emitter, ' ')) {
-					return false
-				}
-				pound = true
-			}
-			if !write(emitter, comment, &i) {
-				return false
-			}
-			emitter.indention = false
-			breaks = false
-		}
-	}
-	if !breaks && !put_break(emitter) {
-		return false
-	}
-
-	emitter.whitespace = true
-	//emitter.indention = true
-	return true
-}

+ 88 - 268
encode.go

@@ -1,18 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package yaml
 
 import (
@@ -30,12 +15,26 @@ import (
 	"google.golang.org/protobuf/types/known/durationpb"
 )
 
+// jsonNumber is the interface of the encoding/json.Number datatype.
+// Repeating the interface here avoids a dependency on encoding/json, and also
+// supports other libraries like jsoniter, which use a similar datatype with
+// the same interface. Detecting this interface is useful when dealing with
+// structures containing json.Number, which is a string under the hood. The
+// encoder should prefer the use of Int64(), Float64() and string(), in that
+// order, when encoding this type.
+type jsonNumber interface {
+	Float64() (float64, error)
+	Int64() (int64, error)
+	String() string
+}
+
 type encoder struct {
-	emitter  yaml_emitter_t
-	event    yaml_event_t
-	out      []byte
-	flow     bool
-	indent   int
+	emitter yaml_emitter_t
+	event   yaml_event_t
+	out     []byte
+	flow    bool
+	// doneInit holds whether the initial stream_start_event has been
+	// emitted.
 	doneInit bool
 }
 
@@ -59,10 +58,6 @@ func (e *encoder) init() {
 	if e.doneInit {
 		return
 	}
-	if e.indent == 0 {
-		e.indent = 4
-	}
-	e.emitter.best_indent = e.indent
 	yaml_stream_start_event_initialize(&e.event, yaml_UTF8_ENCODING)
 	e.emit()
 	e.doneInit = true
@@ -95,40 +90,35 @@ func (e *encoder) must(ok bool) {
 
 func (e *encoder) marshalDoc(tag string, in reflect.Value) {
 	e.init()
-	var node *Node
-	if in.IsValid() {
-		node, _ = in.Interface().(*Node)
-	}
-	if node != nil && node.Kind == DocumentNode {
-		e.nodev(in)
-	} else {
-		yaml_document_start_event_initialize(&e.event, nil, nil, true)
-		e.emit()
-		e.marshal(tag, in)
-		yaml_document_end_event_initialize(&e.event, true)
-		e.emit()
-	}
+	yaml_document_start_event_initialize(&e.event, nil, nil, true)
+	e.emit()
+	e.marshal(tag, in)
+	yaml_document_end_event_initialize(&e.event, true)
+	e.emit()
 }
 
 func (e *encoder) marshal(tag string, in reflect.Value) {
-	tag = shortTag(tag)
 	if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
 		e.nilv()
 		return
 	}
 	iface := in.Interface()
-	switch value := iface.(type) {
-	case *Node:
-		e.nodev(in)
-		return
-	case Node:
-		if !in.CanAddr() {
-			var n = reflect.New(in.Type()).Elem()
-			n.Set(in)
-			in = n
+	switch m := iface.(type) {
+	case jsonNumber:
+		integer, err := m.Int64()
+		if err == nil {
+			// In this case the json.Number is a valid int64
+			in = reflect.ValueOf(integer)
+			break
 		}
-		e.nodev(in.Addr())
-		return
+		float, err := m.Float64()
+		if err == nil {
+			// In this case the json.Number is a valid float64
+			in = reflect.ValueOf(float)
+			break
+		}
+		// fallback case - no number could be obtained
+		in = reflect.ValueOf(m.String())
 	case time.Time:
 		e.timev(tag, in)
 		return
@@ -136,13 +126,16 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
 		e.timev(tag, in.Elem())
 		return
 	case time.Duration:
-		e.stringv(tag, reflect.ValueOf(value.String()))
+		e.stringv(tag, reflect.ValueOf(m.String()))
 		return
 	case *durationpb.Duration:
-		e.stringv(tag, reflect.ValueOf(value.AsDuration().String()))
+		e.stringv(tag, reflect.ValueOf(m.AsDuration().String()))
+		return
+	case durationpb.Duration:
+		e.stringv(tag, reflect.ValueOf(m.AsDuration().String()))
 		return
 	case Marshaler:
-		v, err := value.MarshalYAML()
+		v, err := m.MarshalYAML()
 		if err != nil {
 			fail(err)
 		}
@@ -150,10 +143,9 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
 			e.nilv()
 			return
 		}
-		e.marshal(tag, reflect.ValueOf(v))
-		return
+		in = reflect.ValueOf(v)
 	case encoding.TextMarshaler:
-		text, err := value.MarshalText()
+		text, err := m.MarshalText()
 		if err != nil {
 			fail(err)
 		}
@@ -168,15 +160,31 @@ func (e *encoder) marshal(tag string, in reflect.Value) {
 	case reflect.Map:
 		e.mapv(tag, in)
 	case reflect.Ptr:
-		e.marshal(tag, in.Elem())
+		if in.Type() == ptrTimeType {
+			e.timev(tag, in.Elem())
+		} else {
+			e.marshal(tag, in.Elem())
+		}
 	case reflect.Struct:
-		e.structv(tag, in)
+		if in.Type() == timeType {
+			e.timev(tag, in)
+		} else {
+			e.structv(tag, in)
+		}
 	case reflect.Slice, reflect.Array:
-		e.slicev(tag, in)
+		if in.Type().Elem() == mapItemType {
+			e.itemsv(tag, in)
+		} else {
+			e.slicev(tag, in)
+		}
 	case reflect.String:
 		e.stringv(tag, in)
 	case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-		e.intv(tag, in)
+		if in.Type() == durationType {
+			e.stringv(tag, reflect.ValueOf(iface.(time.Duration).String()))
+		} else {
+			e.intv(tag, in)
+		}
 	case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
 		e.uintv(tag, in)
 	case reflect.Float32, reflect.Float64:
@@ -199,21 +207,14 @@ func (e *encoder) mapv(tag string, in reflect.Value) {
 	})
 }
 
-func (e *encoder) fieldByIndex(v reflect.Value, index []int) (field reflect.Value) {
-	for _, num := range index {
-		for {
-			if v.Kind() == reflect.Ptr {
-				if v.IsNil() {
-					return reflect.Value{}
-				}
-				v = v.Elem()
-				continue
-			}
-			break
+func (e *encoder) itemsv(tag string, in reflect.Value) {
+	e.mappingv(tag, func() {
+		slice := in.Convert(reflect.TypeOf([]MapItem{})).Interface().([]MapItem)
+		for _, item := range slice {
+			e.marshal("", reflect.ValueOf(item.Key))
+			e.marshal("", reflect.ValueOf(item.Value))
 		}
-		v = v.Field(num)
-	}
-	return v
+	})
 }
 
 func (e *encoder) structv(tag string, in reflect.Value) {
@@ -227,10 +228,7 @@ func (e *encoder) structv(tag string, in reflect.Value) {
 			if info.Inline == nil {
 				value = in.Field(info.Num)
 			} else {
-				value = e.fieldByIndex(in, info.Inline)
-				if !value.IsValid() {
-					continue
-				}
+				value = in.FieldByIndex(info.Inline)
 			}
 			if info.OmitEmpty && isZero(value) {
 				continue
@@ -247,7 +245,7 @@ func (e *encoder) structv(tag string, in reflect.Value) {
 				sort.Sort(keys)
 				for _, k := range keys {
 					if _, found := sinfo.FieldsMap[k.String()]; found {
-						panic(fmt.Sprintf("cannot have key %q in inlined map: conflicts with struct field", k.String()))
+						panic(fmt.Sprintf("Can't have key %q in inlined map; conflicts with struct field", k.String()))
 					}
 					e.marshal("", k)
 					e.flow = false
@@ -311,28 +309,13 @@ func isBase60Float(s string) (result bool) {
 // is bogus. In practice parsers do not enforce the "\.[0-9_]*" suffix.
 var base60float = regexp.MustCompile(`^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+(?:\.[0-9_]*)?$`)
 
-// isOldBool returns whether s is bool notation as defined in YAML 1.1.
-//
-// We continue to force strings that YAML 1.1 would interpret as booleans to be
-// rendered as quotes strings so that the marshalled output valid for YAML 1.1
-// parsing.
-func isOldBool(s string) (result bool) {
-	switch s {
-	case "y", "Y", "yes", "Yes", "YES", "on", "On", "ON",
-		"n", "N", "no", "No", "NO", "off", "Off", "OFF":
-		return true
-	default:
-		return false
-	}
-}
-
 func (e *encoder) stringv(tag string, in reflect.Value) {
 	var style yaml_scalar_style_t
 	s := in.String()
 	canUsePlain := true
 	switch {
 	case !utf8.ValidString(s):
-		if tag == binaryTag {
+		if tag == yaml_BINARY_TAG {
 			failf("explicitly tagged !!binary data must be base64-encoded")
 		}
 		if tag != "" {
@@ -340,31 +323,27 @@ func (e *encoder) stringv(tag string, in reflect.Value) {
 		}
 		// It can't be encoded directly as YAML so use a binary tag
 		// and encode it as base64.
-		tag = binaryTag
+		tag = yaml_BINARY_TAG
 		s = encodeBase64(s)
 	case tag == "":
 		// Check to see if it would resolve to a specific
 		// tag when encoded unquoted. If it doesn't,
 		// there's no need to quote it.
 		rtag, _ := resolve("", s)
-		canUsePlain = rtag == strTag && !(isBase60Float(s) || isOldBool(s))
+		canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s)
 	}
 	// Note: it's possible for user code to emit invalid YAML
 	// if they explicitly specify a tag and a string containing
 	// text that's incompatible with that tag.
 	switch {
 	case strings.Contains(s, "\n"):
-		if e.flow {
-			style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
-		} else {
-			style = yaml_LITERAL_SCALAR_STYLE
-		}
+		style = yaml_LITERAL_SCALAR_STYLE
 	case canUsePlain:
 		style = yaml_PLAIN_SCALAR_STYLE
 	default:
 		style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
 	}
-	e.emitScalar(s, "", tag, style, nil, nil, nil, nil)
+	e.emitScalar(s, "", tag, style)
 }
 
 func (e *encoder) boolv(tag string, in reflect.Value) {
@@ -374,23 +353,23 @@ func (e *encoder) boolv(tag string, in reflect.Value) {
 	} else {
 		s = "false"
 	}
-	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
 }
 
 func (e *encoder) intv(tag string, in reflect.Value) {
 	s := strconv.FormatInt(in.Int(), 10)
-	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
 }
 
 func (e *encoder) uintv(tag string, in reflect.Value) {
 	s := strconv.FormatUint(in.Uint(), 10)
-	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
 }
 
 func (e *encoder) timev(tag string, in reflect.Value) {
 	t := in.Interface().(time.Time)
 	s := t.Format(time.RFC3339Nano)
-	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
 }
 
 func (e *encoder) floatv(tag string, in reflect.Value) {
@@ -409,174 +388,15 @@ func (e *encoder) floatv(tag string, in reflect.Value) {
 	case "NaN":
 		s = ".nan"
 	}
-	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+	e.emitScalar(s, "", tag, yaml_PLAIN_SCALAR_STYLE)
 }
 
 func (e *encoder) nilv() {
-	e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE, nil, nil, nil, nil)
+	e.emitScalar("null", "", "", yaml_PLAIN_SCALAR_STYLE)
 }
 
-func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, head, line, foot, tail []byte) {
-	// TODO Kill this function. Replace all initialize calls by their underlining Go literals.
+func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t) {
 	implicit := tag == ""
-	if !implicit {
-		tag = longTag(tag)
-	}
 	e.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
-	e.event.head_comment = head
-	e.event.line_comment = line
-	e.event.foot_comment = foot
-	e.event.tail_comment = tail
 	e.emit()
 }
-
-func (e *encoder) nodev(in reflect.Value) {
-	e.node(in.Interface().(*Node), "")
-}
-
-func (e *encoder) node(node *Node, tail string) {
-	// Zero nodes behave as nil.
-	if node.Kind == 0 && node.IsZero() {
-		e.nilv()
-		return
-	}
-
-	// If the tag was not explicitly requested, and dropping it won't change the
-	// implicit tag of the value, don't include it in the presentation.
-	var tag = node.Tag
-	var stag = shortTag(tag)
-	var forceQuoting bool
-	if tag != "" && node.Style&TaggedStyle == 0 {
-		if node.Kind == ScalarNode {
-			if stag == strTag && node.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0 {
-				tag = ""
-			} else {
-				rtag, _ := resolve("", node.Value)
-				if rtag == stag {
-					tag = ""
-				} else if stag == strTag {
-					tag = ""
-					forceQuoting = true
-				}
-			}
-		} else {
-			var rtag string
-			switch node.Kind {
-			case MappingNode:
-				rtag = mapTag
-			case SequenceNode:
-				rtag = seqTag
-			}
-			if rtag == stag {
-				tag = ""
-			}
-		}
-	}
-
-	switch node.Kind {
-	case DocumentNode:
-		yaml_document_start_event_initialize(&e.event, nil, nil, true)
-		e.event.head_comment = []byte(node.HeadComment)
-		e.emit()
-		for _, node := range node.Content {
-			e.node(node, "")
-		}
-		yaml_document_end_event_initialize(&e.event, true)
-		e.event.foot_comment = []byte(node.FootComment)
-		e.emit()
-
-	case SequenceNode:
-		style := yaml_BLOCK_SEQUENCE_STYLE
-		if node.Style&FlowStyle != 0 {
-			style = yaml_FLOW_SEQUENCE_STYLE
-		}
-		e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style))
-		e.event.head_comment = []byte(node.HeadComment)
-		e.emit()
-		for _, node := range node.Content {
-			e.node(node, "")
-		}
-		e.must(yaml_sequence_end_event_initialize(&e.event))
-		e.event.line_comment = []byte(node.LineComment)
-		e.event.foot_comment = []byte(node.FootComment)
-		e.emit()
-
-	case MappingNode:
-		style := yaml_BLOCK_MAPPING_STYLE
-		if node.Style&FlowStyle != 0 {
-			style = yaml_FLOW_MAPPING_STYLE
-		}
-		yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(longTag(tag)), tag == "", style)
-		e.event.tail_comment = []byte(tail)
-		e.event.head_comment = []byte(node.HeadComment)
-		e.emit()
-
-		// The tail logic below moves the foot comment of prior keys to the following key,
-		// since the value for each key may be a nested structure and the foot needs to be
-		// processed only the entirety of the value is streamed. The last tail is processed
-		// with the mapping end event.
-		var tail string
-		for i := 0; i+1 < len(node.Content); i += 2 {
-			k := node.Content[i]
-			foot := k.FootComment
-			if foot != "" {
-				kopy := *k
-				kopy.FootComment = ""
-				k = &kopy
-			}
-			e.node(k, tail)
-			tail = foot
-
-			v := node.Content[i+1]
-			e.node(v, "")
-		}
-
-		yaml_mapping_end_event_initialize(&e.event)
-		e.event.tail_comment = []byte(tail)
-		e.event.line_comment = []byte(node.LineComment)
-		e.event.foot_comment = []byte(node.FootComment)
-		e.emit()
-
-	case AliasNode:
-		yaml_alias_event_initialize(&e.event, []byte(node.Value))
-		e.event.head_comment = []byte(node.HeadComment)
-		e.event.line_comment = []byte(node.LineComment)
-		e.event.foot_comment = []byte(node.FootComment)
-		e.emit()
-
-	case ScalarNode:
-		value := node.Value
-		if !utf8.ValidString(value) {
-			if stag == binaryTag {
-				failf("explicitly tagged !!binary data must be base64-encoded")
-			}
-			if stag != "" {
-				failf("cannot marshal invalid UTF-8 data as %s", stag)
-			}
-			// It can't be encoded directly as YAML so use a binary tag
-			// and encode it as base64.
-			tag = binaryTag
-			value = encodeBase64(value)
-		}
-
-		style := yaml_PLAIN_SCALAR_STYLE
-		switch {
-		case node.Style&DoubleQuotedStyle != 0:
-			style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
-		case node.Style&SingleQuotedStyle != 0:
-			style = yaml_SINGLE_QUOTED_SCALAR_STYLE
-		case node.Style&LiteralStyle != 0:
-			style = yaml_LITERAL_SCALAR_STYLE
-		case node.Style&FoldedStyle != 0:
-			style = yaml_FOLDED_SCALAR_STYLE
-		case strings.Contains(value, "\n"):
-			style = yaml_LITERAL_SCALAR_STYLE
-		case forceQuoting:
-			style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
-		}
-
-		e.emitScalar(value, node.Anchor, tag, style, []byte(node.HeadComment), []byte(node.LineComment), []byte(node.FootComment), []byte(tail))
-	default:
-		failf("cannot encode node with unknown kind %d", node.Kind)
-	}
-}

+ 61 - 151
encode_test.go

@@ -1,18 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package yaml_test
 
 import (
@@ -30,6 +15,24 @@ import (
 	. "gopkg.in/check.v1"
 )
 
+type jsonNumberT string
+
+func (j jsonNumberT) Int64() (int64, error) {
+	val, err := strconv.Atoi(string(j))
+	if err != nil {
+		return 0, err
+	}
+	return int64(val), nil
+}
+
+func (j jsonNumberT) Float64() (float64, error) {
+	return strconv.ParseFloat(string(j), 64)
+}
+
+func (j jsonNumberT) String() string {
+	return string(j)
+}
+
 var marshalIntTest = 123
 
 var marshalTests = []struct {
@@ -113,16 +116,16 @@ var marshalTests = []struct {
 		"v: \"\"\n",
 	}, {
 		map[string][]string{"v": []string{"A", "B"}},
-		"v:\n    - A\n    - B\n",
+		"v:\n- A\n- B\n",
 	}, {
 		map[string][]string{"v": []string{"A", "B\nC"}},
-		"v:\n    - A\n    - |-\n      B\n      C\n",
+		"v:\n- A\n- |-\n  B\n  C\n",
 	}, {
 		map[string][]interface{}{"v": []interface{}{"A", 1, map[string][]int{"B": []int{2, 3}}}},
-		"v:\n    - A\n    - 1\n    - B:\n        - 2\n        - 3\n",
+		"v:\n- A\n- 1\n- B:\n  - 2\n  - 3\n",
 	}, {
 		map[string]interface{}{"a": map[interface{}]interface{}{"b": "c"}},
-		"a:\n    b: c\n",
+		"a:\n  b: c\n",
 	}, {
 		map[string]interface{}{"a": "-"},
 		"a: '-'\n",
@@ -144,14 +147,14 @@ var marshalTests = []struct {
 				B string
 			}
 		}{struct{ B string }{"c"}},
-		"a:\n    b: c\n",
+		"a:\n  b: c\n",
 	}, {
 		&struct {
 			A *struct {
 				B string
 			}
 		}{&struct{ B string }{"c"}},
-		"a:\n    b: c\n",
+		"a:\n  b: c\n",
 	}, {
 		&struct {
 			A *struct {
@@ -164,10 +167,10 @@ var marshalTests = []struct {
 		"a: 1\n",
 	}, {
 		&struct{ A []int }{[]int{1, 2}},
-		"a:\n    - 1\n    - 2\n",
+		"a:\n- 1\n- 2\n",
 	}, {
 		&struct{ A [2]int }{[2]int{1, 2}},
-		"a:\n    - 1\n    - 2\n",
+		"a:\n- 1\n- 2\n",
 	}, {
 		&struct {
 			B int "a"
@@ -176,12 +179,6 @@ var marshalTests = []struct {
 	}, {
 		&struct{ A bool }{true},
 		"a: true\n",
-	}, {
-		&struct{ A string }{"true"},
-		"a: \"true\"\n",
-	}, {
-		&struct{ A string }{"off"},
-		"a: \"off\"\n",
 	},
 
 	// Conditional flag
@@ -267,11 +264,6 @@ var marshalTests = []struct {
 			} "a,flow"
 		}{struct{ B, D string }{"c", "e"}},
 		"a: {b: c, d: e}\n",
-	}, {
-		&struct {
-			A string "a,flow"
-		}{"b\nc"},
-		"a: \"b\\nc\"\n",
 	},
 
 	// Unexported field
@@ -300,26 +292,6 @@ var marshalTests = []struct {
 		}{1, inlineB{2, inlineC{3}}},
 		"a: 1\nb: 2\nc: 3\n",
 	},
-	// Struct inlining as a pointer
-	{
-		&struct {
-			A int
-			C *inlineB `yaml:",inline"`
-		}{1, &inlineB{2, inlineC{3}}},
-		"a: 1\nb: 2\nc: 3\n",
-	}, {
-		&struct {
-			A int
-			C *inlineB `yaml:",inline"`
-		}{1, nil},
-		"a: 1\n",
-	}, {
-		&struct {
-			A int
-			D *inlineD `yaml:",inline"`
-		}{1, &inlineD{&inlineC{3}, 4}},
-		"a: 1\nc: 3\nd: 4\n",
-	},
 
 	// Map inlining
 	{
@@ -358,7 +330,13 @@ var marshalTests = []struct {
 		"a: !!binary gIGC\n",
 	}, {
 		map[string]string{"a": strings.Repeat("\x90", 54)},
-		"a: !!binary |\n    " + strings.Repeat("kJCQ", 17) + "kJ\n    CQ\n",
+		"a: !!binary |\n  " + strings.Repeat("kJCQ", 17) + "kJ\n  CQ\n",
+	},
+
+	// Ordered maps.
+	{
+		&yaml.MapSlice{{"b", 2}, {"a", 1}, {"d", 4}, {"c", 3}, {"sub", yaml.MapSlice{{"e", 5}}}},
+		"b: 2\na: 1\nd: 4\nc: 3\nsub:\n  e: 5\n",
 	},
 
 	// Encode unicode as utf-8 rather than in escaped form.
@@ -407,93 +385,39 @@ var marshalTests = []struct {
 		map[string]string{"a": "你好 #comment"},
 		"a: '你好 #comment'\n",
 	},
-
-	// Ensure MarshalYAML also gets called on the result of MarshalYAML itself.
 	{
-		&marshalerType{marshalerType{true}},
-		"true\n",
-	}, {
-		&marshalerType{&marshalerType{true}},
-		"true\n",
+		map[string]interface{}{"a": jsonNumberT("5")},
+		"a: 5\n",
 	},
-
-	// Check indentation of maps inside sequences inside maps.
 	{
-		map[string]interface{}{"a": map[string]interface{}{"b": []map[string]int{{"c": 1, "d": 2}}}},
-		"a:\n    b:\n        - c: 1\n          d: 2\n",
+		map[string]interface{}{"a": jsonNumberT("100.5")},
+		"a: 100.5\n",
 	},
-
-	// Strings with tabs were disallowed as literals (issue #471).
 	{
-		map[string]string{"a": "\tB\n\tC\n"},
-		"a: |\n    \tB\n    \tC\n",
+		map[string]interface{}{"a": jsonNumberT("bogus")},
+		"a: bogus\n",
 	},
+}
 
-	// Ensure that strings do not wrap
-	{
-		map[string]string{"a": "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 "},
-		"a: 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 '\n",
-	},
+func (s *S) TestLineWrapping(c *C) {
+	var v = map[string]string{
+		"a": "abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 ",
+	}
+	data, err := yaml.Marshal(v)
+	c.Assert(err, IsNil)
+	c.Assert(string(data), Equals,
+		"a: 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz\n"+
+			"  ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 '\n")
 
-	// yaml.Node
-	{
-		&struct {
-			Value yaml.Node
-		}{
-			yaml.Node{
-				Kind:  yaml.ScalarNode,
-				Tag:   "!!str",
-				Value: "foo",
-				Style: yaml.SingleQuotedStyle,
-			},
-		},
-		"value: 'foo'\n",
-	}, {
-		yaml.Node{
-			Kind:  yaml.ScalarNode,
-			Tag:   "!!str",
-			Value: "foo",
-			Style: yaml.SingleQuotedStyle,
-		},
-		"'foo'\n",
-	},
+	// The API does not allow this process to be reversed as it's intended
+	// for migration only. v3 drops this method and instead offers more
+	// control on a per encoding basis.
+	yaml.FutureLineWrap()
 
-	// Enforced tagging with shorthand notation (issue #616).
-	{
-		&struct {
-			Value yaml.Node
-		}{
-			yaml.Node{
-				Kind:  yaml.ScalarNode,
-				Style: yaml.TaggedStyle,
-				Value: "foo",
-				Tag:   "!!str",
-			},
-		},
-		"value: !!str foo\n",
-	}, {
-		&struct {
-			Value yaml.Node
-		}{
-			yaml.Node{
-				Kind:  yaml.MappingNode,
-				Style: yaml.TaggedStyle,
-				Tag:   "!!map",
-			},
-		},
-		"value: !!map {}\n",
-	}, {
-		&struct {
-			Value yaml.Node
-		}{
-			yaml.Node{
-				Kind:  yaml.SequenceNode,
-				Style: yaml.TaggedStyle,
-				Tag:   "!!seq",
-			},
-		},
-		"value: !!seq []\n",
-	},
+	data, err = yaml.Marshal(v)
+	c.Assert(err, IsNil)
+	c.Assert(string(data), Equals,
+		"a: 'abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890 '\n")
 }
 
 func (s *S) TestMarshal(c *C) {
@@ -553,13 +477,13 @@ var marshalErrorTests = []struct {
 		B       int
 		inlineB ",inline"
 	}{1, inlineB{2, inlineC{3}}},
-	panic: `duplicated key 'b' in struct struct \{ B int; .*`,
+	panic: `Duplicated key 'b' in struct struct \{ B int; .*`,
 }, {
 	value: &struct {
 		A int
 		B map[string]int ",inline"
 	}{1, map[string]int{"a": 2}},
-	panic: `cannot have key "a" in inlined map: conflicts with struct field`,
+	panic: `Can't have key "a" in inlined map; conflicts with struct field`,
 }}
 
 func (s *S) TestMarshalErrors(c *C) {
@@ -593,8 +517,8 @@ var marshalerTests = []struct {
 	data  string
 	value interface{}
 }{
-	{"_:\n    hi: there\n", map[interface{}]interface{}{"hi": "there"}},
-	{"_:\n    - 1\n    - A\n", []interface{}{1, "A"}},
+	{"_:\n  hi: there\n", map[interface{}]interface{}{"hi": "there"}},
+	{"_:\n- 1\n- A\n", []interface{}{1, "A"}},
 	{"_: 10\n", 10},
 	{"_: null\n", nil},
 	{"_: BAR!\n", "BAR!"},
@@ -645,17 +569,6 @@ func (s *S) TestMarshalerError(c *C) {
 	c.Assert(err, Equals, failingErr)
 }
 
-func (s *S) TestSetIndent(c *C) {
-	var buf bytes.Buffer
-	enc := yaml.NewEncoder(&buf)
-	enc.SetIndent(8)
-	err := enc.Encode(map[string]interface{}{"a": map[string]interface{}{"b": map[string]string{"c": "d"}}})
-	c.Assert(err, Equals, nil)
-	err = enc.Close()
-	c.Assert(err, Equals, nil)
-	c.Assert(buf.String(), Equals, "a:\n        b:\n                c: d\n")
-}
-
 func (s *S) TestSortedOutput(c *C) {
 	order := []interface{}{
 		false,
@@ -701,9 +614,6 @@ func (s *S) TestSortedOutput(c *C) {
 		"d7abc",
 		"d12",
 		"d12a",
-		"e2b",
-		"e4b",
-		"e21a",
 	}
 	m := make(map[interface{}]int)
 	for _, k := range order {

+ 0 - 15
example_embedded_test.go

@@ -1,18 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package yaml_test
 
 import (

+ 1 - 1
go.mod

@@ -1,6 +1,6 @@
 module git.ikuban.com/server/yaml
 
-go 1.16
+go 1.15
 
 require (
 	google.golang.org/protobuf v1.27.1

+ 10 - 0
go.sum

@@ -0,0 +1,10 @@
+github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
+github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
+github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
+google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
+google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=

+ 0 - 2886
node_test.go

@@ -1,2886 +0,0 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-package yaml_test
-
-import (
-	"bytes"
-	"fmt"
-	"os"
-
-	"git.ikuban.com/server/yaml"
-	. "gopkg.in/check.v1"
-	"io"
-	"strings"
-)
-
-var nodeTests = []struct {
-	yaml string
-	node yaml.Node
-}{
-	{
-		"null\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  "null",
-				Tag:    "!!null",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"[encode]null\n",
-		yaml.Node{},
-	}, {
-		"foo\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  "foo",
-				Tag:    "!!str",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"\"foo\"\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Style:  yaml.DoubleQuotedStyle,
-				Value:  "foo",
-				Tag:    "!!str",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"'foo'\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Style:  yaml.SingleQuotedStyle,
-				Value:  "foo",
-				Tag:    "!!str",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"!!str 123\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Style:  yaml.TaggedStyle,
-				Value:  "123",
-				Tag:    "!!str",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		// Although the node isn't TaggedStyle, dropping the tag would change the value.
-		"[encode]!!binary gIGC\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  "gIGC",
-				Tag:    "!!binary",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		// Item doesn't have a tag, but needs to be binary encoded due to its content.
-		"[encode]!!binary gIGC\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  "\x80\x81\x82",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		// Same, but with strings we can just quote them.
-		"[encode]\"123\"\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  "123",
-				Tag:    "!!str",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"!tag:something 123\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Style:  yaml.TaggedStyle,
-				Value:  "123",
-				Tag:    "!tag:something",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"[encode]!tag:something 123\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  "123",
-				Tag:    "!tag:something",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"!tag:something {}\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Style:  yaml.TaggedStyle | yaml.FlowStyle,
-				Tag:    "!tag:something",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"[encode]!tag:something {}\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Style:  yaml.FlowStyle,
-				Tag:    "!tag:something",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"!tag:something []\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Style:  yaml.TaggedStyle | yaml.FlowStyle,
-				Tag:    "!tag:something",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"[encode]!tag:something []\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Style:  yaml.FlowStyle,
-				Tag:    "!tag:something",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"''\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Style:  yaml.SingleQuotedStyle,
-				Value:  "",
-				Tag:    "!!str",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"|\n  foo\n  bar\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Style:  yaml.LiteralStyle,
-				Value:  "foo\nbar\n",
-				Tag:    "!!str",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"true\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  "true",
-				Tag:    "!!bool",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"-10\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  "-10",
-				Tag:    "!!int",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"4294967296\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  "4294967296",
-				Tag:    "!!int",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"0.1000\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  "0.1000",
-				Tag:    "!!float",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"-.inf\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  "-.inf",
-				Tag:    "!!float",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		".nan\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.ScalarNode,
-				Value:  ".nan",
-				Tag:    "!!float",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"{}\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Style:  yaml.FlowStyle,
-				Value:  "",
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-			}},
-		},
-	}, {
-		"a: b c\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Value:  "",
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Value:  "a",
-					Tag:    "!!str",
-					Line:   1,
-					Column: 1,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Value:  "b c",
-					Tag:    "!!str",
-					Line:   1,
-					Column: 4,
-				}},
-			}},
-		},
-	}, {
-		"a:\n  b: c\n  d: e\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Value:  "a",
-					Tag:    "!!str",
-					Line:   1,
-					Column: 1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   2,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Value:  "b",
-						Tag:    "!!str",
-						Line:   2,
-						Column: 3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Value:  "c",
-						Tag:    "!!str",
-						Line:   2,
-						Column: 6,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Value:  "d",
-						Tag:    "!!str",
-						Line:   3,
-						Column: 3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Value:  "e",
-						Tag:    "!!str",
-						Line:   3,
-						Column: 6,
-					}},
-				}},
-			}},
-		},
-	}, {
-		"a:\n  - b: c\n    d: e\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Value:  "a",
-					Tag:    "!!str",
-					Line:   1,
-					Column: 1,
-				}, {
-					Kind:   yaml.SequenceNode,
-					Tag:    "!!seq",
-					Line:   2,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:   yaml.MappingNode,
-						Tag:    "!!map",
-						Line:   2,
-						Column: 5,
-						Content: []*yaml.Node{{
-							Kind:   yaml.ScalarNode,
-							Value:  "b",
-							Tag:    "!!str",
-							Line:   2,
-							Column: 5,
-						}, {
-							Kind:   yaml.ScalarNode,
-							Value:  "c",
-							Tag:    "!!str",
-							Line:   2,
-							Column: 8,
-						}, {
-							Kind:   yaml.ScalarNode,
-							Value:  "d",
-							Tag:    "!!str",
-							Line:   3,
-							Column: 5,
-						}, {
-							Kind:   yaml.ScalarNode,
-							Value:  "e",
-							Tag:    "!!str",
-							Line:   3,
-							Column: 8,
-						}},
-					}},
-				}},
-			}},
-		},
-	}, {
-		"a: # AI\n  - b\nc:\n  - d\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "a",
-					LineComment: "# AI",
-					Line:        1,
-					Column:      1,
-				}, {
-					Kind: yaml.SequenceNode,
-					Tag:  "!!seq",
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "b",
-						Line:   2,
-						Column: 5,
-					}},
-					Line:   2,
-					Column: 3,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "c",
-					Line:   3,
-					Column: 1,
-				}, {
-					Kind: yaml.SequenceNode,
-					Tag:  "!!seq",
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "d",
-						Line:   4,
-						Column: 5,
-					}},
-					Line:   4,
-					Column: 3,
-				}},
-			}},
-		},
-	}, {
-		"[decode]a:\n  # HM\n  - # HB1\n    # HB2\n    b: # IB\n      c # IC\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Style:  0x0,
-					Tag:    "!!str",
-					Value:  "a",
-					Line:   1,
-					Column: 1,
-				}, {
-					Kind:   yaml.SequenceNode,
-					Tag:    "!!seq",
-					Line:   3,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.MappingNode,
-						Tag:         "!!map",
-						HeadComment: "# HM",
-						Line:        5,
-						Column:      5,
-						Content: []*yaml.Node{{
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Value:       "b",
-							HeadComment: "# HB1\n# HB2",
-							LineComment: "# IB",
-							Line:        5,
-							Column:      5,
-						}, {
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Value:       "c",
-							LineComment: "# IC",
-							Line:        6,
-							Column:      7,
-						}},
-					}},
-				}},
-			}},
-		},
-	}, {
-		// When encoding the value above, it loses b's inline comment.
-		"[encode]a:\n  # HM\n  - # HB1\n    # HB2\n    b: c # IC\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Style:  0x0,
-					Tag:    "!!str",
-					Value:  "a",
-					Line:   1,
-					Column: 1,
-				}, {
-					Kind:   yaml.SequenceNode,
-					Tag:    "!!seq",
-					Line:   3,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.MappingNode,
-						Tag:         "!!map",
-						HeadComment: "# HM",
-						Line:        5,
-						Column:      5,
-						Content: []*yaml.Node{{
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Value:       "b",
-							HeadComment: "# HB1\n# HB2",
-							LineComment: "# IB",
-							Line:        5,
-							Column:      5,
-						}, {
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Value:       "c",
-							LineComment: "# IC",
-							Line:        6,
-							Column:      7,
-						}},
-					}},
-				}},
-			}},
-		},
-	}, {
-		// Multiple cases of comment inlining next to mapping keys.
-		"a: | # IA\n  str\nb: >- # IB\n  str\nc: # IC\n  - str\nd: # ID\n  str:\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "a",
-					Line:   1,
-					Column: 1,
-				}, {
-					Kind:        yaml.ScalarNode,
-					Style:       yaml.LiteralStyle,
-					Tag:         "!!str",
-					Value:       "str\n",
-					LineComment: "# IA",
-					Line:        1,
-					Column:      4,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "b",
-					Line:   3,
-					Column: 1,
-				}, {
-					Kind:        yaml.ScalarNode,
-					Style:       yaml.FoldedStyle,
-					Tag:         "!!str",
-					Value:       "str",
-					LineComment: "# IB",
-					Line:        3,
-					Column:      4,
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "c",
-					LineComment: "# IC",
-					Line:        5,
-					Column:      1,
-				}, {
-					Kind:   yaml.SequenceNode,
-					Tag:    "!!seq",
-					Line:   6,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "str",
-						Line:   6,
-						Column: 5,
-					}},
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "d",
-					LineComment: "# ID",
-					Line:        7,
-					Column:      1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   8,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "str",
-						Line:   8,
-						Column: 3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!null",
-						Line:   8,
-						Column: 7,
-					}},
-				}},
-			}},
-		},
-	}, {
-		// Indentless sequence.
-		"[decode]a:\n# HM\n- # HB1\n  # HB2\n  b: # IB\n    c # IC\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "a",
-					Line:   1,
-					Column: 1,
-				}, {
-					Kind:   yaml.SequenceNode,
-					Tag:    "!!seq",
-					Line:   3,
-					Column: 1,
-					Content: []*yaml.Node{{
-						Kind:        yaml.MappingNode,
-						Tag:         "!!map",
-						HeadComment: "# HM",
-						Line:        5,
-						Column:      3,
-						Content: []*yaml.Node{{
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Value:       "b",
-							HeadComment: "# HB1\n# HB2",
-							LineComment: "# IB",
-							Line:        5,
-							Column:      3,
-						}, {
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Value:       "c",
-							LineComment: "# IC",
-							Line:        6,
-							Column:      5,
-						}},
-					}},
-				}},
-			}},
-		},
-	}, {
-		"- a\n- b\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Value:  "",
-				Tag:    "!!seq",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Value:  "a",
-					Tag:    "!!str",
-					Line:   1,
-					Column: 3,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Value:  "b",
-					Tag:    "!!str",
-					Line:   2,
-					Column: 3,
-				}},
-			}},
-		},
-	}, {
-		"- a\n- - b\n  - c\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Tag:    "!!seq",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Value:  "a",
-					Tag:    "!!str",
-					Line:   1,
-					Column: 3,
-				}, {
-					Kind:   yaml.SequenceNode,
-					Tag:    "!!seq",
-					Line:   2,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Value:  "b",
-						Tag:    "!!str",
-						Line:   2,
-						Column: 5,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Value:  "c",
-						Tag:    "!!str",
-						Line:   3,
-						Column: 5,
-					}},
-				}},
-			}},
-		},
-	}, {
-		"[a, b]\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Style:  yaml.FlowStyle,
-				Value:  "",
-				Tag:    "!!seq",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Value:  "a",
-					Tag:    "!!str",
-					Line:   1,
-					Column: 2,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Value:  "b",
-					Tag:    "!!str",
-					Line:   1,
-					Column: 5,
-				}},
-			}},
-		},
-	}, {
-		"- a\n- [b, c]\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Tag:    "!!seq",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Value:  "a",
-					Tag:    "!!str",
-					Line:   1,
-					Column: 3,
-				}, {
-					Kind:   yaml.SequenceNode,
-					Tag:    "!!seq",
-					Style:  yaml.FlowStyle,
-					Line:   2,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Value:  "b",
-						Tag:    "!!str",
-						Line:   2,
-						Column: 4,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Value:  "c",
-						Tag:    "!!str",
-						Line:   2,
-						Column: 7,
-					}},
-				}},
-			}},
-		},
-	}, {
-		"a: &x 1\nb: &y 2\nc: *x\nd: *y\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Line:   1,
-				Column: 1,
-				Tag:    "!!map",
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Value:  "a",
-					Tag:    "!!str",
-					Line:   1,
-					Column: 1,
-				},
-					saveNode("x", &yaml.Node{
-						Kind:   yaml.ScalarNode,
-						Value:  "1",
-						Tag:    "!!int",
-						Anchor: "x",
-						Line:   1,
-						Column: 4,
-					}),
-					{
-						Kind:   yaml.ScalarNode,
-						Value:  "b",
-						Tag:    "!!str",
-						Line:   2,
-						Column: 1,
-					},
-					saveNode("y", &yaml.Node{
-						Kind:   yaml.ScalarNode,
-						Value:  "2",
-						Tag:    "!!int",
-						Anchor: "y",
-						Line:   2,
-						Column: 4,
-					}),
-					{
-						Kind:   yaml.ScalarNode,
-						Value:  "c",
-						Tag:    "!!str",
-						Line:   3,
-						Column: 1,
-					}, {
-						Kind:   yaml.AliasNode,
-						Value:  "x",
-						Alias:  dropNode("x"),
-						Line:   3,
-						Column: 4,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Value:  "d",
-						Tag:    "!!str",
-						Line:   4,
-						Column: 1,
-					}, {
-						Kind:   yaml.AliasNode,
-						Value:  "y",
-						Tag:    "",
-						Alias:  dropNode("y"),
-						Line:   4,
-						Column: 4,
-					}},
-			}},
-		},
-	}, {
-
-		"# One\n# Two\ntrue # Three\n# Four\n# Five\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   3,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:        yaml.ScalarNode,
-				Value:       "true",
-				Tag:         "!!bool",
-				Line:        3,
-				Column:      1,
-				HeadComment: "# One\n# Two",
-				LineComment: "# Three",
-				FootComment: "# Four\n# Five",
-			}},
-		},
-	}, {
-
-		"# š\ntrue # š\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   2,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:        yaml.ScalarNode,
-				Value:       "true",
-				Tag:         "!!bool",
-				Line:        2,
-				Column:      1,
-				HeadComment: "# š",
-				LineComment: "# š",
-			}},
-		},
-	}, {
-
-		"[decode]\n# One\n\n# Two\n\n# Three\ntrue # Four\n# Five\n\n# Six\n\n# Seven\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        7,
-			Column:      1,
-			HeadComment: "# One\n\n# Two",
-			FootComment: "# Six\n\n# Seven",
-			Content: []*yaml.Node{{
-				Kind:        yaml.ScalarNode,
-				Value:       "true",
-				Tag:         "!!bool",
-				Line:        7,
-				Column:      1,
-				HeadComment: "# Three",
-				LineComment: "# Four",
-				FootComment: "# Five",
-			}},
-		},
-	}, {
-		// Write out the pound character if missing from comments.
-		"[encode]# One\n# Two\ntrue # Three\n# Four\n# Five\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   3,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:        yaml.ScalarNode,
-				Value:       "true",
-				Tag:         "!!bool",
-				Line:        3,
-				Column:      1,
-				HeadComment: "One\nTwo\n",
-				LineComment: "Three\n",
-				FootComment: "Four\nFive\n",
-			}},
-		},
-	}, {
-		"[encode]#   One\n#   Two\ntrue #   Three\n#   Four\n#   Five\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   3,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:        yaml.ScalarNode,
-				Value:       "true",
-				Tag:         "!!bool",
-				Line:        3,
-				Column:      1,
-				HeadComment: "  One\n  Two",
-				LineComment: "  Three",
-				FootComment: "  Four\n  Five",
-			}},
-		},
-	}, {
-		"# DH1\n\n# DH2\n\n# H1\n# H2\ntrue # I\n# F1\n# F2\n\n# DF1\n\n# DF2\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        7,
-			Column:      1,
-			HeadComment: "# DH1\n\n# DH2",
-			FootComment: "# DF1\n\n# DF2",
-			Content: []*yaml.Node{{
-				Kind:        yaml.ScalarNode,
-				Value:       "true",
-				Tag:         "!!bool",
-				Line:        7,
-				Column:      1,
-				HeadComment: "# H1\n# H2",
-				LineComment: "# I",
-				FootComment: "# F1\n# F2",
-			}},
-		},
-	}, {
-		"# DH1\n\n# DH2\n\n# HA1\n# HA2\nka: va # IA\n# FA1\n# FA2\n\n# HB1\n# HB2\nkb: vb # IB\n# FB1\n# FB2\n\n# DF1\n\n# DF2\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        7,
-			Column:      1,
-			HeadComment: "# DH1\n\n# DH2",
-			FootComment: "# DF1\n\n# DF2",
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   7,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Line:        7,
-					Column:      1,
-					Tag:         "!!str",
-					Value:       "ka",
-					HeadComment: "# HA1\n# HA2",
-					FootComment: "# FA1\n# FA2",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Line:        7,
-					Column:      5,
-					Tag:         "!!str",
-					Value:       "va",
-					LineComment: "# IA",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Line:        13,
-					Column:      1,
-					Tag:         "!!str",
-					Value:       "kb",
-					HeadComment: "# HB1\n# HB2",
-					FootComment: "# FB1\n# FB2",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Line:        13,
-					Column:      5,
-					Tag:         "!!str",
-					Value:       "vb",
-					LineComment: "# IB",
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n# DH2\n\n# HA1\n# HA2\n- la # IA\n# FA1\n# FA2\n\n# HB1\n# HB2\n- lb # IB\n# FB1\n# FB2\n\n# DF1\n\n# DF2\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        7,
-			Column:      1,
-			HeadComment: "# DH1\n\n# DH2",
-			FootComment: "# DF1\n\n# DF2",
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Tag:    "!!seq",
-				Line:   7,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        7,
-					Column:      3,
-					Value:       "la",
-					HeadComment: "# HA1\n# HA2",
-					LineComment: "# IA",
-					FootComment: "# FA1\n# FA2",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        13,
-					Column:      3,
-					Value:       "lb",
-					HeadComment: "# HB1\n# HB2",
-					LineComment: "# IB",
-					FootComment: "# FB1\n# FB2",
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n- la # IA\n# HB1\n- lb\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        3,
-			Column:      1,
-			HeadComment: "# DH1",
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Tag:    "!!seq",
-				Line:   3,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        3,
-					Column:      3,
-					Value:       "la",
-					LineComment: "# IA",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        5,
-					Column:      3,
-					Value:       "lb",
-					HeadComment: "# HB1",
-				}},
-			}},
-		},
-	}, {
-		"- la # IA\n- lb # IB\n- lc # IC\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Tag:    "!!seq",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        1,
-					Column:      3,
-					Value:       "la",
-					LineComment: "# IA",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        2,
-					Column:      3,
-					Value:       "lb",
-					LineComment: "# IB",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        3,
-					Column:      3,
-					Value:       "lc",
-					LineComment: "# IC",
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n# HL1\n- - la\n  # HB1\n  - lb\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        4,
-			Column:      1,
-			HeadComment: "# DH1",
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Tag:    "!!seq",
-				Line:   4,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.SequenceNode,
-					Tag:         "!!seq",
-					Line:        4,
-					Column:      3,
-					HeadComment: "# HL1",
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Line:   4,
-						Column: 5,
-						Value:  "la",
-					}, {
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Line:        6,
-						Column:      5,
-						Value:       "lb",
-						HeadComment: "# HB1",
-					}},
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n# HL1\n- # HA1\n  - la\n  # HB1\n  - lb\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        4,
-			Column:      1,
-			HeadComment: "# DH1",
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Tag:    "!!seq",
-				Line:   4,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.SequenceNode,
-					Tag:         "!!seq",
-					Line:        5,
-					Column:      3,
-					HeadComment: "# HL1",
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Line:        5,
-						Column:      5,
-						Value:       "la",
-						HeadComment: "# HA1",
-					}, {
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Line:        7,
-						Column:      5,
-						Value:       "lb",
-						HeadComment: "# HB1",
-					}},
-				}},
-			}},
-		},
-	}, {
-		"[decode]# DH1\n\n# HL1\n- # HA1\n\n  - la\n  # HB1\n  - lb\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        4,
-			Column:      1,
-			HeadComment: "# DH1",
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Tag:    "!!seq",
-				Line:   4,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.SequenceNode,
-					Tag:         "!!seq",
-					Line:        6,
-					Column:      3,
-					HeadComment: "# HL1",
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Line:        6,
-						Column:      5,
-						Value:       "la",
-						HeadComment: "# HA1\n",
-					}, {
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Line:        8,
-						Column:      5,
-						Value:       "lb",
-						HeadComment: "# HB1",
-					}},
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n# HA1\nka:\n  # HB1\n  kb:\n    # HC1\n    # HC2\n    - lc # IC\n    # FC1\n    # FC2\n\n    # HD1\n    - ld # ID\n    # FD1\n\n# DF1\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        4,
-			Column:      1,
-			HeadComment: "# DH1",
-			FootComment: "# DF1",
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   4,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        4,
-					Column:      1,
-					Value:       "ka",
-					HeadComment: "# HA1",
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   6,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Line:        6,
-						Column:      3,
-						Value:       "kb",
-						HeadComment: "# HB1",
-					}, {
-						Kind:   yaml.SequenceNode,
-						Line:   9,
-						Column: 5,
-						Tag:    "!!seq",
-						Content: []*yaml.Node{{
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Line:        9,
-							Column:      7,
-							Value:       "lc",
-							HeadComment: "# HC1\n# HC2",
-							LineComment: "# IC",
-							FootComment: "# FC1\n# FC2",
-						}, {
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Line:        14,
-							Column:      7,
-							Value:       "ld",
-							HeadComment: "# HD1",
-
-							LineComment: "# ID",
-							FootComment: "# FD1",
-						}},
-					}},
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n# HA1\nka:\n  # HB1\n  kb:\n    # HC1\n    # HC2\n    - lc # IC\n    # FC1\n    # FC2\n\n    # HD1\n    - ld # ID\n    # FD1\nke: ve\n\n# DF1\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        4,
-			Column:      1,
-			HeadComment: "# DH1",
-			FootComment: "# DF1",
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   4,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        4,
-					Column:      1,
-					Value:       "ka",
-					HeadComment: "# HA1",
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   6,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Line:        6,
-						Column:      3,
-						Value:       "kb",
-						HeadComment: "# HB1",
-					}, {
-						Kind:   yaml.SequenceNode,
-						Line:   9,
-						Column: 5,
-						Tag:    "!!seq",
-						Content: []*yaml.Node{{
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Line:        9,
-							Column:      7,
-							Value:       "lc",
-							HeadComment: "# HC1\n# HC2",
-							LineComment: "# IC",
-							FootComment: "# FC1\n# FC2",
-						}, {
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Line:        14,
-							Column:      7,
-							Value:       "ld",
-							HeadComment: "# HD1",
-							LineComment: "# ID",
-							FootComment: "# FD1",
-						}},
-					}},
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Line:   16,
-					Column: 1,
-					Value:  "ke",
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Line:   16,
-					Column: 5,
-					Value:  "ve",
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n# DH2\n\n# HA1\n# HA2\nka:\n  # HB1\n  # HB2\n  kb:\n" +
-			"    # HC1\n    # HC2\n    kc:\n      # HD1\n      # HD2\n      kd: vd\n      # FD1\n      # FD2\n" +
-			"    # FC1\n    # FC2\n  # FB1\n  # FB2\n# FA1\n# FA2\n\n# HE1\n# HE2\nke: ve\n# FE1\n# FE2\n\n# DF1\n\n# DF2\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			HeadComment: "# DH1\n\n# DH2",
-			FootComment: "# DF1\n\n# DF2",
-			Line:        7,
-			Column:      1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   7,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "ka",
-					HeadComment: "# HA1\n# HA2",
-					FootComment: "# FA1\n# FA2",
-					Line:        7,
-					Column:      1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   10,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Value:       "kb",
-						HeadComment: "# HB1\n# HB2",
-						FootComment: "# FB1\n# FB2",
-						Line:        10,
-						Column:      3,
-					}, {
-						Kind:   yaml.MappingNode,
-						Tag:    "!!map",
-						Line:   13,
-						Column: 5,
-						Content: []*yaml.Node{{
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Value:       "kc",
-							HeadComment: "# HC1\n# HC2",
-							FootComment: "# FC1\n# FC2",
-							Line:        13,
-							Column:      5,
-						}, {
-							Kind:   yaml.MappingNode,
-							Tag:    "!!map",
-							Line:   16,
-							Column: 7,
-							Content: []*yaml.Node{{
-								Kind:        yaml.ScalarNode,
-								Tag:         "!!str",
-								Value:       "kd",
-								HeadComment: "# HD1\n# HD2",
-								FootComment: "# FD1\n# FD2",
-								Line:        16,
-								Column:      7,
-							}, {
-								Kind:   yaml.ScalarNode,
-								Tag:    "!!str",
-								Value:  "vd",
-								Line:   16,
-								Column: 11,
-							}},
-						}},
-					}},
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "ke",
-					HeadComment: "# HE1\n# HE2",
-					FootComment: "# FE1\n# FE2",
-					Line:        28,
-					Column:      1,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "ve",
-					Line:   28,
-					Column: 5,
-				}},
-			}},
-		},
-	}, {
-		// Same as above but indenting ke in so it's also part of ka's value.
-		"# DH1\n\n# DH2\n\n# HA1\n# HA2\nka:\n  # HB1\n  # HB2\n  kb:\n" +
-			"    # HC1\n    # HC2\n    kc:\n      # HD1\n      # HD2\n      kd: vd\n      # FD1\n      # FD2\n" +
-			"    # FC1\n    # FC2\n  # FB1\n  # FB2\n\n  # HE1\n  # HE2\n  ke: ve\n  # FE1\n  # FE2\n# FA1\n# FA2\n\n# DF1\n\n# DF2\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			HeadComment: "# DH1\n\n# DH2",
-			FootComment: "# DF1\n\n# DF2",
-			Line:        7,
-			Column:      1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   7,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "ka",
-					HeadComment: "# HA1\n# HA2",
-					FootComment: "# FA1\n# FA2",
-					Line:        7,
-					Column:      1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   10,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Value:       "kb",
-						HeadComment: "# HB1\n# HB2",
-						FootComment: "# FB1\n# FB2",
-						Line:        10,
-						Column:      3,
-					}, {
-						Kind:   yaml.MappingNode,
-						Tag:    "!!map",
-						Line:   13,
-						Column: 5,
-						Content: []*yaml.Node{{
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Value:       "kc",
-							HeadComment: "# HC1\n# HC2",
-							FootComment: "# FC1\n# FC2",
-							Line:        13,
-							Column:      5,
-						}, {
-							Kind:   yaml.MappingNode,
-							Tag:    "!!map",
-							Line:   16,
-							Column: 7,
-							Content: []*yaml.Node{{
-								Kind:        yaml.ScalarNode,
-								Tag:         "!!str",
-								Value:       "kd",
-								HeadComment: "# HD1\n# HD2",
-								FootComment: "# FD1\n# FD2",
-								Line:        16,
-								Column:      7,
-							}, {
-								Kind:   yaml.ScalarNode,
-								Tag:    "!!str",
-								Value:  "vd",
-								Line:   16,
-								Column: 11,
-							}},
-						}},
-					}, {
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Value:       "ke",
-						HeadComment: "# HE1\n# HE2",
-						FootComment: "# FE1\n# FE2",
-						Line:        26,
-						Column:      3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "ve",
-						Line:   26,
-						Column: 7,
-					}},
-				}},
-			}},
-		},
-	}, {
-		// Decode only due to lack of newline at the end.
-		"[decode]# HA1\nka:\n  # HB1\n  kb: vb\n  # FB1\n# FA1",
-		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",
-					Value:       "ka",
-					HeadComment: "# HA1",
-					FootComment: "# FA1",
-					Line:        2,
-					Column:      1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   4,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Value:       "kb",
-						HeadComment: "# HB1",
-						FootComment: "# FB1",
-						Line:        4,
-						Column:      3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "vb",
-						Line:   4,
-						Column: 7,
-					}},
-				}},
-			}},
-		},
-	}, {
-		// Same as above, but with newline at the end.
-		"# HA1\nka:\n  # HB1\n  kb: vb\n  # FB1\n# FA1\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",
-					Value:       "ka",
-					HeadComment: "# HA1",
-					FootComment: "# FA1",
-					Line:        2,
-					Column:      1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   4,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Value:       "kb",
-						HeadComment: "# HB1",
-						FootComment: "# FB1",
-						Line:        4,
-						Column:      3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "vb",
-						Line:   4,
-						Column: 7,
-					}},
-				}},
-			}},
-		},
-	}, {
-		// Same as above, but without FB1.
-		"# HA1\nka:\n  # HB1\n  kb: vb\n# FA1\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",
-					Value:       "ka",
-					HeadComment: "# HA1",
-					FootComment: "# FA1",
-					Line:        2,
-					Column:      1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   4,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Value:       "kb",
-						HeadComment: "# HB1",
-						Line:        4,
-						Column:      3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "vb",
-						Line:   4,
-						Column: 7,
-					}},
-				}},
-			}},
-		},
-	}, {
-		// Same as above, but with two newlines at the end. Decode-only for that.
-		"[decode]# HA1\nka:\n  # HB1\n  kb: vb\n  # FB1\n# FA1\n\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",
-					Value:       "ka",
-					HeadComment: "# HA1",
-					FootComment: "# FA1",
-					Line:        2,
-					Column:      1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   4,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Value:       "kb",
-						HeadComment: "# HB1",
-						FootComment: "# FB1",
-						Line:        4,
-						Column:      3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "vb",
-						Line:   4,
-						Column: 7,
-					}},
-				}},
-			}},
-		},
-	}, {
-		// Similar to above, but make HB1 look more like a footer of ka.
-		"[decode]# HA1\nka:\n# HB1\n\n  kb: vb\n# FA1\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",
-					Value:       "ka",
-					HeadComment: "# HA1",
-					FootComment: "# FA1",
-					Line:        2,
-					Column:      1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   5,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Value:       "kb",
-						HeadComment: "# HB1\n",
-						Line:        5,
-						Column:      3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "vb",
-						Line:   5,
-						Column: 7,
-					}},
-				}},
-			}},
-		},
-	}, {
-		"ka:\n  kb: vb\n# FA1\n\nkc: vc\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "ka",
-					Line:        1,
-					Column:      1,
-					FootComment: "# FA1",
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   2,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "kb",
-						Line:   2,
-						Column: 3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "vb",
-						Line:   2,
-						Column: 7,
-					}},
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "kc",
-					Line:   5,
-					Column: 1,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "vc",
-					Line:   5,
-					Column: 5,
-				}},
-			}},
-		},
-	}, {
-		"ka:\n  kb: vb\n# HC1\nkc: vc\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "ka",
-					Line:   1,
-					Column: 1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   2,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "kb",
-						Line:   2,
-						Column: 3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "vb",
-						Line:   2,
-						Column: 7,
-					}},
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "kc",
-					HeadComment: "# HC1",
-					Line:        4,
-					Column:      1,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "vc",
-					Line:   4,
-					Column: 5,
-				}},
-			}},
-		},
-	}, {
-		// Decode only due to empty line before HC1.
-		"[decode]ka:\n  kb: vb\n\n# HC1\nkc: vc\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "ka",
-					Line:   1,
-					Column: 1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   2,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "kb",
-						Line:   2,
-						Column: 3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "vb",
-						Line:   2,
-						Column: 7,
-					}},
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "kc",
-					HeadComment: "# HC1",
-					Line:        5,
-					Column:      1,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "vc",
-					Line:   5,
-					Column: 5,
-				}},
-			}},
-		},
-	}, {
-		// Decode-only due to empty lines around HC1.
-		"[decode]ka:\n  kb: vb\n\n# HC1\n\nkc: vc\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "ka",
-					Line:   1,
-					Column: 1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   2,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "kb",
-						Line:   2,
-						Column: 3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "vb",
-						Line:   2,
-						Column: 7,
-					}},
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "kc",
-					HeadComment: "# HC1\n",
-					Line:        6,
-					Column:      1,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "vc",
-					Line:   6,
-					Column: 5,
-				}},
-			}},
-		},
-	}, {
-		"ka: # IA\n  kb: # IB\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "ka",
-					Line:        1,
-					Column:      1,
-					LineComment: "# IA",
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   2,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Value:       "kb",
-						Line:        2,
-						Column:      3,
-						LineComment: "# IB",
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!null",
-						Line:   2,
-						Column: 6,
-					}},
-				}},
-			}},
-		},
-	}, {
-		"# HA1\nka:\n  # HB1\n  kb: vb\n  # FB1\n# HC1\n# HC2\nkc: vc\n# FC1\n# FC2\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",
-					Value:       "ka",
-					HeadComment: "# HA1",
-					Line:        2,
-					Column:      1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   4,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Value:       "kb",
-						HeadComment: "# HB1",
-						FootComment: "# FB1",
-						Line:        4,
-						Column:      3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "vb",
-						Line:   4,
-						Column: 7,
-					}},
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "kc",
-					HeadComment: "# HC1\n# HC2",
-					FootComment: "# FC1\n# FC2",
-					Line:        8,
-					Column:      1,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "vc",
-					Line:   8,
-					Column: 5,
-				}},
-			}},
-		},
-	}, {
-		// Same as above, but decode only due to empty line between ka's value and kc's headers.
-		"[decode]# HA1\nka:\n  # HB1\n  kb: vb\n  # FB1\n\n# HC1\n# HC2\nkc: vc\n# FC1\n# FC2\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",
-					Value:       "ka",
-					HeadComment: "# HA1",
-					Line:        2,
-					Column:      1,
-				}, {
-					Kind:   yaml.MappingNode,
-					Tag:    "!!map",
-					Line:   4,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Value:       "kb",
-						HeadComment: "# HB1",
-						FootComment: "# FB1",
-						Line:        4,
-						Column:      3,
-					}, {
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "vb",
-						Line:   4,
-						Column: 7,
-					}},
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Value:       "kc",
-					HeadComment: "# HC1\n# HC2",
-					FootComment: "# FC1\n# FC2",
-					Line:        9,
-					Column:      1,
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "vc",
-					Line:   9,
-					Column: 5,
-				}},
-			}},
-		},
-	}, {
-		"# H1\n[la, lb] # I\n# F1\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   2,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:        yaml.SequenceNode,
-				Tag:         "!!seq",
-				Style:       yaml.FlowStyle,
-				Line:        2,
-				Column:      1,
-				HeadComment: "# H1",
-				LineComment: "# I",
-				FootComment: "# F1",
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Line:   2,
-					Column: 2,
-					Value:  "la",
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Line:   2,
-					Column: 6,
-					Value:  "lb",
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n# SH1\n[\n  # HA1\n  la, # IA\n  # FA1\n\n  # HB1\n  lb, # IB\n  # FB1\n]\n# SF1\n\n# DF1\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        4,
-			Column:      1,
-			HeadComment: "# DH1",
-			FootComment: "# DF1",
-			Content: []*yaml.Node{{
-				Kind:        yaml.SequenceNode,
-				Tag:         "!!seq",
-				Style:       yaml.FlowStyle,
-				Line:        4,
-				Column:      1,
-				HeadComment: "# SH1",
-				FootComment: "# SF1",
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        6,
-					Column:      3,
-					Value:       "la",
-					HeadComment: "# HA1",
-					LineComment: "# IA",
-					FootComment: "# FA1",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        10,
-					Column:      3,
-					Value:       "lb",
-					HeadComment: "# HB1",
-					LineComment: "# IB",
-					FootComment: "# FB1",
-				}},
-			}},
-		},
-	}, {
-		// Same as above, but with extra newlines before FB1 and FB2
-		"[decode]# DH1\n\n# SH1\n[\n  # HA1\n  la, # IA\n  # FA1\n\n  # HB1\n  lb, # IB\n\n\n  # FB1\n\n# FB2\n]\n# SF1\n\n# DF1\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        4,
-			Column:      1,
-			HeadComment: "# DH1",
-			FootComment: "# DF1",
-			Content: []*yaml.Node{{
-				Kind:        yaml.SequenceNode,
-				Tag:         "!!seq",
-				Style:       yaml.FlowStyle,
-				Line:        4,
-				Column:      1,
-				HeadComment: "# SH1",
-				FootComment: "# SF1",
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        6,
-					Column:      3,
-					Value:       "la",
-					HeadComment: "# HA1",
-					LineComment: "# IA",
-					FootComment: "# FA1",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        10,
-					Column:      3,
-					Value:       "lb",
-					HeadComment: "# HB1",
-					LineComment: "# IB",
-					FootComment: "# FB1\n\n# FB2",
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n# SH1\n[\n  # HA1\n  la,\n  # FA1\n\n  # HB1\n  lb,\n  # FB1\n]\n# SF1\n\n# DF1\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        4,
-			Column:      1,
-			HeadComment: "# DH1",
-			FootComment: "# DF1",
-			Content: []*yaml.Node{{
-				Kind:        yaml.SequenceNode,
-				Tag:         "!!seq",
-				Style:       yaml.FlowStyle,
-				Line:        4,
-				Column:      1,
-				HeadComment: "# SH1",
-				FootComment: "# SF1",
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        6,
-					Column:      3,
-					Value:       "la",
-					HeadComment: "# HA1",
-					FootComment: "# FA1",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        10,
-					Column:      3,
-					Value:       "lb",
-					HeadComment: "# HB1",
-					FootComment: "# FB1",
-				}},
-			}},
-		},
-	}, {
-		"ka:\n  kb: [\n    # HA1\n    la,\n    # FA1\n\n    # HB1\n    lb,\n    # FB1\n  ]\n",
-		yaml.Node{
-			Kind:   yaml.DocumentNode,
-			Line:   1,
-			Column: 1,
-			Content: []*yaml.Node{{
-				Kind:   yaml.MappingNode,
-				Tag:    "!!map",
-				Line:   1,
-				Column: 1,
-				Content: []*yaml.Node{{
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Value:  "ka",
-					Line:   1,
-					Column: 1,
-				}, {
-					Kind:   0x4,
-					Tag:    "!!map",
-					Line:   2,
-					Column: 3,
-					Content: []*yaml.Node{{
-						Kind:   yaml.ScalarNode,
-						Tag:    "!!str",
-						Value:  "kb",
-						Line:   2,
-						Column: 3,
-					}, {
-						Kind:   yaml.SequenceNode,
-						Style:  0x20,
-						Tag:    "!!seq",
-						Line:   2,
-						Column: 7,
-						Content: []*yaml.Node{{
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Value:       "la",
-							HeadComment: "# HA1",
-							FootComment: "# FA1",
-							Line:        4,
-							Column:      5,
-						}, {
-							Kind:        yaml.ScalarNode,
-							Tag:         "!!str",
-							Value:       "lb",
-							HeadComment: "# HB1",
-							FootComment: "# FB1",
-							Line:        8,
-							Column:      5,
-						}},
-					}},
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n# MH1\n{\n  # HA1\n  ka: va, # IA\n  # FA1\n\n  # HB1\n  kb: vb, # IB\n  # FB1\n}\n# MF1\n\n# DF1\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        4,
-			Column:      1,
-			HeadComment: "# DH1",
-			FootComment: "# DF1",
-			Content: []*yaml.Node{{
-				Kind:        yaml.MappingNode,
-				Tag:         "!!map",
-				Style:       yaml.FlowStyle,
-				Line:        4,
-				Column:      1,
-				HeadComment: "# MH1",
-				FootComment: "# MF1",
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        6,
-					Column:      3,
-					Value:       "ka",
-					HeadComment: "# HA1",
-					FootComment: "# FA1",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        6,
-					Column:      7,
-					Value:       "va",
-					LineComment: "# IA",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        10,
-					Column:      3,
-					Value:       "kb",
-					HeadComment: "# HB1",
-					FootComment: "# FB1",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        10,
-					Column:      7,
-					Value:       "vb",
-					LineComment: "# IB",
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n# MH1\n{\n  # HA1\n  ka: va,\n  # FA1\n\n  # HB1\n  kb: vb,\n  # FB1\n}\n# MF1\n\n# DF1\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        4,
-			Column:      1,
-			HeadComment: "# DH1",
-			FootComment: "# DF1",
-			Content: []*yaml.Node{{
-				Kind:        yaml.MappingNode,
-				Tag:         "!!map",
-				Style:       yaml.FlowStyle,
-				Line:        4,
-				Column:      1,
-				HeadComment: "# MH1",
-				FootComment: "# MF1",
-				Content: []*yaml.Node{{
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        6,
-					Column:      3,
-					Value:       "ka",
-					HeadComment: "# HA1",
-					FootComment: "# FA1",
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Line:   6,
-					Column: 7,
-					Value:  "va",
-				}, {
-					Kind:        yaml.ScalarNode,
-					Tag:         "!!str",
-					Line:        10,
-					Column:      3,
-					Value:       "kb",
-					HeadComment: "# HB1",
-					FootComment: "# FB1",
-				}, {
-					Kind:   yaml.ScalarNode,
-					Tag:    "!!str",
-					Line:   10,
-					Column: 7,
-					Value:  "vb",
-				}},
-			}},
-		},
-	}, {
-		"# DH1\n\n# DH2\n\n# HA1\n# HA2\n- &x la # IA\n# FA1\n# FA2\n\n# HB1\n# HB2\n- *x # IB\n# FB1\n# FB2\n\n# DF1\n\n# DF2\n",
-		yaml.Node{
-			Kind:        yaml.DocumentNode,
-			Line:        7,
-			Column:      1,
-			HeadComment: "# DH1\n\n# DH2",
-			FootComment: "# DF1\n\n# DF2",
-			Content: []*yaml.Node{{
-				Kind:   yaml.SequenceNode,
-				Tag:    "!!seq",
-				Line:   7,
-				Column: 1,
-				Content: []*yaml.Node{
-					saveNode("x", &yaml.Node{
-						Kind:        yaml.ScalarNode,
-						Tag:         "!!str",
-						Line:        7,
-						Column:      3,
-						Value:       "la",
-						HeadComment: "# HA1\n# HA2",
-						LineComment: "# IA",
-						FootComment: "# FA1\n# FA2",
-						Anchor:      "x",
-					}), {
-						Kind:        yaml.AliasNode,
-						Line:        13,
-						Column:      3,
-						Value:       "x",
-						Alias:       dropNode("x"),
-						HeadComment: "# HB1\n# HB2",
-						LineComment: "# IB",
-						FootComment: "# FB1\n# FB2",
-					},
-				},
-			}},
-		},
-	},
-}
-
-func (s *S) TestNodeRoundtrip(c *C) {
-	defer os.Setenv("TZ", os.Getenv("TZ"))
-	os.Setenv("TZ", "UTC")
-	for i, item := range nodeTests {
-		c.Logf("test %d: %q", i, item.yaml)
-
-		if strings.Contains(item.yaml, "#") {
-			var buf bytes.Buffer
-			fprintComments(&buf, &item.node, "    ")
-			c.Logf("  expected comments:\n%s", buf.Bytes())
-		}
-
-		decode := true
-		encode := true
-
-		testYaml := item.yaml
-		if s := strings.TrimPrefix(testYaml, "[decode]"); s != testYaml {
-			encode = false
-			testYaml = s
-		}
-		if s := strings.TrimPrefix(testYaml, "[encode]"); s != testYaml {
-			decode = false
-			testYaml = s
-		}
-
-		if decode {
-			var node yaml.Node
-			err := yaml.Unmarshal([]byte(testYaml), &node)
-			c.Assert(err, IsNil)
-			if strings.Contains(item.yaml, "#") {
-				var buf bytes.Buffer
-				fprintComments(&buf, &node, "    ")
-				c.Logf("  obtained comments:\n%s", buf.Bytes())
-			}
-			c.Assert(&node, DeepEquals, &item.node)
-		}
-		if encode {
-			node := deepCopyNode(&item.node, nil)
-			buf := bytes.Buffer{}
-			enc := yaml.NewEncoder(&buf)
-			enc.SetIndent(2)
-			err := enc.Encode(node)
-			c.Assert(err, IsNil)
-			err = enc.Close()
-			c.Assert(err, IsNil)
-			c.Assert(buf.String(), Equals, testYaml)
-
-			// Ensure there were no mutations to the tree.
-			c.Assert(node, DeepEquals, &item.node)
-		}
-	}
-}
-
-func deepCopyNode(node *yaml.Node, cache map[*yaml.Node]*yaml.Node) *yaml.Node {
-	if n, ok := cache[node]; ok {
-		return n
-	}
-	if cache == nil {
-		cache = make(map[*yaml.Node]*yaml.Node)
-	}
-	copy := *node
-	cache[node] = &copy
-	copy.Content = nil
-	for _, elem := range node.Content {
-		copy.Content = append(copy.Content, deepCopyNode(elem, cache))
-	}
-	if node.Alias != nil {
-		copy.Alias = deepCopyNode(node.Alias, cache)
-	}
-	return &copy
-}
-
-var savedNodes = make(map[string]*yaml.Node)
-
-func saveNode(name string, node *yaml.Node) *yaml.Node {
-	savedNodes[name] = node
-	return node
-}
-
-func peekNode(name string) *yaml.Node {
-	return savedNodes[name]
-}
-
-func dropNode(name string) *yaml.Node {
-	node := savedNodes[name]
-	delete(savedNodes, name)
-	return node
-}
-
-var setStringTests = []struct {
-	str  string
-	yaml string
-	node yaml.Node
-}{
-	{
-		"something simple",
-		"something simple\n",
-		yaml.Node{
-			Kind:  yaml.ScalarNode,
-			Value: "something simple",
-			Tag:   "!!str",
-		},
-	}, {
-		`"quoted value"`,
-		"'\"quoted value\"'\n",
-		yaml.Node{
-			Kind:  yaml.ScalarNode,
-			Value: `"quoted value"`,
-			Tag:   "!!str",
-		},
-	}, {
-		"multi\nline",
-		"|-\n  multi\n  line\n",
-		yaml.Node{
-			Kind:  yaml.ScalarNode,
-			Value: "multi\nline",
-			Tag:   "!!str",
-			Style: yaml.LiteralStyle,
-		},
-	}, {
-		"123",
-		"\"123\"\n",
-		yaml.Node{
-			Kind:  yaml.ScalarNode,
-			Value: "123",
-			Tag:   "!!str",
-		},
-	}, {
-		"multi\nline\n",
-		"|\n  multi\n  line\n",
-		yaml.Node{
-			Kind:  yaml.ScalarNode,
-			Value: "multi\nline\n",
-			Tag:   "!!str",
-			Style: yaml.LiteralStyle,
-		},
-	}, {
-		"\x80\x81\x82",
-		"!!binary gIGC\n",
-		yaml.Node{
-			Kind:  yaml.ScalarNode,
-			Value: "gIGC",
-			Tag:   "!!binary",
-		},
-	},
-}
-
-func (s *S) TestSetString(c *C) {
-	defer os.Setenv("TZ", os.Getenv("TZ"))
-	os.Setenv("TZ", "UTC")
-	for i, item := range setStringTests {
-		c.Logf("test %d: %q", i, item.str)
-
-		var node yaml.Node
-
-		node.SetString(item.str)
-
-		c.Assert(node, DeepEquals, item.node)
-
-		buf := bytes.Buffer{}
-		enc := yaml.NewEncoder(&buf)
-		enc.SetIndent(2)
-		err := enc.Encode(&item.node)
-		c.Assert(err, IsNil)
-		err = enc.Close()
-		c.Assert(err, IsNil)
-		c.Assert(buf.String(), Equals, item.yaml)
-
-		var doc yaml.Node
-		err = yaml.Unmarshal([]byte(item.yaml), &doc)
-		c.Assert(err, IsNil)
-
-		var str string
-		err = node.Decode(&str)
-		c.Assert(err, IsNil)
-		c.Assert(str, Equals, item.str)
-	}
-}
-
-var nodeEncodeDecodeTests = []struct {
-	value interface{}
-	yaml  string
-	node  yaml.Node
-}{{
-	"something simple",
-	"something simple\n",
-	yaml.Node{
-		Kind:  yaml.ScalarNode,
-		Value: "something simple",
-		Tag:   "!!str",
-	},
-}, {
-	`"quoted value"`,
-	"'\"quoted value\"'\n",
-	yaml.Node{
-		Kind:  yaml.ScalarNode,
-		Style: yaml.SingleQuotedStyle,
-		Value: `"quoted value"`,
-		Tag:   "!!str",
-	},
-}, {
-	123,
-	"123",
-	yaml.Node{
-		Kind:  yaml.ScalarNode,
-		Value: `123`,
-		Tag:   "!!int",
-	},
-}, {
-	[]interface{}{1, 2},
-	"[1, 2]",
-	yaml.Node{
-		Kind: yaml.SequenceNode,
-		Tag:  "!!seq",
-		Content: []*yaml.Node{{
-			Kind:  yaml.ScalarNode,
-			Value: "1",
-			Tag:   "!!int",
-		}, {
-			Kind:  yaml.ScalarNode,
-			Value: "2",
-			Tag:   "!!int",
-		}},
-	},
-}, {
-	map[string]interface{}{"a": "b"},
-	"a: b",
-	yaml.Node{
-		Kind: yaml.MappingNode,
-		Tag:  "!!map",
-		Content: []*yaml.Node{{
-			Kind:  yaml.ScalarNode,
-			Value: "a",
-			Tag:   "!!str",
-		}, {
-			Kind:  yaml.ScalarNode,
-			Value: "b",
-			Tag:   "!!str",
-		}},
-	},
-}}
-
-func (s *S) TestNodeEncodeDecode(c *C) {
-	for i, item := range nodeEncodeDecodeTests {
-		c.Logf("Encode/Decode test value #%d: %#v", i, item.value)
-
-		var v interface{}
-		err := item.node.Decode(&v)
-		c.Assert(err, IsNil)
-		c.Assert(v, DeepEquals, item.value)
-
-		var n yaml.Node
-		err = n.Encode(item.value)
-		c.Assert(err, IsNil)
-		c.Assert(n, DeepEquals, item.node)
-	}
-}
-
-func (s *S) TestNodeZeroEncodeDecode(c *C) {
-	// Zero node value behaves as nil when encoding...
-	var n yaml.Node
-	data, err := yaml.Marshal(&n)
-	c.Assert(err, IsNil)
-	c.Assert(string(data), Equals, "null\n")
-
-	// ... and decoding.
-	var v *struct{} = &struct{}{}
-	c.Assert(n.Decode(&v), IsNil)
-	c.Assert(v, IsNil)
-
-	// ... and even when looking for its tag.
-	c.Assert(n.ShortTag(), Equals, "!!null")
-
-	// Kind zero is still unknown, though.
-	n.Line = 1
-	_, err = yaml.Marshal(&n)
-	c.Assert(err, ErrorMatches, "yaml: cannot encode node with unknown kind 0")
-	c.Assert(n.Decode(&v), ErrorMatches, "yaml: cannot decode node with unknown kind 0")
-}
-
-func (s *S) TestNodeOmitEmpty(c *C) {
-	var v struct {
-		A int
-		B yaml.Node ",omitempty"
-	}
-	v.A = 1
-	data, err := yaml.Marshal(&v)
-	c.Assert(err, IsNil)
-	c.Assert(string(data), Equals, "a: 1\n")
-
-	v.B.Line = 1
-	_, err = yaml.Marshal(&v)
-	c.Assert(err, ErrorMatches, "yaml: cannot encode node with unknown kind 0")
-}
-
-func fprintComments(out io.Writer, node *yaml.Node, indent string) {
-	switch node.Kind {
-	case yaml.ScalarNode:
-		fmt.Fprintf(out, "%s<%s> ", indent, node.Value)
-		fprintCommentSet(out, node)
-		fmt.Fprintf(out, "\n")
-	case yaml.DocumentNode:
-		fmt.Fprintf(out, "%s<DOC> ", indent)
-		fprintCommentSet(out, node)
-		fmt.Fprintf(out, "\n")
-		for i := 0; i < len(node.Content); i++ {
-			fprintComments(out, node.Content[i], indent+"  ")
-		}
-	case yaml.MappingNode:
-		fmt.Fprintf(out, "%s<MAP> ", indent)
-		fprintCommentSet(out, node)
-		fmt.Fprintf(out, "\n")
-		for i := 0; i < len(node.Content); i += 2 {
-			fprintComments(out, node.Content[i], indent+"  ")
-			fprintComments(out, node.Content[i+1], indent+"  ")
-		}
-	case yaml.SequenceNode:
-		fmt.Fprintf(out, "%s<SEQ> ", indent)
-		fprintCommentSet(out, node)
-		fmt.Fprintf(out, "\n")
-		for i := 0; i < len(node.Content); i++ {
-			fprintComments(out, node.Content[i], indent+"  ")
-		}
-	}
-}
-
-func fprintCommentSet(out io.Writer, node *yaml.Node) {
-	if len(node.HeadComment)+len(node.LineComment)+len(node.FootComment) > 0 {
-		fmt.Fprintf(out, "%q / %q / %q", node.HeadComment, node.LineComment, node.FootComment)
-	}
-}

+ 1 - 155
parserc.go

@@ -1,25 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-// Copyright (c) 2006-2010 Kirill Simonov
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-// of the Software, and to permit persons to whom the Software is furnished to do
-// so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
 package yaml
 
 import (
@@ -67,46 +45,11 @@ import (
 // Peek the next token in the token queue.
 func peek_token(parser *yaml_parser_t) *yaml_token_t {
 	if parser.token_available || yaml_parser_fetch_more_tokens(parser) {
-		token := &parser.tokens[parser.tokens_head]
-		yaml_parser_unfold_comments(parser, token)
-		return token
+		return &parser.tokens[parser.tokens_head]
 	}
 	return nil
 }
 
-// yaml_parser_unfold_comments walks through the comments queue and joins all
-// comments behind the position of the provided token into the respective
-// top-level comment slices in the parser.
-func yaml_parser_unfold_comments(parser *yaml_parser_t, token *yaml_token_t) {
-	for parser.comments_head < len(parser.comments) && token.start_mark.index >= parser.comments[parser.comments_head].token_mark.index {
-		comment := &parser.comments[parser.comments_head]
-		if len(comment.head) > 0 {
-			if token.typ == yaml_BLOCK_END_TOKEN {
-				// No heads on ends, so keep comment.head for a follow up token.
-				break
-			}
-			if len(parser.head_comment) > 0 {
-				parser.head_comment = append(parser.head_comment, '\n')
-			}
-			parser.head_comment = append(parser.head_comment, comment.head...)
-		}
-		if len(comment.foot) > 0 {
-			if len(parser.foot_comment) > 0 {
-				parser.foot_comment = append(parser.foot_comment, '\n')
-			}
-			parser.foot_comment = append(parser.foot_comment, comment.foot...)
-		}
-		if len(comment.line) > 0 {
-			if len(parser.line_comment) > 0 {
-				parser.line_comment = append(parser.line_comment, '\n')
-			}
-			parser.line_comment = append(parser.line_comment, comment.line...)
-		}
-		*comment = yaml_comment_t{}
-		parser.comments_head++
-	}
-}
-
 // Remove the next token from the queue (must be called after peek_token).
 func skip_token(parser *yaml_parser_t) {
 	parser.token_available = false
@@ -281,32 +224,10 @@ func yaml_parser_parse_document_start(parser *yaml_parser_t, event *yaml_event_t
 		parser.states = append(parser.states, yaml_PARSE_DOCUMENT_END_STATE)
 		parser.state = yaml_PARSE_BLOCK_NODE_STATE
 
-		var head_comment []byte
-		if len(parser.head_comment) > 0 {
-			// [Go] Scan the header comment backwards, and if an empty line is found, break
-			//      the header so the part before the last empty line goes into the
-			//      document header, while the bottom of it goes into a follow up event.
-			for i := len(parser.head_comment) - 1; i > 0; i-- {
-				if parser.head_comment[i] == '\n' {
-					if i == len(parser.head_comment)-1 {
-						head_comment = parser.head_comment[:i]
-						parser.head_comment = parser.head_comment[i+1:]
-						break
-					} else if parser.head_comment[i-1] == '\n' {
-						head_comment = parser.head_comment[:i-1]
-						parser.head_comment = parser.head_comment[i+1:]
-						break
-					}
-				}
-			}
-		}
-
 		*event = yaml_event_t{
 			typ:        yaml_DOCUMENT_START_EVENT,
 			start_mark: token.start_mark,
 			end_mark:   token.end_mark,
-
-			head_comment: head_comment,
 		}
 
 	} else if token.typ != yaml_STREAM_END_TOKEN {
@@ -363,7 +284,6 @@ func yaml_parser_parse_document_content(parser *yaml_parser_t, event *yaml_event
 	if token == nil {
 		return false
 	}
-
 	if token.typ == yaml_VERSION_DIRECTIVE_TOKEN ||
 		token.typ == yaml_TAG_DIRECTIVE_TOKEN ||
 		token.typ == yaml_DOCUMENT_START_TOKEN ||
@@ -407,25 +327,9 @@ func yaml_parser_parse_document_end(parser *yaml_parser_t, event *yaml_event_t)
 		end_mark:   end_mark,
 		implicit:   implicit,
 	}
-	yaml_parser_set_event_comments(parser, event)
-	if len(event.head_comment) > 0 && len(event.foot_comment) == 0 {
-		event.foot_comment = event.head_comment
-		event.head_comment = nil
-	}
 	return true
 }
 
-func yaml_parser_set_event_comments(parser *yaml_parser_t, event *yaml_event_t) {
-	event.head_comment = parser.head_comment
-	event.line_comment = parser.line_comment
-	event.foot_comment = parser.foot_comment
-	parser.head_comment = nil
-	parser.line_comment = nil
-	parser.foot_comment = nil
-	parser.tail_comment = nil
-	parser.stem_comment = nil
-}
-
 // Parse the productions:
 // block_node_or_indentless_sequence    ::=
 //                          ALIAS
@@ -469,7 +373,6 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
 			end_mark:   token.end_mark,
 			anchor:     token.value,
 		}
-		yaml_parser_set_event_comments(parser, event)
 		skip_token(parser)
 		return true
 	}
@@ -583,7 +486,6 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
 			quoted_implicit: quoted_implicit,
 			style:           yaml_style_t(token.style),
 		}
-		yaml_parser_set_event_comments(parser, event)
 		skip_token(parser)
 		return true
 	}
@@ -600,7 +502,6 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
 			implicit:   implicit,
 			style:      yaml_style_t(yaml_FLOW_SEQUENCE_STYLE),
 		}
-		yaml_parser_set_event_comments(parser, event)
 		return true
 	}
 	if token.typ == yaml_FLOW_MAPPING_START_TOKEN {
@@ -615,7 +516,6 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
 			implicit:   implicit,
 			style:      yaml_style_t(yaml_FLOW_MAPPING_STYLE),
 		}
-		yaml_parser_set_event_comments(parser, event)
 		return true
 	}
 	if block && token.typ == yaml_BLOCK_SEQUENCE_START_TOKEN {
@@ -630,10 +530,6 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
 			implicit:   implicit,
 			style:      yaml_style_t(yaml_BLOCK_SEQUENCE_STYLE),
 		}
-		if parser.stem_comment != nil {
-			event.head_comment = parser.stem_comment
-			parser.stem_comment = nil
-		}
 		return true
 	}
 	if block && token.typ == yaml_BLOCK_MAPPING_START_TOKEN {
@@ -648,10 +544,6 @@ func yaml_parser_parse_node(parser *yaml_parser_t, event *yaml_event_t, block, i
 			implicit:   implicit,
 			style:      yaml_style_t(yaml_BLOCK_MAPPING_STYLE),
 		}
-		if parser.stem_comment != nil {
-			event.head_comment = parser.stem_comment
-			parser.stem_comment = nil
-		}
 		return true
 	}
 	if len(anchor) > 0 || len(tag) > 0 {
@@ -698,9 +590,7 @@ func yaml_parser_parse_block_sequence_entry(parser *yaml_parser_t, event *yaml_e
 
 	if token.typ == yaml_BLOCK_ENTRY_TOKEN {
 		mark := token.end_mark
-		prior_head_len := len(parser.head_comment)
 		skip_token(parser)
-		yaml_parser_split_stem_comment(parser, prior_head_len)
 		token = peek_token(parser)
 		if token == nil {
 			return false
@@ -746,9 +636,7 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y
 
 	if token.typ == yaml_BLOCK_ENTRY_TOKEN {
 		mark := token.end_mark
-		prior_head_len := len(parser.head_comment)
 		skip_token(parser)
-		yaml_parser_split_stem_comment(parser, prior_head_len)
 		token = peek_token(parser)
 		if token == nil {
 			return false
@@ -774,32 +662,6 @@ func yaml_parser_parse_indentless_sequence_entry(parser *yaml_parser_t, event *y
 	return true
 }
 
-// Split stem comment from head comment.
-//
-// When a sequence or map is found under a sequence entry, the former head comment
-// is assigned to the underlying sequence or map as a whole, not the individual
-// sequence or map entry as would be expected otherwise. To handle this case the
-// previous head comment is moved aside as the stem comment.
-func yaml_parser_split_stem_comment(parser *yaml_parser_t, stem_len int) {
-	if stem_len == 0 {
-		return
-	}
-
-	token := peek_token(parser)
-	if token.typ != yaml_BLOCK_SEQUENCE_START_TOKEN && token.typ != yaml_BLOCK_MAPPING_START_TOKEN {
-		return
-	}
-
-	parser.stem_comment = parser.head_comment[:stem_len]
-	if len(parser.head_comment) == stem_len {
-		parser.head_comment = nil
-	} else {
-		// Copy suffix to prevent very strange bugs if someone ever appends
-		// further bytes to the prefix in the stem_comment slice above.
-		parser.head_comment = append([]byte(nil), parser.head_comment[stem_len+1:]...)
-	}
-}
-
 // Parse the productions:
 // block_mapping        ::= BLOCK-MAPPING_START
 //                          *******************
@@ -822,19 +684,6 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even
 		return false
 	}
 
-	// [Go] A tail comment was left from the prior mapping value processed. Emit an event
-	//      as it needs to be processed with that value and not the following key.
-	if len(parser.tail_comment) > 0 {
-		*event = yaml_event_t{
-			typ:          yaml_TAIL_COMMENT_EVENT,
-			start_mark:   token.start_mark,
-			end_mark:     token.end_mark,
-			foot_comment: parser.tail_comment,
-		}
-		parser.tail_comment = nil
-		return true
-	}
-
 	if token.typ == yaml_KEY_TOKEN {
 		mark := token.end_mark
 		skip_token(parser)
@@ -860,7 +709,6 @@ func yaml_parser_parse_block_mapping_key(parser *yaml_parser_t, event *yaml_even
 			start_mark: token.start_mark,
 			end_mark:   token.end_mark,
 		}
-		yaml_parser_set_event_comments(parser, event)
 		skip_token(parser)
 		return true
 	}
@@ -972,7 +820,6 @@ func yaml_parser_parse_flow_sequence_entry(parser *yaml_parser_t, event *yaml_ev
 		start_mark: token.start_mark,
 		end_mark:   token.end_mark,
 	}
-	yaml_parser_set_event_comments(parser, event)
 
 	skip_token(parser)
 	return true
@@ -1112,7 +959,6 @@ func yaml_parser_parse_flow_mapping_key(parser *yaml_parser_t, event *yaml_event
 		start_mark: token.start_mark,
 		end_mark:   token.end_mark,
 	}
-	yaml_parser_set_event_comments(parser, event)
 	skip_token(parser)
 	return true
 }

+ 0 - 22
readerc.go

@@ -1,25 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-// Copyright (c) 2006-2010 Kirill Simonov
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-// of the Software, and to permit persons to whom the Software is furnished to do
-// so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
 package yaml
 
 import (

+ 34 - 102
resolve.go

@@ -1,18 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package yaml
 
 import (
@@ -49,14 +34,18 @@ func init() {
 		tag string
 		l   []string
 	}{
-		{true, boolTag, []string{"true", "True", "TRUE"}},
-		{false, boolTag, []string{"false", "False", "FALSE"}},
-		{nil, nullTag, []string{"", "~", "null", "Null", "NULL"}},
-		{math.NaN(), floatTag, []string{".nan", ".NaN", ".NAN"}},
-		{math.Inf(+1), floatTag, []string{".inf", ".Inf", ".INF"}},
-		{math.Inf(+1), floatTag, []string{"+.inf", "+.Inf", "+.INF"}},
-		{math.Inf(-1), floatTag, []string{"-.inf", "-.Inf", "-.INF"}},
-		{"<<", mergeTag, []string{"<<"}},
+		{true, yaml_BOOL_TAG, []string{"y", "Y", "yes", "Yes", "YES"}},
+		{true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
+		{true, yaml_BOOL_TAG, []string{"on", "On", "ON"}},
+		{false, yaml_BOOL_TAG, []string{"n", "N", "no", "No", "NO"}},
+		{false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
+		{false, yaml_BOOL_TAG, []string{"off", "Off", "OFF"}},
+		{nil, yaml_NULL_TAG, []string{"", "~", "null", "Null", "NULL"}},
+		{math.NaN(), yaml_FLOAT_TAG, []string{".nan", ".NaN", ".NAN"}},
+		{math.Inf(+1), yaml_FLOAT_TAG, []string{".inf", ".Inf", ".INF"}},
+		{math.Inf(+1), yaml_FLOAT_TAG, []string{"+.inf", "+.Inf", "+.INF"}},
+		{math.Inf(-1), yaml_FLOAT_TAG, []string{"-.inf", "-.Inf", "-.INF"}},
+		{"<<", yaml_MERGE_TAG, []string{"<<"}},
 	}
 
 	m := resolveMap
@@ -67,37 +56,11 @@ func init() {
 	}
 }
 
-const (
-	nullTag      = "!!null"
-	boolTag      = "!!bool"
-	strTag       = "!!str"
-	intTag       = "!!int"
-	floatTag     = "!!float"
-	timestampTag = "!!timestamp"
-	seqTag       = "!!seq"
-	mapTag       = "!!map"
-	binaryTag    = "!!binary"
-	mergeTag     = "!!merge"
-)
-
-var longTags = make(map[string]string)
-var shortTags = make(map[string]string)
-
-func init() {
-	for _, stag := range []string{nullTag, boolTag, strTag, intTag, floatTag, timestampTag, seqTag, mapTag, binaryTag, mergeTag} {
-		ltag := longTag(stag)
-		longTags[stag] = ltag
-		shortTags[ltag] = stag
-	}
-}
-
 const longTagPrefix = "tag:yaml.org,2002:"
 
 func shortTag(tag string) string {
+	// TODO This can easily be made faster and produce less garbage.
 	if strings.HasPrefix(tag, longTagPrefix) {
-		if stag, ok := shortTags[tag]; ok {
-			return stag
-		}
 		return "!!" + tag[len(longTagPrefix):]
 	}
 	return tag
@@ -105,9 +68,6 @@ func shortTag(tag string) string {
 
 func longTag(tag string) string {
 	if strings.HasPrefix(tag, "!!") {
-		if ltag, ok := longTags[tag]; ok {
-			return ltag
-		}
 		return longTagPrefix + tag[2:]
 	}
 	return tag
@@ -115,7 +75,7 @@ func longTag(tag string) string {
 
 func resolvableTag(tag string) bool {
 	switch tag {
-	case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag:
+	case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG:
 		return true
 	}
 	return false
@@ -124,24 +84,23 @@ func resolvableTag(tag string) bool {
 var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`)
 
 func resolve(tag string, in string) (rtag string, out interface{}) {
-	tag = shortTag(tag)
 	if !resolvableTag(tag) {
 		return tag, in
 	}
 
 	defer func() {
 		switch tag {
-		case "", rtag, strTag, binaryTag:
+		case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG:
 			return
-		case floatTag:
-			if rtag == intTag {
+		case yaml_FLOAT_TAG:
+			if rtag == yaml_INT_TAG {
 				switch v := out.(type) {
 				case int64:
-					rtag = floatTag
+					rtag = yaml_FLOAT_TAG
 					out = float64(v)
 					return
 				case int:
-					rtag = floatTag
+					rtag = yaml_FLOAT_TAG
 					out = float64(v)
 					return
 				}
@@ -156,7 +115,7 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
 	if in != "" {
 		hint = resolveTable[in[0]]
 	}
-	if hint != 0 && tag != strTag && tag != binaryTag {
+	if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG {
 		// Handle things we can lookup in a map.
 		if item, ok := resolveMap[in]; ok {
 			return item.tag, item.value
@@ -174,17 +133,17 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
 			// Not in the map, so maybe a normal float.
 			floatv, err := strconv.ParseFloat(in, 64)
 			if err == nil {
-				return floatTag, floatv
+				return yaml_FLOAT_TAG, floatv
 			}
 
 		case 'D', 'S':
 			// Int, float, or timestamp.
 			// Only try values as a timestamp if the value is unquoted or there's an explicit
 			// !!timestamp tag.
-			if tag == "" || tag == timestampTag {
+			if tag == "" || tag == yaml_TIMESTAMP_TAG {
 				t, ok := parseTimestamp(in)
 				if ok {
-					return timestampTag, t
+					return yaml_TIMESTAMP_TAG, t
 				}
 			}
 
@@ -192,76 +151,49 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
 			intv, err := strconv.ParseInt(plain, 0, 64)
 			if err == nil {
 				if intv == int64(int(intv)) {
-					return intTag, int(intv)
+					return yaml_INT_TAG, int(intv)
 				} else {
-					return intTag, intv
+					return yaml_INT_TAG, intv
 				}
 			}
 			uintv, err := strconv.ParseUint(plain, 0, 64)
 			if err == nil {
-				return intTag, uintv
+				return yaml_INT_TAG, uintv
 			}
 			if yamlStyleFloat.MatchString(plain) {
 				floatv, err := strconv.ParseFloat(plain, 64)
 				if err == nil {
-					return floatTag, floatv
+					return yaml_FLOAT_TAG, floatv
 				}
 			}
 			if strings.HasPrefix(plain, "0b") {
 				intv, err := strconv.ParseInt(plain[2:], 2, 64)
 				if err == nil {
 					if intv == int64(int(intv)) {
-						return intTag, int(intv)
+						return yaml_INT_TAG, int(intv)
 					} else {
-						return intTag, intv
+						return yaml_INT_TAG, intv
 					}
 				}
 				uintv, err := strconv.ParseUint(plain[2:], 2, 64)
 				if err == nil {
-					return intTag, uintv
+					return yaml_INT_TAG, uintv
 				}
 			} else if strings.HasPrefix(plain, "-0b") {
 				intv, err := strconv.ParseInt("-"+plain[3:], 2, 64)
 				if err == nil {
 					if true || intv == int64(int(intv)) {
-						return intTag, int(intv)
-					} else {
-						return intTag, intv
-					}
-				}
-			}
-			// Octals as introduced in version 1.2 of the spec.
-			// Octals from the 1.1 spec, spelled as 0777, are still
-			// decoded by default in v3 as well for compatibility.
-			// May be dropped in v4 depending on how usage evolves.
-			if strings.HasPrefix(plain, "0o") {
-				intv, err := strconv.ParseInt(plain[2:], 8, 64)
-				if err == nil {
-					if intv == int64(int(intv)) {
-						return intTag, int(intv)
-					} else {
-						return intTag, intv
-					}
-				}
-				uintv, err := strconv.ParseUint(plain[2:], 8, 64)
-				if err == nil {
-					return intTag, uintv
-				}
-			} else if strings.HasPrefix(plain, "-0o") {
-				intv, err := strconv.ParseInt("-"+plain[3:], 8, 64)
-				if err == nil {
-					if true || intv == int64(int(intv)) {
-						return intTag, int(intv)
+						return yaml_INT_TAG, int(intv)
 					} else {
-						return intTag, intv
+						return yaml_INT_TAG, intv
 					}
 				}
 			}
 		default:
-			panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")")
+			panic("resolveTable item not yet handled: " + string(rune(hint)) + " (with " + in + ")")
 		}
 	}
-	return strTag, in
+	return yaml_STR_TAG, in
 }
 
 // encodeBase64 encodes s as base64 that is broken up into multiple lines

+ 18 - 345
scannerc.go

@@ -1,25 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-// Copyright (c) 2006-2010 Kirill Simonov
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-// of the Software, and to permit persons to whom the Software is furnished to do
-// so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
 package yaml
 
 import (
@@ -511,9 +489,6 @@ func cache(parser *yaml_parser_t, length int) bool {
 
 // Advance the buffer pointer.
 func skip(parser *yaml_parser_t) {
-	if !is_blank(parser.buffer, parser.buffer_pos) {
-		parser.newlines = 0
-	}
 	parser.mark.index++
 	parser.mark.column++
 	parser.unread--
@@ -527,22 +502,17 @@ func skip_line(parser *yaml_parser_t) {
 		parser.mark.line++
 		parser.unread -= 2
 		parser.buffer_pos += 2
-		parser.newlines++
 	} else if is_break(parser.buffer, parser.buffer_pos) {
 		parser.mark.index++
 		parser.mark.column = 0
 		parser.mark.line++
 		parser.unread--
 		parser.buffer_pos += width(parser.buffer[parser.buffer_pos])
-		parser.newlines++
 	}
 }
 
 // Copy a character to a string buffer and advance pointers.
 func read(parser *yaml_parser_t, s []byte) []byte {
-	if !is_blank(parser.buffer, parser.buffer_pos) {
-		parser.newlines = 0
-	}
 	w := width(parser.buffer[parser.buffer_pos])
 	if w == 0 {
 		panic("invalid character sequence")
@@ -594,7 +564,6 @@ func read_line(parser *yaml_parser_t, s []byte) []byte {
 	parser.mark.column = 0
 	parser.mark.line++
 	parser.unread--
-	parser.newlines++
 	return s
 }
 
@@ -657,13 +626,9 @@ func trace(args ...interface{}) func() {
 func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {
 	// While we need more tokens to fetch, do it.
 	for {
-		// [Go] The comment parsing logic requires a lookahead of two tokens
-		// so that foot comments may be parsed in time of associating them
-		// with the tokens that are parsed before them, and also for line
-		// comments to be transformed into head comments in some edge cases.
-		if parser.tokens_head < len(parser.tokens)-2 {
-			// If a potential simple key is at the head position, we need to fetch
-			// the next token to disambiguate it.
+		if parser.tokens_head != len(parser.tokens) {
+			// If queue is non-empty, check if any potential simple key may
+			// occupy the head position.
 			head_tok_idx, ok := parser.simple_keys_by_tok[parser.tokens_parsed]
 			if !ok {
 				break
@@ -684,7 +649,7 @@ func yaml_parser_fetch_more_tokens(parser *yaml_parser_t) bool {
 }
 
 // The dispatcher for token fetchers.
-func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) {
+func yaml_parser_fetch_next_token(parser *yaml_parser_t) bool {
 	// Ensure that the buffer is initialized.
 	if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
 		return false
@@ -695,19 +660,13 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) {
 		return yaml_parser_fetch_stream_start(parser)
 	}
 
-	scan_mark := parser.mark
-
 	// Eat whitespaces and comments until we reach the next token.
 	if !yaml_parser_scan_to_next_token(parser) {
 		return false
 	}
 
-	// [Go] While unrolling indents, transform the head comments of prior
-	// indentation levels observed after scan_start into foot comments at
-	// the respective indexes.
-
 	// Check the indentation level against the current column.
-	if !yaml_parser_unroll_indent(parser, parser.mark.column, scan_mark) {
+	if !yaml_parser_unroll_indent(parser, parser.mark.column) {
 		return false
 	}
 
@@ -740,26 +699,6 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) {
 		return yaml_parser_fetch_document_indicator(parser, yaml_DOCUMENT_END_TOKEN)
 	}
 
-	comment_mark := parser.mark
-	if len(parser.tokens) > 0 && (parser.flow_level == 0 && buf[pos] == ':' || parser.flow_level > 0 && buf[pos] == ',') {
-		// Associate any following comments with the prior token.
-		comment_mark = parser.tokens[len(parser.tokens)-1].start_mark
-	}
-	defer func() {
-		if !ok {
-			return
-		}
-		if len(parser.tokens) > 0 && parser.tokens[len(parser.tokens)-1].typ == yaml_BLOCK_ENTRY_TOKEN {
-			// Sequence indicators alone have no line comments. It becomes
-			// a head comment for whatever follows.
-			return
-		}
-		if !yaml_parser_scan_line_comment(parser, comment_mark) {
-			ok = false
-			return
-		}
-	}()
-
 	// Is it the flow sequence start indicator?
 	if buf[pos] == '[' {
 		return yaml_parser_fetch_flow_collection_start(parser, yaml_FLOW_SEQUENCE_START_TOKEN)
@@ -853,7 +792,7 @@ func yaml_parser_fetch_next_token(parser *yaml_parser_t) (ok bool) {
 	// if it is followed by a non-space character.
 	//
 	// The last rule is more restrictive than the specification requires.
-	// [Go] TODO Make this logic more reasonable.
+	// [Go] Make this logic more reasonable.
 	//switch parser.buffer[parser.buffer_pos] {
 	//case '-', '?', ':', ',', '?', '-', ',', ':', ']', '[', '}', '{', '&', '#', '!', '*', '>', '|', '"', '\'', '@', '%', '-', '`':
 	//}
@@ -1026,49 +965,19 @@ func yaml_parser_roll_indent(parser *yaml_parser_t, column, number int, typ yaml
 // Pop indentation levels from the indents stack until the current level
 // becomes less or equal to the column.  For each indentation level, append
 // the BLOCK-END token.
-func yaml_parser_unroll_indent(parser *yaml_parser_t, column int, scan_mark yaml_mark_t) bool {
+func yaml_parser_unroll_indent(parser *yaml_parser_t, column int) bool {
 	// In the flow context, do nothing.
 	if parser.flow_level > 0 {
 		return true
 	}
 
-	block_mark := scan_mark
-	block_mark.index--
-
 	// Loop through the indentation levels in the stack.
 	for parser.indent > column {
-
-		// [Go] Reposition the end token before potential following
-		//      foot comments of parent blocks. For that, search
-		//      backwards for recent comments that were at the same
-		//      indent as the block that is ending now.
-		stop_index := block_mark.index
-		for i := len(parser.comments) - 1; i >= 0; i-- {
-			comment := &parser.comments[i]
-
-			if comment.end_mark.index < stop_index {
-				// Don't go back beyond the start of the comment/whitespace scan, unless column < 0.
-				// If requested indent column is < 0, then the document is over and everything else
-				// is a foot anyway.
-				break
-			}
-			if comment.start_mark.column == parser.indent+1 {
-				// This is a good match. But maybe there's a former comment
-				// at that same indent level, so keep searching.
-				block_mark = comment.start_mark
-			}
-
-			// While the end of the former comment matches with
-			// the start of the following one, we know there's
-			// nothing in between and scanning is still safe.
-			stop_index = comment.scan_mark.index
-		}
-
 		// Create a token and append it to the queue.
 		token := yaml_token_t{
 			typ:        yaml_BLOCK_END_TOKEN,
-			start_mark: block_mark,
-			end_mark:   block_mark,
+			start_mark: parser.mark,
+			end_mark:   parser.mark,
 		}
 		yaml_insert_token(parser, -1, &token)
 
@@ -1117,7 +1026,7 @@ func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool {
 	}
 
 	// Reset the indentation level.
-	if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
+	if !yaml_parser_unroll_indent(parser, -1) {
 		return false
 	}
 
@@ -1141,7 +1050,7 @@ func yaml_parser_fetch_stream_end(parser *yaml_parser_t) bool {
 // Produce a VERSION-DIRECTIVE or TAG-DIRECTIVE token.
 func yaml_parser_fetch_directive(parser *yaml_parser_t) bool {
 	// Reset the indentation level.
-	if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
+	if !yaml_parser_unroll_indent(parser, -1) {
 		return false
 	}
 
@@ -1165,7 +1074,7 @@ func yaml_parser_fetch_directive(parser *yaml_parser_t) bool {
 // Produce the DOCUMENT-START or DOCUMENT-END token.
 func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_type_t) bool {
 	// Reset the indentation level.
-	if !yaml_parser_unroll_indent(parser, -1, parser.mark) {
+	if !yaml_parser_unroll_indent(parser, -1) {
 		return false
 	}
 
@@ -1198,7 +1107,6 @@ func yaml_parser_fetch_document_indicator(parser *yaml_parser_t, typ yaml_token_
 
 // Produce the FLOW-SEQUENCE-START or FLOW-MAPPING-START token.
 func yaml_parser_fetch_flow_collection_start(parser *yaml_parser_t, typ yaml_token_type_t) bool {
-
 	// The indicators '[' and '{' may start a simple key.
 	if !yaml_parser_save_simple_key(parser) {
 		return false
@@ -1534,8 +1442,6 @@ func yaml_parser_fetch_plain_scalar(parser *yaml_parser_t) bool {
 // Eat whitespaces and comments until the next token is found.
 func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool {
 
-	scan_mark := parser.mark
-
 	// Until the next token is not found.
 	for {
 		// Allow the BOM mark to start a line.
@@ -1562,33 +1468,13 @@ func yaml_parser_scan_to_next_token(parser *yaml_parser_t) bool {
 			}
 		}
 
-		// Check if we just had a line comment under a sequence entry that
-		// looks more like a header to the following content. Similar to this:
-		//
-		// - # The comment
-		//   - Some data
-		//
-		// If so, transform the line comment to a head comment and reposition.
-		if len(parser.comments) > 0 && len(parser.tokens) > 1 {
-			tokenA := parser.tokens[len(parser.tokens)-2]
-			tokenB := parser.tokens[len(parser.tokens)-1]
-			comment := &parser.comments[len(parser.comments)-1]
-			if tokenA.typ == yaml_BLOCK_SEQUENCE_START_TOKEN && tokenB.typ == yaml_BLOCK_ENTRY_TOKEN && len(comment.line) > 0 && !is_break(parser.buffer, parser.buffer_pos) {
-				// If it was in the prior line, reposition so it becomes a
-				// header of the follow up token. Otherwise, keep it in place
-				// so it becomes a header of the former.
-				comment.head = comment.line
-				comment.line = nil
-				if comment.start_mark.line == parser.mark.line-1 {
-					comment.token_mark = parser.mark
-				}
-			}
-		}
-
 		// Eat a comment until a line break.
 		if parser.buffer[parser.buffer_pos] == '#' {
-			if !yaml_parser_scan_comments(parser, scan_mark) {
-				return false
+			for !is_breakz(parser.buffer, parser.buffer_pos) {
+				skip(parser)
+				if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
+					return false
+				}
 			}
 		}
 
@@ -1686,10 +1572,6 @@ func yaml_parser_scan_directive(parser *yaml_parser_t, token *yaml_token_t) bool
 	}
 
 	if parser.buffer[parser.buffer_pos] == '#' {
-		// [Go] Discard this inline comment for the time being.
-		//if !yaml_parser_scan_line_comment(parser, start_mark) {
-		//	return false
-		//}
 		for !is_breakz(parser.buffer, parser.buffer_pos) {
 			skip(parser)
 			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
@@ -2105,7 +1987,7 @@ func yaml_parser_scan_tag_uri(parser *yaml_parser_t, directive bool, head []byte
 	//      '0'-'9', 'A'-'Z', 'a'-'z', '_', '-', ';', '/', '?', ':', '@', '&',
 	//      '=', '+', '$', ',', '.', '!', '~', '*', '\'', '(', ')', '[', ']',
 	//      '%'.
-	// [Go] TODO Convert this into more reasonable logic.
+	// [Go] Convert this into more reasonable logic.
 	for is_alpha(parser.buffer, parser.buffer_pos) || parser.buffer[parser.buffer_pos] == ';' ||
 		parser.buffer[parser.buffer_pos] == '/' || parser.buffer[parser.buffer_pos] == '?' ||
 		parser.buffer[parser.buffer_pos] == ':' || parser.buffer[parser.buffer_pos] == '@' ||
@@ -2260,9 +2142,6 @@ func yaml_parser_scan_block_scalar(parser *yaml_parser_t, token *yaml_token_t, l
 		}
 	}
 	if parser.buffer[parser.buffer_pos] == '#' {
-		if !yaml_parser_scan_line_comment(parser, start_mark) {
-			return false
-		}
 		for !is_breakz(parser.buffer, parser.buffer_pos) {
 			skip(parser)
 			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
@@ -2830,209 +2709,3 @@ func yaml_parser_scan_plain_scalar(parser *yaml_parser_t, token *yaml_token_t) b
 	}
 	return true
 }
-
-func yaml_parser_scan_line_comment(parser *yaml_parser_t, token_mark yaml_mark_t) bool {
-	if parser.newlines > 0 {
-		return true
-	}
-
-	var start_mark yaml_mark_t
-	var text []byte
-
-	for peek := 0; peek < 512; peek++ {
-		if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) {
-			break
-		}
-		if is_blank(parser.buffer, parser.buffer_pos+peek) {
-			continue
-		}
-		if parser.buffer[parser.buffer_pos+peek] == '#' {
-			seen := parser.mark.index + peek
-			for {
-				if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
-					return false
-				}
-				if is_breakz(parser.buffer, parser.buffer_pos) {
-					if parser.mark.index >= seen {
-						break
-					}
-					if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
-						return false
-					}
-					skip_line(parser)
-				} else if parser.mark.index >= seen {
-					if len(text) == 0 {
-						start_mark = parser.mark
-					}
-					text = read(parser, text)
-				} else {
-					skip(parser)
-				}
-			}
-		}
-		break
-	}
-	if len(text) > 0 {
-		parser.comments = append(parser.comments, yaml_comment_t{
-			token_mark: token_mark,
-			start_mark: start_mark,
-			line:       text,
-		})
-	}
-	return true
-}
-
-func yaml_parser_scan_comments(parser *yaml_parser_t, scan_mark yaml_mark_t) bool {
-	token := parser.tokens[len(parser.tokens)-1]
-
-	if token.typ == yaml_FLOW_ENTRY_TOKEN && len(parser.tokens) > 1 {
-		token = parser.tokens[len(parser.tokens)-2]
-	}
-
-	var token_mark = token.start_mark
-	var start_mark yaml_mark_t
-	var next_indent = parser.indent
-	if next_indent < 0 {
-		next_indent = 0
-	}
-
-	var recent_empty = false
-	var first_empty = parser.newlines <= 1
-
-	var line = parser.mark.line
-	var column = parser.mark.column
-
-	var text []byte
-
-	// The foot line is the place where a comment must start to
-	// still be considered as a foot of the prior content.
-	// If there's some content in the currently parsed line, then
-	// the foot is the line below it.
-	var foot_line = -1
-	if scan_mark.line > 0 {
-		foot_line = parser.mark.line - parser.newlines + 1
-		if parser.newlines == 0 && parser.mark.column > 1 {
-			foot_line++
-		}
-	}
-
-	var peek = 0
-	for ; peek < 512; peek++ {
-		if parser.unread < peek+1 && !yaml_parser_update_buffer(parser, peek+1) {
-			break
-		}
-		column++
-		if is_blank(parser.buffer, parser.buffer_pos+peek) {
-			continue
-		}
-		c := parser.buffer[parser.buffer_pos+peek]
-		var close_flow = parser.flow_level > 0 && (c == ']' || c == '}')
-		if close_flow || is_breakz(parser.buffer, parser.buffer_pos+peek) {
-			// Got line break or terminator.
-			if close_flow || !recent_empty {
-				if close_flow || first_empty && (start_mark.line == foot_line && token.typ != yaml_VALUE_TOKEN || start_mark.column-1 < next_indent) {
-					// This is the first empty line and there were no empty lines before,
-					// so this initial part of the comment is a foot of the prior token
-					// instead of being a head for the following one. Split it up.
-					// Alternatively, this might also be the last comment inside a flow
-					// scope, so it must be a footer.
-					if len(text) > 0 {
-						if start_mark.column-1 < next_indent {
-							// If dedented it's unrelated to the prior token.
-							token_mark = start_mark
-						}
-						parser.comments = append(parser.comments, yaml_comment_t{
-							scan_mark:  scan_mark,
-							token_mark: token_mark,
-							start_mark: start_mark,
-							end_mark:   yaml_mark_t{parser.mark.index + peek, line, column},
-							foot:       text,
-						})
-						scan_mark = yaml_mark_t{parser.mark.index + peek, line, column}
-						token_mark = scan_mark
-						text = nil
-					}
-				} else {
-					if len(text) > 0 && parser.buffer[parser.buffer_pos+peek] != 0 {
-						text = append(text, '\n')
-					}
-				}
-			}
-			if !is_break(parser.buffer, parser.buffer_pos+peek) {
-				break
-			}
-			first_empty = false
-			recent_empty = true
-			column = 0
-			line++
-			continue
-		}
-
-		if len(text) > 0 && (close_flow || column-1 < next_indent && column != start_mark.column) {
-			// The comment at the different indentation is a foot of the
-			// preceding data rather than a head of the upcoming one.
-			parser.comments = append(parser.comments, yaml_comment_t{
-				scan_mark:  scan_mark,
-				token_mark: token_mark,
-				start_mark: start_mark,
-				end_mark:   yaml_mark_t{parser.mark.index + peek, line, column},
-				foot:       text,
-			})
-			scan_mark = yaml_mark_t{parser.mark.index + peek, line, column}
-			token_mark = scan_mark
-			text = nil
-		}
-
-		if parser.buffer[parser.buffer_pos+peek] != '#' {
-			break
-		}
-
-		if len(text) == 0 {
-			start_mark = yaml_mark_t{parser.mark.index + peek, line, column}
-		} else {
-			text = append(text, '\n')
-		}
-
-		recent_empty = false
-
-		// Consume until after the consumed comment line.
-		seen := parser.mark.index + peek
-		for {
-			if parser.unread < 1 && !yaml_parser_update_buffer(parser, 1) {
-				return false
-			}
-			if is_breakz(parser.buffer, parser.buffer_pos) {
-				if parser.mark.index >= seen {
-					break
-				}
-				if parser.unread < 2 && !yaml_parser_update_buffer(parser, 2) {
-					return false
-				}
-				skip_line(parser)
-			} else if parser.mark.index >= seen {
-				text = read(parser, text)
-			} else {
-				skip(parser)
-			}
-		}
-
-		peek = 0
-		column = 0
-		line = parser.mark.line
-		next_indent = parser.indent
-		if next_indent < 0 {
-			next_indent = 0
-		}
-	}
-
-	if len(text) > 0 {
-		parser.comments = append(parser.comments, yaml_comment_t{
-			scan_mark:  scan_mark,
-			token_mark: start_mark,
-			start_mark: start_mark,
-			end_mark:   yaml_mark_t{parser.mark.index + peek - 1, line, column},
-			head:       text,
-		})
-	}
-	return true
-}

+ 1 - 22
sorter.go

@@ -1,18 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package yaml
 
 import (
@@ -52,10 +37,8 @@ func (l keyList) Less(i, j int) bool {
 		return ak < bk
 	}
 	ar, br := []rune(a.String()), []rune(b.String())
-	digits := false
 	for i := 0; i < len(ar) && i < len(br); i++ {
 		if ar[i] == br[i] {
-			digits = unicode.IsDigit(ar[i])
 			continue
 		}
 		al := unicode.IsLetter(ar[i])
@@ -64,11 +47,7 @@ func (l keyList) Less(i, j int) bool {
 			return ar[i] < br[i]
 		}
 		if al || bl {
-			if digits {
-				return al
-			} else {
-				return bl
-			}
+			return bl
 		}
 		var ai, bi int
 		var an, bn int64

+ 0 - 15
suite_test.go

@@ -1,18 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 package yaml_test
 
 import (

+ 0 - 22
writerc.go

@@ -1,25 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-// Copyright (c) 2006-2010 Kirill Simonov
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-// of the Software, and to permit persons to whom the Software is furnished to do
-// so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
 package yaml
 
 // Set the writer error and return false.

+ 65 - 284
yaml.go

@@ -1,18 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//     http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
 // Package yaml implements YAML support for the Go language.
 //
 // Source code and other details for the project are available at GitHub:
@@ -28,16 +13,23 @@ import (
 	"reflect"
 	"strings"
 	"sync"
-	"unicode/utf8"
 )
 
-// The Unmarshaler interface may be implemented by types to customize their
-// behavior when being unmarshaled from a YAML document.
-type Unmarshaler interface {
-	UnmarshalYAML(value *Node) error
+// MapSlice encodes and decodes as a YAML map.
+// The order of keys is preserved when encoding and decoding.
+type MapSlice []MapItem
+
+// MapItem is an item in a MapSlice.
+type MapItem struct {
+	Key, Value interface{}
 }
 
-type obsoleteUnmarshaler interface {
+// The Unmarshaler interface may be implemented by types to customize their
+// behavior when being unmarshaled from a YAML document. The UnmarshalYAML
+// method receives a function that may be called to unmarshal the original
+// YAML value into a field or variable. It is safe to call the unmarshal
+// function parameter more than once if necessary.
+type Unmarshaler interface {
 	UnmarshalYAML(unmarshal func(interface{}) error) error
 }
 
@@ -89,10 +81,18 @@ func Unmarshal(in []byte, out interface{}) (err error) {
 	return unmarshal(in, out, false)
 }
 
+// UnmarshalStrict is like Unmarshal except that any fields that are found
+// in the data that do not have corresponding struct members, or mapping
+// keys that are duplicates, will result in
+// an error.
+func UnmarshalStrict(in []byte, out interface{}) (err error) {
+	return unmarshal(in, out, true)
+}
+
 // A Decoder reads and decodes YAML values from an input stream.
 type Decoder struct {
-	parser      *parser
-	knownFields bool
+	strict bool
+	parser *parser
 }
 
 // NewDecoder returns a new decoder that reads from r.
@@ -105,10 +105,10 @@ func NewDecoder(r io.Reader) *Decoder {
 	}
 }
 
-// KnownFields ensures that the keys in decoded mappings to
-// exist as fields in the struct being decoded into.
-func (dec *Decoder) KnownFields(enable bool) {
-	dec.knownFields = enable
+// SetStrict sets whether strict decoding behaviour is enabled when
+// decoding items in the data (see UnmarshalStrict). By default, decoding is not strict.
+func (dec *Decoder) SetStrict(strict bool) {
+	dec.strict = strict
 }
 
 // Decode reads the next YAML-encoded value from its input
@@ -117,8 +117,7 @@ func (dec *Decoder) KnownFields(enable bool) {
 // See the documentation for Unmarshal for details about the
 // conversion of YAML into a Go value.
 func (dec *Decoder) Decode(v interface{}) (err error) {
-	d := newDecoder()
-	d.knownFields = dec.knownFields
+	d := newDecoder(dec.strict)
 	defer handleErr(&err)
 	node := dec.parser.parse()
 	if node == nil {
@@ -135,27 +134,9 @@ func (dec *Decoder) Decode(v interface{}) (err error) {
 	return nil
 }
 
-// Decode decodes the node and stores its data into the value pointed to by v.
-//
-// See the documentation for Unmarshal for details about the
-// conversion of YAML into a Go value.
-func (n *Node) Decode(v interface{}) (err error) {
-	d := newDecoder()
-	defer handleErr(&err)
-	out := reflect.ValueOf(v)
-	if out.Kind() == reflect.Ptr && !out.IsNil() {
-		out = out.Elem()
-	}
-	d.unmarshal(n, out)
-	if len(d.terrors) > 0 {
-		return &TypeError{d.terrors}
-	}
-	return nil
-}
-
 func unmarshal(in []byte, out interface{}, strict bool) (err error) {
 	defer handleErr(&err)
-	d := newDecoder()
+	d := newDecoder(strict)
 	p := newParser(in)
 	defer p.destroy()
 	node := p.parse()
@@ -252,32 +233,6 @@ func (e *Encoder) Encode(v interface{}) (err error) {
 	return nil
 }
 
-// Encode encodes value v and stores its representation in n.
-//
-// See the documentation for Marshal for details about the
-// conversion of Go values into YAML.
-func (n *Node) Encode(v interface{}) (err error) {
-	defer handleErr(&err)
-	e := newEncoder()
-	defer e.destroy()
-	e.marshalDoc("", reflect.ValueOf(v))
-	e.finish()
-	p := newParser(e.out)
-	p.textless = true
-	defer p.destroy()
-	doc := p.parse()
-	*n = *doc.Content[0]
-	return nil
-}
-
-// SetIndent changes the used indentation used when encoding.
-func (e *Encoder) SetIndent(spaces int) {
-	if spaces < 0 {
-		panic("yaml: cannot indent to a negative number of spaces")
-	}
-	e.encoder.indent = spaces
-}
-
 // Close closes the encoder by writing any remaining data.
 // It does not write a stream terminating string "...".
 func (e *Encoder) Close() (err error) {
@@ -320,167 +275,6 @@ func (e *TypeError) Error() string {
 	return fmt.Sprintf("yaml: unmarshal errors:\n  %s", strings.Join(e.Errors, "\n  "))
 }
 
-type Kind uint32
-
-const (
-	DocumentNode Kind = 1 << iota
-	SequenceNode
-	MappingNode
-	ScalarNode
-	AliasNode
-)
-
-type Style uint32
-
-const (
-	TaggedStyle Style = 1 << iota
-	DoubleQuotedStyle
-	SingleQuotedStyle
-	LiteralStyle
-	FoldedStyle
-	FlowStyle
-)
-
-// Node represents an element in the YAML document hierarchy. While documents
-// are typically encoded and decoded into higher level types, such as structs
-// and maps, Node is an intermediate representation that allows detailed
-// control over the content being decoded or encoded.
-//
-// It's worth noting that although Node offers access into details such as
-// line numbers, colums, and comments, the content when re-encoded will not
-// have its original textual representation preserved. An effort is made to
-// render the data plesantly, and to preserve comments near the data they
-// describe, though.
-//
-// Values that make use of the Node type interact with the yaml package in the
-// same way any other type would do, by encoding and decoding yaml data
-// directly or indirectly into them.
-//
-// For example:
-//
-//     var person struct {
-//             Name    string
-//             Address yaml.Node
-//     }
-//     err := yaml.Unmarshal(data, &person)
-//
-// Or by itself:
-//
-//     var person Node
-//     err := yaml.Unmarshal(data, &person)
-//
-type Node struct {
-	// Kind defines whether the node is a document, a mapping, a sequence,
-	// a scalar value, or an alias to another node. The specific data type of
-	// scalar nodes may be obtained via the ShortTag and LongTag methods.
-	Kind Kind
-
-	// Style allows customizing the apperance of the node in the tree.
-	Style Style
-
-	// Tag holds the YAML tag defining the data type for the value.
-	// When decoding, this field will always be set to the resolved tag,
-	// even when it wasn't explicitly provided in the YAML content.
-	// When encoding, if this field is unset the value type will be
-	// implied from the node properties, and if it is set, it will only
-	// be serialized into the representation if TaggedStyle is used or
-	// the implicit tag diverges from the provided one.
-	Tag string
-
-	// Value holds the unescaped and unquoted represenation of the value.
-	Value string
-
-	// Anchor holds the anchor name for this node, which allows aliases to point to it.
-	Anchor string
-
-	// Alias holds the node that this alias points to. Only valid when Kind is AliasNode.
-	Alias *Node
-
-	// Content holds contained nodes for documents, mappings, and sequences.
-	Content []*Node
-
-	// HeadComment holds any comments in the lines preceding the node and
-	// not separated by an empty line.
-	HeadComment string
-
-	// LineComment holds any comments at the end of the line where the node is in.
-	LineComment string
-
-	// FootComment holds any comments following the node and before empty lines.
-	FootComment string
-
-	// Line and Column hold the node position in the decoded YAML text.
-	// These fields are not respected when encoding the node.
-	Line   int
-	Column int
-}
-
-// IsZero returns whether the node has all of its fields unset.
-func (n *Node) IsZero() bool {
-	return n.Kind == 0 && n.Style == 0 && n.Tag == "" && n.Value == "" && n.Anchor == "" && n.Alias == nil && n.Content == nil &&
-		n.HeadComment == "" && n.LineComment == "" && n.FootComment == "" && n.Line == 0 && n.Column == 0
-}
-
-// LongTag returns the long form of the tag that indicates the data type for
-// the node. If the Tag field isn't explicitly defined, one will be computed
-// based on the node properties.
-func (n *Node) LongTag() string {
-	return longTag(n.ShortTag())
-}
-
-// ShortTag returns the short form of the YAML tag that indicates data type for
-// the node. If the Tag field isn't explicitly defined, one will be computed
-// based on the node properties.
-func (n *Node) ShortTag() string {
-	if n.indicatedString() {
-		return strTag
-	}
-	if n.Tag == "" || n.Tag == "!" {
-		switch n.Kind {
-		case MappingNode:
-			return mapTag
-		case SequenceNode:
-			return seqTag
-		case AliasNode:
-			if n.Alias != nil {
-				return n.Alias.ShortTag()
-			}
-		case ScalarNode:
-			tag, _ := resolve("", n.Value)
-			return tag
-		case 0:
-			// Special case to make the zero value convenient.
-			if n.IsZero() {
-				return nullTag
-			}
-		}
-		return ""
-	}
-	return shortTag(n.Tag)
-}
-
-func (n *Node) indicatedString() bool {
-	return n.Kind == ScalarNode &&
-		(shortTag(n.Tag) == strTag ||
-			(n.Tag == "" || n.Tag == "!") && n.Style&(SingleQuotedStyle|DoubleQuotedStyle|LiteralStyle|FoldedStyle) != 0)
-}
-
-// SetString is a convenience function that sets the node to a string value
-// and defines its style in a pleasant way depending on its content.
-func (n *Node) SetString(s string) {
-	n.Kind = ScalarNode
-	if utf8.ValidString(s) {
-		n.Value = s
-		n.Tag = strTag
-	} else {
-		n.Value = encodeBase64(s)
-		n.Tag = binaryTag
-	}
-	if strings.Contains(n.Value, "\n") {
-		n.Style = LiteralStyle
-	}
-}
-
 // --------------------------------------------------------------------------
 // Maintain a mapping of keys to structure field indexes
 
@@ -495,10 +289,6 @@ type structInfo struct {
 	// InlineMap is the number of the field in the struct that
 	// contains an ,inline map, or -1 if there's none.
 	InlineMap int
-
-	// InlineUnmarshalers holds indexes to inlined fields that
-	// contain unmarshaler values.
-	InlineUnmarshalers [][]int
 }
 
 type fieldInfo struct {
@@ -516,12 +306,6 @@ type fieldInfo struct {
 
 var structMap = make(map[reflect.Type]*structInfo)
 var fieldMapMutex sync.RWMutex
-var unmarshalerType reflect.Type
-
-func init() {
-	var v Unmarshaler
-	unmarshalerType = reflect.ValueOf(&v).Elem().Type()
-}
 
 func getStructInfo(st reflect.Type) (*structInfo, error) {
 	fieldMapMutex.RLock()
@@ -535,7 +319,6 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
 	fieldsMap := make(map[string]fieldInfo)
 	fieldsList := make([]fieldInfo, 0, n)
 	inlineMap := -1
-	inlineUnmarshalers := [][]int(nil)
 	for i := 0; i != n; i++ {
 		field := st.Field(i)
 		if field.PkgPath != "" && !field.Anonymous {
@@ -564,7 +347,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
 				case "inline":
 					inline = true
 				default:
-					return nil, errors.New(fmt.Sprintf("unsupported flag %q in tag %q of type %s", flag, tag, st))
+					return nil, errors.New(fmt.Sprintf("Unsupported flag %q in tag %q of type %s", flag, tag, st))
 				}
 			}
 			tag = fields[0]
@@ -574,47 +357,34 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
 			switch field.Type.Kind() {
 			case reflect.Map:
 				if inlineMap >= 0 {
-					return nil, errors.New("multiple ,inline maps in struct " + st.String())
+					return nil, errors.New("Multiple ,inline maps in struct " + st.String())
 				}
 				if field.Type.Key() != reflect.TypeOf("") {
-					return nil, errors.New("option ,inline needs a map with string keys in struct " + st.String())
+					return nil, errors.New("Option ,inline needs a map with string keys in struct " + st.String())
 				}
 				inlineMap = info.Num
-			case reflect.Struct, reflect.Ptr:
-				ftype := field.Type
-				for ftype.Kind() == reflect.Ptr {
-					ftype = ftype.Elem()
+			case reflect.Struct:
+				sinfo, err := getStructInfo(field.Type)
+				if err != nil {
+					return nil, err
 				}
-				if ftype.Kind() != reflect.Struct {
-					return nil, errors.New("option ,inline may only be used on a struct or map field")
-				}
-				if reflect.PtrTo(ftype).Implements(unmarshalerType) {
-					inlineUnmarshalers = append(inlineUnmarshalers, []int{i})
-				} else {
-					sinfo, err := getStructInfo(ftype)
-					if err != nil {
-						return nil, err
-					}
-					for _, index := range sinfo.InlineUnmarshalers {
-						inlineUnmarshalers = append(inlineUnmarshalers, append([]int{i}, index...))
+				for _, finfo := range sinfo.FieldsList {
+					if _, found := fieldsMap[finfo.Key]; found {
+						msg := "Duplicated key '" + finfo.Key + "' in struct " + st.String()
+						return nil, errors.New(msg)
 					}
-					for _, finfo := range sinfo.FieldsList {
-						if _, found := fieldsMap[finfo.Key]; found {
-							msg := "duplicated key '" + finfo.Key + "' in struct " + st.String()
-							return nil, errors.New(msg)
-						}
-						if finfo.Inline == nil {
-							finfo.Inline = []int{i, finfo.Num}
-						} else {
-							finfo.Inline = append([]int{i}, finfo.Inline...)
-						}
-						finfo.Id = len(fieldsList)
-						fieldsMap[finfo.Key] = finfo
-						fieldsList = append(fieldsList, finfo)
+					if finfo.Inline == nil {
+						finfo.Inline = []int{i, finfo.Num}
+					} else {
+						finfo.Inline = append([]int{i}, finfo.Inline...)
 					}
+					finfo.Id = len(fieldsList)
+					fieldsMap[finfo.Key] = finfo
+					fieldsList = append(fieldsList, finfo)
 				}
 			default:
-				return nil, errors.New("option ,inline may only be used on a struct or map field")
+				//return nil, errors.New("Option ,inline needs a struct value or map field")
+				return nil, errors.New("Option ,inline needs a struct value field")
 			}
 			continue
 		}
@@ -626,7 +396,7 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
 		}
 
 		if _, found = fieldsMap[info.Key]; found {
-			msg := "duplicated key '" + info.Key + "' in struct " + st.String()
+			msg := "Duplicated key '" + info.Key + "' in struct " + st.String()
 			return nil, errors.New(msg)
 		}
 
@@ -636,10 +406,9 @@ func getStructInfo(st reflect.Type) (*structInfo, error) {
 	}
 
 	sinfo = &structInfo{
-		FieldsMap:          fieldsMap,
-		FieldsList:         fieldsList,
-		InlineMap:          inlineMap,
-		InlineUnmarshalers: inlineUnmarshalers,
+		FieldsMap:  fieldsMap,
+		FieldsList: fieldsList,
+		InlineMap:  inlineMap,
 	}
 
 	fieldMapMutex.Lock()
@@ -695,3 +464,15 @@ func isZero(v reflect.Value) bool {
 	}
 	return false
 }
+
+// FutureLineWrap globally disables line wrapping when encoding long strings.
+// This is a temporary and thus deprecated method introduced to faciliate
+// migration towards v3, which offers more control of line lengths on
+// individual encodings, and has a default matching the behavior introduced
+// by this function.
+//
+// The default formatting of v2 was erroneously changed in v2.3.0 and reverted
+// in v2.4.0, at which point this function was introduced to help migration.
+func FutureLineWrap() {
+	disableLineWrapping = true
+}

+ 6 - 73
yamlh.go

@@ -1,25 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-// Copyright (c) 2006-2010 Kirill Simonov
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-// of the Software, and to permit persons to whom the Software is furnished to do
-// so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
 package yaml
 
 import (
@@ -95,13 +73,13 @@ type yaml_scalar_style_t yaml_style_t
 // Scalar styles.
 const (
 	// Let the emitter choose the style.
-	yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = 0
+	yaml_ANY_SCALAR_STYLE yaml_scalar_style_t = iota
 
-	yaml_PLAIN_SCALAR_STYLE         yaml_scalar_style_t = 1 << iota // The plain scalar style.
-	yaml_SINGLE_QUOTED_SCALAR_STYLE                                 // The single-quoted scalar style.
-	yaml_DOUBLE_QUOTED_SCALAR_STYLE                                 // The double-quoted scalar style.
-	yaml_LITERAL_SCALAR_STYLE                                       // The literal scalar style.
-	yaml_FOLDED_SCALAR_STYLE                                        // The folded scalar style.
+	yaml_PLAIN_SCALAR_STYLE         // The plain scalar style.
+	yaml_SINGLE_QUOTED_SCALAR_STYLE // The single-quoted scalar style.
+	yaml_DOUBLE_QUOTED_SCALAR_STYLE // The double-quoted scalar style.
+	yaml_LITERAL_SCALAR_STYLE       // The literal scalar style.
+	yaml_FOLDED_SCALAR_STYLE        // The folded scalar style.
 )
 
 type yaml_sequence_style_t yaml_style_t
@@ -260,7 +238,6 @@ const (
 	yaml_SEQUENCE_END_EVENT   // A SEQUENCE-END event.
 	yaml_MAPPING_START_EVENT  // A MAPPING-START event.
 	yaml_MAPPING_END_EVENT    // A MAPPING-END event.
-	yaml_TAIL_COMMENT_EVENT
 )
 
 var eventStrings = []string{
@@ -275,7 +252,6 @@ var eventStrings = []string{
 	yaml_SEQUENCE_END_EVENT:   "sequence end",
 	yaml_MAPPING_START_EVENT:  "mapping start",
 	yaml_MAPPING_END_EVENT:    "mapping end",
-	yaml_TAIL_COMMENT_EVENT:   "tail comment",
 }
 
 func (e yaml_event_type_t) String() string {
@@ -303,12 +279,6 @@ type yaml_event_t struct {
 	// The list of tag directives (for yaml_DOCUMENT_START_EVENT).
 	tag_directives []yaml_tag_directive_t
 
-	// The comments
-	head_comment []byte
-	line_comment []byte
-	foot_comment []byte
-	tail_comment []byte
-
 	// The anchor (for yaml_SCALAR_EVENT, yaml_SEQUENCE_START_EVENT, yaml_MAPPING_START_EVENT, yaml_ALIAS_EVENT).
 	anchor []byte
 
@@ -584,8 +554,6 @@ type yaml_parser_t struct {
 
 	unread int // The number of unread characters in the buffer.
 
-	newlines int // The number of line breaks since last non-break/non-blank character
-
 	raw_buffer     []byte // The raw buffer.
 	raw_buffer_pos int    // The current position of the buffer.
 
@@ -594,17 +562,6 @@ type yaml_parser_t struct {
 	offset int         // The offset of the current position (in bytes).
 	mark   yaml_mark_t // The mark of the current position.
 
-	// Comments
-
-	head_comment []byte // The current head comments
-	line_comment []byte // The current line comments
-	foot_comment []byte // The current foot comments
-	tail_comment []byte // Foot comment that happens at the end of a block.
-	stem_comment []byte // Comment in item preceding a nested structure (list inside list item, etc)
-
-	comments      []yaml_comment_t // The folded comments for all parsed tokens
-	comments_head int
-
 	// Scanner stuff
 
 	stream_start_produced bool // Have we started to scan the input stream?
@@ -638,17 +595,6 @@ type yaml_parser_t struct {
 	document *yaml_document_t // The currently parsed document.
 }
 
-type yaml_comment_t struct {
-	scan_mark  yaml_mark_t // Position where scanning for comments started
-	token_mark yaml_mark_t // Position after which tokens will be associated with this comment
-	start_mark yaml_mark_t // Position of '#' comment mark
-	end_mark   yaml_mark_t // Position where comment terminated
-
-	head []byte
-	line []byte
-	foot []byte
-}
-
 // Emitter Definitions
 
 // The prototype of a write handler.
@@ -679,10 +625,8 @@ const (
 	yaml_EMIT_DOCUMENT_CONTENT_STATE           // Expect the content of a document.
 	yaml_EMIT_DOCUMENT_END_STATE               // Expect DOCUMENT-END.
 	yaml_EMIT_FLOW_SEQUENCE_FIRST_ITEM_STATE   // Expect the first item of a flow sequence.
-	yaml_EMIT_FLOW_SEQUENCE_TRAIL_ITEM_STATE   // Expect the next item of a flow sequence, with the comma already written out
 	yaml_EMIT_FLOW_SEQUENCE_ITEM_STATE         // Expect an item of a flow sequence.
 	yaml_EMIT_FLOW_MAPPING_FIRST_KEY_STATE     // Expect the first key of a flow mapping.
-	yaml_EMIT_FLOW_MAPPING_TRAIL_KEY_STATE     // Expect the next key of a flow mapping, with the comma already written out
 	yaml_EMIT_FLOW_MAPPING_KEY_STATE           // Expect a key of a flow mapping.
 	yaml_EMIT_FLOW_MAPPING_SIMPLE_VALUE_STATE  // Expect a value for a simple key of a flow mapping.
 	yaml_EMIT_FLOW_MAPPING_VALUE_STATE         // Expect a value of a flow mapping.
@@ -754,9 +698,6 @@ type yaml_emitter_t struct {
 	indention  bool // If the last character was an indentation character (' ', '-', '?', ':')?
 	open_ended bool // If an explicit document end is required?
 
-	space_above bool // Is there's an empty line above?
-	foot_indent int  // The indent used to write the foot comment above, or -1 if none.
-
 	// Anchor analysis.
 	anchor_data struct {
 		anchor []byte // The anchor value.
@@ -780,14 +721,6 @@ type yaml_emitter_t struct {
 		style                 yaml_scalar_style_t // The output style.
 	}
 
-	// Comments
-	head_comment []byte
-	line_comment []byte
-	foot_comment []byte
-	tail_comment []byte
-
-	key_line_comment []byte
-
 	// Dumper stuff
 
 	opened bool // If the stream was already opened?

+ 3 - 28
yamlprivateh.go

@@ -1,25 +1,3 @@
-//
-// Copyright (c) 2011-2019 Canonical Ltd
-// Copyright (c) 2006-2010 Kirill Simonov
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy of
-// this software and associated documentation files (the "Software"), to deal in
-// the Software without restriction, including without limitation the rights to
-// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-// of the Software, and to permit persons to whom the Software is furnished to do
-// so, subject to the following conditions:
-//
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-//
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-
 package yaml
 
 const (
@@ -136,8 +114,7 @@ func is_crlf(b []byte, i int) bool {
 // Check if the character is a line break or NUL.
 func is_breakz(b []byte, i int) bool {
 	//return is_break(b, i) || is_z(b, i)
-	return (
-	// is_break:
+	return (        // is_break:
 	b[i] == '\r' || // CR (#xD)
 		b[i] == '\n' || // LF (#xA)
 		b[i] == 0xC2 && b[i+1] == 0x85 || // NEL (#x85)
@@ -150,8 +127,7 @@ func is_breakz(b []byte, i int) bool {
 // Check if the character is a line break, space, or NUL.
 func is_spacez(b []byte, i int) bool {
 	//return is_space(b, i) || is_breakz(b, i)
-	return (
-	// is_space:
+	return ( // is_space:
 	b[i] == ' ' ||
 		// is_breakz:
 		b[i] == '\r' || // CR (#xD)
@@ -165,8 +141,7 @@ func is_spacez(b []byte, i int) bool {
 // Check if the character is a line break, space, tab, or NUL.
 func is_blankz(b []byte, i int) bool {
 	//return is_blank(b, i) || is_breakz(b, i)
-	return (
-	// is_blank:
+	return ( // is_blank:
 	b[i] == ' ' || b[i] == '\t' ||
 		// is_breakz:
 		b[i] == '\r' || // CR (#xD)