qi parser for 13
This commit is contained in:
parent
d5cd72f4bc
commit
ff7439a922
119
2022/13.cpp
119
2022/13.cpp
|
@ -1,3 +1,4 @@
|
|||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
|
@ -11,21 +12,28 @@
|
|||
#include <aocpp/Startup.hpp>
|
||||
#include <aocpp/Overloaded.hpp>
|
||||
|
||||
#include <boost/spirit/include/qi.hpp>
|
||||
#include <boost/phoenix.hpp>
|
||||
#include <boost/range/irange.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
namespace phx = boost::phoenix;
|
||||
namespace qi = boost::spirit::qi;
|
||||
|
||||
struct Tree;
|
||||
using Trees = std::vector<Tree>;
|
||||
using Input = std::vector<std::array<Tree, 2>>;
|
||||
|
||||
struct Tree {
|
||||
std::variant<std::int64_t, std::vector<Tree>> rep;
|
||||
std::variant<std::int64_t, Trees> rep;
|
||||
};
|
||||
|
||||
auto operator<(Tree const& lhs, Tree const& rhs) -> bool;
|
||||
|
||||
auto operator<(std::vector<Tree> const& x, std::int64_t y) -> bool;
|
||||
auto operator<(std::int64_t x, std::vector<Tree> 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<Tree> 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<Tree> const& y) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
auto operator<(std::vector<Tree> 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<Tree>;
|
||||
template <typename It>
|
||||
class Grammar : public qi::grammar<It, Input()> {
|
||||
qi::rule<It, std::int64_t> leaf;
|
||||
qi::rule<It, Trees()> trees;
|
||||
qi::rule<It, Tree()> tree;
|
||||
qi::rule<It, std::array<Tree, 2>()> treepair;
|
||||
qi::rule<It, Input()> input;
|
||||
|
||||
auto ParseTree(std::string::iterator & begin, std::string::iterator end) -> Tree
|
||||
{
|
||||
if (*begin == '[') {
|
||||
begin++;
|
||||
if (*begin == ']') {
|
||||
begin++;
|
||||
return Tree{std::vector<Tree>{}};
|
||||
}
|
||||
std::vector<Tree> 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<decltype(b)>{}, 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>{}} < Tree{std::vector{Tree{2}}});
|
||||
CHECK(Tree{std::vector<Tree>{}} < 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));
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in New Issue
Block a user