package parse import ( "git.makaay.nl/mauricem/go-parsekit/parse" "git.makaay.nl/mauricem/go-toml/ast" ) // The primary building block of a TOML document is the table. // Tables are filled with key/value pairs. var ( // 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(optionalBlanks, a.Equal, optionalBlanks) // 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. // // 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). bareKeyRune = c.Any(a.ASCIILower, a.ASCIIUpper, a.Digit, a.Underscore, a.Minus) bareKey = c.OneOrMore(bareKeyRune) // 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(optionalBlanks, a.Dot, optionalBlanks) ) func (t *parser) startKeyValuePair(p *parse.API) { key, ok := t.parseKey(p, ast.NewKey()) if ok && p.Handle(t.startAssignment) { if value, ok := t.parseValue(p); ok { err := t.doc.SetKeyValuePair(key, value) if err != nil { p.Error("%s", err) } else if !p.Accept(endOfLineOrComment) { p.Expected("end of line") } } } } // 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) parseKey(p *parse.API, key ast.Key) (ast.Key, bool) { var keyPart string var ok bool switch { case p.Accept(bareKey): keyPart, ok = p.Result().String(), true case p.Peek(a.SingleQuote): keyPart, ok = t.parseLiteralString("key", p) case p.Peek(a.DoubleQuote): keyPart, ok = t.parseBasicString("key", p) default: p.Expected("a key name") return nil, false } if !ok { return nil, false } key = append(key, keyPart) return t.parseEndOfKeyOrDot(p, key) } // 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) parseEndOfKeyOrDot(p *parse.API, key ast.Key) (ast.Key, bool) { if p.Accept(keySeparatorDot) { return t.parseKey(p, key) } return key, true } func (t *parser) startAssignment(p *parse.API) { if !p.Accept(keyAssignment) { p.Expected("a value assignment") } }