Skip to content

Commit 24cd619

Browse files
authored
toml: fix parsing array of tables (#12388)
1 parent db65b65 commit 24cd619

File tree

5 files changed

+70
-17
lines changed

5 files changed

+70
-17
lines changed

vlib/toml/parser/parser.v

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,12 @@ pub fn (mut p Parser) root_table() ? {
362362
t := p.find_table() ?
363363
unsafe {
364364
util.printdbg(@MOD + '.' + @STRUCT + '.' + @FN, 'setting "$key.str()" = $val.to_json() in table ${ptr_str(t)}')
365-
t[key.str()] = val
365+
key_str := key.str()
366+
if _ := t[key_str] {
367+
return error(@MOD + '.' + @STRUCT + '.' + @FN +
368+
' key "$key" is already initialized with a value. At "$p.tok.kind" "$p.tok.lit" in this (excerpt): "...${p.excerpt()}..."')
369+
}
370+
t[key_str] = val
366371
}
367372
}
368373
}
@@ -549,7 +554,11 @@ pub fn (mut p Parser) array_of_tables(mut table map[string]ast.Value) ? {
549554
}
550555
}
551556
p.last_aot = key_str
552-
p.last_aot_index = 0
557+
558+
unsafe {
559+
arr := &(table[p.last_aot] as []ast.Value)
560+
p.last_aot_index = arr.len - 1
561+
}
553562
}
554563

555564
// double_array_of_tables parses next tokens into an array of tables of arrays of `ast.Value`s...
@@ -570,31 +579,44 @@ pub fn (mut p Parser) double_array_of_tables(mut table map[string]ast.Value) ? {
570579
p.check(.rsbr) ?
571580
p.check(.rsbr) ?
572581

582+
p.ignore_while(parser.all_formatting)
583+
573584
ks := key_str.split('.')
574585

575586
if ks.len != 2 {
576587
return error(@MOD + '.' + @STRUCT + '.' + @FN +
577588
' nested array of tables does not support more than 2 levels. (excerpt): "...${p.excerpt()}..."')
578589
}
579590

580-
first := ks[0]
581-
last := ks[1]
591+
first := ks[0] // The array that holds the entries
592+
last := ks[1] // The key the parsed array data should be added to
593+
594+
mut t_arr := &[]ast.Value(0)
595+
mut t_map := ast.Value(ast.Null{})
582596

583597
unsafe {
584598
// NOTE this is starting to get EVEN uglier. TOML is not at all simple at this point...
585-
if p.last_aot != first {
586-
table[first] = []ast.Value{}
587-
p.last_aot = first
588-
mut t_arr := &(table[p.last_aot] as []ast.Value)
589-
t_arr << map[string]ast.Value{}
590-
p.last_aot_index = 0
599+
if first != p.last_aot {
600+
// Implicit allocation
601+
if p.last_aot == '' {
602+
util.printdbg(@MOD + '.' + @STRUCT + '.' + @FN, 'implicit allocation of array for nested key `$key_str`.')
603+
table[first] = []ast.Value{}
604+
p.last_aot = first
605+
t_arr = &(table[p.last_aot] as []ast.Value)
606+
t_arr << ast.Value(map[string]ast.Value{})
607+
p.last_aot_index = t_arr.len - 1
608+
} else {
609+
return error(@MOD + '.' + @STRUCT + '.' + @FN +
610+
' nested array of tables key "$first" does not match "$p.last_aot". (excerpt): "...${p.excerpt()}..."')
611+
}
591612
}
592613

593-
mut t_arr := &(table[p.last_aot] as []ast.Value)
594-
mut t_map := ast.Value(map[string]ast.Value{})
595-
if t_arr.len > 0 {
614+
t_arr = &(table[p.last_aot] as []ast.Value)
615+
t_map = ast.Value(map[string]ast.Value{})
616+
if p.last_aot_index < t_arr.len {
596617
t_map = t_arr[p.last_aot_index]
597618
}
619+
598620
mut t := &(t_map as map[string]ast.Value)
599621

600622
if last in t.keys() {
@@ -617,7 +639,7 @@ pub fn (mut p Parser) double_array_of_tables(mut table map[string]ast.Value) ? {
617639
}
618640
if t_arr.len == 0 {
619641
t_arr << t
620-
p.last_aot_index = 0
642+
p.last_aot_index = t_arr.len - 1
621643
}
622644
}
623645
}

vlib/toml/tests/array_of_tables_1_level_test.v

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ fn test_tables() {
2121

2222
toml_json := toml_doc.to_json()
2323

24-
eprintln(toml_json)
2524
assert toml_json == os.read_file(
2625
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
2726
'.out') or { panic(err) }
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import os
2+
import toml
3+
4+
const (
5+
toml_text = '[[albums]]
6+
name = "Born to Run"
7+
8+
[[albums.songs]]
9+
name = "Jungleland"
10+
11+
[[albums.songs]]
12+
name = "Meeting Across the River"
13+
14+
[[albums]]
15+
name = "Born in the USA"
16+
17+
[[albums.songs]]
18+
name = "Glory Days"
19+
20+
[[albums.songs]]
21+
name = "Dancing in the Dark"'
22+
)
23+
24+
fn test_nested_array_of_tables() {
25+
mut toml_doc := toml.parse(toml_text) or { panic(err) }
26+
27+
toml_json := toml_doc.to_json()
28+
29+
eprintln(toml_json)
30+
assert toml_json == os.read_file(
31+
os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) +
32+
'.out') or { panic(err) }
33+
}

vlib/toml/tests/burntsushi.toml-test_test.v

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,7 @@ const (
2727
'datetime/no-leads-with-milli.toml',
2828
'datetime/no-leads.toml',
2929
// Key
30-
'key/duplicate.toml',
3130
'key/after-table.toml',
32-
'key/duplicate-keys.toml',
3331
'key/after-value.toml',
3432
'key/no-eol.toml',
3533
'key/after-array.toml',
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{ "albums": [ { "name": "Born to Run", "songs": [ { "name": "Jungleland" }, { "name": "Meeting Across the River" } ] }, { "name": "Born in the USA", "songs": [ { "name": "Glory Days" }, { "name": "Dancing in the Dark" } ] } ] }

0 commit comments

Comments
 (0)