feat: allow newlines and trailing commas in inline tables

TOML v1.1.0 relaxes inline table syntax to allow newlines, comments,
and trailing commas, matching the existing behavior of arrays.
This commit is contained in:
João Fernandes
2026-02-11 11:10:20 +00:00
parent dd7970eb93
commit 517ceb4eb8
4 changed files with 213 additions and 36 deletions
+47 -13
View File
@@ -460,12 +460,14 @@ func (p *Parser) parseLiteralString(b []byte) ([]byte, []byte, []byte, error) {
return v, v[1 : len(v)-1], rest, nil
}
//nolint:funlen,cyclop
func (p *Parser) parseInlineTable(b []byte) (reference, []byte, error) {
// inline-table = inline-table-open [ inline-table-keyvals ] inline-table-close
// inline-table-open = %x7B ws ; {
// inline-table-close = ws %x7D ; }
// inline-table-sep = ws %x2C ws ; , Comma
// inline-table-keyvals = keyval [ inline-table-sep inline-table-keyvals ]
tableStart := b
parent := p.builder.Push(Node{
Kind: InlineTable,
Raw: p.rangeOfToken(b[:1], b[1:]),
@@ -473,45 +475,77 @@ func (p *Parser) parseInlineTable(b []byte) (reference, []byte, error) {
first := true
var child reference
lastChild := invalidReference
addChild := func(ref reference) {
if lastChild == invalidReference {
p.builder.AttachChild(parent, ref)
} else {
p.builder.Chain(lastChild, ref)
}
lastChild = ref
}
b = b[1:]
var err error
for len(b) > 0 {
previousB := b
b = p.parseWhitespace(b)
var cref reference
cref, b, err = p.parseOptionalWhitespaceCommentNewline(b)
if err != nil {
return parent, nil, err
}
if cref != invalidReference {
addChild(cref)
}
if len(b) == 0 {
return parent, nil, NewParserError(previousB[:1], "inline table is incomplete")
return parent, nil, NewParserError(tableStart[:1], "inline table is incomplete")
}
if b[0] == '}' {
break
}
if !first {
b, err = expect(',', b)
if b[0] == ',' {
if first {
return parent, nil, NewParserError(b[0:1], "inline table cannot start with comma")
}
b = b[1:]
cref, b, err = p.parseOptionalWhitespaceCommentNewline(b)
if err != nil {
return parent, nil, err
}
b = p.parseWhitespace(b)
if cref != invalidReference {
addChild(cref)
}
} else if !first {
return parent, nil, NewParserError(b[0:1], "inline table entries must be separated by commas")
}
// trailing comma: if '}' follows, stop
if len(b) > 0 && b[0] == '}' {
break
}
var kv reference
kv, b, err = p.parseKeyval(b)
if err != nil {
return parent, nil, err
}
if first {
p.builder.AttachChild(parent, kv)
} else {
p.builder.Chain(child, kv)
addChild(kv)
cref, b, err = p.parseOptionalWhitespaceCommentNewline(b)
if err != nil {
return parent, nil, err
}
if cref != invalidReference {
addChild(cref)
}
child = kv
first = false
}
+81
View File
@@ -331,6 +331,87 @@ func TestParser_AST(t *testing.T) {
},
},
},
{
desc: "multiline inline table",
input: "name = {\n first = \"Tom\",\n last = \"Preston-Werner\"\n}",
ast: astNode{
Kind: KeyValue,
Children: []astNode{
{
Kind: InlineTable,
Children: []astNode{
{
Kind: KeyValue,
Children: []astNode{
{Kind: String, Data: []byte(`Tom`)},
{Kind: Key, Data: []byte(`first`)},
},
},
{
Kind: KeyValue,
Children: []astNode{
{Kind: String, Data: []byte(`Preston-Werner`)},
{Kind: Key, Data: []byte(`last`)},
},
},
},
},
{
Kind: Key,
Data: []byte(`name`),
},
},
},
},
{
desc: "inline table with trailing comma",
input: `name = { first = "Tom", last = "Preston-Werner", }`,
ast: astNode{
Kind: KeyValue,
Children: []astNode{
{
Kind: InlineTable,
Children: []astNode{
{
Kind: KeyValue,
Children: []astNode{
{Kind: String, Data: []byte(`Tom`)},
{Kind: Key, Data: []byte(`first`)},
},
},
{
Kind: KeyValue,
Children: []astNode{
{Kind: String, Data: []byte(`Preston-Werner`)},
{Kind: Key, Data: []byte(`last`)},
},
},
},
},
{
Kind: Key,
Data: []byte(`name`),
},
},
},
},
{
desc: "empty inline table with newline",
input: "name = {\n}",
ast: astNode{
Kind: KeyValue,
Children: []astNode{
{
Kind: InlineTable,
Children: nil,
},
{
Kind: Key,
Data: []byte(`name`),
},
},
},
},
}
for _, e := range examples {