github.com/marinho/drone@v0.2.1-0.20140504195434-d3ba962e89a7/Godeps/_workspace/src/launchpad.net/goyaml/resolve.go (about)

     1  package goyaml
     2  
     3  import (
     4  	"math"
     5  	"strconv"
     6  	"strings"
     7  )
     8  
     9  // TODO: merge, timestamps, base 60 floats, omap.
    10  
    11  type resolveMapItem struct {
    12  	value interface{}
    13  	tag   string
    14  }
    15  
    16  var resolveTable = make([]byte, 256)
    17  var resolveMap = make(map[string]resolveMapItem)
    18  
    19  func init() {
    20  	t := resolveTable
    21  	t[int('+')] = 'S' // Sign
    22  	t[int('-')] = 'S'
    23  	for _, c := range "0123456789" {
    24  		t[int(c)] = 'D' // Digit
    25  	}
    26  	for _, c := range "yYnNtTfFoO~" {
    27  		t[int(c)] = 'M' // In map
    28  	}
    29  	t[int('.')] = '.' // Float (potentially in map)
    30  	t[int('<')] = '<' // Merge
    31  
    32  	var resolveMapList = []struct {
    33  		v   interface{}
    34  		tag string
    35  		l   []string
    36  	}{
    37  		{true, "!!bool", []string{"y", "Y", "yes", "Yes", "YES"}},
    38  		{true, "!!bool", []string{"true", "True", "TRUE"}},
    39  		{true, "!!bool", []string{"on", "On", "ON"}},
    40  		{false, "!!bool", []string{"n", "N", "no", "No", "NO"}},
    41  		{false, "!!bool", []string{"false", "False", "FALSE"}},
    42  		{false, "!!bool", []string{"off", "Off", "OFF"}},
    43  		{nil, "!!null", []string{"~", "null", "Null", "NULL"}},
    44  		{math.NaN(), "!!float", []string{".nan", ".NaN", ".NAN"}},
    45  		{math.Inf(+1), "!!float", []string{".inf", ".Inf", ".INF"}},
    46  		{math.Inf(+1), "!!float", []string{"+.inf", "+.Inf", "+.INF"}},
    47  		{math.Inf(-1), "!!float", []string{"-.inf", "-.Inf", "-.INF"}},
    48  	}
    49  
    50  	m := resolveMap
    51  	for _, item := range resolveMapList {
    52  		for _, s := range item.l {
    53  			m[s] = resolveMapItem{item.v, item.tag}
    54  		}
    55  	}
    56  }
    57  
    58  const longTagPrefix = "tag:yaml.org,2002:"
    59  
    60  func shortTag(tag string) string {
    61  	if strings.HasPrefix(tag, longTagPrefix) {
    62  		return "!!" + tag[len(longTagPrefix):]
    63  	}
    64  	return tag
    65  }
    66  
    67  func resolvableTag(tag string) bool {
    68  	switch tag {
    69  	case "", "!!str", "!!bool", "!!int", "!!float", "!!null":
    70  		return true
    71  	}
    72  	return false
    73  }
    74  
    75  func resolve(tag string, in string) (rtag string, out interface{}) {
    76  	tag = shortTag(tag)
    77  	if !resolvableTag(tag) {
    78  		return tag, in
    79  	}
    80  
    81  	defer func() {
    82  		if tag != "" && tag != rtag {
    83  			panic("Can't decode " + rtag + " '" + in + "' as a " + tag)
    84  		}
    85  	}()
    86  
    87  	if in == "" {
    88  		return "!!null", nil
    89  	}
    90  
    91  	c := resolveTable[in[0]]
    92  	if c == 0 {
    93  		// It's a string for sure. Nothing to do.
    94  		return "!!str", in
    95  	}
    96  
    97  	// Handle things we can lookup in a map.
    98  	if item, ok := resolveMap[in]; ok {
    99  		return item.tag, item.value
   100  	}
   101  
   102  	switch c {
   103  	case 'M':
   104  		// We've already checked the map above.
   105  
   106  	case '.':
   107  		// Not in the map, so maybe a normal float.
   108  		floatv, err := strconv.ParseFloat(in, 64)
   109  		if err == nil {
   110  			return "!!float", floatv
   111  		}
   112  	// XXX Handle base 60 floats here (WTF!)
   113  
   114  	case 'D', 'S':
   115  		// Int, float, or timestamp.
   116  		for i := 0; i != len(in); i++ {
   117  			if in[i] == '_' {
   118  				in = strings.Replace(in, "_", "", -1)
   119  				break
   120  			}
   121  		}
   122  		intv, err := strconv.ParseInt(in, 0, 64)
   123  		if err == nil {
   124  			if intv == int64(int(intv)) {
   125  				return "!!int", int(intv)
   126  			} else {
   127  				return "!!int", intv
   128  			}
   129  		}
   130  		floatv, err := strconv.ParseFloat(in, 64)
   131  		if err == nil {
   132  			return "!!float", floatv
   133  		}
   134  		if strings.HasPrefix(in, "0b") {
   135  			intv, err := strconv.ParseInt(in[2:], 2, 64)
   136  			if err == nil {
   137  				return "!!int", int(intv)
   138  			}
   139  		} else if strings.HasPrefix(in, "-0b") {
   140  			intv, err := strconv.ParseInt(in[3:], 2, 64)
   141  			if err == nil {
   142  				return "!!int", -int(intv)
   143  			}
   144  		}
   145  	// XXX Handle timestamps here.
   146  
   147  	case '<':
   148  		// XXX Handle merge (<<) here.
   149  
   150  	default:
   151  		panic("resolveTable item not yet handled: " +
   152  			string([]byte{c}) + " (with " + in + ")")
   153  	}
   154  	return "!!str", in
   155  }