yaml/resolve.go

138 строки
3.4 KiB
Go
Исходник Обычный вид История

2011-01-05 16:17:34 +03:00
package goyaml
import (
"strconv"
"strings"
"math"
)
// TODO: Support merge, timestamps, and base 60 floats.
type stdTag int
var StrTag = stdTag(1)
var BoolTag = stdTag(2)
var IntTag = stdTag(3)
var FloatTag = stdTag(4)
func (t stdTag) String() string {
switch t {
case StrTag:
return "tag:yaml.org,2002:str"
case BoolTag:
return "tag:yaml.org,2002:bool"
case IntTag:
return "tag:yaml.org,2002:int"
case FloatTag:
return "tag:yaml.org,2002:float"
default:
panic("Internal error: missing tag case")
}
return ""
}
type resolveMapItem struct {
value interface{}
tag stdTag
}
var resolveTable = make([]byte, 256)
var resolveMap = make(map[string]resolveMapItem)
func init() {
t := resolveTable
t[int('+')] = 'S' // Sign
t[int('-')] = 'S'
for _, c := range "0123456789" {
t[int(c)] = 'D' // Digit
}
for _, c := range "yYnNtTfFoO" {
t[int(c)] = 'M' // In map
}
t[int('.')] = '.' // Float (potentially in map)
t[int('<')] = '<' // Merge
var resolveMapList = []struct{v interface{}; tag stdTag; l []string} {
{true, BoolTag, []string{"y", "Y", "yes", "Yes", "YES"}},
{true, BoolTag, []string{"true", "True", "TRUE"}},
{true, BoolTag, []string{"on", "On", "ON"}},
{false, BoolTag, []string{"n", "N", "no", "No", "NO"}},
{false, BoolTag, []string{"false", "False", "FALSE"}},
{false, BoolTag, []string{"off", "Off", "OFF"}},
{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"}},
}
m := resolveMap
for _, item := range resolveMapList {
for _, s := range item.l {
m[s] = resolveMapItem{item.v, item.tag}
}
}
}
func resolve(in string) (out interface{}, tag stdTag) {
if in == "" {
return in, tag
}
c := resolveTable[in[0]]
if c == 0 {
// It's a string for sure. Nothing to do.
return in, StrTag
}
// Handle things we can lookup in a map.
if item, ok := resolveMap[in]; ok {
return item.value, item.tag
}
switch c {
case 'M':
// We've already checked the map above.
case '.':
// Not in the map, so maybe a normal float.
floatv, err := strconv.Atof(in)
if err == nil {
return floatv, FloatTag
}
// XXX Handle base 60 floats here.
case 'D', 'S':
// Int, float, or timestamp.
for i := 0; i != len(in); i++ {
if in[i] == '_' {
in = strings.Replace(in, "_", "", -1)
break
}
}
intv, err := strconv.Btoi64(in, 0)
if err == nil {
if intv == int64(int(intv)) {
return int(intv), IntTag
} else {
return intv, IntTag
}
}
floatv, err := strconv.Atof(in)
if err == nil {
return floatv, FloatTag
}
// XXX Handle timestamps here.
case '<':
// XXX Handle merge (<<) here.
default:
panic("resolveTable item not yet handled: " +
string([]byte{c}) + " (with " + in +")")
}
return in, StrTag
}