Skip to content

Commit 4223137

Browse files
authored
Fix inline tables with dotted keys inside inline arrays (#400)
For example this: arr = [ {a.b.c = 1}, ] Would lose the a.b context, and it would just be map["c" = 1].
1 parent 45e7e49 commit 4223137

File tree

9 files changed

+164
-30
lines changed

9 files changed

+164
-30
lines changed
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
name = { first = "Tom", last = "Preston-Werner" }
2-
point = { x = 1, y = 2 }
3-
simple = { a = 1 }
4-
str-key = { "a" = 1 }
1+
name = { first = "Tom", last = "Preston-Werner" }
2+
point = { x = 1, y = 2 }
3+
simple = { a = 1 }
4+
str-key = { "a" = 1 }
55
table-array = [{ "a" = 1 }, { "b" = 2 }]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
{
2+
"arr-1": [
3+
{
4+
"a": {
5+
"b": {
6+
"type": "integer",
7+
"value": "1"
8+
}
9+
}
10+
}
11+
],
12+
"arr-2": [
13+
{
14+
"type": "string",
15+
"value": "str"
16+
},
17+
{
18+
"a": {
19+
"b": {
20+
"type": "integer",
21+
"value": "1"
22+
}
23+
}
24+
}
25+
],
26+
"arr-3": [
27+
{
28+
"a": {
29+
"b": {
30+
"type": "integer",
31+
"value": "1"
32+
}
33+
}
34+
},
35+
{
36+
"a": {
37+
"b": {
38+
"type": "integer",
39+
"value": "2"
40+
}
41+
}
42+
}
43+
],
44+
"arr-4": [
45+
{
46+
"type": "string",
47+
"value": "str"
48+
},
49+
{
50+
"a": {
51+
"b": {
52+
"type": "integer",
53+
"value": "1"
54+
}
55+
}
56+
},
57+
{
58+
"a": {
59+
"b": {
60+
"type": "integer",
61+
"value": "2"
62+
}
63+
}
64+
}
65+
]
66+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
arr-1 = [{a.b = 1}]
2+
arr-2 = ["str", {a.b = 1}]
3+
4+
arr-3 = [{a.b = 1}, {a.b = 2}]
5+
arr-4 = ["str", {a.b = 1}, {a.b = 2}]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"top": {
3+
"dot": {
4+
"dot": [
5+
{
6+
"dot": {
7+
"dot": {
8+
"dot": {
9+
"type": "integer",
10+
"value": "1"
11+
}
12+
}
13+
}
14+
},
15+
{
16+
"dot": {
17+
"dot": {
18+
"dot": {
19+
"type": "integer",
20+
"value": "2"
21+
}
22+
}
23+
}
24+
}
25+
]
26+
}
27+
}
28+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
top.dot.dot = [
2+
{dot.dot.dot = 1},
3+
{dot.dot.dot = 2},
4+
]
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"arr": [
3+
{
4+
"a": {
5+
"b": [
6+
{
7+
"c": {
8+
"d": {
9+
"type": "integer",
10+
"value": "1"
11+
}
12+
}
13+
}
14+
]
15+
}
16+
}
17+
]
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
arr = [
2+
{a.b = [{c.d = 1}]}
3+
]

meta.go

+7
Original file line numberDiff line numberDiff line change
@@ -133,9 +133,16 @@ func (k Key) maybeQuoted(i int) string {
133133
return k[i]
134134
}
135135

136+
// Like append(), but only increase the cap by 1.
136137
func (k Key) add(piece string) Key {
138+
if cap(k) > len(k) {
139+
return append(k, piece)
140+
}
137141
newKey := make(Key, len(k)+1)
138142
copy(newKey, k)
139143
newKey[len(k)] = piece
140144
return newKey
141145
}
146+
147+
func (k Key) parent() Key { return k[:len(k)-1] } // all except the last piece.
148+
func (k Key) last() string { return k[len(k)-1] } // last piece of this key.

parse.go

+29-26
Original file line numberDiff line numberDiff line change
@@ -196,11 +196,11 @@ func (p *parser) topLevel(item item) {
196196
p.assertEqual(itemKeyEnd, k.typ)
197197

198198
/// The current key is the last part.
199-
p.currentKey = key[len(key)-1]
199+
p.currentKey = key.last()
200200

201201
/// All the other parts (if any) are the context; need to set each part
202202
/// as implicit.
203-
context := key[:len(key)-1]
203+
context := key.parent()
204204
for i := range context {
205205
p.addImplicitContext(append(p.context, context[i:i+1]...))
206206
}
@@ -209,7 +209,8 @@ func (p *parser) topLevel(item item) {
209209
/// Set value.
210210
vItem := p.next()
211211
val, typ := p.value(vItem, false)
212-
p.set(p.currentKey, val, typ, vItem.pos)
212+
p.setValue(p.currentKey, val)
213+
p.setType(p.currentKey, typ, vItem.pos)
213214

214215
/// Remove the context we added (preserving any context from [tbl] lines).
215216
p.context = outerContext
@@ -434,7 +435,7 @@ func (p *parser) valueArray(it item) (any, tomlType) {
434435

435436
func (p *parser) valueInlineTable(it item, parentIsArray bool) (any, tomlType) {
436437
var (
437-
hash = make(map[string]any)
438+
topHash = make(map[string]any)
438439
outerContext = p.context
439440
outerKey = p.currentKey
440441
)
@@ -462,27 +463,38 @@ func (p *parser) valueInlineTable(it item, parentIsArray bool) (any, tomlType) {
462463
p.assertEqual(itemKeyEnd, k.typ)
463464

464465
/// The current key is the last part.
465-
p.currentKey = key[len(key)-1]
466+
p.currentKey = key.last()
466467

467468
/// All the other parts (if any) are the context; need to set each part
468469
/// as implicit.
469-
context := key[:len(key)-1]
470+
context := key.parent()
470471
for i := range context {
471472
p.addImplicitContext(append(p.context, context[i:i+1]...))
472473
}
473474
p.ordered = append(p.ordered, p.context.add(p.currentKey))
474475

475476
/// Set the value.
476477
val, typ := p.value(p.next(), false)
477-
p.set(p.currentKey, val, typ, it.pos)
478+
p.setValue(p.currentKey, val)
479+
p.setType(p.currentKey, typ, it.pos)
480+
481+
hash := topHash
482+
for _, c := range context {
483+
h, ok := hash[c]
484+
if !ok {
485+
h = make(map[string]any)
486+
hash[c] = h
487+
}
488+
hash = h.(map[string]any)
489+
}
478490
hash[p.currentKey] = val
479491

480492
/// Restore context.
481493
p.context = prevContext
482494
}
483495
p.context = outerContext
484496
p.currentKey = outerKey
485-
return hash, tomlHash
497+
return topHash, tomlHash
486498
}
487499

488500
// numHasLeadingZero checks if this number has leading zeroes, allowing for '0',
@@ -537,15 +549,13 @@ func numPeriodsOK(s string) bool {
537549
// Establishing the context also makes sure that the key isn't a duplicate, and
538550
// will create implicit hashes automatically.
539551
func (p *parser) addContext(key Key, array bool) {
540-
var ok bool
541-
542-
// Always start at the top level and drill down for our context.
552+
/// Always start at the top level and drill down for our context.
543553
hashContext := p.mapping
544554
keyContext := make(Key, 0, len(key)-1)
545555

546-
// We only need implicit hashes for key[0:-1]
547-
for _, k := range key[0 : len(key)-1] {
548-
_, ok = hashContext[k]
556+
/// We only need implicit hashes for the parents.
557+
for _, k := range key.parent() {
558+
_, ok := hashContext[k]
549559
keyContext = append(keyContext, k)
550560

551561
// No key? Make an implicit hash and move on.
@@ -573,7 +583,7 @@ func (p *parser) addContext(key Key, array bool) {
573583
if array {
574584
// If this is the first element for this array, then allocate a new
575585
// list of tables for it.
576-
k := key[len(key)-1]
586+
k := key.last()
577587
if _, ok := hashContext[k]; !ok {
578588
hashContext[k] = make([]map[string]any, 0, 4)
579589
}
@@ -586,15 +596,9 @@ func (p *parser) addContext(key Key, array bool) {
586596
p.panicf("Key '%s' was already created and cannot be used as an array.", key)
587597
}
588598
} else {
589-
p.setValue(key[len(key)-1], make(map[string]any))
599+
p.setValue(key.last(), make(map[string]any))
590600
}
591-
p.context = append(p.context, key[len(key)-1])
592-
}
593-
594-
// set calls setValue and setType.
595-
func (p *parser) set(key string, val any, typ tomlType, pos Position) {
596-
p.setValue(key, val)
597-
p.setType(key, typ, pos)
601+
p.context = append(p.context, key.last())
598602
}
599603

600604
// setValue sets the given key to the given value in the current context.
@@ -644,9 +648,8 @@ func (p *parser) setValue(key string, value any) {
644648
p.removeImplicit(keyContext)
645649
return
646650
}
647-
648-
// Otherwise, we have a concrete key trying to override a previous
649-
// key, which is *always* wrong.
651+
// Otherwise, we have a concrete key trying to override a previous key,
652+
// which is *always* wrong.
650653
p.panicf("Key '%s' has already been defined.", keyContext)
651654
}
652655

0 commit comments

Comments
 (0)