fix(parser): reset dotted-key and array records per array-of-tables element
Assisted-by: GLM 5.3
This commit is contained in:
@@ -46,6 +46,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||||||
check covered `[table]` headers and dotted keys but not the intermediate
|
check covered `[table]` headers and dotted keys but not the intermediate
|
||||||
steps of an array-of-tables header, so such a document silently extended the
|
steps of an array-of-tables header, so such a document silently extended the
|
||||||
inline table.
|
inline table.
|
||||||
|
- A new element of an array of tables starts a fresh scope for dotted-key paths
|
||||||
|
and nested arrays of tables: `[[a]]`, `b.c = 1`, `[[a]]`, `[a.b]` parses, as
|
||||||
|
the TOML examples in the spec shape it. The records of the previous element
|
||||||
|
falsely rejected the same paths in the next one.
|
||||||
- A float with an exponent marker but no digits (`1e`, `0.0E`) is rejected;
|
- A float with an exponent marker but no digits (`1e`, `0.0E`) is rejected;
|
||||||
the exponent requires at least one digit.
|
the exponent requires at least one digit.
|
||||||
- A date-time offset outside 00:00 through 23:59 is rejected; such offsets
|
- A date-time offset outside 00:00 through 23:59 is rejected; such offsets
|
||||||
|
|||||||
@@ -498,6 +498,36 @@ func TestRejectsInlineTableExtension(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A new element of an array of tables starts a fresh scope: sub-table headers,
|
||||||
|
// nested arrays of tables, and dotted-key paths recorded for the previous
|
||||||
|
// element must not block the same paths in the next one.
|
||||||
|
func TestArrayOfTablesFreshScopePerElement(t *testing.T) {
|
||||||
|
cases := map[string]string{
|
||||||
|
"nested array of tables": "[[a]]\n[[a.b]]\nx = 1\n[[a]]\n[a.b]\ny = 2\n",
|
||||||
|
"dotted key": "[[a]]\nb.c = 1\n[[a]]\n[a.b]\nd = 2\n",
|
||||||
|
}
|
||||||
|
for name, doc := range cases {
|
||||||
|
tree, err := Parse([]byte(doc))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("%s: %v", name, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
elements := tree["a"].([]map[string]any)
|
||||||
|
if len(elements) != 2 {
|
||||||
|
t.Errorf("%s: len(a) = %d, want 2", name, len(elements))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Within one element the redefinition rules keep applying.
|
||||||
|
for name, doc := range map[string]string{
|
||||||
|
"header over dotted in one element": "[[a]]\nb.c = 1\n[a.b]\nd = 2\n",
|
||||||
|
"table over nested array": "[[a]]\n[[a.b]]\n[a.b]\nx = 1\n",
|
||||||
|
} {
|
||||||
|
if _, err := Parse([]byte(doc)); err == nil {
|
||||||
|
t.Errorf("%s: expected an error, got none", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestRejectsSpecInvalid(t *testing.T) {
|
func TestRejectsSpecInvalid(t *testing.T) {
|
||||||
cases := map[string]string{
|
cases := map[string]string{
|
||||||
"single-digit hour": "a = 2023-10-01T1:32:00Z\n",
|
"single-digit hour": "a = 2023-10-01T1:32:00Z\n",
|
||||||
|
|||||||
@@ -274,18 +274,17 @@ func (p *parser) freezeInline(path []string, val any) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// resetScopeUnder forgets the header and freeze records nested under key, which
|
// resetScopeUnder forgets the definition records nested under key, which
|
||||||
// belong to the previous element of an array of tables.
|
// belong to the previous element of an array of tables: headers, frozen
|
||||||
|
// inline tables, dotted-key paths, and nested arrays of tables all start
|
||||||
|
// fresh in the new element.
|
||||||
func (p *parser) resetScopeUnder(key []string) {
|
func (p *parser) resetScopeUnder(key []string) {
|
||||||
prefix := pathKey(key) + "\x00"
|
prefix := pathKey(key) + "\x00"
|
||||||
for k := range p.headers {
|
for _, m := range []map[string]bool{p.headers, p.frozen, p.dotted, p.arrays} {
|
||||||
if strings.HasPrefix(k, prefix) {
|
for k := range m {
|
||||||
delete(p.headers, k)
|
if strings.HasPrefix(k, prefix) {
|
||||||
}
|
delete(m, k)
|
||||||
}
|
}
|
||||||
for k := range p.frozen {
|
|
||||||
if strings.HasPrefix(k, prefix) {
|
|
||||||
delete(p.frozen, k)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user