From 5d43a9af2a577e2a2d238cb8b9fbb837302e2974 Mon Sep 17 00:00:00 2001 From: Jacek Galowicz Date: Wed, 25 Oct 2023 13:42:55 +0100 Subject: [PATCH] Enable clang-tidy --- benchmark/main.cpp | 136 ++-- flake.nix | 3 +- include/attoparsecpp/math_expression.hpp | 58 +- include/attoparsecpp/parser.hpp | 469 ++++++------- test/gdb.cpp | 60 +- test/math_expression.cpp | 266 ++++---- test/test.cpp | 804 +++++++++++------------ 7 files changed, 911 insertions(+), 885 deletions(-) diff --git a/benchmark/main.cpp b/benchmark/main.cpp index 586f22a..905a38e 100644 --- a/benchmark/main.cpp +++ b/benchmark/main.cpp @@ -1,108 +1,112 @@ #include #include -#include #include +#include #include - -static constexpr size_t max_range {10000000}; -#define BENCH_COMPLX(x) BENCHMARK(x)->RangeMultiplier(10)->Range(10, max_range)->Complexity(benchmark::oN) +static constexpr size_t max_range{10000000}; +#define BENCH_COMPLX(x) \ + BENCHMARK(x) \ + ->RangeMultiplier(10) \ + ->Range(10, max_range) \ + ->Complexity(benchmark::oN) using namespace apl; std::string self_concat(const char *s, size_t times) { - std::ostringstream ss; - while (times--) { ss << s; } - return ss.str(); + std::ostringstream ss; + while (times--) { + ss << s; + } + return ss.str(); } static void measure_word_parsing(benchmark::State &state) { - const size_t size {static_cast(state.range(0))}; - const auto p {many(noneOf(' '))}; - const std::string s {self_concat("a", size)}; - - for (auto _ : state) { - auto r {parse_result(p, s)->data()}; - benchmark::DoNotOptimize(r); - } - state.SetComplexityN(state.range(0)); + const size_t size{static_cast(state.range(0))}; + const auto p{many(noneOf(' '))}; + const std::string s{self_concat("a", size)}; + + for (auto _ : state) { + auto r{parse_result(p, s)->data()}; + benchmark::DoNotOptimize(r); + } + state.SetComplexityN(state.range(0)); } BENCH_COMPLX(measure_word_parsing); static void measure_vector_filling(benchmark::State &state) { - const size_t size {static_cast(state.range(0))}; - const auto p {manyV(token(integer), false, size)}; - const std::string s {self_concat("1 ", size)}; - - for (auto _ : state) { - auto r {parse_result(p, s)}; - auto res {r->data()}; - benchmark::DoNotOptimize(res); - assert(r->size() == size); - } - state.SetComplexityN(state.range(0)); + const size_t size{static_cast(state.range(0))}; + const auto p{manyV(token(integer), false, size)}; + const std::string s{self_concat("1 ", size)}; + + for (auto _ : state) { + auto r{parse_result(p, s)}; + auto res{r->data()}; + benchmark::DoNotOptimize(res); + assert(r->size() == size); + } + state.SetComplexityN(state.range(0)); } BENCH_COMPLX(measure_vector_filling); static auto csv_line(size_t reserve_items = 0) { - return [reserve_items] (str_pos pos) { - const auto comma_whitespace {prefixed(oneOf(','), many(oneOf(' ')))}; - return sep_by1(integer, comma_whitespace, reserve_items)(pos); - }; + return [reserve_items](str_pos pos) { + const auto comma_whitespace{prefixed(oneOf(','), many(oneOf(' ')))}; + return sep_by1(integer, comma_whitespace, reserve_items)(pos); + }; } static void csv_vector_of_ints(benchmark::State &state) { - const size_t size {static_cast(state.range(0))}; - assert(size > 0); - const std::string s {std::string{"1"} + self_concat(", 1", size - 1)}; - const auto p {csv_line(size)}; - - for (auto _ : state) { - const auto r {parse_result(p, s)}; - auto res {r->data()}; - benchmark::DoNotOptimize(res); - assert(r->size() == size); - } - state.SetComplexityN(state.range(0)); + const size_t size{static_cast(state.range(0))}; + assert(size > 0); + const std::string s{std::string{"1"} + self_concat(", 1", size - 1)}; + const auto p{csv_line(size)}; + + for (auto _ : state) { + const auto r{parse_result(p, s)}; + auto res{r->data()}; + benchmark::DoNotOptimize(res); + assert(r->size() == size); + } + state.SetComplexityN(state.range(0)); } BENCH_COMPLX(csv_vector_of_ints); static void sum_of_ints(benchmark::State &state) { - const auto size {static_cast(state.range(0))}; - assert(size > 0); - const std::string s {std::string{"1 "} + self_concat("+ 1", size - 1)}; - - for (auto _ : state) { - const auto r {parse_result(expr, s)}; - bool res {*r == size}; - benchmark::DoNotOptimize(res); - assert(res); - } - state.SetComplexityN(state.range(0)); + const auto size{static_cast(state.range(0))}; + assert(size > 0); + const std::string s{std::string{"1 "} + self_concat("+ 1", size - 1)}; + + for (auto _ : state) { + const auto r{parse_result(expr, s)}; + bool res{*r == size}; + benchmark::DoNotOptimize(res); + assert(res); + } + state.SetComplexityN(state.range(0)); } BENCH_COMPLX(sum_of_ints); static void product_of_ints(benchmark::State &state) { - const size_t size {static_cast(state.range(0))}; - assert(size > 0); - const std::string s {std::string{"1 "} + self_concat("* 1", size - 1)}; - - for (auto _ : state) { - const auto r {parse_result(expr, s)}; - bool res {r == 1}; - benchmark::DoNotOptimize(res); - assert(res); - } - state.SetComplexityN(state.range(0)); + const size_t size{static_cast(state.range(0))}; + assert(size > 0); + const std::string s{std::string{"1 "} + self_concat("* 1", size - 1)}; + + for (auto _ : state) { + const auto r{parse_result(expr, s)}; + bool res{r == 1}; + benchmark::DoNotOptimize(res); + assert(res); + } + state.SetComplexityN(state.range(0)); } BENCH_COMPLX(product_of_ints); - BENCHMARK_MAIN(); diff --git a/flake.nix b/flake.nix index ddf8f53..17aeb4c 100644 --- a/flake.nix +++ b/flake.nix @@ -45,9 +45,10 @@ pre-commit-check = inputs.pre-commit-hooks.lib.${system}.run { src = ./.; hooks = { + clang-format.enable = true; + deadnix.enable = true; nixpkgs-fmt.enable = true; statix.enable = true; - deadnix.enable = true; }; }; }; diff --git a/include/attoparsecpp/math_expression.hpp b/include/attoparsecpp/math_expression.hpp index 6776e13..21fc3d1 100644 --- a/include/attoparsecpp/math_expression.hpp +++ b/include/attoparsecpp/math_expression.hpp @@ -16,42 +16,54 @@ namespace apl { -static parser add_op(str_pos &p) -{ - if (p.at_end()) { return {}; } - - switch (*p) { - case '+': p.next(); return {[](int a, int b) { return a + b; }}; - case '-': p.next(); return {[](int a, int b) { return a - b; }}; - default: return {}; - } +static parser add_op(str_pos &p) { + if (p.at_end()) { + return {}; + } + + switch (*p) { + case '+': + p.next(); + return {[](int a, int b) { return a + b; }}; + case '-': + p.next(); + return {[](int a, int b) { return a - b; }}; + default: + return {}; + } } -static parser mul_op(str_pos &p) -{ - if (p.at_end()) { return {}; } +static parser mul_op(str_pos &p) { + if (p.at_end()) { + return {}; + } - switch (*p) { - case '*': p.next(); return {[](int a, int b) { return a * b; }}; - case '/': p.next(); return {[](int a, int b) { return a / b; }}; - default: return {}; - } + switch (*p) { + case '*': + p.next(); + return {[](int a, int b) { return a * b; }}; + case '/': + p.next(); + return {[](int a, int b) { return a / b; }}; + default: + return {}; + } } -static parser expr( str_pos &p); -static parser term( str_pos &p); +static parser expr(str_pos &p); +static parser term(str_pos &p); static parser factor(str_pos &p); static parser expr(str_pos &p) { - return chainl1(token(term), token(add_op))(p); + return chainl1(token(term), token(add_op))(p); }; static parser term(str_pos &p) { - return chainl1(token(factor), token(mul_op))(p); + return chainl1(token(factor), token(mul_op))(p); }; static parser factor(str_pos &p) { - return choice(base_integer(10), clasped(oneOf('('), oneOf(')'), expr))(p); + return choice(base_integer(10), clasped(oneOf('('), oneOf(')'), expr))(p); } -} +} // namespace apl diff --git a/include/attoparsecpp/parser.hpp b/include/attoparsecpp/parser.hpp index 33169d9..854e2e3 100644 --- a/include/attoparsecpp/parser.hpp +++ b/include/attoparsecpp/parser.hpp @@ -12,243 +12,249 @@ namespace apl { #ifndef __USE_OWN_STRPOS_IMPL__ struct str_pos { - using str_it = std::string::const_iterator; + using str_it = std::string::const_iterator; - str_it it; - str_it end_it; + str_it it; + str_it end_it; - str_pos(const std::string &s) : it{s.cbegin()}, end_it{s.cend()} {} + str_pos(const std::string &s) : it{s.cbegin()}, end_it{s.cend()} {} - std::optional peek() const { - if (!at_end()) { return {*it}; } - return {}; + std::optional peek() const { + if (!at_end()) { + return {*it}; } + return {}; + } - char operator*() const { return *it; } + char operator*() const { return *it; } - str_pos& next() { - ++it; - return *this; - } + str_pos &next() { + ++it; + return *this; + } - char consume() { - return *(it++); - } + char consume() { return *(it++); } - size_t size() const { return end_it - it; } + size_t size() const { return end_it - it; } - bool at_end() const { return size() == 0; } + bool at_end() const { return size() == 0; } }; #endif -template -using parser = std::optional; +template using parser = std::optional; template -using parser_ret = std::invoke_result_t; +using parser_ret = std::invoke_result_t; template using parser_payload_type = typename parser_ret::value_type; -template -static auto not_at_end(F f) -{ - return [f] (str_pos &p) -> parser> { - if (p.at_end()) { return {}; } - return f(p); - }; +template static auto not_at_end(F f) { + return [f](str_pos &p) -> parser> { + if (p.at_end()) { + return {}; + } + return f(p); + }; } static parser anyChar(str_pos &pos) __attribute__((unused)); static parser anyChar(str_pos &pos) { - return not_at_end([] (str_pos &p) -> parser { - return {p.consume()}; - })(pos); + return not_at_end([](str_pos &p) -> parser { return {p.consume()}; })( + pos); } -template -static auto sat(F predicate) { - return not_at_end([predicate] (str_pos &p) -> parser { - if (predicate(*p)) { - return {p.consume()}; - } - return {}; - }); +template static auto sat(F predicate) { + return not_at_end([predicate](str_pos &p) -> parser { + if (predicate(*p)) { + return {p.consume()}; + } + return {}; + }); } static parser number(str_pos &pos) __attribute__((unused)); static parser number(str_pos &pos) { - return sat([](char c) { return '0' <= c && c <= '9'; })(pos); + return sat([](char c) { return '0' <= c && c <= '9'; })(pos); } static parser hexnumber(str_pos &pos) __attribute__((unused)); static parser hexnumber(str_pos &pos) { - return sat([](char c) { return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f'); })(pos); + return sat([](char c) { + return ('0' <= c && c <= '9') || ('a' <= c && c <= 'f'); + })(pos); } namespace detail { template -static bool equalTo(const L& lhs, const T &rhs) { - return lhs == rhs; +static bool equalTo(const L &lhs, const T &rhs) { + return lhs == rhs; } -template -static bool equalTo(const L& lhs, const T &rhs, const Ts& ... ts) { - return lhs == rhs || equalTo(lhs, ts...); +template +static bool equalTo(const L &lhs, const T &rhs, const Ts &...ts) { + return lhs == rhs || equalTo(lhs, ts...); } template -static bool unequalTo(const L& lhs, const T &rhs) { - return lhs != rhs; +static bool unequalTo(const L &lhs, const T &rhs) { + return lhs != rhs; } -template -static bool unequalTo(const L& lhs, const T &rhs, const Ts& ... ts) { - return lhs != rhs && unequalTo(lhs, ts...); +template +static bool unequalTo(const L &lhs, const T &rhs, const Ts &...ts) { + return lhs != rhs && unequalTo(lhs, ts...); } -} +} // namespace detail -template -static auto noneOf(Cs ... cs) { - return sat([cs...] (char c) { return detail::unequalTo(c, cs...); }); +template static auto noneOf(Cs... cs) { + return sat([cs...](char c) { return detail::unequalTo(c, cs...); }); } -template -static auto oneOf(Cs ... cs) { - return sat([cs...] (char c) { return detail::equalTo(c, cs...); }); +template static auto oneOf(Cs... cs) { + return sat([cs...](char c) { return detail::equalTo(c, cs...); }); } static auto const_string(std::string s) __attribute__((unused)); static auto const_string(std::string s) { - return [s] (str_pos &pos) -> parser { - for (const char c : s) { - if (auto ret {oneOf(c)(pos)}) { - } else { - return {}; - } - } - return {s}; - }; + return [s](str_pos &pos) -> parser { + for (const char c : s) { + if (auto ret{oneOf(c)(pos)}) { + } else { + return {}; + } + } + return {s}; + }; } - - template static auto many(Parser p, bool minimum_one = false) { - return [p, minimum_one] (str_pos &pos) -> parser { - std::string s; - while (auto ret {p(pos)}) { - s.push_back(*ret); - } - if (minimum_one && s.empty()) { return {}; } - return {std::move(s)}; - }; + return [p, minimum_one](str_pos &pos) -> parser { + std::string s; + while (auto ret{p(pos)}) { + s.push_back(*ret); + } + if (minimum_one && s.empty()) { + return {}; + } + return {std::move(s)}; + }; } -template -static auto many1(Parser p) { - return many(p, true); -} +template static auto many1(Parser p) { return many(p, true); } template > -static auto manyV(Parser p, bool minimum_one = false, size_t reserve_items = 0) { - return [p, minimum_one, reserve_items] (str_pos &pos) -> parser> { +static auto manyV(Parser p, bool minimum_one = false, + size_t reserve_items = 0) { + return + [p, minimum_one, reserve_items](str_pos &pos) -> parser> { std::vector v; v.reserve(reserve_items); - while (auto ret {p(pos)}) { - v.push_back(*ret); + while (auto ret{p(pos)}) { + v.push_back(*ret); + } + if (minimum_one && v.empty()) { + return {}; } - if (minimum_one && v.empty()) { return {}; } return {std::move(v)}; - }; + }; } template static auto manyV1(Parser p, size_t reserve_items = 0) { - return manyV(p, true, reserve_items); + return manyV(p, true, reserve_items); } template static auto base_integer(size_t base, size_t max_digits = ~0ull) { - return [base, max_digits] (str_pos &p) -> parser { - IntType accum {0}; - size_t digits {0}; - const auto num_f = base == 16 ? hexnumber : number; - parser_ret ret; - while (digits < max_digits && (ret = num_f(p))) { - ++digits; - const char c {*ret}; - accum = base * accum + c; - if (base == 16 && ('a' <= c && c <= 'z')) { accum = accum + 10 - 'a'; } - else { accum -= '0'; } - } - if (!digits) { return {}; } - return {accum}; - }; + return [base, max_digits](str_pos &p) -> parser { + IntType accum{0}; + size_t digits{0}; + const auto num_f = base == 16 ? hexnumber : number; + parser_ret ret; + while (digits < max_digits && (ret = num_f(p))) { + ++digits; + const char c{*ret}; + accum = base * accum + c; + if (base == 16 && ('a' <= c && c <= 'z')) { + accum = accum + 10 - 'a'; + } else { + accum -= '0'; + } + } + if (!digits) { + return {}; + } + return {accum}; + }; } static parser integer(str_pos &p) __attribute__((unused)); static parser integer(str_pos &p) { - return not_at_end([] (str_pos &pos) -> parser { - size_t base {10}; - if (*pos == '0') { - base = 8; - pos = pos.next(); - if (pos.at_end()) { - return {0}; - } else if (*pos == 'x') { - base = 16; - pos = pos.next(); - } else if (*pos < '0' || '9' < *pos) { - return {0}; - } - } - return base_integer(base)(pos); - })(p); + return not_at_end([](str_pos &pos) -> parser { + size_t base{10}; + if (*pos == '0') { + base = 8; + pos = pos.next(); + if (pos.at_end()) { + return {0}; + } else if (*pos == 'x') { + base = 16; + pos = pos.next(); + } else if (*pos < '0' || '9' < *pos) { + return {0}; + } + } + return base_integer(base)(pos); + })(p); } -template -static auto token(Parser parser) { - return not_at_end([parser] (str_pos &p) -> parser_ret { - if (auto ret {parser(p)}) { - if (auto ret2 {many(oneOf(' ', '\t'))(p)}) { - return ret; - } - } - return {}; - }); +template static auto token(Parser parser) { + return not_at_end([parser](str_pos &p) -> parser_ret { + if (auto ret{parser(p)}) { + if (auto ret2{many(oneOf(' ', '\t'))(p)}) { + return ret; + } + } + return {}; + }); } template > -static auto sep_by(TParser item_parser, SepParser sep_parser, bool minimum_one = false, size_t reserve_items = 0) { - return [item_parser, sep_parser, minimum_one, reserve_items] - (str_pos &pos) -> parser> { - std::vector v; - v.reserve(reserve_items); - while (auto ret {item_parser(pos)}) { - v.emplace_back(std::move(*ret)); - auto sep_ret {sep_parser(pos)}; - if (!sep_ret) { - break; - } - } - if (minimum_one && v.empty()) { return {}; } - return {std::move(v)}; - }; +static auto sep_by(TParser item_parser, SepParser sep_parser, + bool minimum_one = false, size_t reserve_items = 0) { + return [item_parser, sep_parser, minimum_one, + reserve_items](str_pos &pos) -> parser> { + std::vector v; + v.reserve(reserve_items); + while (auto ret{item_parser(pos)}) { + v.emplace_back(std::move(*ret)); + auto sep_ret{sep_parser(pos)}; + if (!sep_ret) { + break; + } + } + if (minimum_one && v.empty()) { + return {}; + } + return {std::move(v)}; + }; } template > -static auto sep_by1(TParser item_parser, SepParser sep_parser, size_t reserve_items = 0) { - return sep_by(item_parser, sep_parser, true, reserve_items); +static auto sep_by1(TParser item_parser, SepParser sep_parser, + size_t reserve_items = 0) { + return sep_by(item_parser, sep_parser, true, reserve_items); } namespace detail { @@ -256,129 +262,130 @@ namespace detail { template static parser>> apply_parsers(str_pos &pos, const Parser &parser) { - if (auto ret {parser(pos)}) { - return {std::make_tuple(std::move(*ret))}; + if (auto ret{parser(pos)}) { + return {std::make_tuple(std::move(*ret))}; + } + return {}; +} + +template +static parser< + std::tuple, parser_payload_type...>> +apply_parsers(str_pos &pos, const Parser &parser, + const Parsers &...rest_parsers) { + if (auto ret{parser(pos)}) { + if (auto ret_rest{apply_parsers(pos, rest_parsers...)}) { + return {std::tuple_cat(std::make_tuple(std::move(*ret)), + std::move(*ret_rest))}; } - return {}; + } + return {}; } -template -static parser, parser_payload_type ...>> -apply_parsers(str_pos &pos, const Parser &parser, const Parsers& ... rest_parsers) { - if (auto ret {parser(pos)}) { - if (auto ret_rest {apply_parsers(pos, rest_parsers...)}) { - return {std::tuple_cat(std::make_tuple(std::move(*ret)), - std::move(*ret_rest))}; - } - } - return {}; -} - -} +} // namespace detail -template -static auto tuple_of(Parsers ... parsers) { - return [parsers...] (str_pos &pos) { return detail::apply_parsers(pos, parsers...); }; +template static auto tuple_of(Parsers... parsers) { + return [parsers...](str_pos &pos) { + return detail::apply_parsers(pos, parsers...); + }; } template -static auto chainl1(Parser1 item_parser, Parser2 op_parser) -{ - using T = parser_payload_type; - return [item_parser, op_parser] (str_pos &p) -> parser { - auto i {item_parser(p)}; - if (!i) { return {}; } - auto accum {*i}; - auto op {op_parser(p)}; - - while (op) { - auto b {item_parser(p)}; - if (!b) { return {accum}; } - - accum = (*op)(accum, *b); - - op = op_parser(p); - } +static auto chainl1(Parser1 item_parser, Parser2 op_parser) { + using T = parser_payload_type; + return [item_parser, op_parser](str_pos &p) -> parser { + auto i{item_parser(p)}; + if (!i) { + return {}; + } + auto accum{*i}; + auto op{op_parser(p)}; + + while (op) { + auto b{item_parser(p)}; + if (!b) { return {accum}; - }; + } + + accum = (*op)(accum, *b); + + op = op_parser(p); + } + return {accum}; + }; } template -static auto prefixed(Parser1 prefix_parser, Parser2 parser) -{ - return [prefix_parser, parser] (str_pos &pos) -> parser_ret { - if (auto ret1 {prefix_parser(pos)}) { - return parser(pos); - } - return parser_ret{}; - }; +static auto prefixed(Parser1 prefix_parser, Parser2 parser) { + return [prefix_parser, parser](str_pos &pos) -> parser_ret { + if (auto ret1{prefix_parser(pos)}) { + return parser(pos); + } + return parser_ret{}; + }; } template -static auto postfixed(Parser1 suffix_parser, Parser2 parser) -{ - return [suffix_parser, parser] (str_pos &pos) -> parser_ret { - if (auto ret1 {parser(pos)}) { - if (auto ret2 {suffix_parser(pos)}) { - return ret1; - } - } - return {}; - }; +static auto postfixed(Parser1 suffix_parser, Parser2 parser) { + return [suffix_parser, parser](str_pos &pos) -> parser_ret { + if (auto ret1{parser(pos)}) { + if (auto ret2{suffix_parser(pos)}) { + return ret1; + } + } + return {}; + }; } template -static auto clasped(Parser1 open_parser, Parser2 close_parser, Parser3 parser) -{ - return postfixed(close_parser, prefixed(open_parser, parser)); +static auto clasped(Parser1 open_parser, Parser2 close_parser, Parser3 parser) { + return postfixed(close_parser, prefixed(open_parser, parser)); } namespace detail { template static parser_ret apply_parser_choice(str_pos &pos, Parser p) { - return p(pos); + return p(pos); } -template -static parser_ret apply_parser_choice(str_pos &pos, Parser p, Parsers ... ps) { - if (auto ret {p(pos)}) { return ret; } - return apply_parser_choice(pos, ps...); -} +template +static parser_ret apply_parser_choice(str_pos &pos, Parser p, + Parsers... ps) { + if (auto ret{p(pos)}) { + return ret; + } + return apply_parser_choice(pos, ps...); } +} // namespace detail -template -static auto choice(Parsers ... ps) -{ - return [ps...] (str_pos &pos) { - return detail::apply_parser_choice(pos, ps...); - }; +template static auto choice(Parsers... ps) { + return + [ps...](str_pos &pos) { return detail::apply_parser_choice(pos, ps...); }; } -template -static auto map(P p, F f) { - return [p, f] (str_pos &pos) -> parser)>::type> { - if (auto ret {p(pos)}) { - return {f(*ret)}; +template static auto map(P p, F f) { + return + [p, f](str_pos &pos) + -> parser)>::type> { + if (auto ret{p(pos)}) { + return {f(*ret)}; } return {}; - }; + }; } - template static auto run_parser(Parser &&p, const std::string &s) - -> std::pair, str_pos> -{ - str_pos pos {s}; - return {p(pos), pos}; + -> std::pair, str_pos> { + str_pos pos{s}; + return {p(pos), pos}; } template static auto parse_result(Parser &&p, const std::string &s) - -> parser_ret -{ - str_pos pos {s}; - return p(pos); + -> parser_ret { + str_pos pos{s}; + return p(pos); } -} +} // namespace apl diff --git a/test/gdb.cpp b/test/gdb.cpp index 795f01c..416f188 100644 --- a/test/gdb.cpp +++ b/test/gdb.cpp @@ -1,5 +1,5 @@ -#include #include +#include #include @@ -7,39 +7,41 @@ using namespace apl; using namespace std::string_literals; static uint8_t gdb_checksum(const std::string &s) { - auto add {[](uint8_t l, char r) { - return l + static_cast(r); - }}; - return std::accumulate(std::begin(s), std::end(s), static_cast(0), add); + auto add{[](uint8_t l, char r) { return l + static_cast(r); }}; + return std::accumulate(std::begin(s), std::end(s), static_cast(0), + add); } static parser checksum_parser(str_pos &pos) { - if (const auto payload_str {clasped(oneOf('$'), oneOf('#'), many(noneOf('#')))(pos)}) { - const auto ref_chksum {base_integer(16, 2)(pos)}; - if (ref_chksum == gdb_checksum(*payload_str)) { return payload_str; } + if (const auto payload_str{ + clasped(oneOf('$'), oneOf('#'), many(noneOf('#')))(pos)}) { + const auto ref_chksum{base_integer(16, 2)(pos)}; + if (ref_chksum == gdb_checksum(*payload_str)) { + return payload_str; } - return {}; + } + return {}; } -SCENARIO( "gdb parser", "[gdb_parser]" ) { - GIVEN( "add_op parser" ) { - WHEN( "given empty string" ) { - const auto r {run_parser(checksum_parser, "")}; - REQUIRE( !r.first ); - } - WHEN( "Given valid empty string" ) { - const auto r {run_parser(checksum_parser, "$#00")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == ""s ); - } - WHEN( "Given valid string" ) { - const auto r {run_parser(checksum_parser, "$vMustReplyEmpty#3a")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == "vMustReplyEmpty"s ); - } - WHEN( "Given invalid string" ) { - const auto r {run_parser(checksum_parser, "$vMustReplyEmpty#3b")}; - REQUIRE( !r.first ); - } +SCENARIO("gdb parser", "[gdb_parser]") { + GIVEN("add_op parser") { + WHEN("given empty string") { + const auto r{run_parser(checksum_parser, "")}; + REQUIRE(!r.first); + } + WHEN("Given valid empty string") { + const auto r{run_parser(checksum_parser, "$#00")}; + REQUIRE(!!r.first); + REQUIRE(r.first == ""s); + } + WHEN("Given valid string") { + const auto r{run_parser(checksum_parser, "$vMustReplyEmpty#3a")}; + REQUIRE(!!r.first); + REQUIRE(r.first == "vMustReplyEmpty"s); + } + WHEN("Given invalid string") { + const auto r{run_parser(checksum_parser, "$vMustReplyEmpty#3b")}; + REQUIRE(!r.first); } + } } diff --git a/test/math_expression.cpp b/test/math_expression.cpp index 33be8d0..32f27ff 100644 --- a/test/math_expression.cpp +++ b/test/math_expression.cpp @@ -4,141 +4,141 @@ using namespace apl; -SCENARIO( "op parser", "[math_expression_parser]" ) { - GIVEN( "add_op parser" ) { - WHEN( "given empty string" ) { - const auto r {run_parser(add_op, "")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "given non-op string" ) { - const auto r {run_parser(add_op, "a")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "given plus string" ) { - const auto r {run_parser(add_op, "+")}; - REQUIRE( !!r.first ); - REQUIRE( (*r.first)(100, 10) == 110 ); - } - WHEN( "given minus string" ) { - const auto r {run_parser(add_op, "-")}; - REQUIRE( !!r.first ); - REQUIRE( (*r.first)(100, 10) == 90 ); - } - } - GIVEN( "mul_op parser" ) { - WHEN( "given empty string" ) { - const auto r {run_parser(mul_op, "")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "given non-op string" ) { - const auto r {run_parser(mul_op, "a")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "given multiplication string" ) { - const auto r {run_parser(mul_op, "*")}; - REQUIRE( !!r.first ); - REQUIRE( (*r.first)(10, 2) == 20 ); - } - WHEN( "given division string" ) { - const auto r {run_parser(mul_op, "/")}; - REQUIRE( !!r.first ); - REQUIRE( (*r.first)(10, 2) == 5 ); - } +SCENARIO("op parser", "[math_expression_parser]") { + GIVEN("add_op parser") { + WHEN("given empty string") { + const auto r{run_parser(add_op, "")}; + REQUIRE_FALSE(!!r.first); } + WHEN("given non-op string") { + const auto r{run_parser(add_op, "a")}; + REQUIRE_FALSE(!!r.first); + } + WHEN("given plus string") { + const auto r{run_parser(add_op, "+")}; + REQUIRE(!!r.first); + REQUIRE((*r.first)(100, 10) == 110); + } + WHEN("given minus string") { + const auto r{run_parser(add_op, "-")}; + REQUIRE(!!r.first); + REQUIRE((*r.first)(100, 10) == 90); + } + } + GIVEN("mul_op parser") { + WHEN("given empty string") { + const auto r{run_parser(mul_op, "")}; + REQUIRE_FALSE(!!r.first); + } + WHEN("given non-op string") { + const auto r{run_parser(mul_op, "a")}; + REQUIRE_FALSE(!!r.first); + } + WHEN("given multiplication string") { + const auto r{run_parser(mul_op, "*")}; + REQUIRE(!!r.first); + REQUIRE((*r.first)(10, 2) == 20); + } + WHEN("given division string") { + const auto r{run_parser(mul_op, "/")}; + REQUIRE(!!r.first); + REQUIRE((*r.first)(10, 2) == 5); + } + } } -SCENARIO( "math expression parser", "[math_expression_parser]" ) { - GIVEN( "Sum parsing" ) { - WHEN( "empty string" ) { - const auto r {run_parser(expr, "")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "string with single integer" ) { - const auto r {run_parser(expr, "123")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 123 ); - } - WHEN( "string with 2 ints" ) { - const auto r {run_parser(expr, "123 + 456")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 579 ); - } - WHEN( "string with 5 ints" ) { - const auto r {run_parser(expr, "1 + 2 + 3 - 4 - 5")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == -3 ); - } - } - GIVEN( "Product parsing" ) { - WHEN( "string with 2 ints" ) { - const auto r {run_parser(expr, "10 * 5")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 50 ); - } - WHEN( "string with 5 ints" ) { - const auto r {run_parser(expr, "1 * 2 * 3 * 4 / 2")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 12 ); - } - } - GIVEN( "Mixed product/sum parsing" ) { - WHEN( "multiplication then addition" ) { - const auto r {run_parser(expr, "2 * 3 + 5")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 11 ); - } - WHEN( "addition then multiplication" ) { - const auto r {run_parser(expr, "2 + 3 * 5")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 17 ); - } - } - GIVEN( "Expressions in parentheses" ) { - WHEN( "number surrounded by a lot of parentheses" ) { - const auto r {run_parser(expr, "((((((2))))))")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 2 ); - } - } - GIVEN( "Mixed expressions with sub-expressions in parentheses" ) { - WHEN( "multiplication then addition" ) { - const auto r {run_parser(expr, "(2 * 3) + 5")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 11 ); - } - WHEN( "addition then multiplication" ) { - const auto r {run_parser(expr, "2 * (3 + 5)")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 16 ); - } - WHEN( "expression is a bit more complex" ) { - const auto r {run_parser(expr, "1 + (2 * (4 + 3) + 12 * 12 - (6 / 3))")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 157 ); - } - } - // TODO: Following tests are wrong. Parser should actually fail. - GIVEN( "Trailing operations" ) { - WHEN( "string with single int and trailing +" ) { - const std::string str {"123 +"}; - const auto r {run_parser(expr, str)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 123 ); - REQUIRE( r.second.at_end() ); - } - WHEN( "string with 2 ints and trailing +" ) { - const std::string str {"123 - 0 +"}; - const auto r {run_parser(expr, str)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 123 ); - REQUIRE( r.second.at_end() ); - } - WHEN( "mixed expression with trailing +" ) { - const std::string str {"1 + (2 * (4 + 3) + 12 * 12 - (6 / 3)) +"}; - const auto r {run_parser(expr, str)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 157 ); - REQUIRE( r.second.at_end() ); - } +SCENARIO("math expression parser", "[math_expression_parser]") { + GIVEN("Sum parsing") { + WHEN("empty string") { + const auto r{run_parser(expr, "")}; + REQUIRE_FALSE(!!r.first); + } + WHEN("string with single integer") { + const auto r{run_parser(expr, "123")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 123); + } + WHEN("string with 2 ints") { + const auto r{run_parser(expr, "123 + 456")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 579); + } + WHEN("string with 5 ints") { + const auto r{run_parser(expr, "1 + 2 + 3 - 4 - 5")}; + REQUIRE(!!r.first); + REQUIRE(r.first == -3); + } + } + GIVEN("Product parsing") { + WHEN("string with 2 ints") { + const auto r{run_parser(expr, "10 * 5")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 50); + } + WHEN("string with 5 ints") { + const auto r{run_parser(expr, "1 * 2 * 3 * 4 / 2")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 12); + } + } + GIVEN("Mixed product/sum parsing") { + WHEN("multiplication then addition") { + const auto r{run_parser(expr, "2 * 3 + 5")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 11); + } + WHEN("addition then multiplication") { + const auto r{run_parser(expr, "2 + 3 * 5")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 17); + } + } + GIVEN("Expressions in parentheses") { + WHEN("number surrounded by a lot of parentheses") { + const auto r{run_parser(expr, "((((((2))))))")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 2); + } + } + GIVEN("Mixed expressions with sub-expressions in parentheses") { + WHEN("multiplication then addition") { + const auto r{run_parser(expr, "(2 * 3) + 5")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 11); + } + WHEN("addition then multiplication") { + const auto r{run_parser(expr, "2 * (3 + 5)")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 16); + } + WHEN("expression is a bit more complex") { + const auto r{run_parser(expr, "1 + (2 * (4 + 3) + 12 * 12 - (6 / 3))")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 157); + } + } + // TODO: Following tests are wrong. Parser should actually fail. + GIVEN("Trailing operations") { + WHEN("string with single int and trailing +") { + const std::string str{"123 +"}; + const auto r{run_parser(expr, str)}; + REQUIRE(!!r.first); + REQUIRE(r.first == 123); + REQUIRE(r.second.at_end()); + } + WHEN("string with 2 ints and trailing +") { + const std::string str{"123 - 0 +"}; + const auto r{run_parser(expr, str)}; + REQUIRE(!!r.first); + REQUIRE(r.first == 123); + REQUIRE(r.second.at_end()); + } + WHEN("mixed expression with trailing +") { + const std::string str{"1 + (2 * (4 + 3) + 12 * 12 - (6 / 3)) +"}; + const auto r{run_parser(expr, str)}; + REQUIRE(!!r.first); + REQUIRE(r.first == 157); + REQUIRE(r.second.at_end()); } + } } diff --git a/test/test.cpp b/test/test.cpp index baf5690..24e2c0e 100644 --- a/test/test.cpp +++ b/test/test.cpp @@ -10,430 +10,430 @@ using namespace apl; using namespace std::string_literals; -SCENARIO( "Fundamental parsers", "[parser]" ) { - GIVEN( "anyChar" ) { - WHEN( "given an empty string" ) { - const auto r {run_parser(anyChar, "")}; - REQUIRE( !r.first ); - } - WHEN( "given an alphabetic string \"a\"" ) { - const auto r {run_parser(anyChar, "a")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 'a' ); - REQUIRE( r.second.at_end() == true ); - } - WHEN( "given an alphabetic string \"ab\"" ) { - const std::string s {"ab"}; - const auto r {run_parser(anyChar, s)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 'a' ); - REQUIRE( r.second.size() == 1); - REQUIRE( r.second.peek() == 'b' ); - } - } - GIVEN( "number" ) { - WHEN( "given an empty string" ) { - const auto r {run_parser(number, "")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "given an alphabetic string" ) { - const auto r {run_parser(number, "a")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "given a number string" ) { - const auto r {run_parser(number, "5")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == '5' ); - REQUIRE( r.second.at_end() == true ); - } - } - GIVEN( "const_string" ) { - const auto p {const_string("abcdef")}; - WHEN( "given an empty string" ) { - const auto r {run_parser(p, "")}; - REQUIRE( !r.first ); - } - WHEN( "given bad string" ) { - const auto r {run_parser(p, "xzy")}; - REQUIRE( !r.first ); - } - WHEN( "given partly correct string" ) { - const auto r {run_parser(p, "abc")}; - REQUIRE( !r.first ); - } - WHEN( "given correct string" ) { - const auto r {run_parser(p, "abcdef")}; - REQUIRE( r.first ); - REQUIRE( r.first == "abcdef"s ); - } - WHEN( "given correct string plus suffix, rest string not consumed" ) { - const std::string s {"abcdefg"}; - const auto r {run_parser(p, s)}; - REQUIRE( r.first ); - REQUIRE( r.first == "abcdef"s ); - REQUIRE( r.second.peek() == 'g' ); - } +SCENARIO("Fundamental parsers", "[parser]") { + GIVEN("anyChar") { + WHEN("given an empty string") { + const auto r{run_parser(anyChar, "")}; + REQUIRE(!r.first); } + WHEN("given an alphabetic string \"a\"") { + const auto r{run_parser(anyChar, "a")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 'a'); + REQUIRE(r.second.at_end() == true); + } + WHEN("given an alphabetic string \"ab\"") { + const std::string s{"ab"}; + const auto r{run_parser(anyChar, s)}; + REQUIRE(!!r.first); + REQUIRE(r.first == 'a'); + REQUIRE(r.second.size() == 1); + REQUIRE(r.second.peek() == 'b'); + } + } + GIVEN("number") { + WHEN("given an empty string") { + const auto r{run_parser(number, "")}; + REQUIRE_FALSE(!!r.first); + } + WHEN("given an alphabetic string") { + const auto r{run_parser(number, "a")}; + REQUIRE_FALSE(!!r.first); + } + WHEN("given a number string") { + const auto r{run_parser(number, "5")}; + REQUIRE(!!r.first); + REQUIRE(r.first == '5'); + REQUIRE(r.second.at_end() == true); + } + } + GIVEN("const_string") { + const auto p{const_string("abcdef")}; + WHEN("given an empty string") { + const auto r{run_parser(p, "")}; + REQUIRE(!r.first); + } + WHEN("given bad string") { + const auto r{run_parser(p, "xzy")}; + REQUIRE(!r.first); + } + WHEN("given partly correct string") { + const auto r{run_parser(p, "abc")}; + REQUIRE(!r.first); + } + WHEN("given correct string") { + const auto r{run_parser(p, "abcdef")}; + REQUIRE(r.first); + REQUIRE(r.first == "abcdef"s); + } + WHEN("given correct string plus suffix, rest string not consumed") { + const std::string s{"abcdefg"}; + const auto r{run_parser(p, s)}; + REQUIRE(r.first); + REQUIRE(r.first == "abcdef"s); + REQUIRE(r.second.peek() == 'g'); + } + } } -SCENARIO( "many parser combinations", "[parser]" ) { - GIVEN( "many noneOf 'a'" ) { - const auto p {many(noneOf('a'))}; - WHEN( "given list of 'a's" ) { - const auto r {run_parser(p, "aaa")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == ""s ); - REQUIRE( r.second.size() == 3 ); - } - WHEN( "given bba" ) { - const std::string s {"bba"}; - const auto r {run_parser(p, s)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == "bb"s ); - REQUIRE( r.second.size() == 1 ); - REQUIRE( r.second.peek() == 'a' ); - } - } - GIVEN( "many oneOf 'b'" ) { - const auto p {many(oneOf('b'))}; - WHEN( "given list of 'a's" ) { - const auto r {run_parser(p, "aaa")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == ""s ); - REQUIRE( r.second.size() == 3 ); - } - WHEN( "given bba" ) { - const std::string s {"bba"}; - const auto r {run_parser(p, s)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == "bb"s ); - REQUIRE( r.second.size() == 1 ); - REQUIRE( r.second.peek() == 'a' ); - } +SCENARIO("many parser combinations", "[parser]") { + GIVEN("many noneOf 'a'") { + const auto p{many(noneOf('a'))}; + WHEN("given list of 'a's") { + const auto r{run_parser(p, "aaa")}; + REQUIRE(!!r.first); + REQUIRE(r.first == ""s); + REQUIRE(r.second.size() == 3); + } + WHEN("given bba") { + const std::string s{"bba"}; + const auto r{run_parser(p, s)}; + REQUIRE(!!r.first); + REQUIRE(r.first == "bb"s); + REQUIRE(r.second.size() == 1); + REQUIRE(r.second.peek() == 'a'); + } + } + GIVEN("many oneOf 'b'") { + const auto p{many(oneOf('b'))}; + WHEN("given list of 'a's") { + const auto r{run_parser(p, "aaa")}; + REQUIRE(!!r.first); + REQUIRE(r.first == ""s); + REQUIRE(r.second.size() == 3); } + WHEN("given bba") { + const std::string s{"bba"}; + const auto r{run_parser(p, s)}; + REQUIRE(!!r.first); + REQUIRE(r.first == "bb"s); + REQUIRE(r.second.size() == 1); + REQUIRE(r.second.peek() == 'a'); + } + } } -SCENARIO( "manyV parser combinations" ) { - GIVEN( "manyV string parser" ) { - const auto p {manyV(anyChar)}; - using vect_t = std::vector; - WHEN( "given empty string" ) { - const auto r {run_parser(p, "")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == vect_t{} ); - } - WHEN( "given string \"abc\"" ) { - const auto r {run_parser(p, "abc")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == vect_t{'a', 'b', 'c'}); - REQUIRE( r.second.at_end() ); - } - } - GIVEN( "manyV integer parser" ) { - const auto p {manyV(integer)}; - using vect_t = std::vector; - WHEN( "given empty string" ) { - const auto r {run_parser(p, "")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == vect_t{} ); - } +SCENARIO("manyV parser combinations") { + GIVEN("manyV string parser") { + const auto p{manyV(anyChar)}; + using vect_t = std::vector; + WHEN("given empty string") { + const auto r{run_parser(p, "")}; + REQUIRE(!!r.first); + REQUIRE(r.first == vect_t{}); + } + WHEN("given string \"abc\"") { + const auto r{run_parser(p, "abc")}; + REQUIRE(!!r.first); + REQUIRE(r.first == vect_t{'a', 'b', 'c'}); + REQUIRE(r.second.at_end()); + } + } + GIVEN("manyV integer parser") { + const auto p{manyV(integer)}; + using vect_t = std::vector; + WHEN("given empty string") { + const auto r{run_parser(p, "")}; + REQUIRE(!!r.first); + REQUIRE(r.first == vect_t{}); + } - WHEN( "given string \"123\"" ) { - const auto r {run_parser(p, "123")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == vect_t{123} ); - REQUIRE( r.second.at_end() ); - } - } - GIVEN( "manyV token integer parser" ) { - const auto p {manyV(token(integer))}; - using vect_t = std::vector; - WHEN( "given empty string" ) { - const auto r {run_parser(p, "")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == vect_t{} ); - } - WHEN( "given string \"123\"" ) { - const auto r {run_parser(p, "123")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == vect_t{123} ); - REQUIRE( r.second.at_end() ); - } - WHEN( "given string \"1 2 3 \"" ) { - const auto r {run_parser(p, "1 2 3 ")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == vect_t{1, 2, 3} ); - REQUIRE( r.second.at_end() ); - } - WHEN( "given string \"1 2 3\"" ) { - const auto r {run_parser(p, "1 2 3")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == vect_t{1, 2, 3} ); - REQUIRE( r.second.at_end() ); - } - WHEN( "given string \"1 2 3ABC\"" ) { - const std::string s {"1 2 3ABC"}; - const auto r {run_parser(p, s)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == vect_t{1, 2, 3} ); - REQUIRE_FALSE( r.second.at_end() ); - REQUIRE( r.second.peek() == 'A' ); - } + WHEN("given string \"123\"") { + const auto r{run_parser(p, "123")}; + REQUIRE(!!r.first); + REQUIRE(r.first == vect_t{123}); + REQUIRE(r.second.at_end()); + } + } + GIVEN("manyV token integer parser") { + const auto p{manyV(token(integer))}; + using vect_t = std::vector; + WHEN("given empty string") { + const auto r{run_parser(p, "")}; + REQUIRE(!!r.first); + REQUIRE(r.first == vect_t{}); + } + WHEN("given string \"123\"") { + const auto r{run_parser(p, "123")}; + REQUIRE(!!r.first); + REQUIRE(r.first == vect_t{123}); + REQUIRE(r.second.at_end()); } + WHEN("given string \"1 2 3 \"") { + const auto r{run_parser(p, "1 2 3 ")}; + REQUIRE(!!r.first); + REQUIRE(r.first == vect_t{1, 2, 3}); + REQUIRE(r.second.at_end()); + } + WHEN("given string \"1 2 3\"") { + const auto r{run_parser(p, "1 2 3")}; + REQUIRE(!!r.first); + REQUIRE(r.first == vect_t{1, 2, 3}); + REQUIRE(r.second.at_end()); + } + WHEN("given string \"1 2 3ABC\"") { + const std::string s{"1 2 3ABC"}; + const auto r{run_parser(p, s)}; + REQUIRE(!!r.first); + REQUIRE(r.first == vect_t{1, 2, 3}); + REQUIRE_FALSE(r.second.at_end()); + REQUIRE(r.second.peek() == 'A'); + } + } } -SCENARIO( "int parser", "[parser]" ) { - GIVEN( "empty string" ) { - const auto r {run_parser(base_integer(10), "")}; - REQUIRE_FALSE( !!r.first ); - } - GIVEN( "string '1'" ) { - const auto r {run_parser(base_integer(10), "1")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 1 ); - REQUIRE( r.second.size() == 0 ); - } - GIVEN( "string '1 '" ) { - const auto r {run_parser(base_integer(10), "1 ")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 1 ); - REQUIRE( r.second.size() == 1 ); - } - GIVEN( "string '123'" ) { - const auto r {run_parser(base_integer(10), "123")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 123 ); - REQUIRE( r.second.size() == 0 ); - } - GIVEN( "string '123' but only parsing first 2 digits" ) { - const auto r {run_parser(base_integer(10, 2), "123")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 12 ); - REQUIRE( r.second.size() == 1 ); - } +SCENARIO("int parser", "[parser]") { + GIVEN("empty string") { + const auto r{run_parser(base_integer(10), "")}; + REQUIRE_FALSE(!!r.first); + } + GIVEN("string '1'") { + const auto r{run_parser(base_integer(10), "1")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 1); + REQUIRE(r.second.size() == 0); + } + GIVEN("string '1 '") { + const auto r{run_parser(base_integer(10), "1 ")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 1); + REQUIRE(r.second.size() == 1); + } + GIVEN("string '123'") { + const auto r{run_parser(base_integer(10), "123")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 123); + REQUIRE(r.second.size() == 0); + } + GIVEN("string '123' but only parsing first 2 digits") { + const auto r{run_parser(base_integer(10, 2), "123")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 12); + REQUIRE(r.second.size() == 1); + } } -SCENARIO( "auto int parser", "[parser]" ) { - GIVEN( "empty string" ) { - const auto r {run_parser(integer, "")}; - REQUIRE( !r.first ); - } - GIVEN( "string '0'" ) { - const auto r {run_parser(integer, "0")}; - REQUIRE( r.first == 0 ); - } - GIVEN( "string '0 '" ) { - const std::string s {"0 "}; - const auto r {run_parser(integer, s)}; - REQUIRE( r.first == 0 ); - REQUIRE( r.second.size() == 1 ); - REQUIRE( r.second.peek() == ' ' ); - } - GIVEN( "string '1'" ) { - const auto r {run_parser(integer, "1")}; - REQUIRE( r.first == 1 ); - } - GIVEN( "string '01'" ) { - const auto r {run_parser(integer, "01")}; - REQUIRE( r.first == 1 ); - } - GIVEN( "string '0x1'" ) { - const auto r {run_parser(integer, "0x1")}; - REQUIRE( r.first == 1 ); - } - GIVEN( "string '12345'" ) { - const auto r {run_parser(integer, "12345")}; - REQUIRE( r.first == 12345 ); - } - GIVEN( "string '012345'" ) { - const auto r {run_parser(integer, "012345")}; - REQUIRE( r.first == 012345 ); - } - GIVEN( "string '0x123abc'" ) { - const auto r {run_parser(integer, "0x123abc")}; - REQUIRE( r.first == 0x123abc ); - } +SCENARIO("auto int parser", "[parser]") { + GIVEN("empty string") { + const auto r{run_parser(integer, "")}; + REQUIRE(!r.first); + } + GIVEN("string '0'") { + const auto r{run_parser(integer, "0")}; + REQUIRE(r.first == 0); + } + GIVEN("string '0 '") { + const std::string s{"0 "}; + const auto r{run_parser(integer, s)}; + REQUIRE(r.first == 0); + REQUIRE(r.second.size() == 1); + REQUIRE(r.second.peek() == ' '); + } + GIVEN("string '1'") { + const auto r{run_parser(integer, "1")}; + REQUIRE(r.first == 1); + } + GIVEN("string '01'") { + const auto r{run_parser(integer, "01")}; + REQUIRE(r.first == 1); + } + GIVEN("string '0x1'") { + const auto r{run_parser(integer, "0x1")}; + REQUIRE(r.first == 1); + } + GIVEN("string '12345'") { + const auto r{run_parser(integer, "12345")}; + REQUIRE(r.first == 12345); + } + GIVEN("string '012345'") { + const auto r{run_parser(integer, "012345")}; + REQUIRE(r.first == 012345); + } + GIVEN("string '0x123abc'") { + const auto r{run_parser(integer, "0x123abc")}; + REQUIRE(r.first == 0x123abc); + } } -SCENARIO( "prefix/postfix parser", "[parser]" ) { - const auto spaces {many(oneOf(' '))}; - GIVEN( "prefix parser" ) { - const auto p {prefixed(spaces, anyChar)}; - WHEN( "parsing empty string" ) { - const auto r {run_parser(p, "")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "parsing prefix-only string" ) { - const auto r {run_parser(p, " ")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "parsing space-prefixed string \" ab\"" ) { - const std::string s {" ab"}; - const auto r {run_parser(p, s)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 'a' ); - REQUIRE( r.second.peek() == 'b' ); - } - } - GIVEN( "postfix parser" ) { - const auto p {postfixed(oneOf(']'), anyChar)}; - WHEN( "parsing empty string" ) { - const auto r {run_parser(p, "")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "parsing postfix-only string" ) { - const auto r {run_parser(p, "]")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "parsing string \"a]b\"" ) { - const std::string s {"a]b"}; - const auto r {run_parser(p, s)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 'a' ); - REQUIRE( r.second.peek() == 'b' ); - } - } - GIVEN( "clasped parser" ) { - const auto p {clasped(oneOf('('), oneOf(')'), integer)}; - WHEN( "parsing empty string" ) { - const auto r {run_parser(p, "")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "parsing clasp-onlue string" ) { - const auto r {run_parser(p, "()")}; - REQUIRE_FALSE( !!r.first ); - } - WHEN( "parsing string \"(123)\"" ) { - const std::string s {"(123)x"}; - const auto r {run_parser(p, s)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 123 ); - REQUIRE( r.second.peek() == 'x' ); - } +SCENARIO("prefix/postfix parser", "[parser]") { + const auto spaces{many(oneOf(' '))}; + GIVEN("prefix parser") { + const auto p{prefixed(spaces, anyChar)}; + WHEN("parsing empty string") { + const auto r{run_parser(p, "")}; + REQUIRE_FALSE(!!r.first); + } + WHEN("parsing prefix-only string") { + const auto r{run_parser(p, " ")}; + REQUIRE_FALSE(!!r.first); } + WHEN("parsing space-prefixed string \" ab\"") { + const std::string s{" ab"}; + const auto r{run_parser(p, s)}; + REQUIRE(!!r.first); + REQUIRE(r.first == 'a'); + REQUIRE(r.second.peek() == 'b'); + } + } + GIVEN("postfix parser") { + const auto p{postfixed(oneOf(']'), anyChar)}; + WHEN("parsing empty string") { + const auto r{run_parser(p, "")}; + REQUIRE_FALSE(!!r.first); + } + WHEN("parsing postfix-only string") { + const auto r{run_parser(p, "]")}; + REQUIRE_FALSE(!!r.first); + } + WHEN("parsing string \"a]b\"") { + const std::string s{"a]b"}; + const auto r{run_parser(p, s)}; + REQUIRE(!!r.first); + REQUIRE(r.first == 'a'); + REQUIRE(r.second.peek() == 'b'); + } + } + GIVEN("clasped parser") { + const auto p{clasped(oneOf('('), oneOf(')'), integer)}; + WHEN("parsing empty string") { + const auto r{run_parser(p, "")}; + REQUIRE_FALSE(!!r.first); + } + WHEN("parsing clasp-onlue string") { + const auto r{run_parser(p, "()")}; + REQUIRE_FALSE(!!r.first); + } + WHEN("parsing string \"(123)\"") { + const std::string s{"(123)x"}; + const auto r{run_parser(p, s)}; + REQUIRE(!!r.first); + REQUIRE(r.first == 123); + REQUIRE(r.second.peek() == 'x'); + } + } } -SCENARIO( "sep_by parsers", "[parser]" ) { - const auto whitespace {many(oneOf(' ', '\t'))}; - const auto comma_whitespace {prefixed(oneOf(','), whitespace)}; - const auto p {sep_by(integer, comma_whitespace, true)}; - GIVEN( "sep_by int comma" ) { - WHEN( "given an empty string" ) { - const auto r {run_parser(p, "")}; - REQUIRE( !r.first ); - } - WHEN( "given single item" ) { - const auto r {run_parser(p, "1")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == std::vector{1} ); - } - WHEN( "given multiple items without spaces" ) { - const auto r {run_parser(p, "1,2,3,4")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == std::vector{1, 2, 3, 4} ); - } - WHEN( "given multiple items with spaces" ) { - const auto r {run_parser(p, "1, 2, 3, 4,\t 5")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == std::vector{1, 2, 3, 4, 5} ); - } - } - GIVEN( "sep_by newline sep_by comma integers (CSV)" ) { - const auto csv_p {sep_by(p, many(oneOf('\n'), true), true)}; - using csv_vect = std::vector>; - WHEN( "given empty string" ) { - const auto r {run_parser(csv_p, "")}; - REQUIRE( !r.first ); - } - WHEN( "given 1 csv line without following newline" ) { - const auto r {run_parser(csv_p, "1,2,3")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == csv_vect{ {1, 2, 3} } ); - } - WHEN( "given 1 csv line with following newline" ) { - const auto r {run_parser(csv_p, "1,2,3\n")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == csv_vect{ {1, 2, 3} } ); - } - WHEN( "given 3 csv lines" ) { - const auto r {run_parser(csv_p, "1,2,3\n4, 5, 6\n7, 8, 9")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == csv_vect{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9} } ); - } - WHEN( "given 3 csv lines with lots of newlines inbetween" ) { - const auto r {run_parser(csv_p, "1,2,3\n\n\n\n4, 5, 6\n\n\n\n7, 8, 9\n\n\n")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == csv_vect{ {1, 2, 3}, {4, 5, 6}, {7, 8, 9} } ); - } +SCENARIO("sep_by parsers", "[parser]") { + const auto whitespace{many(oneOf(' ', '\t'))}; + const auto comma_whitespace{prefixed(oneOf(','), whitespace)}; + const auto p{sep_by(integer, comma_whitespace, true)}; + GIVEN("sep_by int comma") { + WHEN("given an empty string") { + const auto r{run_parser(p, "")}; + REQUIRE(!r.first); + } + WHEN("given single item") { + const auto r{run_parser(p, "1")}; + REQUIRE(!!r.first); + REQUIRE(r.first == std::vector{1}); + } + WHEN("given multiple items without spaces") { + const auto r{run_parser(p, "1,2,3,4")}; + REQUIRE(!!r.first); + REQUIRE(r.first == std::vector{1, 2, 3, 4}); + } + WHEN("given multiple items with spaces") { + const auto r{run_parser(p, "1, 2, 3, 4,\t 5")}; + REQUIRE(!!r.first); + REQUIRE(r.first == std::vector{1, 2, 3, 4, 5}); + } + } + GIVEN("sep_by newline sep_by comma integers (CSV)") { + const auto csv_p{sep_by(p, many(oneOf('\n'), true), true)}; + using csv_vect = std::vector>; + WHEN("given empty string") { + const auto r{run_parser(csv_p, "")}; + REQUIRE(!r.first); + } + WHEN("given 1 csv line without following newline") { + const auto r{run_parser(csv_p, "1,2,3")}; + REQUIRE(!!r.first); + REQUIRE(r.first == csv_vect{{1, 2, 3}}); } + WHEN("given 1 csv line with following newline") { + const auto r{run_parser(csv_p, "1,2,3\n")}; + REQUIRE(!!r.first); + REQUIRE(r.first == csv_vect{{1, 2, 3}}); + } + WHEN("given 3 csv lines") { + const auto r{run_parser(csv_p, "1,2,3\n4, 5, 6\n7, 8, 9")}; + REQUIRE(!!r.first); + REQUIRE(r.first == csv_vect{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + } + WHEN("given 3 csv lines with lots of newlines inbetween") { + const auto r{ + run_parser(csv_p, "1,2,3\n\n\n\n4, 5, 6\n\n\n\n7, 8, 9\n\n\n")}; + REQUIRE(!!r.first); + REQUIRE(r.first == csv_vect{{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}); + } + } } - -SCENARIO( "tuple_of parsers", "[parser]" ) { - GIVEN( "tuple of int, int" ) { - const auto whitespace {many(oneOf(' ', '\t'))}; - const auto comma_whitespace {prefixed(oneOf(','), whitespace)}; - const auto p {tuple_of(integer, prefixed(comma_whitespace, integer))}; - WHEN( "given an empty string" ) { - const auto r {run_parser(p, "")}; - REQUIRE( !r.first ); - } - WHEN( "given valid string" ) { - const auto r {run_parser(p, "123, 456")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == std::make_tuple(123, 456) ); - } - } - GIVEN( "tuple of int, alphaword, vector int" ) { - const auto whitespace {many(oneOf(' ', '\t'))}; - const auto comma_whitespace {prefixed(oneOf(','), whitespace)}; - const auto alphaword {many(sat([] (char c) { return 'a' <= c && c <= 'z'; }), true)}; - const auto p {tuple_of(integer, - prefixed(comma_whitespace, alphaword), - prefixed(comma_whitespace, sep_by(integer, comma_whitespace)) - )}; - WHEN( "given an empty string" ) { - const auto r {run_parser(p, "")}; - REQUIRE( !r.first ); - } - WHEN( "given valid string" ) { - const auto r {run_parser(p, "123, abc, 100, 200, 300")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == std::make_tuple(123, std::string{"abc"}, std::vector{100, 200, 300}) ); - } +SCENARIO("tuple_of parsers", "[parser]") { + GIVEN("tuple of int, int") { + const auto whitespace{many(oneOf(' ', '\t'))}; + const auto comma_whitespace{prefixed(oneOf(','), whitespace)}; + const auto p{tuple_of(integer, prefixed(comma_whitespace, integer))}; + WHEN("given an empty string") { + const auto r{run_parser(p, "")}; + REQUIRE(!r.first); + } + WHEN("given valid string") { + const auto r{run_parser(p, "123, 456")}; + REQUIRE(!!r.first); + REQUIRE(r.first == std::make_tuple(123, 456)); + } + } + GIVEN("tuple of int, alphaword, vector int") { + const auto whitespace{many(oneOf(' ', '\t'))}; + const auto comma_whitespace{prefixed(oneOf(','), whitespace)}; + const auto alphaword{ + many(sat([](char c) { return 'a' <= c && c <= 'z'; }), true)}; + const auto p{tuple_of( + integer, prefixed(comma_whitespace, alphaword), + prefixed(comma_whitespace, sep_by(integer, comma_whitespace)))}; + WHEN("given an empty string") { + const auto r{run_parser(p, "")}; + REQUIRE(!r.first); } + WHEN("given valid string") { + const auto r{run_parser(p, "123, abc, 100, 200, 300")}; + REQUIRE(!!r.first); + REQUIRE(r.first == std::make_tuple(123, std::string{"abc"}, + std::vector{100, 200, 300})); + } + } } -SCENARIO( "choice parsers", "[parser]" ) { - GIVEN( "choice of A B number" ) { - const auto p {choice(oneOf('A'), oneOf('B'), number)}; - WHEN( "given an empty string" ) { - const auto r {run_parser(p, "")}; - REQUIRE( !r.first ); - } - WHEN( "given A" ) { - const std::string s {"A "}; - const auto r {run_parser(p, s)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 'A' ); - REQUIRE( r.second.peek() == ' ' ); - } - WHEN( "given B" ) { - const auto r {run_parser(p, "B")}; - REQUIRE( !!r.first ); - REQUIRE( r.first == 'B' ); - } - WHEN( "given C" ) { - const auto r {run_parser(p, "C")}; - REQUIRE( !r.first ); - } - WHEN( "given 5" ) { - const std::string s {"5 "}; - const auto r {run_parser(p, s)}; - REQUIRE( !!r.first ); - REQUIRE( r.first == '5' ); - REQUIRE( r.second.peek() == ' ' ); - } +SCENARIO("choice parsers", "[parser]") { + GIVEN("choice of A B number") { + const auto p{choice(oneOf('A'), oneOf('B'), number)}; + WHEN("given an empty string") { + const auto r{run_parser(p, "")}; + REQUIRE(!r.first); } - + WHEN("given A") { + const std::string s{"A "}; + const auto r{run_parser(p, s)}; + REQUIRE(!!r.first); + REQUIRE(r.first == 'A'); + REQUIRE(r.second.peek() == ' '); + } + WHEN("given B") { + const auto r{run_parser(p, "B")}; + REQUIRE(!!r.first); + REQUIRE(r.first == 'B'); + } + WHEN("given C") { + const auto r{run_parser(p, "C")}; + REQUIRE(!r.first); + } + WHEN("given 5") { + const std::string s{"5 "}; + const auto r{run_parser(p, s)}; + REQUIRE(!!r.first); + REQUIRE(r.first == '5'); + REQUIRE(r.second.peek() == ' '); + } + } }