go-toml/keyvaluepair.go

117 lines
3.7 KiB
Go

package parser
import (
"git.makaay.nl/mauricem/go-parsekit/parse"
)
// The primary building block of a TOML document is the key/value pair.
var (
dropWhitespace = m.Drop(a.Whitespace.Optional())
dropBlanks = m.Drop(a.Blanks.Optional())
// Keys are on the left of the equals sign and values are on the right.
// Blank 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(dropBlanks, a.Equal, dropBlanks)
// 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. Blanks
// around dot-separated parts are ignored, however, best practice is to
// not use any extraneous blanks.
keySeparatorDot = c.Seq(dropBlanks, a.Dot, dropBlanks)
)
func (t *parser) startKeyValuePair(p *parse.API) {
for {
p.Accept(dropWhitespace)
switch {
case p.Peek(a.Hash):
p.Handle(t.startComment)
case p.Peek(startOfKey):
p.Handle(t.startKey, t.startAssignment, t.startValue)
default:
p.ExpectEndOfFile()
return
}
if p.IsStoppedOrInError() {
return
}
}
}
// 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.
//
// 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).
func (t *parser) startKey(p *parse.API) {
endFunc := func(str string) {
t.emitCommand(cKey, str)
p.Handle(t.endOfKeyOrDot)
}
switch {
case p.Accept(bareKey):
endFunc(p.Result().String())
case p.Peek(a.SingleQuote):
if str, ok := t.parseLiteralString("key", p); ok {
endFunc(str)
}
case p.Peek(a.DoubleQuote):
if str, ok := t.parseBasicString("key", p); ok {
endFunc(str)
}
default:
p.Expected("a key name")
}
}
// 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.
func (t *parser) endOfKeyOrDot(p *parse.API) {
if p.Accept(keySeparatorDot) {
t.emitCommand(cNewKeyLvl)
p.Handle(t.startKey)
}
}
func (t *parser) startAssignment(p *parse.API) {
if p.Accept(keyAssignment) {
t.emitCommand(cAssign)
} else {
p.Expected("a value assignment")
}
}
// Values must be of the following types: String, Integer, Float, Boolean,
// Datetime, Array, or Inline Table. Unspecified values are invalid.
func (t *parser) startValue(p *parse.API) {
if p.Peek(c.Any(a.SingleQuote, a.DoubleQuote)) {
p.Handle(t.startString)
} else {
p.Expected("a value")
}
}