From ff7439a92281b068c77d0388c6cbddd9987498ad Mon Sep 17 00:00:00 2001 From: Eric Mertens Date: Tue, 28 Mar 2023 16:37:47 -0700 Subject: [PATCH] qi parser for 13 --- 2022/13.cpp | 119 +++++++++++++++++++++++--------------------- 2022/CMakeLists.txt | 2 +- 2 files changed, 64 insertions(+), 57 deletions(-) diff --git a/2022/13.cpp b/2022/13.cpp index ea6b720..cde1d71 100644 --- a/2022/13.cpp +++ b/2022/13.cpp @@ -1,3 +1,4 @@ +#include #include #include #include @@ -11,21 +12,28 @@ #include #include +#include +#include +#include + namespace { +namespace phx = boost::phoenix; +namespace qi = boost::spirit::qi; + +struct Tree; +using Trees = std::vector; +using Input = std::vector>; + struct Tree { - std::variant> rep; + std::variant rep; }; -auto operator<(Tree const& lhs, Tree const& rhs) -> bool; - -auto operator<(std::vector const& x, std::int64_t y) -> bool; -auto operator<(std::int64_t x, std::vector const& y) -> bool; auto operator<(Tree const& x, std::int64_t y) -> bool; auto operator<(std::int64_t x, Tree const& y) -> bool; -auto operator<(std::int64_t x, std::vector const& y) -> bool { +auto operator<(std::int64_t x, Trees const& y) -> bool { switch (y.size()) { case 0: return false; case 1: return x < y[0]; @@ -33,7 +41,7 @@ auto operator<(std::int64_t x, std::vector const& y) -> bool { } } -auto operator<(std::vector const& x, std::int64_t y) -> bool { +auto operator<(Trees const& x, std::int64_t y) -> bool { switch (x.size()) { case 0: return true; default: return x[0] < y; @@ -52,68 +60,67 @@ auto operator<(Tree const& lhs, Tree const& rhs) -> bool { return std::visit(std::less(), lhs.rep, rhs.rep); } -using Input = std::vector; +template +class Grammar : public qi::grammar { + qi::rule leaf; + qi::rule trees; + qi::rule tree; + qi::rule()> treepair; + qi::rule input; -auto ParseTree(std::string::iterator & begin, std::string::iterator end) -> Tree -{ - if (*begin == '[') { - begin++; - if (*begin == ']') { - begin++; - return Tree{std::vector{}}; - } - std::vector subtrees; - top: subtrees.emplace_back(ParseTree(begin, end)); - switch (*begin++) { - case ',': goto top; - case ']': return Tree{std::move(subtrees)}; - default: throw std::runtime_error{"bad tree list"}; - } - } else if (isdigit(*begin)) { - auto number_end = std::find_if_not(begin, end, isdigit); - std::string number_str {begin, number_end}; - begin = number_end; - std::int64_t number = std::stoll(number_str); - return Tree{number}; - } else { - throw std::runtime_error{"bad tree start"}; +public: + Grammar() : Grammar::base_type{input} { + using namespace qi::labels; + + leaf = qi::long_long; + trees = "[" >> -(tree % ",") >> "]"; + tree = leaf [ bind(&Tree::rep, _val) = _1 ] + | trees [ bind(&Tree::rep, _val) = _1 ]; + treepair = (tree >> "\n" >> tree >> "\n") [(_val[0] = _1, _val[1] = _2)]; + input = -(treepair % "\n"); } -} +}; auto Parse(std::istream & in) -> Input { Input result; - std::string line; - while (std::getline(in, line)) { - auto cursor = line.begin(); - result.push_back(ParseTree(cursor, line.end())); - std::getline(in, line); - cursor = line.begin(); - result.push_back(ParseTree(cursor, line.end())); - std::getline(in, line); // skip blank + std::string content {std::istreambuf_iterator{in}, {}}; + auto b {content.begin()}; + auto const e {content.end()}; + + if (!qi::parse(b, e, Grammar{}, result) || b != e) { + throw std::runtime_error{"tree parser failed"}; } return result; } -auto Part1(Input const& input) -> std::int64_t +auto Part1(Input const& input) -> std::size_t { std::int64_t result {0}; - for (std::size_t i = 0; i < input.size(); i += 2) { - if (input[i] < input[i+1]) { - result += i/2 + 1; + for (auto const i : boost::irange(input.size())) { + if (input[i][0] < input[i][1]) { + result += i + 1; } } return result; } -auto Part2(Input const& input) -> std::int64_t +auto Part2(Input const& input) -> std::size_t { - Tree two {std::vector{Tree{std::vector{Tree{2}}}}}; - Tree six {std::vector{Tree{std::vector{Tree{6}}}}}; - auto ix2 = std::count_if(input.begin(), input.end(), [&](auto const& x) { return x < two; }); - auto ix6 = std::count_if(input.begin(), input.end(), [&](auto const& x) { return x < six; }); - return (ix2+1) * (ix6+2); + Tree two{2}, six{6}; // Indistinguishable from [[2]] and [[6]] + std::size_t ix2{1}, ix6{2}; + for (auto const& pair : input) { + for (auto const& tree : pair) { + if (tree < six) { + ix6++; + if (tree < two) { + ix2++; + } + } + } + } + return ix2 * ix6; } } // namespace @@ -122,11 +129,11 @@ TEST_SUITE("2022-13 examples") { TEST_CASE("simple ordering") { CHECK(Tree{1} < Tree{2}); CHECK(!(Tree{2} < Tree{1})); - CHECK(Tree{std::vector{Tree{1}}} < Tree{std::vector{Tree{2}}}); - CHECK(Tree{1} < Tree{std::vector{Tree{2}}}); - CHECK(!(Tree{std::vector{Tree{2}}} < Tree{1})); - CHECK(Tree{std::vector{}} < Tree{std::vector{Tree{2}}}); - CHECK(Tree{std::vector{}} < Tree{std::vector{Tree{2}}}); + CHECK(Tree{Trees{Tree{1}}} < Tree{Trees{Tree{2}}}); + CHECK(Tree{1} < Tree{Trees{Tree{2}}}); + CHECK(!(Tree{Trees{Tree{2}}} < Tree{1})); + CHECK(Tree{Trees{}} < Tree{Trees{Tree{2}}}); + CHECK(Tree{Trees{}} < Tree{Trees{Tree{2}}}); } TEST_CASE("documented example") { std::istringstream in { @@ -154,7 +161,7 @@ TEST_SUITE("2022-13 examples") { "[1,[2,[3,[4,[5,6,7]]]],8,9]\n" "[1,[2,[3,[4,[5,6,0]]]],8,9]\n" }; - auto input = Parse(in); + auto const input = Parse(in); CHECK(13 == Part1(input)); CHECK(140 == Part2(input)); } diff --git a/2022/CMakeLists.txt b/2022/CMakeLists.txt index d9b40c0..822951b 100644 --- a/2022/CMakeLists.txt +++ b/2022/CMakeLists.txt @@ -29,7 +29,7 @@ add_executable(2022_12 12.cpp) target_link_libraries(2022_12 aocpp) add_executable(2022_13 13.cpp) -target_link_libraries(2022_13 aocpp) +target_link_libraries(2022_13 aocpp Boost::headers) add_executable(2022_16 16.cpp) target_link_libraries(2022_16 aocpp Boost::headers)