diff --git a/vlib/toml/parser/parser.v b/vlib/toml/parser/parser.v index 5f4a2c956c..2a1fdfabc3 100644 --- a/vlib/toml/parser/parser.v +++ b/vlib/toml/parser/parser.v @@ -362,7 +362,12 @@ pub fn (mut p Parser) root_table() ? { t := p.find_table() ? unsafe { util.printdbg(@MOD + '.' + @STRUCT + '.' + @FN, 'setting "$key.str()" = $val.to_json() in table ${ptr_str(t)}') - t[key.str()] = val + key_str := key.str() + if _ := t[key_str] { + return error(@MOD + '.' + @STRUCT + '.' + @FN + + ' key "$key" is already initialized with a value. At "$p.tok.kind" "$p.tok.lit" in this (excerpt): "...${p.excerpt()}..."') + } + t[key_str] = val } } } @@ -549,7 +554,11 @@ pub fn (mut p Parser) array_of_tables(mut table map[string]ast.Value) ? { } } p.last_aot = key_str - p.last_aot_index = 0 + + unsafe { + arr := &(table[p.last_aot] as []ast.Value) + p.last_aot_index = arr.len - 1 + } } // double_array_of_tables parses next tokens into an array of tables of arrays of `ast.Value`s... @@ -570,6 +579,8 @@ pub fn (mut p Parser) double_array_of_tables(mut table map[string]ast.Value) ? { p.check(.rsbr) ? p.check(.rsbr) ? + p.ignore_while(parser.all_formatting) + ks := key_str.split('.') if ks.len != 2 { @@ -577,24 +588,35 @@ pub fn (mut p Parser) double_array_of_tables(mut table map[string]ast.Value) ? { ' nested array of tables does not support more than 2 levels. (excerpt): "...${p.excerpt()}..."') } - first := ks[0] - last := ks[1] + first := ks[0] // The array that holds the entries + last := ks[1] // The key the parsed array data should be added to + + mut t_arr := &[]ast.Value(0) + mut t_map := ast.Value(ast.Null{}) unsafe { // NOTE this is starting to get EVEN uglier. TOML is not at all simple at this point... - if p.last_aot != first { - table[first] = []ast.Value{} - p.last_aot = first - mut t_arr := &(table[p.last_aot] as []ast.Value) - t_arr << map[string]ast.Value{} - p.last_aot_index = 0 + if first != p.last_aot { + // Implicit allocation + if p.last_aot == '' { + util.printdbg(@MOD + '.' + @STRUCT + '.' + @FN, 'implicit allocation of array for nested key `$key_str`.') + table[first] = []ast.Value{} + p.last_aot = first + t_arr = &(table[p.last_aot] as []ast.Value) + t_arr << ast.Value(map[string]ast.Value{}) + p.last_aot_index = t_arr.len - 1 + } else { + return error(@MOD + '.' + @STRUCT + '.' + @FN + + ' nested array of tables key "$first" does not match "$p.last_aot". (excerpt): "...${p.excerpt()}..."') + } } - mut t_arr := &(table[p.last_aot] as []ast.Value) - mut t_map := ast.Value(map[string]ast.Value{}) - if t_arr.len > 0 { + t_arr = &(table[p.last_aot] as []ast.Value) + t_map = ast.Value(map[string]ast.Value{}) + if p.last_aot_index < t_arr.len { t_map = t_arr[p.last_aot_index] } + mut t := &(t_map as map[string]ast.Value) if last in t.keys() { @@ -617,7 +639,7 @@ pub fn (mut p Parser) double_array_of_tables(mut table map[string]ast.Value) ? { } if t_arr.len == 0 { t_arr << t - p.last_aot_index = 0 + p.last_aot_index = t_arr.len - 1 } } } diff --git a/vlib/toml/tests/array_of_tables_1_level_test.v b/vlib/toml/tests/array_of_tables_1_level_test.v index 92eefca3c3..7464cd08d8 100644 --- a/vlib/toml/tests/array_of_tables_1_level_test.v +++ b/vlib/toml/tests/array_of_tables_1_level_test.v @@ -21,7 +21,6 @@ fn test_tables() { toml_json := toml_doc.to_json() - eprintln(toml_json) assert toml_json == os.read_file( os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) + '.out') or { panic(err) } diff --git a/vlib/toml/tests/array_of_tables_2_level_test.v b/vlib/toml/tests/array_of_tables_2_level_test.v new file mode 100644 index 0000000000..5b834cd962 --- /dev/null +++ b/vlib/toml/tests/array_of_tables_2_level_test.v @@ -0,0 +1,33 @@ +import os +import toml + +const ( + toml_text = '[[albums]] +name = "Born to Run" + + [[albums.songs]] + name = "Jungleland" + + [[albums.songs]] + name = "Meeting Across the River" + +[[albums]] +name = "Born in the USA" + + [[albums.songs]] + name = "Glory Days" + + [[albums.songs]] + name = "Dancing in the Dark"' +) + +fn test_nested_array_of_tables() { + mut toml_doc := toml.parse(toml_text) or { panic(err) } + + toml_json := toml_doc.to_json() + + eprintln(toml_json) + assert toml_json == os.read_file( + os.real_path(os.join_path(os.dir(@FILE), 'testdata', os.file_name(@FILE).all_before_last('.'))) + + '.out') or { panic(err) } +} diff --git a/vlib/toml/tests/burntsushi.toml-test_test.v b/vlib/toml/tests/burntsushi.toml-test_test.v index 846976fa87..6a1e8c0839 100644 --- a/vlib/toml/tests/burntsushi.toml-test_test.v +++ b/vlib/toml/tests/burntsushi.toml-test_test.v @@ -27,9 +27,7 @@ const ( 'datetime/no-leads-with-milli.toml', 'datetime/no-leads.toml', // Key - 'key/duplicate.toml', 'key/after-table.toml', - 'key/duplicate-keys.toml', 'key/after-value.toml', 'key/no-eol.toml', 'key/after-array.toml', diff --git a/vlib/toml/tests/testdata/array_of_tables_2_level_test.out b/vlib/toml/tests/testdata/array_of_tables_2_level_test.out new file mode 100644 index 0000000000..585d78aa03 --- /dev/null +++ b/vlib/toml/tests/testdata/array_of_tables_2_level_test.out @@ -0,0 +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" } ] } ] } \ No newline at end of file