Browse Source

Resolve Node tags on decoding and use short tags.

This has several good consequences, such as ensuring the value
won't change type on manipulation unless that's intended, and
preventing reparsing of the value repeatedly to understand and
decode it.
Gustavo Niemeyer 7 years ago
parent
commit
891da3a908
5 changed files with 518 additions and 267 deletions
  1. 68 55
      decode.go
  2. 3 0
      decode_test.go
  3. 48 11
      encode.go
  4. 340 172
      node_test.go
  5. 59 29
      resolve.go

+ 68 - 55
decode.go

@@ -25,7 +25,8 @@ const (
 type Style uint32
 type Style uint32
 
 
 const (
 const (
-	DoubleQuotedStyle Style = 1 << iota
+	TaggedStyle Style = 1 << iota
+	DoubleQuotedStyle
 	SingleQuotedStyle
 	SingleQuotedStyle
 	LiteralStyle
 	LiteralStyle
 	FoldedStyle
 	FoldedStyle
@@ -47,44 +48,37 @@ type Node struct {
 	Footer   string
 	Footer   string
 }
 }
 
 
-func (n *Node) implicit() bool {
-	return n.Style&(SingleQuotedStyle|DoubleQuotedStyle) == 0 && (n.Tag == "" || n.Tag == "!")
+func (n *Node) LongTag() string {
+	return longTag(n.ShortTag())
 }
 }
 
 
-// TODO Quite some garbage being generated by these common functions.
-
 func (n *Node) ShortTag() string {
 func (n *Node) ShortTag() string {
-	tag := n.LongTag()
-	if strings.HasPrefix(tag, longTagPrefix) {
-		return "!!" + tag[len(longTagPrefix):]
+	if n.indicatedString() {
+		return strTag
 	}
 	}
-	return tag
-}
-
-func (n *Node) LongTag() string {
 	if n.Tag == "" || n.Tag == "!" {
 	if n.Tag == "" || n.Tag == "!" {
 		switch n.Kind {
 		switch n.Kind {
 		case MappingNode:
 		case MappingNode:
-			return yaml_MAP_TAG
+			return mapTag
 		case SequenceNode:
 		case SequenceNode:
-			return yaml_SEQ_TAG
+			return seqTag
 		case AliasNode:
 		case AliasNode:
 			if n.Alias != nil {
 			if n.Alias != nil {
-				return n.Alias.LongTag()
+				return n.Alias.ShortTag()
 			}
 			}
 		case ScalarNode:
 		case ScalarNode:
-			if n.Style&(SingleQuotedStyle|DoubleQuotedStyle) != 0 {
-				return yaml_STR_TAG
-			}
 			tag, _ := resolve("", n.Value)
 			tag, _ := resolve("", n.Value)
 			return tag
 			return tag
 		}
 		}
 		return ""
 		return ""
-
-	} else if strings.HasPrefix(n.Tag, "!!") {
-		return longTagPrefix + n.Tag[2:]
 	}
 	}
-	return n.Tag
+	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)
 }
 }
 
 
 func (n *Node) SetString(s string) {
 func (n *Node) SetString(s string) {
@@ -92,7 +86,7 @@ func (n *Node) SetString(s string) {
 	n.Value = s
 	n.Value = s
 	if strings.Contains(s, "\n") {
 	if strings.Contains(s, "\n") {
 		n.Style = LiteralStyle
 		n.Style = LiteralStyle
-	} else if n.LongTag() != "tag:yaml.org,2002:str" {
+	} else if n.ShortTag() != strTag {
 		n.Style = DoubleQuotedStyle
 		n.Style = DoubleQuotedStyle
 	}
 	}
 }
 }
@@ -228,9 +222,21 @@ func (p *parser) parse() *Node {
 	}
 	}
 }
 }
 
 
