diff --git a/tests/test_parse_array.cpp b/tests/test_parse_array.cpp index 053d4c5..2d118c2 100644 --- a/tests/test_parse_array.cpp +++ b/tests/test_parse_array.cpp @@ -8,30 +8,30 @@ using namespace detail; BOOST_AUTO_TEST_CASE(test_oneline_array) { - TOML11_TEST_PARSE_EQUAL(parse_array, "[]", array()); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array, "[]", array()); { array a(5); a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); a[3] = toml::value(1); a[4] = toml::value(5); - TOML11_TEST_PARSE_EQUAL(parse_array, "[3,1,4,1,5]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array, "[3,1,4,1,5]", a); } { array a(3); a[0] = toml::value("foo"); a[1] = toml::value("bar"); a[2] = toml::value("baz"); - TOML11_TEST_PARSE_EQUAL(parse_array, "[\"foo\", \"bar\", \"baz\"]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array, "[\"foo\", \"bar\", \"baz\"]", a); } { array a(5); a[0] = toml::value(3); a[1] = toml::value(1); a[2] = toml::value(4); a[3] = toml::value(1); a[4] = toml::value(5); - TOML11_TEST_PARSE_EQUAL(parse_array, "[3,1,4,1,5,]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array, "[3,1,4,1,5,]", a); } { array a(3); a[0] = toml::value("foo"); a[1] = toml::value("bar"); a[2] = toml::value("baz"); - TOML11_TEST_PARSE_EQUAL(parse_array, "[\"foo\", \"bar\", \"baz\",]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array, "[\"foo\", \"bar\", \"baz\",]", a); } } @@ -66,8 +66,8 @@ BOOST_AUTO_TEST_CASE(test_oneline_array_value) BOOST_AUTO_TEST_CASE(test_multiline_array) { - TOML11_TEST_PARSE_EQUAL(parse_array>, "[\n#comment\n]", typename basic_value< discard_comments>::array_type()); - TOML11_TEST_PARSE_EQUAL(parse_array>, "[\n#comment\n]", typename basic_value::array_type()); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\n#comment\n]", typename basic_value< discard_comments>::array_type()); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\n#comment\n]", typename basic_value::array_type()); { typename basic_value::array_type a(5); @@ -76,7 +76,7 @@ BOOST_AUTO_TEST_CASE(test_multiline_array) a[2] = basic_value(4); a[3] = basic_value(1); a[4] = basic_value(5); - TOML11_TEST_PARSE_EQUAL(parse_array>, "[3,\n1,\n4,\n1,\n5]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[3,\n1,\n4,\n1,\n5]", a); } { typename basic_value::array_type a(5); @@ -85,7 +85,7 @@ BOOST_AUTO_TEST_CASE(test_multiline_array) a[2] = basic_value(4); a[3] = basic_value(1); a[4] = basic_value(5); - TOML11_TEST_PARSE_EQUAL(parse_array>, "[3,\n1,\n4,\n1,\n5]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[3,\n1,\n4,\n1,\n5]", a); } { @@ -95,7 +95,7 @@ BOOST_AUTO_TEST_CASE(test_multiline_array) a[2] = basic_value(4); a[3] = basic_value(1); a[4] = basic_value(5); - TOML11_TEST_PARSE_EQUAL(parse_array>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a); } { typename basic_value::array_type a(5); @@ -104,7 +104,7 @@ BOOST_AUTO_TEST_CASE(test_multiline_array) a[2] = basic_value(4, {"comment"}); a[3] = basic_value(1, {"comment"}); a[4] = basic_value(5, {"comment"}); - TOML11_TEST_PARSE_EQUAL(parse_array>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[3,#comment\n1,#comment\n4,#comment\n1,#comment\n5 #comment\n]", a); } @@ -113,14 +113,14 @@ BOOST_AUTO_TEST_CASE(test_multiline_array) a[0] = basic_value("foo"); a[1] = basic_value("bar"); a[2] = basic_value("baz"); - TOML11_TEST_PARSE_EQUAL(parse_array>, "[\"foo\",\n\"bar\",\n\"baz\"]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\"foo\",\n\"bar\",\n\"baz\"]", a); } { typename basic_value::array_type a(3); a[0] = basic_value("foo"); a[1] = basic_value("bar"); a[2] = basic_value("baz"); - TOML11_TEST_PARSE_EQUAL(parse_array>, "[\"foo\",\n\"bar\",\n\"baz\"]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\"foo\",\n\"bar\",\n\"baz\"]", a); } { @@ -128,14 +128,14 @@ BOOST_AUTO_TEST_CASE(test_multiline_array) a[0] = basic_value("foo"); a[1] = basic_value("b#r"); a[2] = basic_value("b#z"); - TOML11_TEST_PARSE_EQUAL(parse_array>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a); } { typename basic_value::array_type a(3); a[0] = basic_value("foo", {"comment"}); a[1] = basic_value("b#r", {"comment"}); a[2] = basic_value("b#z", {"comment"}); - TOML11_TEST_PARSE_EQUAL(parse_array>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a); + TOML11_TEST_PARSE_EQUAL_VAT(parse_array>, "[\"foo\",#comment\n\"b#r\",#comment\n\"b#z\"#comment\n]", a); } } diff --git a/tests/test_parse_aux.hpp b/tests/test_parse_aux.hpp index b9fd07c..331d4c8 100644 --- a/tests/test_parse_aux.hpp +++ b/tests/test_parse_aux.hpp @@ -20,11 +20,27 @@ do { \ } while(false); \ /**/ +#define TOML11_TEST_PARSE_EQUAL_VAT(psr, tkn, expct) \ +do { \ + const std::string token(tkn); \ + toml::detail::location loc("test", token); \ + const auto result = psr(loc, 0); \ + BOOST_TEST(result.is_ok()); \ + if(result.is_ok()){ \ + BOOST_TEST(result.unwrap().first == expct); \ + } else { \ + std::cerr << "parser " << #psr << " failed with input `"; \ + std::cerr << token << "`.\n"; \ + std::cerr << "reason: " << result.unwrap_err() << '\n'; \ + } \ +} while(false); \ +/**/ + #define TOML11_TEST_PARSE_EQUAL_VALUE(psr, tkn, expct) \ do { \ const std::string token(tkn); \ toml::detail::location loc("test", token); \ - const auto result = psr(loc); \ + const auto result = psr(loc, 0); \ BOOST_TEST(result.is_ok()); \ if(result.is_ok()){ \ BOOST_TEST(result.unwrap() == expct); \ @@ -35,3 +51,4 @@ do { \ } \ } while(false); \ /**/ + diff --git a/tests/test_parse_inline_table.cpp b/tests/test_parse_inline_table.cpp index 9217a23..a11767c 100644 --- a/tests/test_parse_inline_table.cpp +++ b/tests/test_parse_inline_table.cpp @@ -8,19 +8,19 @@ using namespace detail; BOOST_AUTO_TEST_CASE(test_inline_table) { - TOML11_TEST_PARSE_EQUAL(parse_inline_table, "{}", table()); + TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table, "{}", table()); { table t; t["foo"] = toml::value(42); t["bar"] = toml::value("baz"); - TOML11_TEST_PARSE_EQUAL(parse_inline_table, "{foo = 42, bar = \"baz\"}", t); + TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table, "{foo = 42, bar = \"baz\"}", t); } { table t; table t_sub; t_sub["name"] = toml::value("pug"); t["type"] = toml::value(t_sub); - TOML11_TEST_PARSE_EQUAL(parse_inline_table, "{type.name = \"pug\"}", t); + TOML11_TEST_PARSE_EQUAL_VAT(parse_inline_table, "{type.name = \"pug\"}", t); } } diff --git a/toml/literal.hpp b/toml/literal.hpp index 04fbbc1..5086a76 100644 --- a/toml/literal.hpp +++ b/toml/literal.hpp @@ -53,7 +53,7 @@ literal_internal_impl(::toml::detail::location loc) // If it is neither a table-key or a array-of-table-key, it may be a value. if(!is_table_key && !is_aots_key) { - if(auto data = ::toml::detail::parse_value(loc)) + if(auto data = ::toml::detail::parse_value(loc, 0)) { return data.unwrap(); } diff --git a/toml/parser.hpp b/toml/parser.hpp index 497d5a2..a8553dc 100644 --- a/toml/parser.hpp +++ b/toml/parser.hpp @@ -8,6 +8,7 @@ #include "combinator.hpp" #include "lexer.hpp" +#include "macros.hpp" #include "region.hpp" #include "result.hpp" #include "types.hpp" @@ -22,6 +23,11 @@ #endif // __cpp_lib_filesystem #endif // TOML11_DISABLE_STD_FILESYSTEM +// the previous commit works with 500+ recursions. so it may be too small. +// but in most cases, i think we don't need such a deep recursion of +// arrays or inline-tables. +#define TOML11_VALUE_RECURSION_LIMIT 64 + namespace toml { namespace detail @@ -1138,15 +1144,23 @@ parse_key(location& loc) // forward-decl to implement parse_array and parse_table template -result parse_value(location&); +result parse_value(location&, const std::size_t n_rec); template result, std::string> -parse_array(location& loc) +parse_array(location& loc, const std::size_t n_rec) { using value_type = Value; using array_type = typename value_type::array_type; + if(n_rec > TOML11_VALUE_RECURSION_LIMIT) + { + // parse_array does not have any way to handle recursive error currently... + throw syntax_error(std::string("toml::parse_array: recursion limit (" + TOML11_STRINGIZE(TOML11_VALUE_RECURSION_LIMIT) ") exceeded"), + source_location(loc)); + } + const auto first = loc.iter(); if(loc.iter() == loc.end()) { @@ -1173,7 +1187,7 @@ parse_array(location& loc) region(loc, first, loc.iter()))); } - if(auto val = parse_value(loc)) + if(auto val = parse_value(loc, n_rec+1)) { // After TOML v1.0.0-rc.1, array becomes to be able to have values // with different types. So here we will omit this by default. @@ -1247,7 +1261,7 @@ parse_array(location& loc) template result, region>, Value>, std::string> -parse_key_value_pair(location& loc) +parse_key_value_pair(location& loc, const std::size_t n_rec) { using value_type = Value; @@ -1294,7 +1308,7 @@ parse_key_value_pair(location& loc) } const auto after_kvsp = loc.iter(); // err msg - auto val = parse_value(loc); + auto val = parse_value(loc, n_rec); if(!val) { std::string msg; @@ -1341,7 +1355,7 @@ result, region>, std::string> parse_array_table_key(location& loc); template result, std::string> -parse_inline_table(location& loc); +parse_inline_table(location& loc, const std::size_t n_rec); // The following toml file is allowed. // ```toml @@ -1379,7 +1393,7 @@ bool is_valid_forward_table_definition(const Value& fwd, const Value& inserting, inserting_reg = ptr->str(); } location inserting_def("internal", std::move(inserting_reg)); - if(const auto inlinetable = parse_inline_table(inserting_def)) + if(const auto inlinetable = parse_inline_table(inserting_def, 0)) { // check if we are overwriting existing table. // ```toml @@ -1810,11 +1824,18 @@ insert_nested_key(typename Value::table_type& root, const Value& v, template result, std::string> -parse_inline_table(location& loc) +parse_inline_table(location& loc, const std::size_t n_rec) { using value_type = Value; using table_type = typename value_type::table_type; + if(n_rec > TOML11_VALUE_RECURSION_LIMIT) + { + throw syntax_error(std::string("toml::parse_inline_table: recursion limit (" + TOML11_STRINGIZE(TOML11_VALUE_RECURSION_LIMIT) ") exceeded"), + source_location(loc)); + } + const auto first = loc.iter(); table_type retval; if(!(loc.iter() != loc.end() && *loc.iter() == '{')) @@ -1835,7 +1856,7 @@ parse_inline_table(location& loc) // it starts from "{". it should be formatted as inline-table while(loc.iter() != loc.end()) { - const auto kv_r = parse_key_value_pair(loc); + const auto kv_r = parse_key_value_pair(loc, n_rec+1); if(!kv_r) { return err(kv_r.unwrap_err()); @@ -2079,7 +2100,7 @@ parse_value_helper(result, std::string> rslt) } template -result parse_value(location& loc) +result parse_value(location& loc, const std::size_t n_rec) { const auto first = loc.iter(); if(first == loc.end()) @@ -2104,8 +2125,8 @@ result parse_value(location& loc) case value_t::local_datetime : {return parse_value_helper(parse_local_datetime(loc) );} case value_t::local_date : {return parse_value_helper(parse_local_date(loc) );} case value_t::local_time : {return parse_value_helper(parse_local_time(loc) );} - case value_t::array : {return parse_value_helper(parse_array(loc) );} - case value_t::table : {return parse_value_helper(parse_inline_table(loc));} + case value_t::array : {return parse_value_helper(parse_array(loc, n_rec));} + case value_t::table : {return parse_value_helper(parse_inline_table(loc, n_rec));} default: { const auto msg = format_underline("toml::parse_value: " @@ -2270,7 +2291,7 @@ parse_ml_table(location& loc) return ok(tab); } - if(const auto kv = parse_key_value_pair(loc)) + if(const auto kv = parse_key_value_pair(loc, 0)) { const auto& kvpair = kv.unwrap(); const std::vector& keys = kvpair.first.first;