go-toml/keyvaluepair.go

81 lines
2.6 KiB
Go

package parser
import "git.makaay.nl/mauricem/go-parsekit"
// The primary building block of a TOML document is the key/value pair.
var (
// Keys are on the left of the equals sign and values are on the right.
// Whitespace is ignored around key names and values. The key, equals
// sign, and value must be on the same line (though some values can be
// broken over multiple lines).
keyAssignment = c.Seq(c.Opt(a.Whitespace), a.Equal, c.Opt(a.Whitespace))
// A key may be either bare, quoted or dotted. Bare keys may only
// contain ASCII letters, ASCII digits, underscores, and dashes
// (A-Za-z0-9_-). Note that bare keys are allowed to be composed of only
// ASCII digits, e.g. 1234, but are always interpreted as strings.
bareKeyRune = c.Any(a.ASCIILower, a.ASCIIUpper, a.Digit, a.Underscore, a.Minus)
bareKey = c.OneOrMore(bareKeyRune)
// Quoted keys follow the exact same rules as either basic strings or
// literal strings and allow you to use a much broader set of key names.
// Best practice is to use bare keys except when absolutely necessary.
// A bare key must be non-empty, but an empty quoted key is allowed
// (though discouraged).
startOfKey = c.Any(bareKeyRune, a.SingleQuote, a.DoubleQuote)
// Dotted keys are a sequence of bare or quoted keys joined with a dot.
// This allows for grouping similar properties together. Whitespace
// around dot-separated parts is ignored, however, best practice is to
// not use any extraneous whitespace.
keySeparatorDot = c.Seq(c.Opt(a.Whitespace), a.Dot, c.Opt(a.Whitespace))
)
func startKeyValuePair(p *parsekit.P) {
switch {
case p.On(a.WhitespaceAndNewlines).Skip().RouteRepeat().End():
case p.On(a.Hash).RouteTo(startComment).ThenReturnHere().End():
case p.On(startOfKey).RouteTo(startKey).End():
default:
p.ExpectEndOfFile()
}
}
func startKey(p *parsekit.P) {
p.Expects("a key name")
p.On(bareKeyRune).RouteTo(startBareKey)
}
func startBareKey(p *parsekit.P) {
p.Expects("a bare key name")
if p.On(bareKey).Accept().End() {
p.EmitLiteral(ItemKey)
p.RouteTo(endOfKeyOrDot)
}
}
func endOfKeyOrDot(p *parsekit.P) {
if p.On(keySeparatorDot).Skip().End() {
p.Emit(ItemKeyDot, ".")
p.RouteTo(startKey)
} else {
p.RouteTo(startAssignment)
}
}
func startAssignment(p *parsekit.P) {
p.Expects("a value assignment")
if p.On(keyAssignment).Skip().End() {
p.Emit(ItemAssignment, "=")
p.RouteTo(startValue)
}
}
// Values must be of the following types: String, Integer, Float, Boolean,
// Datetime, Array, or Inline Table. Unspecified values are invalid.
func startValue(p *parsekit.P) {
p.Expects("a value")
p.On(c.Any(a.SingleQuote, a.DoubleQuote)).RouteTo(startString)
}