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(lower, upper, 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) { switch { case p.On(bareKeyRune).RouteTo(startBareKey): default: p.UnexpectedInput("a valid key name") } } func startBareKey(p *parsekit.P) { 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(startKeyAssignment) } } func startKeyAssignment(p *parsekit.P) { if p.On(keyAssignment).Skip() { p.Emit(ItemAssignment, "=") p.RouteTo(startValue) } else { p.UnexpectedInput("a value assignment") } } // 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) { switch { case p.On(anyQuote).RouteTo(startString): default: p.UnexpectedInput("a value") } }