-func (p *parser) node(kind NodeKind) *Node {
+func (p *parser) node(kind NodeKind, 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)
+	}
 	return &Node{
 	return &Node{
 		Kind:   kind,
 		Kind:   kind,
+		Tag:    tag,
+		Value:  value,
+		Style:  style,
 		Line:   p.event.start_mark.line + 1,
 		Line:   p.event.start_mark.line + 1,
 		Column: p.event.start_mark.column + 1,
 		Column: p.event.start_mark.column + 1,
 		Header: string(p.event.header_comment),
 		Header: string(p.event.header_comment),
@@ -246,7 +252,7 @@ func (p *parser) parseChild(parent *Node) *Node {
 }
 }
 
 
 func (p *parser) document() *Node {
 func (p *parser) document() *Node {
-	n := p.node(DocumentNode)
+	n := p.node(DocumentNode, "", "", "")
 	p.doc = n
 	p.doc = n
 	p.expect(yaml_DOCUMENT_START_EVENT)
 	p.expect(yaml_DOCUMENT_START_EVENT)
 	p.parseChild(n)
 	p.parseChild(n)
@@ -258,8 +264,7 @@ func (p *parser) document() *Node {
 }
 }
 
 
 func (p *parser) alias() *Node {
 func (p *parser) alias() *Node {
-	n := p.node(AliasNode)
-	n.Value = string(p.event.anchor)
+	n := p.node(AliasNode, "", "", string(p.event.anchor))
 	n.Alias = p.anchors[n.Value]
 	n.Alias = p.anchors[n.Value]
 	if n.Alias == nil {
 	if n.Alias == nil {
 		failf("unknown anchor '%s' referenced", n.Value)
 		failf("unknown anchor '%s' referenced", n.Value)
@@ -269,30 +274,39 @@ func (p *parser) alias() *Node {
 }
 }
 
 
 func (p *parser) scalar() *Node {
 func (p *parser) scalar() *Node {
-	n := p.node(ScalarNode)
-	n.Value = string(p.event.value)
-	n.Tag = string(p.event.tag)
-	style := p.event.scalar_style()
+	var parsedStyle = p.event.scalar_style()
+	var nodeStyle Style
 	switch {
 	switch {
-	case style&yaml_DOUBLE_QUOTED_SCALAR_STYLE != 0:
-		n.Style = DoubleQuotedStyle
-	case style&yaml_SINGLE_QUOTED_SCALAR_STYLE != 0:
-		n.Style = SingleQuotedStyle
-	case style&yaml_LITERAL_SCALAR_STYLE != 0:
-		n.Style = LiteralStyle
-	case style&yaml_FOLDED_SCALAR_STYLE != 0:
-		n.Style = FoldedStyle
+	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
 	p.anchor(n, p.event.anchor)
 	p.anchor(n, p.event.anchor)
 	p.expect(yaml_SCALAR_EVENT)
 	p.expect(yaml_SCALAR_EVENT)
 	return n
 	return n
 }
 }
 
 
 func (p *parser) sequence() *Node {
 func (p *parser) sequence() *Node {
-	n := p.node(SequenceNode)
-	n.Tag = string(p.event.tag)
+	n := p.node(SequenceNode, seqTag, string(p.event.tag), "")
 	if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 {
 	if p.event.sequence_style()&yaml_FLOW_SEQUENCE_STYLE != 0 {
-		n.Style = FlowStyle
+		n.Style |= FlowStyle
 	}
 	}
 	p.anchor(n, p.event.anchor)
 	p.anchor(n, p.event.anchor)
 	p.expect(yaml_SEQUENCE_START_EVENT)
 	p.expect(yaml_SEQUENCE_START_EVENT)
@@ -306,8 +320,7 @@ func (p *parser) sequence() *Node {
 }
 }
 
 
 func (p *parser) mapping() *Node {
 func (p *parser) mapping() *Node {
-	n := p.node(MappingNode)
-	n.Tag = string(p.event.tag)
+	n := p.node(MappingNode, mapTag, string(p.event.tag), "")
 	if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 {
 	if p.event.mapping_style()&yaml_FLOW_MAPPING_STYLE != 0 {
 		n.Style |= FlowStyle
 		n.Style |= FlowStyle
 	}
 	}
@@ -367,7 +380,7 @@ func (d *decoder) terror(n *Node, tag string, out reflect.Value) {
 		tag = n.Tag
 		tag = n.Tag
 	}
 	}
 	value := n.Value
 	value := n.Value
-	if tag != yaml_SEQ_TAG && tag != yaml_MAP_TAG {
+	if tag != seqTag && tag != mapTag {
 		if len(value) > 10 {
 		if len(value) > 10 {
 			value = " `" + value[:7] + "...`"
 			value = " `" + value[:7] + "...`"
 		} else {
 		} else {
@@ -414,7 +427,7 @@ func (d *decoder) callObsoleteUnmarshaler(n *Node, u obsoleteUnmarshaler) (good
 //
 //
 // If n holds a null value, prepare returns before doing anything.
 // 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) {
 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()) {
+	if n.ShortTag() == nullTag {
 		return out, false, false
 		return out, false, false
 	}
 	}
 	again := true
 	again := true
@@ -501,12 +514,12 @@ func resetMap(out reflect.Value) {
 func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 	var tag string
 	var tag string
 	var resolved interface{}
 	var resolved interface{}
-	if n.Tag == "" && !n.implicit() {
-		tag = yaml_STR_TAG
+	if n.indicatedString() {
+		tag = strTag
 		resolved = n.Value
 		resolved = n.Value
 	} else {
 	} else {
 		tag, resolved = resolve(n.Tag, n.Value)
 		tag, resolved = resolve(n.Tag, n.Value)
-		if tag == yaml_BINARY_TAG {
+		if tag == binaryTag {
 			data, err := base64.StdEncoding.DecodeString(resolved.(string))
 			data, err := base64.StdEncoding.DecodeString(resolved.(string))
 			if err != nil {
 			if err != nil {
 				failf("!!binary value contains invalid base64 data")
 				failf("!!binary value contains invalid base64 data")
@@ -533,7 +546,7 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 		u, ok := out.Addr().Interface().(encoding.TextUnmarshaler)
 		u, ok := out.Addr().Interface().(encoding.TextUnmarshaler)
 		if ok {
 		if ok {
 			var text []byte
 			var text []byte
-			if tag == yaml_BINARY_TAG {
+			if tag == binaryTag {
 				text = []byte(resolved.(string))
 				text = []byte(resolved.(string))
 			} else {
 			} else {
 				// We let any value be unmarshaled into TextUnmarshaler.
 				// We let any value be unmarshaled into TextUnmarshaler.
@@ -550,7 +563,7 @@ func (d *decoder) scalar(n *Node, out reflect.Value) bool {
 	}
 	}
 	switch out.Kind() {
 	switch out.Kind() {
 	case reflect.String:
 	case reflect.String:
-		if tag == yaml_BINARY_TAG {
+		if tag == binaryTag {
 			out.SetString(resolved.(string))
 			out.SetString(resolved.(string))
 			return true
 			return true
 		}
 		}
@@ -695,7 +708,7 @@ func (d *decoder) sequence(n *Node, out reflect.Value) (good bool) {
 		iface = out
 		iface = out
 		out = settableValueOf(make([]interface{}, l))
 		out = settableValueOf(make([]interface{}, l))
 	default:
 	default:
-		d.terror(n, yaml_SEQ_TAG, out)
+		d.terror(n, seqTag, out)
 		return false
 		return false
 	}
 	}
 	et := out.Type().Elem()
 	et := out.Type().Elem()
@@ -748,7 +761,7 @@ func (d *decoder) mapping(n *Node, out reflect.Value) (good bool) {
 		}
 		}
 		iface.Set(out)
 		iface.Set(out)
 	default:
 	default:
-		d.terror(n, yaml_MAP_TAG, out)
+		d.terror(n, mapTag, out)
 		return false
 		return false
 	}
 	}
 
 
@@ -800,7 +813,7 @@ func isStringMap(n *Node) bool {
 	}
 	}
 	l := len(n.Children)
 	l := len(n.Children)
 	for i := 0; i < l; i++ {
 	for i := 0; i < l; i++ {
-		if n.Children[i].LongTag() != yaml_STR_TAG {
+		if n.Children[i].ShortTag() != strTag {
 			return false
 			return false
 		}
 		}
 	}
 	}
@@ -897,5 +910,5 @@ func (d *decoder) merge(n *Node, out reflect.Value) {
 }
 }
 
 
 func isMerge(n *Node) bool {
 func isMerge(n *Node) bool {
-	return n.Kind == ScalarNode && n.Value == "<<" && (n.implicit() || n.Tag == yaml_MERGE_TAG)
+	return n.Kind == ScalarNode && n.Value == "<<" && (n.Tag == "" || n.Tag == "!" || shortTag(n.Tag) == mergeTag)
 }
 }

+ 3 - 0
decode_test.go

@@ -231,6 +231,9 @@ var unmarshalTests = []struct {
 	}, {
 	}, {
 		"a: {b: c}",
 		"a: {b: c}",
 		&struct{ A *struct{ B string } }{&struct{ B string }{"c"}},
 		&struct{ A *struct{ B string } }{&struct{ B string }{"c"}},
+	}, {
+		"a: 'null'",
+		&struct{ A *unmarshalerType }{&unmarshalerType{"null"}},
 	}, {
 	}, {
 		"a: {b: c}",
 		"a: {b: c}",
 		&struct{ A map[string]string }{map[string]string{"b": "c"}},
 		&struct{ A map[string]string }{map[string]string{"b": "c"}},

+ 48 - 11
encode.go

@@ -94,6 +94,7 @@ func (e *encoder) marshalDoc(tag string, in reflect.Value) {
 }
 }
 
 
 func (e *encoder) marshal(tag string, in reflect.Value) {
 func (e *encoder) marshal(tag string, in reflect.Value) {
+	tag = shortTag(tag)
 	if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
 	if !in.IsValid() || in.Kind() == reflect.Ptr && in.IsNil() {
 		e.nilv()
 		e.nilv()
 		return
 		return
@@ -267,7 +268,7 @@ func (e *encoder) stringv(tag string, in reflect.Value) {
 	canUsePlain := true
 	canUsePlain := true
 	switch {
 	switch {
 	case !utf8.ValidString(s):
 	case !utf8.ValidString(s):
-		if tag == yaml_BINARY_TAG {
+		if tag == binaryTag {
 			failf("explicitly tagged !!binary data must be base64-encoded")
 			failf("explicitly tagged !!binary data must be base64-encoded")
 		}
 		}
 		if tag != "" {
 		if tag != "" {
@@ -275,14 +276,14 @@ func (e *encoder) stringv(tag string, in reflect.Value) {
 		}
 		}
 		// It can't be encoded directly as YAML so use a binary tag
 		// It can't be encoded directly as YAML so use a binary tag
 		// and encode it as base64.
 		// and encode it as base64.
-		tag = yaml_BINARY_TAG
+		tag = binaryTag
 		s = encodeBase64(s)
 		s = encodeBase64(s)
 	case tag == "":
 	case tag == "":
 		// Check to see if it would resolve to a specific
 		// Check to see if it would resolve to a specific
 		// tag when encoded unquoted. If it doesn't,
 		// tag when encoded unquoted. If it doesn't,
 		// there's no need to quote it.
 		// there's no need to quote it.
 		rtag, _ := resolve("", s)
 		rtag, _ := resolve("", s)
-		canUsePlain = rtag == yaml_STR_TAG && !isBase60Float(s)
+		canUsePlain = rtag == strTag && !isBase60Float(s)
 	}
 	}
 	// Note: it's possible for user code to emit invalid YAML
 	// Note: it's possible for user code to emit invalid YAML
 	// if they explicitly specify a tag and a string containing
 	// if they explicitly specify a tag and a string containing
@@ -350,6 +351,9 @@ func (e *encoder) nilv() {
 func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, header, inline, footer []byte) {
 func (e *encoder) emitScalar(value, anchor, tag string, style yaml_scalar_style_t, header, inline, footer []byte) {
 	// TODO Kill this function. Replace all initialize calls by their underlining Go literals.
 	// TODO Kill this function. Replace all initialize calls by their underlining Go literals.
 	implicit := tag == ""
 	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.must(yaml_scalar_event_initialize(&e.event, []byte(anchor), []byte(tag), []byte(value), implicit, implicit, style))
 	e.event.header_comment = header
 	e.event.header_comment = header
 	e.event.inline_comment = inline
 	e.event.inline_comment = inline
@@ -362,6 +366,38 @@ func (e *encoder) nodev(in reflect.Value) {
 }
 }
 
 
 func (e *encoder) node(node *Node) {
 func (e *encoder) node(node *Node) {
+	// 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 rtag string
+	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 {
+			switch node.Kind {
+			case MappingNode:
+				rtag = mapTag
+			case SequenceNode:
+				rtag = seqTag
+			}
+			if rtag == stag {
+				tag = ""
+			}
+		}
+	}
+
 	switch node.Kind {
 	switch node.Kind {
 	case DocumentNode:
 	case DocumentNode:
 		yaml_document_start_event_initialize(&e.event, nil, nil, true)
 		yaml_document_start_event_initialize(&e.event, nil, nil, true)
@@ -379,7 +415,7 @@ func (e *encoder) node(node *Node) {
 		if node.Style&FlowStyle != 0 {
 		if node.Style&FlowStyle != 0 {
 			style = yaml_FLOW_SEQUENCE_STYLE
 			style = yaml_FLOW_SEQUENCE_STYLE
 		}
 		}
-		e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(node.Tag), node.implicit(), style))
+		e.must(yaml_sequence_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style))
 		e.event.header_comment = []byte(node.Header)
 		e.event.header_comment = []byte(node.Header)
 		e.emit()
 		e.emit()
 		for _, node := range node.Children {
 		for _, node := range node.Children {
@@ -395,7 +431,7 @@ func (e *encoder) node(node *Node) {
 		if node.Style&FlowStyle != 0 {
 		if node.Style&FlowStyle != 0 {
 			style = yaml_FLOW_MAPPING_STYLE
 			style = yaml_FLOW_MAPPING_STYLE
 		}
 		}
-		yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(node.Tag), node.implicit(), style)
+		yaml_mapping_start_event_initialize(&e.event, []byte(node.Anchor), []byte(tag), tag == "", style)
 		e.event.header_comment = []byte(node.Header)
 		e.event.header_comment = []byte(node.Header)
 		e.emit()
 		e.emit()
 
 
@@ -425,17 +461,18 @@ func (e *encoder) node(node *Node) {
 			style = yaml_LITERAL_SCALAR_STYLE
 			style = yaml_LITERAL_SCALAR_STYLE
 		case node.Style&FoldedStyle != 0:
 		case node.Style&FoldedStyle != 0:
 			style = yaml_FOLDED_SCALAR_STYLE
 			style = yaml_FOLDED_SCALAR_STYLE
-		}
-
-		if style == yaml_PLAIN_SCALAR_STYLE && strings.Contains(node.Value, "\n") {
+		case strings.Contains(node.Value, "\n"):
 			style = yaml_LITERAL_SCALAR_STYLE
 			style = yaml_LITERAL_SCALAR_STYLE
+		case forceQuoting:
+			style = yaml_DOUBLE_QUOTED_SCALAR_STYLE
 		}
 		}
-		e.emitScalar(node.Value, node.Anchor, node.Tag, style, []byte(node.Header), []byte(node.Inline), []byte(node.Footer))
+
+		e.emitScalar(node.Value, node.Anchor, tag, style, []byte(node.Header), []byte(node.Inline), []byte(node.Footer))
 
 
 		// TODO Check if binaries are being decoded into node.Value or not.
 		// TODO Check if binaries are being decoded into node.Value or not.
 		//switch {
 		//switch {
 		//if !utf8.ValidString(s) {
 		//if !utf8.ValidString(s) {
-		//	if tag == yaml_BINARY_TAG {
+		//	if tag == binaryTag {
 		//		failf("explicitly tagged !!binary data must be base64-encoded")
 		//		failf("explicitly tagged !!binary data must be base64-encoded")
 		//	}
 		//	}
 		//	if tag != "" {
 		//	if tag != "" {
@@ -443,7 +480,7 @@ func (e *encoder) node(node *Node) {
 		//	}
 		//	}
 		//	// It can't be encoded directly as YAML so use a binary tag
 		//	// It can't be encoded directly as YAML so use a binary tag
 		//	// and encode it as base64.
 		//	// and encode it as base64.
-		//	tag = yaml_BINARY_TAG
+		//	tag = binaryTag
 		//	s = encodeBase64(s)
 		//	s = encodeBase64(s)
 		//}
 		//}
 	}
 	}

File diff suppressed because it is too large
+ 340 - 172
node_test.go


+ 59 - 29
resolve.go

@@ -34,14 +34,14 @@ func init() {
 		tag string
 		tag string
 		l   []string
 		l   []string
 	}{
 	}{
-		{true, yaml_BOOL_TAG, []string{"true", "True", "TRUE"}},
-		{false, yaml_BOOL_TAG, []string{"false", "False", "FALSE"}},
-		{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{"<<"}},
+		{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{"<<"}},
 	}
 	}
 
 
 	m := resolveMap
 	m := resolveMap
@@ -52,11 +52,37 @@ 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:"
 const longTagPrefix = "tag:yaml.org,2002:"
 
 
 func shortTag(tag string) string {
 func shortTag(tag string) string {
-	// TODO This can easily be made faster and produce less garbage.
 	if strings.HasPrefix(tag, longTagPrefix) {
 	if strings.HasPrefix(tag, longTagPrefix) {
+		if stag, ok := shortTags[tag]; ok {
+			return stag
+		}
 		return "!!" + tag[len(longTagPrefix):]
 		return "!!" + tag[len(longTagPrefix):]
 	}
 	}
 	return tag
 	return tag
@@ -64,6 +90,9 @@ func shortTag(tag string) string {
 
 
 func longTag(tag string) string {
 func longTag(tag string) string {
 	if strings.HasPrefix(tag, "!!") {
 	if strings.HasPrefix(tag, "!!") {
+		if ltag, ok := longTags[tag]; ok {
+			return ltag
+		}
 		return longTagPrefix + tag[2:]
 		return longTagPrefix + tag[2:]
 	}
 	}
 	return tag
 	return tag
@@ -71,7 +100,7 @@ func longTag(tag string) string {
 
 
 func resolvableTag(tag string) bool {
 func resolvableTag(tag string) bool {
 	switch tag {
 	switch tag {
-	case "", yaml_STR_TAG, yaml_BOOL_TAG, yaml_INT_TAG, yaml_FLOAT_TAG, yaml_NULL_TAG, yaml_TIMESTAMP_TAG:
+	case "", strTag, boolTag, intTag, floatTag, nullTag, timestampTag:
 		return true
 		return true
 	}
 	}
 	return false
 	return false
@@ -80,23 +109,24 @@ func resolvableTag(tag string) bool {
 var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`)
 var yamlStyleFloat = regexp.MustCompile(`^[-+]?(\.[0-9]+|[0-9]+(\.[0-9]*)?)([eE][-+]?[0-9]+)?$`)
 
 
 func resolve(tag string, in string) (rtag string, out interface{}) {
 func resolve(tag string, in string) (rtag string, out interface{}) {
+	tag = shortTag(tag)
 	if !resolvableTag(tag) {
 	if !resolvableTag(tag) {
 		return tag, in
 		return tag, in
 	}
 	}
 
 
 	defer func() {
 	defer func() {
 		switch tag {
 		switch tag {
-		case "", rtag, yaml_STR_TAG, yaml_BINARY_TAG:
+		case "", rtag, strTag, binaryTag:
 			return
 			return
-		case yaml_FLOAT_TAG:
-			if rtag == yaml_INT_TAG {
+		case floatTag:
+			if rtag == intTag {
 				switch v := out.(type) {
 				switch v := out.(type) {
 				case int64:
 				case int64:
-					rtag = yaml_FLOAT_TAG
+					rtag = floatTag
 					out = float64(v)
 					out = float64(v)
 					return
 					return
 				case int:
 				case int:
-					rtag = yaml_FLOAT_TAG
+					rtag = floatTag
 					out = float64(v)
 					out = float64(v)
 					return
 					return
 				}
 				}
@@ -111,7 +141,7 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
 	if in != "" {
 	if in != "" {
 		hint = resolveTable[in[0]]
 		hint = resolveTable[in[0]]
 	}
 	}
-	if hint != 0 && tag != yaml_STR_TAG && tag != yaml_BINARY_TAG {
+	if hint != 0 && tag != strTag && tag != binaryTag {
 		// Handle things we can lookup in a map.
 		// Handle things we can lookup in a map.
 		if item, ok := resolveMap[in]; ok {
 		if item, ok := resolveMap[in]; ok {
 			return item.tag, item.value
 			return item.tag, item.value
@@ -129,17 +159,17 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
 			// Not in the map, so maybe a normal float.
 			// Not in the map, so maybe a normal float.
 			floatv, err := strconv.ParseFloat(in, 64)
 			floatv, err := strconv.ParseFloat(in, 64)
 			if err == nil {
 			if err == nil {
-				return yaml_FLOAT_TAG, floatv
+				return floatTag, floatv
 			}
 			}
 
 
 		case 'D', 'S':
 		case 'D', 'S':
 			// Int, float, or timestamp.
 			// Int, float, or timestamp.
 			// Only try values as a timestamp if the value is unquoted or there's an explicit
 			// Only try values as a timestamp if the value is unquoted or there's an explicit
 			// !!timestamp tag.
 			// !!timestamp tag.
-			if tag == "" || tag == yaml_TIMESTAMP_TAG {
+			if tag == "" || tag == timestampTag {
 				t, ok := parseTimestamp(in)
 				t, ok := parseTimestamp(in)
 				if ok {
 				if ok {
-					return yaml_TIMESTAMP_TAG, t
+					return timestampTag, t
 				}
 				}
 			}
 			}
 
 
@@ -147,41 +177,41 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
 			intv, err := strconv.ParseInt(plain, 0, 64)
 			intv, err := strconv.ParseInt(plain, 0, 64)
 			if err == nil {
 			if err == nil {
 				if intv == int64(int(intv)) {
 				if intv == int64(int(intv)) {
-					return yaml_INT_TAG, int(intv)
+					return intTag, int(intv)
 				} else {
 				} else {
-					return yaml_INT_TAG, intv
+					return intTag, intv
 				}
 				}
 			}
 			}
 			uintv, err := strconv.ParseUint(plain, 0, 64)
 			uintv, err := strconv.ParseUint(plain, 0, 64)
 			if err == nil {
 			if err == nil {
-				return yaml_INT_TAG, uintv
+				return intTag, uintv
 			}
 			}
 			if yamlStyleFloat.MatchString(plain) {
 			if yamlStyleFloat.MatchString(plain) {
 				floatv, err := strconv.ParseFloat(plain, 64)
 				floatv, err := strconv.ParseFloat(plain, 64)
 				if err == nil {
 				if err == nil {
-					return yaml_FLOAT_TAG, floatv
+					return floatTag, floatv
 				}
 				}
 			}
 			}
 			if strings.HasPrefix(plain, "0b") {
 			if strings.HasPrefix(plain, "0b") {
 				intv, err := strconv.ParseInt(plain[2:], 2, 64)
 				intv, err := strconv.ParseInt(plain[2:], 2, 64)
 				if err == nil {
 				if err == nil {
 					if intv == int64(int(intv)) {
 					if intv == int64(int(intv)) {
-						return yaml_INT_TAG, int(intv)
+						return intTag, int(intv)
 					} else {
 					} else {
-						return yaml_INT_TAG, intv
+						return intTag, intv
 					}
 					}
 				}
 				}
 				uintv, err := strconv.ParseUint(plain[2:], 2, 64)
 				uintv, err := strconv.ParseUint(plain[2:], 2, 64)
 				if err == nil {
 				if err == nil {
-					return yaml_INT_TAG, uintv
+					return intTag, uintv
 				}
 				}
 			} else if strings.HasPrefix(plain, "-0b") {
 			} else if strings.HasPrefix(plain, "-0b") {
 				intv, err := strconv.ParseInt("-" + plain[3:], 2, 64)
 				intv, err := strconv.ParseInt("-" + plain[3:], 2, 64)
 				if err == nil {
 				if err == nil {
 					if true || intv == int64(int(intv)) {
 					if true || intv == int64(int(intv)) {
-						return yaml_INT_TAG, int(intv)
+						return intTag, int(intv)
 					} else {
 					} else {
-						return yaml_INT_TAG, intv
+						return intTag, intv
 					}
 					}
 				}
 				}
 			}
 			}
@@ -189,7 +219,7 @@ func resolve(tag string, in string) (rtag string, out interface{}) {
 			panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")")
 			panic("internal error: missing handler for resolver table: " + string(rune(hint)) + " (with " + in + ")")
 		}
 		}
 	}
 	}
-	return yaml_STR_TAG, in
+	return strTag, in
 }
 }
 
 
 // encodeBase64 encodes s as base64 that is broken up into multiple lines
 // encodeBase64 encodes s as base64 that is broken up into multiple lines

Some files were not shown because too many files changed in this diff