package parser import "github.com/mmakaay/toml/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.Sequence(optionalWhitespace, equal, optionalWhitespace) // 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.AnyOf(asciiLower, asciiUpper, digit, underscore, dash) 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.AnyOf(bareKeyRune, anyQuote) // 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. keySeparatordDot = c.Sequence(optionalWhitespace, dot, optionalWhitespace) ) func startKeyValuePair(p *parsekit.P) { p.On(whitespaceOrNewlines).Skip() switch { case p.On(hash).Stay(): p.RouteTo(startComment).ThenReturnHere() case p.On(startOfKey).RouteTo(startKey): default: p.RouteTo(endOfFile) // TODO Make end of file a Matcher, so this can be simpler. } } 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() { p.EmitLiteral(ItemKey) p.RouteTo(endOfKeyOrDot) } } func endOfKeyOrDot(p *parsekit.P) { if p.On(keySeparatordDot).Skip() { 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() { 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(anyQuote).RouteTo(startString) }