From acf88b7fc93582685f71f90f50e257e18fcdc39b Mon Sep 17 00:00:00 2001 From: Alexander Medvednikov Date: Wed, 25 Mar 2026 22:48:33 +0300 Subject: [PATCH] toml: fix nested tables array parsing issue (fixes #26747) --- vlib/toml/parser/parser.v | 34 ++++++++++++++++++++++++++++++ vlib/toml/tests/value_query_test.v | 20 ++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/vlib/toml/parser/parser.v b/vlib/toml/parser/parser.v index 3e643ab32..84ca1f53b 100644 --- a/vlib/toml/parser/parser.v +++ b/vlib/toml/parser/parser.v @@ -980,6 +980,40 @@ pub fn (mut p Parser) double_array_of_tables(mut table map[string]ast.Value) ! { mut t_map := ast.Value(ast.Null{}) unsafe { + if table_first := table[first.str()] { + if table_first is map[string]ast.Value { + mut t := &(table_first as map[string]ast.Value) + if val := t[last.str()] { + if val is []ast.Value { + mut arr := &val + arr << p.array_of_tables_contents()! + t[last.str()] = arr + } else { + return error(@MOD + '.' + @STRUCT + '.' + @FN + + ' t[${last.str()}] is not an array. (excerpt): "...${p.excerpt()}..."') + } + } else { + t[last.str()] = p.array_of_tables_contents()! + } + p.last_aot.clear() + p.last_aot_index = 0 + return + } + } else { + util.printdbg(@MOD + '.' + @STRUCT + '.' + @FN, 'implicit allocation of map for `${first}` in dotted key `${dotted_key}`.') + mut t := &map[string]ast.Value{} + p.implicit_declared << first + // NOTE: We register this implicit allocation also as *explicit* to be able to catch a special case like: + // https://github.com/toml-lang/toml-test/blob/576db852/tests/invalid/table/array-implicit.toml + // See also: undo_special_case_01 + p.explicit_declared << first + t[last.str()] = p.array_of_tables_contents()! + table[first.str()] = ast.Value(t) + p.last_aot.clear() + p.last_aot_index = 0 + return + } + // NOTE this is starting to get EVEN uglier. TOML is not *at all* simple at this point... if first != p.last_aot { util.printdbg(@MOD + '.' + @STRUCT + '.' + @FN, '${first} != ${p.last_aot}') diff --git a/vlib/toml/tests/value_query_test.v b/vlib/toml/tests/value_query_test.v index 82bb7137b..3cf770d02 100644 --- a/vlib/toml/tests/value_query_test.v +++ b/vlib/toml/tests/value_query_test.v @@ -47,6 +47,16 @@ const toml_text_2 = " 'TEST_PATH' = '/tmp/test' " +const toml_text_3 = ' +[[foo.bar]] +baz = 1 +bzz = 1 + +[[foo.bar]] +baz = 2 +bzz = 2 +' + fn test_value_query_in_array() { toml_doc := toml.parse_text(toml_text) or { panic(err) } mut value := toml_doc.value('themes[0].colors[1]').string() @@ -109,3 +119,13 @@ fn test_any_value_query_2() { assert defaults.value('run.flags[0]').string() == '-f 1' assert defaults.value('env[0].RUN_TIME').int() == 5 } + +fn test_any_value_query_for_nested_tables_array() { + toml_doc := toml.parse_text(toml_text_3) or { panic(err) } + items := toml_doc.value('foo.bar').array() + assert items.len == 2 + assert items[0].value('baz').int() == 1 + assert items[0].value('bzz').int() == 1 + assert items[1].value('baz').int() == 2 + assert items[1].value('bzz').int() == 2 +} -- 2.39.5