// 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, S(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[S("table")].as()); CHECK(tbl[S("table")].as
()->size() == 0_sz); REQUIRE(tbl[S("table-1")].as
()); CHECK(tbl[S("table-1")].as
()->size() == 2_sz); CHECK(tbl[S("table-1")][S("key1")] == S("some string"sv)); CHECK(tbl[S("table-1")][S("key2")] == 123); REQUIRE(tbl[S("table-2")].as
()); CHECK(tbl[S("table-2")].as
()->size() == 2_sz); CHECK(tbl[S("table-2")][S("key1")] == S("another string"sv)); CHECK(tbl[S("table-2")][S("key2")] == 456); REQUIRE(tbl[S("dog")].as
()); CHECK(tbl[S("dog")].as
()->size() == 1_sz); REQUIRE(tbl[S("dog")][S("tater.man")].as
()); CHECK(tbl[S("dog")][S("tater.man")].as
()->size() == 1_sz); CHECK(tbl[S("dog")][S("tater.man")][S("type")][S("name")] == S("pug"sv)); CHECK(tbl[S("a")].as
()); CHECK(tbl[S("a")][S("b")].as
()); CHECK(tbl[S("a")][S("b")][S("c")].as
()); CHECK(tbl[S("d")].as
()); CHECK(tbl[S("d")][S("e")].as
()); CHECK(tbl[S("d")][S("e")][S("f")].as
()); CHECK(tbl[S("g")].as
()); CHECK(tbl[S("g")][S("h")].as
()); CHECK(tbl[S("g")][S("h")][S("i")].as
()); CHECK(tbl[S("j")].as
()); CHECK(tbl[S("j")][S("ʞ")].as
()); CHECK(tbl[S("j")][S("ʞ")][S("l")].as
()); REQUIRE(tbl[S("fruit")].as
()); CHECK(tbl[S("fruit")][S("apple")][S("color")] == S("red"sv)); CHECK(tbl[S("fruit")][S("apple")][S("taste")][S("sweet")] == true); CHECK(tbl[S("fruit")][S("apple")][S("texture")][S("smooth")] == true); } ); parsing_should_fail(FILE_LINE_ARGS, S(R"( # DO NOT DO THIS [fruit] apple = "red" [fruit] orange = "orange" )"sv)); parsing_should_fail(FILE_LINE_ARGS, S(R"( # DO NOT DO THIS EITHER [fruit] apple = "red" [fruit.apple] texture = "smooth" )"sv)); parsing_should_fail(FILE_LINE_ARGS, S(R"( [fruit] apple.color = "red" apple.taste.sweet = true [fruit.apple] )"sv)); parsing_should_fail(FILE_LINE_ARGS, S(R"( [fruit] apple.color = "red" apple.taste.sweet = true [fruit.apple.taste] )"sv)); parsing_should_succeed( FILE_LINE_ARGS, S(R"( # VALID BUT DISCOURAGED [fruit.apple] [animal] [fruit.orange] )"sv), [](table&& tbl) { REQUIRE(tbl[S("animal")].as
()); CHECK(tbl[S("animal")].as
()->size() == 0_sz); REQUIRE(tbl[S("fruit")].as
()); CHECK(tbl[S("fruit")].as
()->size() == 2_sz); REQUIRE(tbl[S("fruit")][S("apple")].as
()); REQUIRE(tbl[S("fruit")][S("orange")].as
()); } ); parsing_should_succeed( FILE_LINE_ARGS, S(R"( # RECOMMENDED [fruit.apple] [fruit.orange] [animal] )"sv), [](table&& tbl) { REQUIRE(tbl[S("animal")].as
()); CHECK(tbl[S("animal")].as
()->size() == 0_sz); REQUIRE(tbl[S("fruit")].as
()); CHECK(tbl[S("fruit")].as
()->size() == 2_sz); REQUIRE(tbl[S("fruit")][S("apple")].as
()); REQUIRE(tbl[S("fruit")][S("orange")].as
()); } ); parsing_should_fail(FILE_LINE_ARGS, S(R"([])"sv)); } TEST_CASE("parsing - inline tables") { parsing_should_succeed( FILE_LINE_ARGS, S(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[S("name")].as
()); CHECK(tbl[S("name")].as
()->size() == 2_sz); CHECK(tbl[S("name")][S("first")] == S("Tom"sv)); CHECK(tbl[S("name")][S("last")] == S("Preston-Werner"sv)); REQUIRE(tbl[S("point")].as
()); CHECK(tbl[S("point")].as
()->size() == 2_sz); CHECK(tbl[S("point")][S("x")] == 1); CHECK(tbl[S("point")][S("y")] == 2); REQUIRE(tbl[S("animal")].as
()); CHECK(tbl[S("animal")].as
()->size() == 1_sz); REQUIRE(tbl[S("animal")][S("type")].as
()); CHECK(tbl[S("animal")][S("type")].as
()->size() == 1_sz); CHECK(tbl[S("animal")][S("type")][S("name")] == S("pug"sv)); REQUIRE(tbl[S("product")].as
()); CHECK(tbl[S("product")].as
()->size() == 1_sz); REQUIRE(tbl[S("product")][S("type")].as
()); CHECK(tbl[S("product")][S("type")].as
()->size() == 1_sz); CHECK(tbl[S("product")][S("type")][S("name")] == S("Nail"sv)); } ); parsing_should_fail(FILE_LINE_ARGS, S(R"( [product] type = { name = "Nail" } type.edible = false # INVALID )"sv)); parsing_should_fail(FILE_LINE_ARGS, S(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, S(R"( test = { val1 = "foo", val2 = [ 1, 2, 3 ], val3 = "bar" } )"sv), [](table&& tbl) { REQUIRE(tbl[S("test")].as
()); CHECK(tbl[S("test")].as
()->size() == 3_sz); CHECK(tbl[S("test")][S("val1")] == S("foo"sv)); REQUIRE(tbl[S("test")][S("val2")].as()); CHECK(tbl[S("test")][S("val2")].as()->size() == 3_sz); CHECK(tbl[S("test")][S("val2")][0] == 1); CHECK(tbl[S("test")][S("val2")][1] == 2); CHECK(tbl[S("test")][S("val2")][2] == 3); CHECK(tbl[S("test")][S("val3")] == S("bar"sv)); } ); // toml/issues/516 (newlines/trailing commas in inline tables) #if TOML_LANG_UNRELEASED { parsing_should_succeed( FILE_LINE_ARGS, S(R"( name = { first = "Tom", last = "Preston-Werner", } )"sv), [](table&& tbl) { REQUIRE(tbl[S("name")].as
()); CHECK(tbl[S("name")].as
()->size() == 2_sz); CHECK(tbl[S("name")][S("first")] == S("Tom"sv)); CHECK(tbl[S("name")][S("last")] == S("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, S(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, S(R"( name = { first = "Tom", last = "Preston-Werner" } )"sv)); } #endif } TEST_CASE("parsing - arrays-of-tables") { parsing_should_succeed( FILE_LINE_ARGS, S(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[S("points")].as()); CHECK(tbl[S("points")].as()->size() == 3_sz); CHECK(tbl[S("points")].as()->is_homogeneous()); CHECK(tbl[S("points")].as()->is_array_of_tables()); CHECK(tbl[S("points")][0][S("x")] == 1); CHECK(tbl[S("points")][0][S("y")] == 2); CHECK(tbl[S("points")][0][S("z")] == 3); CHECK(tbl[S("points")][1][S("x")] == 7); CHECK(tbl[S("points")][1][S("y")] == 8); CHECK(tbl[S("points")][1][S("z")] == 9); CHECK(tbl[S("points")][2][S("x")] == 2); CHECK(tbl[S("points")][2][S("y")] == 4); CHECK(tbl[S("points")][2][S("z")] == 8); REQUIRE(tbl[S("products")].as()); CHECK(tbl[S("products")].as()->size() == 3_sz); CHECK(tbl[S("products")].as()->is_homogeneous()); CHECK(tbl[S("products")].as()->is_array_of_tables()); REQUIRE(tbl[S("products")][0].as
()); CHECK(tbl[S("products")][0].as
()->size() == 2_sz); CHECK(tbl[S("products")][0][S("name")] == S("Hammer"sv)); CHECK(tbl[S("products")][0][S("sku")] == 738594937); REQUIRE(tbl[S("products")][1].as
()); CHECK(tbl[S("products")][1].as
()->size() == 0_sz); REQUIRE(tbl[S("products")][2].as
()); CHECK(tbl[S("products")][2].as
()->size() == 3_sz); CHECK(tbl[S("products")][2][S("name")] == S("Nail"sv)); CHECK(tbl[S("products")][2][S("sku")] == 284758393); CHECK(tbl[S("products")][2][S("color")] == S("gray"sv)); REQUIRE(tbl[S("fruit")].as()); CHECK(tbl[S("fruit")].as()->size() == 2_sz); CHECK(tbl[S("fruit")].as()->is_homogeneous()); CHECK(tbl[S("fruit")].as()->is_array_of_tables()); REQUIRE(tbl[S("fruit")][0].as
()); CHECK(tbl[S("fruit")][0].as
()->size() == 3_sz); CHECK(tbl[S("fruit")][0][S("name")] == S("apple"sv)); REQUIRE(tbl[S("fruit")][0][S("physical")].as
()); CHECK(tbl[S("fruit")][0][S("physical")].as
()->size() == 2_sz); CHECK(tbl[S("fruit")][0][S("physical")][S("color")] == S("red"sv)); CHECK(tbl[S("fruit")][0][S("physical")][S("shape")] == S("round"sv)); REQUIRE(tbl[S("fruit")][0][S("variety")].as()); CHECK(tbl[S("fruit")][0][S("variety")].as()->size() == 2_sz); CHECK(tbl[S("fruit")][0][S("variety")].as()->is_homogeneous()); CHECK(tbl[S("fruit")][0][S("variety")].as()->is_array_of_tables()); CHECK(tbl[S("fruit")][0][S("variety")][0][S("name")] == S("red delicious"sv)); CHECK(tbl[S("fruit")][0][S("variety")][1][S("name")] == S("granny smith"sv)); REQUIRE(tbl[S("fruit")][1].as
()); CHECK(tbl[S("fruit")][1].as
()->size() == 2_sz); CHECK(tbl[S("fruit")][1][S("name")] == S("banana"sv)); REQUIRE(tbl[S("fruit")][1][S("variety")].as()); CHECK(tbl[S("fruit")][1][S("variety")].as()->size() == 1_sz); CHECK(tbl[S("fruit")][1][S("variety")].as()->is_homogeneous()); CHECK(tbl[S("fruit")][1][S("variety")].as()->is_array_of_tables()); CHECK(tbl[S("fruit")][1][S("variety")][0][S("name")] == S("plantain"sv)); } ); parsing_should_fail(FILE_LINE_ARGS, S(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, S(R"( # INVALID TOML DOC fruit = [] [[fruit]] # Not allowed )"sv)); parsing_should_fail(FILE_LINE_ARGS, S(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, S(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)); }