// This file is a part of toml++ and is subject to the the terms of the MIT license. // Copyright (c) 2019-2020 Mark Gillard // See https://github.com/marzer/tomlplusplus/blob/master/LICENSE for the full license text. // SPDX-License-Identifier: MIT #include "tests.h" TEST_CASE("parsing - tables") { parsing_should_succeed( FILE_LINE_ARGS, R"( [table] [table-1] key1 = "some string" key2 = 123 [table-2] key1 = "another string" key2 = 456 [dog."tater.man"] type.name = "pug" [a.b.c] # this is best practice [ d.e.f ] # same as [d.e.f] [ g . h . i ] # same as [g.h.i] [ j . "ʞ" . 'l' ] # same as [j."ʞ".'l'] # [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 [fruit] apple.color = "red" apple.taste.sweet = true [fruit.apple.texture] # you can add sub-tables smooth = true )"sv, [](table&& tbl) { REQUIRE(tbl["table"].as()); CHECK(tbl["table"].as
()->size() == 0_sz); REQUIRE(tbl["table-1"].as
()); CHECK(tbl["table-1"].as
()->size() == 2_sz); CHECK(tbl["table-1"]["key1"] == "some string"sv); CHECK(tbl["table-1"]["key2"] == 123); REQUIRE(tbl["table-2"].as
()); CHECK(tbl["table-2"].as
()->size() == 2_sz); CHECK(tbl["table-2"]["key1"] == "another string"sv); CHECK(tbl["table-2"]["key2"] == 456); REQUIRE(tbl["dog"].as
()); CHECK(tbl["dog"].as
()->size() == 1_sz); REQUIRE(tbl["dog"]["tater.man"].as
()); CHECK(tbl["dog"]["tater.man"].as
()->size() == 1_sz); CHECK(tbl["dog"]["tater.man"]["type"]["name"] == "pug"sv); CHECK(tbl["a"].as
()); CHECK(tbl["a"]["b"].as
()); CHECK(tbl["a"]["b"]["c"].as
()); CHECK(tbl["d"].as
()); CHECK(tbl["d"]["e"].as
()); CHECK(tbl["d"]["e"]["f"].as
()); CHECK(tbl["g"].as
()); CHECK(tbl["g"]["h"].as
()); CHECK(tbl["g"]["h"]["i"].as
()); CHECK(tbl["j"].as
()); CHECK(tbl["j"]["ʞ"].as
()); CHECK(tbl["j"]["ʞ"]["l"].as
()); REQUIRE(tbl["fruit"].as
()); CHECK(tbl["fruit"]["apple"]["color"] == "red"sv); CHECK(tbl["fruit"]["apple"]["taste"]["sweet"] == true); CHECK(tbl["fruit"]["apple"]["texture"]["smooth"] == true); } ); 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); parsing_should_fail(FILE_LINE_ARGS, R"( [fruit] apple.color = "red" apple.taste.sweet = true [fruit.apple] )"sv); parsing_should_fail(FILE_LINE_ARGS, R"( [fruit] apple.color = "red" apple.taste.sweet = true [fruit.apple.taste] )"sv); parsing_should_succeed( FILE_LINE_ARGS, R"( # VALID BUT DISCOURAGED [fruit.apple] [animal] [fruit.orange] )"sv, [](table&& tbl) { REQUIRE(tbl["animal"].as
()); CHECK(tbl["animal"].as
()->size() == 0_sz); REQUIRE(tbl["fruit"].as
()); CHECK(tbl["fruit"].as
()->size() == 2_sz); REQUIRE(tbl["fruit"]["apple"].as
()); REQUIRE(tbl["fruit"]["orange"].as
()); } ); parsing_should_succeed( FILE_LINE_ARGS, R"( # RECOMMENDED [fruit.apple] [fruit.orange] [animal] )"sv, [](table&& tbl) { REQUIRE(tbl["animal"].as
()); CHECK(tbl["animal"].as
()->size() == 0_sz); REQUIRE(tbl["fruit"].as
()); CHECK(tbl["fruit"].as
()->size() == 2_sz); REQUIRE(tbl["fruit"]["apple"].as
()); REQUIRE(tbl["fruit"]["orange"].as
()); } ); parsing_should_fail(FILE_LINE_ARGS, R"([])"sv); } TEST_CASE("parsing - inline tables") { parsing_should_succeed( FILE_LINE_ARGS, R"( name = { first = "Tom", last = "Preston-Werner" } point = { x = 1, y = 2 } animal = { type.name = "pug" } [product] type = { name = "Nail" } )"sv, [](table&& tbl) { REQUIRE(tbl["name"].as
()); CHECK(tbl["name"].as
()->size() == 2_sz); CHECK(tbl["name"]["first"] == "Tom"sv); CHECK(tbl["name"]["last"] == "Preston-Werner"sv); REQUIRE(tbl["point"].as
()); CHECK(tbl["point"].as
()->size() == 2_sz); CHECK(tbl["point"]["x"] == 1); CHECK(tbl["point"]["y"] == 2); REQUIRE(tbl["animal"].as
()); CHECK(tbl["animal"].as
()->size() == 1_sz); REQUIRE(tbl["animal"]["type"].as
()); CHECK(tbl["animal"]["type"].as
()->size() == 1_sz); CHECK(tbl["animal"]["type"]["name"] == "pug"sv); REQUIRE(tbl["product"].as
()); CHECK(tbl["product"].as
()->size() == 1_sz); REQUIRE(tbl["product"]["type"].as
()); CHECK(tbl["product"]["type"].as
()->size() == 1_sz); CHECK(tbl["product"]["type"]["name"] == "Nail"sv); } ); parsing_should_fail(FILE_LINE_ARGS, R"( [product] type = { name = "Nail" } type.edible = false # INVALID )"sv); 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
()); CHECK(tbl["test"].as
()->size() == 3_sz); CHECK(tbl["test"]["val1"] == "foo"sv); REQUIRE(tbl["test"]["val2"].as()); CHECK(tbl["test"]["val2"].as()->size() == 3_sz); 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
()); CHECK(tbl["name"].as
()->size() == 2_sz); 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()); CHECK(tbl["points"].as()->size() == 3_sz); CHECK(tbl["points"].as()->is_homogeneous()); CHECK(tbl["points"].as()->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()); CHECK(tbl["products"].as()->size() == 3_sz); CHECK(tbl["products"].as()->is_homogeneous()); CHECK(tbl["products"].as()->is_array_of_tables()); REQUIRE(tbl["products"][0].as
()); CHECK(tbl["products"][0].as
()->size() == 2_sz); CHECK(tbl["products"][0]["name"] == "Hammer"sv); CHECK(tbl["products"][0]["sku"] == 738594937); REQUIRE(tbl["products"][1].as
()); CHECK(tbl["products"][1].as
()->size() == 0_sz); REQUIRE(tbl["products"][2].as
()); CHECK(tbl["products"][2].as
()->size() == 3_sz); CHECK(tbl["products"][2]["name"] == "Nail"sv); CHECK(tbl["products"][2]["sku"] == 284758393); CHECK(tbl["products"][2]["color"] == "gray"sv); REQUIRE(tbl["fruit"].as()); CHECK(tbl["fruit"].as()->size() == 2_sz); CHECK(tbl["fruit"].as()->is_homogeneous()); CHECK(tbl["fruit"].as()->is_array_of_tables()); REQUIRE(tbl["fruit"][0].as
()); CHECK(tbl["fruit"][0].as
()->size() == 3_sz); CHECK(tbl["fruit"][0]["name"] == "apple"sv); REQUIRE(tbl["fruit"][0]["physical"].as
()); CHECK(tbl["fruit"][0]["physical"].as
()->size() == 2_sz); CHECK(tbl["fruit"][0]["physical"]["color"] == "red"sv); CHECK(tbl["fruit"][0]["physical"]["shape"] == "round"sv); REQUIRE(tbl["fruit"][0]["variety"].as()); CHECK(tbl["fruit"][0]["variety"].as()->size() == 2_sz); CHECK(tbl["fruit"][0]["variety"].as()->is_homogeneous()); CHECK(tbl["fruit"][0]["variety"].as()->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
()); CHECK(tbl["fruit"][1].as
()->size() == 2_sz); CHECK(tbl["fruit"][1]["name"] == "banana"sv); REQUIRE(tbl["fruit"][1]["variety"].as()); CHECK(tbl["fruit"][1]["variety"].as()->size() == 1_sz); CHECK(tbl["fruit"][1]["variety"].as()->is_homogeneous()); CHECK(tbl["fruit"][1]["variety"].as()->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); }