diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:44:05 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 18:44:05 +0300 |
| commit | 0b24459ac12b6cf9fd5a401d647796ca254a8fa8 (patch) | |
| tree | f2fd66e2476976a51e2a51330fd95dc6e87b24c1 /tomlplusplus/tests/parsing_tables.cpp | |
| parent | b85e90fc3480da0e6a48da73201a0b22488cc650 (diff) | |
| parent | 1c8b7466e4946fcc3bf20484c0e1d001202cca5a (diff) | |
| download | Project-Tick-0b24459ac12b6cf9fd5a401d647796ca254a8fa8.tar.gz Project-Tick-0b24459ac12b6cf9fd5a401d647796ca254a8fa8.zip | |
Add 'tomlplusplus/' from commit '1c8b7466e4946fcc3bf20484c0e1d001202cca5a'
git-subtree-dir: tomlplusplus
git-subtree-mainline: b85e90fc3480da0e6a48da73201a0b22488cc650
git-subtree-split: 1c8b7466e4946fcc3bf20484c0e1d001202cca5a
Diffstat (limited to 'tomlplusplus/tests/parsing_tables.cpp')
| -rw-r--r-- | tomlplusplus/tests/parsing_tables.cpp | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/tomlplusplus/tests/parsing_tables.cpp b/tomlplusplus/tests/parsing_tables.cpp new file mode 100644 index 0000000000..417427e579 --- /dev/null +++ b/tomlplusplus/tests/parsing_tables.cpp @@ -0,0 +1,595 @@ +// This file is a part of toml++ and is subject to the the terms of the MIT license. +// Copyright (c) Mark Gillard <mark.gillard@outlook.com.au> +// See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. +// SPDX-License-Identifier: MIT + +#include "tests.hpp" + +TEST_CASE("parsing - tables") +{ + // these are the examples from https://toml.io/en/v1.0.0#table + + // "Tables are defined by headers, with square brackets on a line by themselves." + parsing_should_succeed(FILE_LINE_ARGS, + "[table]"sv, + [](table&& tbl) + { + REQUIRE(tbl["table"].as_table()); + CHECK(tbl["table"].as_table()->empty()); + CHECK(tbl["table"].as_table()->size() == 0u); + }); + parsing_should_fail(FILE_LINE_ARGS, "[]"sv); + + // "Under that, and until the next header or EOF, are the key/values of that table. + // Key/value pairs within tables are not guaranteed to be in any specific order." + parsing_should_succeed(FILE_LINE_ARGS, + R"( + [table-1] + key1 = "some string" + key2 = 123 + + [table-2] + key1 = "another string" + key2 = 456 + )"sv, + [](table&& tbl) + { + REQUIRE(tbl["table-1"].as_table()); + CHECK(tbl["table-1"].as_table()->size() == 2u); + CHECK(tbl["table-1"]["key1"] == "some string"sv); + CHECK(tbl["table-1"]["key2"] == 123); + + REQUIRE(tbl["table-2"].as_table()); + CHECK(tbl["table-2"].as_table()->size() == 2u); + CHECK(tbl["table-2"]["key1"] == "another string"sv); + CHECK(tbl["table-2"]["key2"] == 456); + }); + + // "Naming rules for tables are the same as for keys." (i.e. can be quoted) + parsing_should_succeed(FILE_LINE_ARGS, + R"( + [dog."tater.man"] + type.name = "pug" + )"sv, + [](table&& tbl) + { + REQUIRE(tbl["dog"].as_table()); + CHECK(tbl["dog"].as_table()->size() == 1u); + + REQUIRE(tbl["dog"]["tater.man"].as_table()); + CHECK(tbl["dog"]["tater.man"].as_table()->size() == 1u); + CHECK(tbl["dog"]["tater.man"]["type"]["name"] == "pug"sv); + }); + + // "Whitespace around the key is ignored. However, best practice is to not use any extraneous whitespace." + parsing_should_succeed(FILE_LINE_ARGS, + R"( + [a.b.c] # this is best practice + [ d.e.f ] # same as [d.e.f] + [ g . h . i ] # same as [g.h.i] + [ j . "k" . 'l' ] # same as [j."k".'l'] + )"sv, + [](table&& tbl) + { + CHECK(tbl["a"].as_table()); + CHECK(tbl["a"]["b"].as_table()); + CHECK(tbl["a"]["b"]["c"].as_table()); + + CHECK(tbl["d"].as_table()); + CHECK(tbl["d"]["e"].as_table()); + CHECK(tbl["d"]["e"]["f"].as_table()); + + CHECK(tbl["g"].as_table()); + CHECK(tbl["g"]["h"].as_table()); + CHECK(tbl["g"]["h"]["i"].as_table()); + + CHECK(tbl["j"].as_table()); + CHECK(tbl["j"]["k"].as_table()); + CHECK(tbl["j"]["k"]["l"].as_table()); + }); + + // "You don't need to specify all the super-tables if you don't want to. TOML knows how to do it for you." + parsing_should_succeed(FILE_LINE_ARGS, + R"( + # [x] you + # [x.y] don't + # [x.y.z] need these + [x.y.z.w] # for this to work + + [x] # defining a super-table afterwards is ok + )"sv, + [](table&& tbl) + { + CHECK(tbl["x"].as_table()); + CHECK(tbl["x"]["y"].as_table()); + CHECK(tbl["x"]["y"]["z"].as_table()); + CHECK(tbl["x"]["y"]["z"]["w"].as_table()); + }); + + // "Like keys, you cannot define a table more than once. Doing so is invalid." + parsing_should_fail(FILE_LINE_ARGS, R"( + # DO NOT DO THIS + + [fruit] + apple = "red" + + [fruit] + orange = "orange" + )"sv); + parsing_should_fail(FILE_LINE_ARGS, R"( + # DO NOT DO THIS EITHER + + [fruit] + apple = "red" + + [fruit.apple] + texture = "smooth" + )"sv); + + // "Defining tables out-of-order is discouraged." + parsing_should_succeed(FILE_LINE_ARGS, + R"( + # VALID BUT DISCOURAGED + [fruit.apple] + [animal] + [fruit.orange] + )"sv, + [](table&& tbl) + { + CHECK(tbl["fruit"].as_table()); + CHECK(tbl["fruit"]["apple"].as_table()); + CHECK(tbl["animal"].as_table()); + CHECK(tbl["fruit"]["orange"].as_table()); + }); + parsing_should_succeed(FILE_LINE_ARGS, + R"( + # RECOMMENDED + [fruit.apple] + [fruit.orange] + [animal] + )"sv, + [](table&& tbl) + { + CHECK(tbl["fruit"].as_table()); + CHECK(tbl["fruit"]["apple"].as_table()); + CHECK(tbl["fruit"]["orange"].as_table()); + CHECK(tbl["animal"].as_table()); + }); + + // "The top-level table, also called the root table, starts at the beginning of the document + // and ends just before the first table header (or EOF)." + parsing_should_succeed(FILE_LINE_ARGS, + R"( + # Top-level table begins. + name = "Fido" + breed = "pug" + + # Top-level table ends. + [owner] + name = "Regina Dogman" + member_since = 1999-08-04 + )"sv, + [](table&& tbl) + { + CHECK(tbl["name"].as_string()); + CHECK(*tbl["name"].as_string() == "Fido"sv); + CHECK(tbl["breed"].as_string()); + CHECK(*tbl["breed"].as_string() == "pug"sv); + + CHECK(tbl["owner"].as_table()); + CHECK(*tbl["owner"]["name"].as_string() == "Regina Dogman"sv); + + static constexpr auto member_since = toml::date{ 1999, 8, 4 }; + CHECK(*tbl["owner"]["member_since"].as_date() == member_since); + }); + + // "Dotted keys create and define a table for each key part before the last one, + // provided that such tables were not previously created." + parsing_should_succeed(FILE_LINE_ARGS, + R"( + fruit.apple.color = "red" + # Defines a table named fruit + # Defines a table named fruit.apple + + fruit.apple.taste.sweet = true + # Defines a table named fruit.apple.taste + # fruit and fruit.apple were already created + )"sv, + [](table&& tbl) + { + CHECK(tbl["fruit"].as_table()); + CHECK(tbl["fruit"]["apple"].as_table()); + CHECK(tbl["fruit"]["apple"]["color"].as_string()); + CHECK(*tbl["fruit"]["apple"]["color"].as_string() == "red"sv); + + CHECK(tbl["fruit"]["apple"]["taste"].as_table()); + CHECK(tbl["fruit"]["apple"]["taste"]["sweet"].as_boolean()); + CHECK(*tbl["fruit"]["apple"]["taste"]["sweet"].as_boolean() == true); + }); + + // "Since tables cannot be defined more than once, redefining such tables using a [table] header is not allowed." + parsing_should_fail(FILE_LINE_ARGS, R"( + [fruit] + apple.color = "red" + apple.taste.sweet = true + + [fruit.apple] # INVALID + )"sv); + parsing_should_fail(FILE_LINE_ARGS, R"( + [fruit] + apple.color = "red" + apple.taste.sweet = true + + [fruit.apple.taste] # INVALID + )"sv); + + // "Likewise, using dotted keys to redefine tables already defined in [table] form is not allowed." + parsing_should_fail(FILE_LINE_ARGS, R"( + [fruit.apple.taste] + sweet = true + + [fruit] + apple.taste = { sweet = false } # INVALID + )"sv); + parsing_should_fail(FILE_LINE_ARGS, R"( + [fruit.apple.taste] + sweet = true + + [fruit] + apple.taste.foo = "bar" # INVALID + )"sv); + + // "The [table] form can, however, be used to define sub-tables within tables defined via dotted keys." + parsing_should_succeed(FILE_LINE_ARGS, + R"( + [fruit] + apple.color = "red" + apple.taste.sweet = true + + [fruit.apple.texture] # you can add sub-tables + smooth = true + )"sv, + [](table&& tbl) + { + CHECK(tbl["fruit"].as_table()); + CHECK(tbl["fruit"]["apple"].as_table()); + CHECK(tbl["fruit"]["apple"]["color"].as_string()); + CHECK(*tbl["fruit"]["apple"]["color"].as_string() == "red"sv); + + CHECK(tbl["fruit"]["apple"]["texture"].as_table()); + CHECK(tbl["fruit"]["apple"]["texture"]["smooth"].as_boolean()); + CHECK(*tbl["fruit"]["apple"]["texture"]["smooth"].as_boolean() == true); + }); + parsing_should_fail(FILE_LINE_ARGS, R"( + [fruit] + apple.color = "red" + apple.taste.sweet = true + + [fruit.apple] + shape = "round" + + [fruit.apple.texture] + smooth = true + )"sv); + + // same as above but the table order is reversed. + // see: https://github.com/toml-lang/toml/issues/769 + parsing_should_succeed(FILE_LINE_ARGS, + R"( + [fruit.apple.texture] + smooth = true + + [fruit] + apple.color = "red" + apple.taste.sweet = true + )"sv, + [](table&& tbl) + { + CHECK(tbl["fruit"].as_table()); + CHECK(tbl["fruit"]["apple"].as_table()); + CHECK(tbl["fruit"]["apple"]["color"].as_string()); + CHECK(*tbl["fruit"]["apple"]["color"].as_string() == "red"sv); + + CHECK(tbl["fruit"]["apple"]["texture"].as_table()); + CHECK(tbl["fruit"]["apple"]["texture"]["smooth"].as_boolean()); + CHECK(*tbl["fruit"]["apple"]["texture"]["smooth"].as_boolean() == true); + }); +} + +TEST_CASE("parsing - inline tables") +{ + // these are the examples from https://toml.io/en/v1.0.0#inline-table + + parsing_should_succeed(FILE_LINE_ARGS, + R"( + name = { first = "Tom", last = "Preston-Werner" } + point = { x = 1, y = 2 } + animal = { type.name = "pug" } + )"sv, + [](table&& tbl) + { + REQUIRE(tbl["name"].as_table()); + CHECK(tbl["name"].as_table()->size() == 2u); + CHECK(tbl["name"].as_table()->is_inline()); + CHECK(tbl["name"]["first"] == "Tom"sv); + CHECK(tbl["name"]["last"] == "Preston-Werner"sv); + + REQUIRE(tbl["point"].as_table()); + CHECK(tbl["point"].as_table()->size() == 2u); + CHECK(tbl["point"].as_table()->is_inline()); + CHECK(tbl["point"]["x"] == 1); + CHECK(tbl["point"]["y"] == 2); + + REQUIRE(tbl["animal"].as_table()); + CHECK(tbl["animal"].as_table()->size() == 1u); + CHECK(tbl["animal"].as_table()->is_inline()); + REQUIRE(tbl["animal"]["type"].as_table()); + CHECK(tbl["animal"]["type"].as_table()->size() == 1u); + CHECK(tbl["animal"]["type"]["name"] == "pug"sv); + }); + + // "Inline tables are fully self-contained and define all keys and sub-tables within them. + // Keys and sub-tables cannot be added outside the braces." + parsing_should_fail(FILE_LINE_ARGS, R"( + [product] + type = { name = "Nail" } + type.edible = false # INVALID + )"sv); + + // "Similarly, inline tables cannot be used to add keys or sub-tables to an already-defined table." + parsing_should_fail(FILE_LINE_ARGS, R"( + [product] + type.name = "Nail" + type = { edible = false } # INVALID + )"sv); + + // "newlines are allowed between the curly braces [if] they are valid within a value." + parsing_should_succeed(FILE_LINE_ARGS, + R"( + test = { val1 = "foo", val2 = [ + 1, 2, + 3 + ], val3 = "bar" } + )"sv, + [](table&& tbl) + { + REQUIRE(tbl["test"].as_table()); + CHECK(tbl["test"].as_table()->size() == 3u); + CHECK(tbl["test"]["val1"] == "foo"sv); + REQUIRE(tbl["test"]["val2"].as<array>()); + CHECK(tbl["test"]["val2"].as<array>()->size() == 3u); + CHECK(tbl["test"]["val2"][0] == 1); + CHECK(tbl["test"]["val2"][1] == 2); + CHECK(tbl["test"]["val2"][2] == 3); + CHECK(tbl["test"]["val3"] == "bar"sv); + }); + +// toml/issues/516 (newlines/trailing commas in inline tables) +#if TOML_LANG_UNRELEASED + { + parsing_should_succeed(FILE_LINE_ARGS, + R"( + name = { + first = "Tom", + last = "Preston-Werner", + } + )"sv, + [](table&& tbl) + { + REQUIRE(tbl["name"].as_table()); + CHECK(tbl["name"].as_table()->size() == 2u); + CHECK(tbl["name"]["first"] == "Tom"sv); + CHECK(tbl["name"]["last"] == "Preston-Werner"sv); + }); + } +#else + { + // "A terminating comma (also called trailing comma) is not permitted after the last key/value pair in an inline + // table." + parsing_should_fail(FILE_LINE_ARGS, R"(name = { first = "Tom", last = "Preston-Werner", })"sv); + + // "No newlines are allowed between the curly braces unless they are valid within a value." + parsing_should_fail(FILE_LINE_ARGS, R"( + name = { + first = "Tom", + last = "Preston-Werner" + } + )"sv); + } +#endif +} + +TEST_CASE("parsing - arrays-of-tables") +{ + parsing_should_succeed(FILE_LINE_ARGS, + R"( + points = [ { x = 1, y = 2, z = 3 }, + { x = 7, y = 8, z = 9 }, + { x = 2, y = 4, z = 8 } ] + + [[products]] + name = "Hammer" + sku = 738594937 + + [[products]] + + [[products]] + name = "Nail" + sku = 284758393 + + color = "gray" + + [[fruit]] + name = "apple" + + [fruit.physical] # subtable + color = "red" + shape = "round" + + [[fruit.variety]] # nested array of tables + name = "red delicious" + + [[fruit.variety]] + name = "granny smith" + + [[fruit]] + name = "banana" + + [[fruit.variety]] + name = "plantain" + + )"sv, + [](table&& tbl) + { + REQUIRE(tbl["points"].as<array>()); + CHECK(tbl["points"].as<array>()->size() == 3u); + CHECK(tbl["points"].as<array>()->is_homogeneous()); + CHECK(tbl["points"].as<array>()->is_array_of_tables()); + CHECK(tbl["points"][0]["x"] == 1); + CHECK(tbl["points"][0]["y"] == 2); + CHECK(tbl["points"][0]["z"] == 3); + CHECK(tbl["points"][1]["x"] == 7); + CHECK(tbl["points"][1]["y"] == 8); + CHECK(tbl["points"][1]["z"] == 9); + CHECK(tbl["points"][2]["x"] == 2); + CHECK(tbl["points"][2]["y"] == 4); + CHECK(tbl["points"][2]["z"] == 8); + + REQUIRE(tbl["products"].as<array>()); + CHECK(tbl["products"].as<array>()->size() == 3u); + CHECK(tbl["products"].as<array>()->is_homogeneous()); + CHECK(tbl["products"].as<array>()->is_array_of_tables()); + + REQUIRE(tbl["products"][0].as_table()); + CHECK(tbl["products"][0].as_table()->size() == 2u); + CHECK(tbl["products"][0]["name"] == "Hammer"sv); + CHECK(tbl["products"][0]["sku"] == 738594937); + + REQUIRE(tbl["products"][1].as_table()); + CHECK(tbl["products"][1].as_table()->size() == 0u); + + REQUIRE(tbl["products"][2].as_table()); + CHECK(tbl["products"][2].as_table()->size() == 3u); + CHECK(tbl["products"][2]["name"] == "Nail"sv); + CHECK(tbl["products"][2]["sku"] == 284758393); + CHECK(tbl["products"][2]["color"] == "gray"sv); + + REQUIRE(tbl["fruit"].as<array>()); + CHECK(tbl["fruit"].as<array>()->size() == 2u); + CHECK(tbl["fruit"].as<array>()->is_homogeneous()); + CHECK(tbl["fruit"].as<array>()->is_array_of_tables()); + + REQUIRE(tbl["fruit"][0].as_table()); + CHECK(tbl["fruit"][0].as_table()->size() == 3u); + CHECK(tbl["fruit"][0]["name"] == "apple"sv); + + REQUIRE(tbl["fruit"][0]["physical"].as_table()); + CHECK(tbl["fruit"][0]["physical"].as_table()->size() == 2u); + CHECK(tbl["fruit"][0]["physical"]["color"] == "red"sv); + CHECK(tbl["fruit"][0]["physical"]["shape"] == "round"sv); + + REQUIRE(tbl["fruit"][0]["variety"].as<array>()); + CHECK(tbl["fruit"][0]["variety"].as<array>()->size() == 2u); + CHECK(tbl["fruit"][0]["variety"].as<array>()->is_homogeneous()); + CHECK(tbl["fruit"][0]["variety"].as<array>()->is_array_of_tables()); + CHECK(tbl["fruit"][0]["variety"][0]["name"] == "red delicious"sv); + CHECK(tbl["fruit"][0]["variety"][1]["name"] == "granny smith"sv); + + REQUIRE(tbl["fruit"][1].as_table()); + CHECK(tbl["fruit"][1].as_table()->size() == 2u); + CHECK(tbl["fruit"][1]["name"] == "banana"sv); + + REQUIRE(tbl["fruit"][1]["variety"].as<array>()); + CHECK(tbl["fruit"][1]["variety"].as<array>()->size() == 1u); + CHECK(tbl["fruit"][1]["variety"].as<array>()->is_homogeneous()); + CHECK(tbl["fruit"][1]["variety"].as<array>()->is_array_of_tables()); + CHECK(tbl["fruit"][1]["variety"][0]["name"] == "plantain"sv); + }); + + parsing_should_fail(FILE_LINE_ARGS, R"( +# INVALID TOML DOC +[fruit.physical] # subtable, but to which parent element should it belong? + color = "red" + shape = "round" + +[[fruit]] # parser must throw an error upon discovering that "fruit" is + # an array rather than a table + name = "apple" +)"sv); + + parsing_should_fail(FILE_LINE_ARGS, R"( +# INVALID TOML DOC +fruit = [] + +[[fruit]] # Not allowed +)"sv); + + parsing_should_fail(FILE_LINE_ARGS, R"( +# INVALID TOML DOC +[[fruit]] + name = "apple" + + [[fruit.variety]] + name = "red delicious" + + # INVALID: This table conflicts with the previous array of tables + [fruit.variety] + name = "granny smith" +)"sv); + + parsing_should_fail(FILE_LINE_ARGS, R"( +# INVALID TOML DOC +[[fruit]] + name = "apple" + + [fruit.physical] + color = "red" + shape = "round" + + # INVALID: This array of tables conflicts with the previous table + [[fruit.physical]] + color = "green" +)"sv); +} + +TEST_CASE("parsing - keys") +{ + parsing_should_succeed(FILE_LINE_ARGS, + R"( +[a.b] +c = "10.0.0.1" +d = "frontend" +e = { f.g = 79.5, h = 72.0 } + )"sv, + [](table&& tbl) + { + // ensure types are sane first + REQUIRE(tbl["a"].is_table()); + REQUIRE(tbl["a"]["b"].is_table()); + REQUIRE(tbl["a"]["b"]["c"]); + REQUIRE(tbl["a"]["b"]["d"]); + REQUIRE(tbl["a"]["b"]["e"].is_table()); + REQUIRE(tbl["a"]["b"]["e"]["f"].is_table()); + REQUIRE(tbl["a"]["b"]["e"]["f"]["g"]); + REQUIRE(tbl["a"]["b"]["e"]["h"]); + + const auto check_key = + [&](const auto& t, std::string_view k, source_position b, source_position e) + { + const toml::key& found_key = t.as_table()->find(k)->first; + CHECK(found_key.str() == k); + CHECK(found_key.source().begin == b); + CHECK(found_key.source().end == e); + CHECK(found_key.source().path == tbl.source().path); + }; + + check_key(tbl, "a", { 2, 2 }, { 2, 3 }); + check_key(tbl["a"], "b", { 2, 4 }, { 2, 5 }); + check_key(tbl["a"]["b"], "c", { 3, 1 }, { 3, 2 }); + check_key(tbl["a"]["b"], "d", { 4, 1 }, { 4, 2 }); + check_key(tbl["a"]["b"], "e", { 5, 1 }, { 5, 2 }); + check_key(tbl["a"]["b"]["e"], "f", { 5, 7 }, { 5, 8 }); + check_key(tbl["a"]["b"]["e"]["f"], "g", { 5, 9 }, { 5, 10 }); + check_key(tbl["a"]["b"]["e"], "h", { 5, 19 }, { 5, 20 }); + }); +} |
