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.InOptionalBlanks(a.Equal) // 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). isBareKeyRune = func(b byte) bool { return ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '_' || b == '-') } bareKeyRune = a.ByteByCallback(isBareKeyRune) bareKey = a.BytesByCallback(isBareKeyRune) // 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.InOptionalBlanks(a.Dot) ) 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.SetError("%s", err) } else if !p.Skip(endOfLineOrComment) { p.Expected("end of line") } } else { // Should have been handled by the value parsing code. // This is a safety net. if !p.IsStoppedOrInError() { panic("Bug: value parsing did not return a successful value, neither an error") } } } } // 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 strType stringType var ok bool switch { case p.Accept(bareKey): keyPart, ok = p.Result.String(), true case p.Peek(detectString): keyPart, strType, ok = t.parseString(p) if strType != strTypeBasic && strType != strTypeLiteral { p.Expected("a key name") // TODO more specific error telling about the abuse of multi-line string? } 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.Skip(keySeparatorDot) { return t.parseKey(p, key) } return key, true } func (t *parser) startAssignment(p *parse.API) { if !p.Skip(keyAssignment) { p.Expected("a value assignment") } }