mirror of
https://github.com/boostorg/parser.git
synced 2026-01-19 04:22:13 +00:00
not assign over valid values in a sequence's attribute after successfully parsing a subsquent nope-attributes parser. Test cases by Andreas Buhr. Fixes #279 Fixes #285
521 lines
14 KiB
C++
521 lines
14 KiB
C++
/**
|
|
* Copyright (C) 2024 T. Zachary Laine
|
|
*
|
|
* Distributed under the Boost Software License, Version 1.0. (See
|
|
* accompanying file LICENSE_1_0.txt or copy at
|
|
* http://www.boost.org/LICENSE_1_0.txt)
|
|
*/
|
|
|
|
#include <boost/parser/parser.hpp>
|
|
#include <boost/parser/transcode_view.hpp>
|
|
|
|
#if __has_include(<boost/optional/optional.hpp>)
|
|
#define TEST_BOOST_OPTIONAL 1
|
|
#include <boost/optional/optional.hpp>
|
|
#else
|
|
#define TEST_BOOST_OPTIONAL 0
|
|
#endif
|
|
|
|
#include <set>
|
|
|
|
#include <boost/core/lightweight_test.hpp>
|
|
|
|
|
|
using namespace boost::parser;
|
|
|
|
|
|
void github_issue_36()
|
|
{
|
|
namespace bp = boost::parser;
|
|
|
|
auto id = bp::lexeme[+bp::char_(
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz0123456789")];
|
|
auto ids = +id;
|
|
|
|
std::string str;
|
|
std::vector<std::string> vec;
|
|
|
|
bp::parse("id1", id, bp::ws, str); // (1)
|
|
BOOST_TEST(str == "id1");
|
|
str.clear();
|
|
bp::parse("id1 id2", ids, bp::ws, vec); // (2)
|
|
BOOST_TEST(vec == std::vector<std::string>({"id1", "id2"}));
|
|
|
|
// Intentionally ill-formed.
|
|
// bp::parse("i1 i2", ids, bp::ws, str); // (3)
|
|
}
|
|
|
|
namespace issue_50 {
|
|
struct X
|
|
{
|
|
char a;
|
|
int b;
|
|
|
|
bool operator<(X rhs) const { return a < rhs.a; }
|
|
};
|
|
|
|
struct Y
|
|
{
|
|
std::vector<X> x;
|
|
int c;
|
|
};
|
|
|
|
struct Y2
|
|
{
|
|
std::set<X> x;
|
|
int c;
|
|
};
|
|
|
|
static_assert(
|
|
boost::parser::detail::is_struct_compatible<
|
|
Y,
|
|
boost::parser::
|
|
tuple<std::vector<boost::parser::tuple<char, int>>, int>>());
|
|
|
|
static_assert(
|
|
boost::parser::detail::is_struct_compatible<
|
|
Y2,
|
|
boost::parser::
|
|
tuple<std::vector<boost::parser::tuple<char, int>>, int>>());
|
|
}
|
|
|
|
void github_issue_50()
|
|
{
|
|
using namespace issue_50;
|
|
|
|
namespace bp = boost::parser;
|
|
|
|
{
|
|
auto parse_x = bp::char_ >> bp::int_;
|
|
auto parse_y = +parse_x >> bp::int_;
|
|
|
|
Y y;
|
|
auto b = bp::parse("d 3 4", parse_y, bp::ws, y);
|
|
BOOST_TEST(b);
|
|
|
|
BOOST_TEST(y.x[0].a == 'd');
|
|
BOOST_TEST(y.x[0].b == 3);
|
|
BOOST_TEST(y.c == 4);
|
|
}
|
|
|
|
{
|
|
auto parse_x = bp::char_ >> bp::int_;
|
|
auto parse_y = +parse_x >> bp::int_;
|
|
|
|
Y2 y;
|
|
auto b = bp::parse("d 3 4", parse_y, bp::ws, y);
|
|
BOOST_TEST(b);
|
|
|
|
BOOST_TEST(y.x.begin()->a == 'd');
|
|
BOOST_TEST(y.x.begin()->b == 3);
|
|
BOOST_TEST(y.c == 4);
|
|
}
|
|
}
|
|
|
|
namespace issue_52 {
|
|
struct X
|
|
{
|
|
char a;
|
|
int b;
|
|
};
|
|
|
|
struct Y
|
|
{
|
|
std::vector<X> x;
|
|
int c;
|
|
};
|
|
|
|
struct Z
|
|
{
|
|
std::vector<Y> y;
|
|
int d;
|
|
};
|
|
|
|
struct W
|
|
{
|
|
std::vector<Z> z;
|
|
int e;
|
|
};
|
|
}
|
|
|
|
void github_issue_52()
|
|
{
|
|
using namespace issue_52;
|
|
|
|
namespace bp = boost::parser;
|
|
auto parse_x = bp::char_ >> bp::int_;
|
|
auto parse_y = +parse_x >> bp::int_;
|
|
auto parse_z = +parse_y >> bp::char_;
|
|
auto parse_w = +parse_z >> bp::int_;
|
|
|
|
{
|
|
Z z;
|
|
auto b = bp::parse("d 2 3 c", parse_z, bp::ws, z);
|
|
BOOST_TEST(b);
|
|
|
|
BOOST_TEST(z.y[0].x[0].a == 'd');
|
|
BOOST_TEST(z.y[0].x[0].b == 2);
|
|
BOOST_TEST(z.y[0].c == 3);
|
|
BOOST_TEST(z.d == 'c');
|
|
}
|
|
{
|
|
W w;
|
|
auto b = bp::parse("d 2 3 c 4", parse_w, bp::ws, w);
|
|
BOOST_TEST(b);
|
|
|
|
BOOST_TEST(w.z[0].y[0].x[0].a == 'd');
|
|
BOOST_TEST(w.z[0].y[0].x[0].b == 2);
|
|
BOOST_TEST(w.z[0].y[0].c == 3);
|
|
BOOST_TEST(w.z[0].d == 'c');
|
|
BOOST_TEST(w.e == 4);
|
|
}
|
|
}
|
|
|
|
void github_issue_78()
|
|
{
|
|
namespace bp = boost::parser;
|
|
std::vector<int> result;
|
|
auto b = bp::parse("3 4 c", +bp::int_, bp::ws, result);
|
|
BOOST_TEST(!b);
|
|
BOOST_TEST(result.empty());
|
|
}
|
|
|
|
namespace issue_90 { namespace parser {
|
|
const auto string =
|
|
('"' >> boost::parser::lexeme[*(boost::parser::char_ - '"')]) > '"';
|
|
const auto specifier = string > ':' > string;
|
|
}}
|
|
|
|
void github_issue_90()
|
|
{
|
|
using namespace issue_90;
|
|
namespace bp = boost::parser;
|
|
|
|
std::string input = R"( "dd" : "2" )";
|
|
std::pair<std::string, std::string> result;
|
|
|
|
auto b = bp::parse(input, parser::specifier, bp::ws, result);
|
|
BOOST_TEST(b);
|
|
BOOST_TEST(result.first == "dd");
|
|
BOOST_TEST(result.second == "2");
|
|
}
|
|
|
|
namespace github_issue_125_ {
|
|
namespace bp = boost::parser;
|
|
constexpr bp::
|
|
rule<struct replacement_field_rule, std::optional<unsigned short>>
|
|
replacement_field = "replacement_field";
|
|
constexpr auto replacement_field_def = bp::lit('{') >> -bp::ushort_;
|
|
BOOST_PARSER_DEFINE_RULES(replacement_field);
|
|
}
|
|
|
|
void github_issue_125()
|
|
{
|
|
namespace bp = boost::parser;
|
|
using namespace github_issue_125_;
|
|
|
|
unsigned short integer_found = 99;
|
|
auto print_repl_field = [&](auto & ctx) {
|
|
const std::optional<unsigned short> & val = bp::_attr(ctx);
|
|
if (val)
|
|
integer_found = *val;
|
|
else
|
|
integer_found = 77;
|
|
};
|
|
|
|
{
|
|
integer_found = 99;
|
|
auto result = bp::parse("{9", replacement_field[print_repl_field]);
|
|
BOOST_TEST(result);
|
|
BOOST_TEST(integer_found == 9);
|
|
}
|
|
{
|
|
integer_found = 99;
|
|
auto result = bp::parse("{", replacement_field[print_repl_field]);
|
|
BOOST_TEST(result);
|
|
BOOST_TEST(integer_found == 77);
|
|
}
|
|
}
|
|
|
|
void github_issue_209()
|
|
{
|
|
namespace bp = boost::parser;
|
|
|
|
BOOST_TEST(std::is_sorted(
|
|
std::begin(bp::detail::char_set<detail::punct_chars>::chars),
|
|
std::end(bp::detail::char_set<detail::punct_chars>::chars)));
|
|
|
|
BOOST_TEST(std::is_sorted(
|
|
std::begin(bp::detail::char_set<detail::symb_chars>::chars),
|
|
std::end(bp::detail::char_set<detail::symb_chars>::chars)));
|
|
|
|
BOOST_TEST(std::is_sorted(
|
|
std::begin(bp::detail::char_set<detail::lower_case_chars>::chars),
|
|
std::end(bp::detail::char_set<detail::lower_case_chars>::chars)));
|
|
|
|
BOOST_TEST(std::is_sorted(
|
|
std::begin(bp::detail::char_set<detail::upper_case_chars>::chars),
|
|
std::end(bp::detail::char_set<detail::upper_case_chars>::chars)));
|
|
}
|
|
|
|
void github_issue_223()
|
|
{
|
|
namespace bp = boost::parser;
|
|
|
|
// failing case
|
|
{
|
|
std::vector<char> v;
|
|
const auto parser = *('x' | bp::char_('y'));
|
|
bp::parse("xy", parser, bp::ws, v);
|
|
|
|
BOOST_TEST(v.size() == 1);
|
|
BOOST_TEST(v == std::vector<char>({'y'}));
|
|
|
|
// the assert fails since there are two elements in the vector: '\0'
|
|
// and 'y'. Seems pretty surprising to me
|
|
}
|
|
|
|
// working case
|
|
{
|
|
const auto parser = *('x' | bp::char_('y'));
|
|
const auto result = bp::parse("xy", parser, bp::ws);
|
|
|
|
BOOST_TEST(result->size() == 1);
|
|
BOOST_TEST(*(*result)[0] == 'y');
|
|
|
|
// success, the vector has only one 'y' element
|
|
}
|
|
}
|
|
|
|
namespace github_issue_248_ {
|
|
namespace bp = boost::parser;
|
|
|
|
static constexpr bp::rule<struct symbol, int> symbol = "//";
|
|
static constexpr bp::rule<struct vector, std::vector<int>> list =
|
|
"<int>(,<int>)*";
|
|
static constexpr bp::rule<struct working, std::vector<int>> working =
|
|
"working";
|
|
static constexpr bp::rule<struct failing, std::vector<int>> failing =
|
|
"failing";
|
|
|
|
static auto const symbol_def = bp::symbols<int>{{"//", 0}};
|
|
static constexpr auto list_def = bp::int_ % ',';
|
|
static constexpr auto working_def = -symbol >> (bp::int_ % ',');
|
|
static constexpr auto failing_def = -symbol >> list;
|
|
|
|
BOOST_PARSER_DEFINE_RULES(symbol, list, working, failing);
|
|
}
|
|
|
|
void github_issue_248()
|
|
{
|
|
namespace bp = boost::parser;
|
|
|
|
using namespace github_issue_248_;
|
|
|
|
{
|
|
auto const result = bp::parse("//1,2,3", working, bp::ws);
|
|
auto const expected = std::vector<int>{0, 1, 2, 3};
|
|
BOOST_TEST(result.has_value());
|
|
bool const equal = std::equal(
|
|
result->begin(), result->end(), expected.begin(), expected.end());
|
|
BOOST_TEST(equal);
|
|
if (!equal) {
|
|
std::cout << "contents of *result:\n";
|
|
for (auto x : *result) {
|
|
std::cout << x << '\n';
|
|
}
|
|
std::cout << '\n';
|
|
}
|
|
}
|
|
{
|
|
auto const result = bp::parse("//1,2,3", failing, bp::ws);
|
|
auto const expected = std::vector<int>{0, 1, 2, 3};
|
|
BOOST_TEST(result.has_value());
|
|
bool const equal = std::equal(
|
|
result->begin(), result->end(), expected.begin(), expected.end());
|
|
BOOST_TEST(equal);
|
|
if (!equal) {
|
|
std::cout << "contents of *result:\n";
|
|
for (auto x : *result) {
|
|
std::cout << x << '\n';
|
|
}
|
|
std::cout << '\n';
|
|
}
|
|
}
|
|
}
|
|
|
|
#if BOOST_PARSER_USE_CONCEPTS
|
|
namespace github_issue_268_ {
|
|
namespace bp = boost::parser;
|
|
constexpr bp::rule<struct name, std::string_view> name = "name";
|
|
auto name_def = bp::string_view[bp::lexeme[+(
|
|
bp::lower | bp::upper | bp::digit | bp::char_("_"))]];
|
|
BOOST_PARSER_DEFINE_RULES(name)
|
|
constexpr bp::rule<struct qd_vec, std::vector<double>> qd_vec = "qd_vec";
|
|
auto qd_vec_def = bp::lit("\"") >>
|
|
bp::double_ %
|
|
(bp::lit(",") |
|
|
(bp::lit("\"") >> bp::lit(",") >> bp::lit("\""))) >>
|
|
bp::lit('\"');
|
|
BOOST_PARSER_DEFINE_RULES(qd_vec)
|
|
struct lu_table_template_1
|
|
{
|
|
std::vector<double> index_1;
|
|
std::string_view variable_1;
|
|
};
|
|
constexpr boost::parser::
|
|
rule<struct lu_table_template_1_tag, lu_table_template_1>
|
|
lu_table_template_1_rule = "lu_table_template_1";
|
|
auto lu_table_template_1_rule_def = (bp::lit("index_1") >> '(' >> qd_vec >>
|
|
')' >> ';') >>
|
|
(bp::lit("variable_1") >> ':' >> name >>
|
|
';');
|
|
BOOST_PARSER_DEFINE_RULES(lu_table_template_1_rule)
|
|
|
|
constexpr boost::parser::
|
|
rule<struct lu_table_template_1_permut_tag, lu_table_template_1>
|
|
lu_table_template_1_permut_rule = "lu_table_template_1";
|
|
auto lu_table_template_1_permut_rule_def =
|
|
(bp::lit("index_1") >> '(' >> qd_vec >> ')' >> ';') ||
|
|
(bp::lit("variable_1") >> ':' >> name >> ';');
|
|
BOOST_PARSER_DEFINE_RULES(lu_table_template_1_permut_rule)
|
|
}
|
|
#endif
|
|
|
|
void github_issue_268()
|
|
{
|
|
#if BOOST_PARSER_USE_CONCEPTS
|
|
namespace bp = boost::parser;
|
|
using namespace github_issue_268_;
|
|
std::string inputstring = "index_1 ( \"1\" ) ; variable_1 : bier;";
|
|
|
|
auto const def_result = bp::parse(
|
|
inputstring, lu_table_template_1_rule_def, bp::blank, bp::trace::off);
|
|
std::cout << "seq_parser generates this type:\n"
|
|
<< typeid(def_result.value()).name() << std::endl;
|
|
BOOST_TEST(def_result);
|
|
|
|
auto const permut_def_result = bp::parse(
|
|
inputstring,
|
|
lu_table_template_1_permut_rule_def,
|
|
bp::blank,
|
|
bp::trace::off);
|
|
std::cout << "permut_parser generates this type:\n"
|
|
<< typeid(permut_def_result.value()).name() << std::endl;
|
|
BOOST_TEST(permut_def_result);
|
|
|
|
auto const result = bp::parse(
|
|
inputstring, lu_table_template_1_rule, bp::blank, bp::trace::off);
|
|
std::cout << "seq_parser in rule generates this type:\n"
|
|
<< typeid(result.value()).name() << std::endl;
|
|
BOOST_TEST(result);
|
|
|
|
auto const permut_result = bp::parse(
|
|
inputstring,
|
|
lu_table_template_1_permut_rule,
|
|
bp::blank,
|
|
bp::trace::off);
|
|
std::cout << "permut_parser generates this type:\n"
|
|
<< typeid(permut_result.value()).name() << std::endl;
|
|
BOOST_TEST(permut_result);
|
|
#endif
|
|
}
|
|
|
|
void github_issue_279()
|
|
{
|
|
namespace bp = boost::parser;
|
|
|
|
{
|
|
constexpr auto condition_clause =
|
|
bp::lit(U"while") > bp::lit(U"someexpression") >> bp::attr(true);
|
|
|
|
constexpr auto do_statement =
|
|
bp::lexeme[bp::lit(U"do") >> &bp::ws] > -condition_clause > bp::eol;
|
|
|
|
auto const result =
|
|
bp::parse(U"do\n", do_statement, bp::blank, bp::trace::off);
|
|
BOOST_TEST(result);
|
|
std::optional<bool> const & condition = result.value();
|
|
BOOST_TEST(!condition.has_value());
|
|
}
|
|
|
|
{
|
|
constexpr auto condition_clause =
|
|
bp::lit(U"while") > bp::lit(U"someexpression") >> bp::attr(true);
|
|
|
|
constexpr auto do_statement_reverse =
|
|
-condition_clause > bp::lexeme[bp::lit(U"do") >> &bp::ws] > bp::eol;
|
|
|
|
auto const result =
|
|
bp::parse(U"do\n", do_statement_reverse, bp::blank, bp::trace::off);
|
|
BOOST_TEST(result);
|
|
std::optional<bool> const & condition = result.value();
|
|
BOOST_TEST(!condition.has_value());
|
|
}
|
|
}
|
|
|
|
namespace github_issue_285_ {
|
|
namespace bp = boost::parser;
|
|
|
|
struct Content
|
|
{
|
|
~Content()
|
|
{
|
|
int setbreakpointhere = 0;
|
|
(void)setbreakpointhere;
|
|
}
|
|
};
|
|
constexpr bp::rule<struct content_tag, std::shared_ptr<Content>> content =
|
|
"content";
|
|
constexpr auto content_action = [](auto & ctx) {
|
|
std::shared_ptr<Content> & result = _val(ctx);
|
|
result = std::make_shared<Content>();
|
|
};
|
|
constexpr auto content_def =
|
|
(bp::lit(U"content") >> bp::eol)[content_action];
|
|
BOOST_PARSER_DEFINE_RULES(content);
|
|
}
|
|
|
|
void github_issue_285()
|
|
{
|
|
using namespace github_issue_285_;
|
|
namespace bp = boost::parser;
|
|
|
|
constexpr auto prolog = bp::lit(U"prolog") >> bp::eol;
|
|
|
|
constexpr auto epilog =
|
|
bp::no_case[bp::lexeme[bp::lit(U"epi") >> bp::lit(U"log")]] >> bp::eol;
|
|
|
|
constexpr auto full_parser = prolog >> content >> epilog;
|
|
|
|
std::string teststring =
|
|
"prolog\n"
|
|
"content\n"
|
|
"epilog\n";
|
|
|
|
// "content" produces a shared_ptr with the result.
|
|
// The "epilog" parser must not delete the result.
|
|
|
|
auto const result = bp::parse(teststring, full_parser, bp::blank);
|
|
BOOST_TEST(result);
|
|
BOOST_TEST(result.value().get() != nullptr);
|
|
}
|
|
|
|
|
|
int main()
|
|
{
|
|
github_issue_36();
|
|
github_issue_50();
|
|
github_issue_52();
|
|
github_issue_78();
|
|
github_issue_90();
|
|
github_issue_125();
|
|
github_issue_209();
|
|
github_issue_223();
|
|
github_issue_248();
|
|
github_issue_268();
|
|
github_issue_279();
|
|
github_issue_285();
|
|
return boost::report_errors();
|
|
}
|