gopkg.in/alecthomas/gometalinter.v3@v3.0.0/_linters/src/github.com/BurntSushi/toml/parse.go (about)

     1  package toml
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  	"time"
     8  	"unicode"
     9  	"unicode/utf8"
    10  )
    11  
    12  type parser struct {
    13  	mapping map[string]interface{}
    14  	types   map[string]tomlType
    15  	lx      *lexer
    16  
    17  	// A list of keys in the order that they appear in the TOML data.
    18  	ordered []Key
    19  
    20  	// the full key for the current hash in scope
    21  	context Key
    22  
    23  	// the base key name for everything except hashes
    24  	currentKey string
    25  
    26  	// rough approximation of line number
    27  	approxLine int
    28  
    29  	// A map of 'key.group.names' to whether they were created implicitly.
    30  	implicits map[string]bool
    31  }
    32  
    33  type parseError string
    34  
    35  func (pe parseError) Error() string {
    36  	return string(pe)
    37  }
    38  
    39  func parse(data string) (p *parser, err error) {
    40  	defer func() {
    41  		if r := recover(); r != nil {
    42  			var ok bool
    43  			if err, ok = r.(parseError); ok {
    44  				return
    45  			}
    46  			panic(r)
    47  		}
    48  	}()
    49  
    50  	p = &parser{
    51  		mapping:   make(map[string]interface{}),
    52  		types:     make(map[string]tomlType),
    53  		lx:        lex(data),
    54  		ordered:   make([]Key, 0),
    55  		implicits: make(map[string]bool),
    56  	}
    57  	for {
    58  		item := p.next()
    59  		if item.typ == itemEOF {
    60  			break
    61  		}
    62  		p.topLevel(item)
    63  	}
    64  
    65  	return p, nil
    66  }
    67  
    68  func (p *parser) panicf(format string, v ...interface{}) {
    69  	msg := fmt.Sprintf("Near line %d (last key parsed '%s'): %s",
    70  		p.approxLine, p.current(), fmt.Sprintf(format, v...))
    71  	panic(parseError(msg))
    72  }
    73  
    74  func (p *parser) next() item {
    75  	it := p.lx.nextItem()
    76  	if it.typ == itemError {
    77  		p.panicf("%s", it.val)
    78  	}
    79  	return it
    80  }
    81  
    82  func (p *parser) bug(format string, v ...interface{}) {
    83  	panic(fmt.Sprintf("BUG: "+format+"\n\n", v...))
    84  }
    85  
    86  func (p *parser) expect(typ itemType) item {
    87  	it := p.next()
    88  	p.assertEqual(typ, it.typ)
    89  	return it
    90  }
    91  
    92  func (p *parser) assertEqual(expected, got itemType) {
    93  	if expected != got {
    94  		p.bug("Expected '%s' but got '%s'.", expected, got)
    95  	}
    96  }
    97  
    98  func (p *parser) topLevel(item item) {
    99  	switch item.typ {
   100  	case itemCommentStart:
   101  		p.approxLine = item.line
   102  		p.expect(itemText)
   103  	case itemTableStart:
   104  		kg := p.next()
   105  		p.approxLine = kg.line
   106  
   107  		var key Key
   108  		for ; kg.typ != itemTableEnd && kg.typ != itemEOF; kg = p.next() {
   109  			key = append(key, p.keyString(kg))
   110  		}
   111  		p.assertEqual(itemTableEnd, kg.typ)
   112  
   113  		p.establishContext(key, false)
   114  		p.setType("", tomlHash)
   115  		p.ordered = append(p.ordered, key)
   116  	case itemArrayTableStart:
   117  		kg := p.next()
   118  		p.approxLine = kg.line
   119  
   120  		var key Key
   121  		for ; kg.typ != itemArrayTableEnd && kg.typ != itemEOF; kg = p.next() {
   122  			key = append(key, p.keyString(kg))
   123  		}
   124  		p.assertEqual(itemArrayTableEnd, kg.typ)
   125  
   126  		p.establishContext(key, true)
   127  		p.setType("", tomlArrayHash)
   128  		p.ordered = append(p.ordered, key)
   129  	case itemKeyStart:
   130  		kname := p.next()
   131  		p.approxLine = kname.line
   132  		p.currentKey = p.keyString(kname)
   133  
   134  		val, typ := p.value(p.next())
   135  		p.setValue(p.currentKey, val)
   136  		p.setType(p.currentKey, typ)
   137  		p.ordered = append(p.ordered, p.context.add(p.currentKey))
   138  		p.currentKey = ""
   139  	default:
   140  		p.bug("Unexpected type at top level: %s", item.typ)
   141  	}
   142  }
   143  
   144  // Gets a string for a key (or part of a key in a table name).
   145  func (p *parser) keyString(it item) string {
   146  	switch it.typ {
   147  	case itemText:
   148  		return it.val
   149  	case itemString, itemMultilineString,
   150  		itemRawString, itemRawMultilineString:
   151  		s, _ := p.value(it)
   152  		return s.(string)
   153  	default:
   154  		p.bug("Unexpected key type: %s", it.typ)
   155  		panic("unreachable")
   156  	}
   157  }
   158  
   159  // value translates an expected value from the lexer into a Go value wrapped
   160  // as an empty interface.
   161  func (p *parser) value(it item) (interface{}, tomlType) {
   162  	switch it.typ {
   163  	case itemString:
   164  		return p.replaceEscapes(it.val), p.typeOfPrimitive(it)
   165  	case itemMultilineString:
   166  		trimmed := stripFirstNewline(stripEscapedWhitespace(it.val))
   167  		return p.replaceEscapes(trimmed), p.typeOfPrimitive(it)
   168  	case itemRawString:
   169  		return it.val, p.typeOfPrimitive(it)
   170  	case itemRawMultilineString:
   171  		return stripFirstNewline(it.val), p.typeOfPrimitive(it)
   172  	case itemBool:
   173  		switch it.val {
   174  		case "true":
   175  			return true, p.typeOfPrimitive(it)
   176  		case "false":
   177  			return false, p.typeOfPrimitive(it)
   178  		}
   179  		p.bug("Expected boolean value, but got '%s'.", it.val)
   180  	case itemInteger:
   181  		if !numUnderscoresOK(it.val) {
   182  			p.panicf("Invalid integer %q: underscores must be surrounded by digits",
   183  				it.val)
   184  		}
   185  		val := strings.Replace(it.val, "_", "", -1)
   186  		num, err := strconv.ParseInt(val, 10, 64)
   187  		if err != nil {
   188  			// Distinguish integer values. Normally, it'd be a bug if the lexer
   189  			// provides an invalid integer, but it's possible that the number is
   190  			// out of range of valid values (which the lexer cannot determine).
   191  			// So mark the former as a bug but the latter as a legitimate user
   192  			// error.
   193  			if e, ok := err.(*strconv.NumError); ok &&
   194  				e.Err == strconv.ErrRange {
   195  
   196  				p.panicf("Integer '%s' is out of the range of 64-bit "+
   197  					"signed integers.", it.val)
   198  			} else {
   199  				p.bug("Expected integer value, but got '%s'.", it.val)
   200  			}
   201  		}
   202  		return num, p.typeOfPrimitive(it)
   203  	case itemFloat:
   204  		parts := strings.FieldsFunc(it.val, func(r rune) bool {
   205  			switch r {
   206  			case '.', 'e', 'E':
   207  				return true
   208  			}
   209  			return false
   210  		})
   211  		for _, part := range parts {
   212  			if !numUnderscoresOK(part) {
   213  				p.panicf("Invalid float %q: underscores must be "+
   214  					"surrounded by digits", it.val)
   215  			}
   216  		}
   217  		if !numPeriodsOK(it.val) {
   218  			// As a special case, numbers like '123.' or '1.e2',
   219  			// which are valid as far as Go/strconv are concerned,
   220  			// must be rejected because TOML says that a fractional
   221  			// part consists of '.' followed by 1+ digits.
   222  			p.panicf("Invalid float %q: '.' must be followed "+
   223  				"by one or more digits", it.val)
   224  		}
   225  		val := strings.Replace(it.val, "_", "", -1)
   226  		num, err := strconv.ParseFloat(val, 64)
   227  		if err != nil {
   228  			if e, ok := err.(*strconv.NumError); ok &&
   229  				e.Err == strconv.ErrRange {
   230  
   231  				p.panicf("Float '%s' is out of the range of 64-bit "+
   232  					"IEEE-754 floating-point numbers.", it.val)
   233  			} else {
   234  				p.panicf("Invalid float value: %q", it.val)
   235  			}
   236  		}
   237  		return num, p.typeOfPrimitive(it)
   238  	case itemDatetime:
   239  		var t time.Time
   240  		var ok bool
   241  		var err error
   242  		for _, format := range []string{
   243  			"2006-01-02T15:04:05Z07:00",
   244  			"2006-01-02T15:04:05",
   245  			"2006-01-02",
   246  		} {
   247  			t, err = time.ParseInLocation(format, it.val, time.Local)
   248  			if err == nil {
   249  				ok = true
   250  				break
   251  			}
   252  		}
   253  		if !ok {
   254  			p.panicf("Invalid TOML Datetime: %q.", it.val)
   255  		}
   256  		return t, p.typeOfPrimitive(it)
   257  	case itemArray:
   258  		array := make([]interface{}, 0)
   259  		types := make([]tomlType, 0)
   260  
   261  		for it = p.next(); it.typ != itemArrayEnd; it = p.next() {
   262  			if it.typ == itemCommentStart {
   263  				p.expect(itemText)
   264  				continue
   265  			}
   266  
   267  			val, typ := p.value(it)
   268  			array = append(array, val)
   269  			types = append(types, typ)
   270  		}
   271  		return array, p.typeOfArray(types)
   272  	case itemInlineTableStart:
   273  		var (
   274  			hash         = make(map[string]interface{})
   275  			outerContext = p.context
   276  			outerKey     = p.currentKey
   277  		)
   278  
   279  		p.context = append(p.context, p.currentKey)
   280  		p.currentKey = ""
   281  		for it := p.next(); it.typ != itemInlineTableEnd; it = p.next() {
   282  			if it.typ != itemKeyStart {
   283  				p.bug("Expected key start but instead found %q, around line %d",
   284  					it.val, p.approxLine)
   285  			}
   286  			if it.typ == itemCommentStart {
   287  				p.expect(itemText)
   288  				continue
   289  			}
   290  
   291  			// retrieve key
   292  			k := p.next()
   293  			p.approxLine = k.line
   294  			kname := p.keyString(k)
   295  
   296  			// retrieve value
   297  			p.currentKey = kname
   298  			val, typ := p.value(p.next())
   299  			// make sure we keep metadata up to date
   300  			p.setType(kname, typ)
   301  			p.ordered = append(p.ordered, p.context.add(p.currentKey))
   302  			hash[kname] = val
   303  		}
   304  		p.context = outerContext
   305  		p.currentKey = outerKey
   306  		return hash, tomlHash
   307  	}
   308  	p.bug("Unexpected value type: %s", it.typ)
   309  	panic("unreachable")
   310  }
   311  
   312  // numUnderscoresOK checks whether each underscore in s is surrounded by
   313  // characters that are not underscores.
   314  func numUnderscoresOK(s string) bool {
   315  	accept := false
   316  	for _, r := range s {
   317  		if r == '_' {
   318  			if !accept {
   319  				return false
   320  			}
   321  			accept = false
   322  			continue
   323  		}
   324  		accept = true
   325  	}
   326  	return accept
   327  }
   328  
   329  // numPeriodsOK checks whether every period in s is followed by a digit.
   330  func numPeriodsOK(s string) bool {
   331  	period := false
   332  	for _, r := range s {
   333  		if period && !isDigit(r) {
   334  			return false
   335  		}
   336  		period = r == '.'
   337  	}
   338  	return !period
   339  }
   340  
   341  // establishContext sets the current context of the parser,
   342  // where the context is either a hash or an array of hashes. Which one is
   343  // set depends on the value of the `array` parameter.
   344  //
   345  // Establishing the context also makes sure that the key isn't a duplicate, and
   346  // will create implicit hashes automatically.
   347  func (p *parser) establishContext(key Key, array bool) {
   348  	var ok bool
   349  
   350  	// Always start at the top level and drill down for our context.
   351  	hashContext := p.mapping
   352  	keyContext := make(Key, 0)
   353  
   354  	// We only need implicit hashes for key[0:-1]
   355  	for _, k := range key[0 : len(key)-1] {
   356  		_, ok = hashContext[k]
   357  		keyContext = append(keyContext, k)
   358  
   359  		// No key? Make an implicit hash and move on.
   360  		if !ok {
   361  			p.addImplicit(keyContext)
   362  			hashContext[k] = make(map[string]interface{})
   363  		}
   364  
   365  		// If the hash context is actually an array of tables, then set
   366  		// the hash context to the last element in that array.
   367  		//
   368  		// Otherwise, it better be a table, since this MUST be a key group (by
   369  		// virtue of it not being the last element in a key).
   370  		switch t := hashContext[k].(type) {
   371  		case []map[string]interface{}:
   372  			hashContext = t[len(t)-1]
   373  		case map[string]interface{}:
   374  			hashContext = t
   375  		default:
   376  			p.panicf("Key '%s' was already created as a hash.", keyContext)
   377  		}
   378  	}
   379  
   380  	p.context = keyContext
   381  	if array {
   382  		// If this is the first element for this array, then allocate a new
   383  		// list of tables for it.
   384  		k := key[len(key)-1]
   385  		if _, ok := hashContext[k]; !ok {
   386  			hashContext[k] = make([]map[string]interface{}, 0, 5)
   387  		}
   388  
   389  		// Add a new table. But make sure the key hasn't already been used
   390  		// for something else.
   391  		if hash, ok := hashContext[k].([]map[string]interface{}); ok {
   392  			hashContext[k] = append(hash, make(map[string]interface{}))
   393  		} else {
   394  			p.panicf("Key '%s' was already created and cannot be used as "+
   395  				"an array.", keyContext)
   396  		}
   397  	} else {
   398  		p.setValue(key[len(key)-1], make(map[string]interface{}))
   399  	}
   400  	p.context = append(p.context, key[len(key)-1])
   401  }
   402  
   403  // setValue sets the given key to the given value in the current context.
   404  // It will make sure that the key hasn't already been defined, account for
   405  // implicit key groups.
   406  func (p *parser) setValue(key string, value interface{}) {
   407  	var tmpHash interface{}
   408  	var ok bool
   409  
   410  	hash := p.mapping
   411  	keyContext := make(Key, 0)
   412  	for _, k := range p.context {
   413  		keyContext = append(keyContext, k)
   414  		if tmpHash, ok = hash[k]; !ok {
   415  			p.bug("Context for key '%s' has not been established.", keyContext)
   416  		}
   417  		switch t := tmpHash.(type) {
   418  		case []map[string]interface{}:
   419  			// The context is a table of hashes. Pick the most recent table
   420  			// defined as the current hash.
   421  			hash = t[len(t)-1]
   422  		case map[string]interface{}:
   423  			hash = t
   424  		default:
   425  			p.bug("Expected hash to have type 'map[string]interface{}', but "+
   426  				"it has '%T' instead.", tmpHash)
   427  		}
   428  	}
   429  	keyContext = append(keyContext, key)
   430  
   431  	if _, ok := hash[key]; ok {
   432  		// Typically, if the given key has already been set, then we have
   433  		// to raise an error since duplicate keys are disallowed. However,
   434  		// it's possible that a key was previously defined implicitly. In this
   435  		// case, it is allowed to be redefined concretely. (See the
   436  		// `tests/valid/implicit-and-explicit-after.toml` test in `toml-test`.)
   437  		//
   438  		// But we have to make sure to stop marking it as an implicit. (So that
   439  		// another redefinition provokes an error.)
   440  		//
   441  		// Note that since it has already been defined (as a hash), we don't
   442  		// want to overwrite it. So our business is done.
   443  		if p.isImplicit(keyContext) {
   444  			p.removeImplicit(keyContext)
   445  			return
   446  		}
   447  
   448  		// Otherwise, we have a concrete key trying to override a previous
   449  		// key, which is *always* wrong.
   450  		p.panicf("Key '%s' has already been defined.", keyContext)
   451  	}
   452  	hash[key] = value
   453  }
   454  
   455  // setType sets the type of a particular value at a given key.
   456  // It should be called immediately AFTER setValue.
   457  //
   458  // Note that if `key` is empty, then the type given will be applied to the
   459  // current context (which is either a table or an array of tables).
   460  func (p *parser) setType(key string, typ tomlType) {
   461  	keyContext := make(Key, 0, len(p.context)+1)
   462  	for _, k := range p.context {
   463  		keyContext = append(keyContext, k)
   464  	}
   465  	if len(key) > 0 { // allow type setting for hashes
   466  		keyContext = append(keyContext, key)
   467  	}
   468  	p.types[keyContext.String()] = typ
   469  }
   470  
   471  // addImplicit sets the given Key as having been created implicitly.
   472  func (p *parser) addImplicit(key Key) {
   473  	p.implicits[key.String()] = true
   474  }
   475  
   476  // removeImplicit stops tagging the given key as having been implicitly
   477  // created.
   478  func (p *parser) removeImplicit(key Key) {
   479  	p.implicits[key.String()] = false
   480  }
   481  
   482  // isImplicit returns true if the key group pointed to by the key was created
   483  // implicitly.
   484  func (p *parser) isImplicit(key Key) bool {
   485  	return p.implicits[key.String()]
   486  }
   487  
   488  // current returns the full key name of the current context.
   489  func (p *parser) current() string {
   490  	if len(p.currentKey) == 0 {
   491  		return p.context.String()
   492  	}
   493  	if len(p.context) == 0 {
   494  		return p.currentKey
   495  	}
   496  	return fmt.Sprintf("%s.%s", p.context, p.currentKey)
   497  }
   498  
   499  func stripFirstNewline(s string) string {
   500  	if len(s) == 0 || s[0] != '\n' {
   501  		return s
   502  	}
   503  	return s[1:]
   504  }
   505  
   506  func stripEscapedWhitespace(s string) string {
   507  	esc := strings.Split(s, "\\\n")
   508  	if len(esc) > 1 {
   509  		for i := 1; i < len(esc); i++ {
   510  			esc[i] = strings.TrimLeftFunc(esc[i], unicode.IsSpace)
   511  		}
   512  	}
   513  	return strings.Join(esc, "")
   514  }
   515  
   516  func (p *parser) replaceEscapes(str string) string {
   517  	var replaced []rune
   518  	s := []byte(str)
   519  	r := 0
   520  	for r < len(s) {
   521  		if s[r] != '\\' {
   522  			c, size := utf8.DecodeRune(s[r:])
   523  			r += size
   524  			replaced = append(replaced, c)
   525  			continue
   526  		}
   527  		r += 1
   528  		if r >= len(s) {
   529  			p.bug("Escape sequence at end of string.")
   530  			return ""
   531  		}
   532  		switch s[r] {
   533  		default:
   534  			p.bug("Expected valid escape code after \\, but got %q.", s[r])
   535  			return ""
   536  		case 'b':
   537  			replaced = append(replaced, rune(0x0008))
   538  			r += 1
   539  		case 't':
   540  			replaced = append(replaced, rune(0x0009))
   541  			r += 1
   542  		case 'n':
   543  			replaced = append(replaced, rune(0x000A))
   544  			r += 1
   545  		case 'f':
   546  			replaced = append(replaced, rune(0x000C))
   547  			r += 1
   548  		case 'r':
   549  			replaced = append(replaced, rune(0x000D))
   550  			r += 1
   551  		case '"':
   552  			replaced = append(replaced, rune(0x0022))
   553  			r += 1
   554  		case '\\':
   555  			replaced = append(replaced, rune(0x005C))
   556  			r += 1
   557  		case 'u':
   558  			// At this point, we know we have a Unicode escape of the form
   559  			// `uXXXX` at [r, r+5). (Because the lexer guarantees this
   560  			// for us.)
   561  			escaped := p.asciiEscapeToUnicode(s[r+1 : r+5])
   562  			replaced = append(replaced, escaped)
   563  			r += 5
   564  		case 'U':
   565  			// At this point, we know we have a Unicode escape of the form
   566  			// `uXXXX` at [r, r+9). (Because the lexer guarantees this
   567  			// for us.)
   568  			escaped := p.asciiEscapeToUnicode(s[r+1 : r+9])
   569  			replaced = append(replaced, escaped)
   570  			r += 9
   571  		}
   572  	}
   573  	return string(replaced)
   574  }
   575  
   576  func (p *parser) asciiEscapeToUnicode(bs []byte) rune {
   577  	s := string(bs)
   578  	hex, err := strconv.ParseUint(strings.ToLower(s), 16, 32)
   579  	if err != nil {
   580  		p.bug("Could not parse '%s' as a hexadecimal number, but the "+
   581  			"lexer claims it's OK: %s", s, err)
   582  	}
   583  	if !utf8.ValidRune(rune(hex)) {
   584  		p.panicf("Escaped character '\\u%s' is not valid UTF-8.", s)
   585  	}
   586  	return rune(hex)
   587  }
   588  
   589  func isStringType(ty itemType) bool {
   590  	return ty == itemString || ty == itemMultilineString ||
   591  		ty == itemRawString || ty == itemRawMultilineString
   592  }