extract parser driver

This commit is contained in:
Eric Mertens 2023-04-08 12:08:51 -07:00
parent a38a105e6f
commit c221dc9bf2
8 changed files with 139 additions and 129 deletions

View File

@ -12,6 +12,7 @@
#include <doctest.h> #include <doctest.h>
#include <aocpp/Parsing.hpp>
#include <aocpp/Startup.hpp> #include <aocpp/Startup.hpp>
namespace { namespace {
@ -69,13 +70,12 @@ auto operator<(Packet const& lhs, Packet const& rhs) -> bool {
// Packets can be integer literals or a list of packets. // Packets can be integer literals or a list of packets.
// A list of packets is bracketed with square brackets and is comma-separated. // A list of packets is bracketed with square brackets and is comma-separated.
// No additional whitespace is tolerated in the input stream. // No additional whitespace is tolerated in the input stream.
template <typename It> class Grammar : public qi::grammar<std::string::const_iterator, PacketPairs()> {
class Grammar : public qi::grammar<It, PacketPairs()> { qi::rule<iterator_type, std::int64_t> integer;
qi::rule<It, std::int64_t> integer; qi::rule<iterator_type, Packets()> packets;
qi::rule<It, Packets()> packets; qi::rule<iterator_type, Packet()> packet;
qi::rule<It, Packet()> packet; qi::rule<iterator_type, PacketPair()> packetPair;
qi::rule<It, PacketPair()> packetPair; qi::rule<iterator_type, PacketPairs()> packetPairs;
qi::rule<It, PacketPairs()> packetPairs;
public: public:
Grammar() : Grammar::base_type{packetPairs} { Grammar() : Grammar::base_type{packetPairs} {
@ -91,22 +91,6 @@ public:
} }
}; };
/// Parse the complete input stream according to the rules defined in 'Grammar'.
/// Throws: std::runtime_error on failed parse
auto Parse(std::istream & in) -> PacketPairs
{
auto result = PacketPairs{};
auto const content = std::string{std::istreambuf_iterator{in}, {}};
auto b = content.begin(); // updated on successful parse
auto const e = content.end();
if (!qi::parse(b, e, Grammar<decltype(b)>{}, result) || b != e) {
throw std::runtime_error{"Bad packet: " + content};
}
return result;
}
// Return the sum 1-based indexes of the pairs where the first element is less than the second. // Return the sum 1-based indexes of the pairs where the first element is less than the second.
auto Part1(PacketPairs const& packetPairs) -> std::size_t auto Part1(PacketPairs const& packetPairs) -> std::size_t
{ {
@ -183,7 +167,7 @@ TEST_SUITE("2022-13 examples") {
"[1,[2,[3,[4,[5,6,7]]]],8,9]\n" "[1,[2,[3,[4,[5,6,7]]]],8,9]\n"
"[1,[2,[3,[4,[5,6,0]]]],8,9]\n" "[1,[2,[3,[4,[5,6,0]]]],8,9]\n"
}; };
auto const packetPairs = Parse(in); auto const packetPairs = aocpp::ParseGrammar(Grammar{}, in);
CHECK(13 == Part1(packetPairs)); CHECK(13 == Part1(packetPairs));
CHECK(140 == Part2(packetPairs)); CHECK(140 == Part2(packetPairs));
} }
@ -191,7 +175,7 @@ TEST_SUITE("2022-13 examples") {
auto Main(std::istream & in, std::ostream & out) -> void auto Main(std::istream & in, std::ostream & out) -> void
{ {
auto const packetPairs = Parse(in); auto const packetPairs = aocpp::ParseGrammar(Grammar{}, in);
out << "Part 1: " << Part1(packetPairs) << std::endl; out << "Part 1: " << Part1(packetPairs) << std::endl;
out << "Part 2: " << Part2(packetPairs) << std::endl; out << "Part 2: " << Part2(packetPairs) << std::endl;
} }

View File

@ -15,6 +15,7 @@
#include <doctest.h> #include <doctest.h>
#include <aocpp/Parsing.hpp>
#include <aocpp/Startup.hpp> #include <aocpp/Startup.hpp>
#include <aocpp/Coord.hpp> #include <aocpp/Coord.hpp>
@ -28,11 +29,10 @@ using Input = std::vector<std::vector<aocpp::Coord>>;
const aocpp::Coord TOP {500, 0}; const aocpp::Coord TOP {500, 0};
// Input file grammar // Input file grammar
template <typename It> class Grammar : public qi::grammar<std::string::const_iterator, Input()> {
class Grammar : public qi::grammar<It, Input()> { qi::rule<iterator_type, aocpp::Coord> coord;
qi::rule<It, aocpp::Coord> coord; qi::rule<iterator_type, std::vector<aocpp::Coord>()> line;
qi::rule<It, std::vector<aocpp::Coord>()> line; qi::rule<iterator_type, Input()> input;
qi::rule<It, Input()> input;
public: public:
Grammar() : Grammar::base_type{input} { Grammar() : Grammar::base_type{input} {
@ -46,22 +46,6 @@ public:
} }
}; };
/// Parse the complete input stream according to the rules defined in 'Grammar'.
/// Throws: std::runtime_error on failed parse
auto Parse(std::istream & in) -> Input
{
auto result = Input{};
auto const content = std::string{std::istreambuf_iterator{in}, {}};
auto b = content.begin(); // updated on successful parse
auto const e = content.end();
if (!qi::parse(b, e, Grammar<decltype(b)>{}, result) || b != e) {
throw std::runtime_error{"Bad input file: " + content};
}
return result;
}
auto ExpandLines(Input const& lines) -> std::unordered_set<aocpp::Coord> auto ExpandLines(Input const& lines) -> std::unordered_set<aocpp::Coord>
{ {
auto result = std::unordered_set<aocpp::Coord>{}; auto result = std::unordered_set<aocpp::Coord>{};
@ -175,7 +159,7 @@ TEST_SUITE("2022-14 examples") {
"498,4 -> 498,6 -> 496,6\n" "498,4 -> 498,6 -> 496,6\n"
"503,4 -> 502,4 -> 502,9 -> 494,9\n" "503,4 -> 502,4 -> 502,9 -> 494,9\n"
}; };
auto const input = Parse(in); auto const input = aocpp::ParseGrammar(Grammar{}, in);
auto const bottom = FindBottom(input); auto const bottom = FindBottom(input);
auto world = ExpandLines(input); auto world = ExpandLines(input);
CHECK(24 == Part1(bottom, world)); CHECK(24 == Part1(bottom, world));
@ -185,7 +169,7 @@ TEST_SUITE("2022-14 examples") {
auto Main(std::istream & in, std::ostream & out) -> void auto Main(std::istream & in, std::ostream & out) -> void
{ {
auto const input = Parse(in); auto const input = aocpp::ParseGrammar(Grammar{}, in);
auto const bottom = FindBottom(input); auto const bottom = FindBottom(input);
auto world = ExpandLines(input); auto world = ExpandLines(input);
out << "Part 1: " << Part1(bottom, world) << std::endl; out << "Part 1: " << Part1(bottom, world) << std::endl;

View File

@ -12,6 +12,7 @@
#include <doctest.h> #include <doctest.h>
#include <aocpp/Coord.hpp> #include <aocpp/Coord.hpp>
#include <aocpp/Parsing.hpp>
#include <aocpp/Startup.hpp> #include <aocpp/Startup.hpp>
namespace { namespace {
@ -27,11 +28,10 @@ struct Entry {
using Input = std::vector<Entry>; using Input = std::vector<Entry>;
// Input file grammar // Input file grammar
template <typename It> class Grammar : public qi::grammar<std::string::const_iterator, Input()> {
class Grammar : public qi::grammar<It, Input()> { qi::rule<iterator_type, aocpp::Coord> coord;
qi::rule<It, aocpp::Coord> coord; qi::rule<iterator_type, Entry()> entry;
qi::rule<It, Entry()> entry; qi::rule<iterator_type, Input()> input;
qi::rule<It, Input()> input;
public: public:
Grammar() : Grammar::base_type{input} { Grammar() : Grammar::base_type{input} {
@ -50,22 +50,6 @@ public:
} }
}; };
/// Parse the complete input stream according to the rules defined in 'Grammar'.
/// Throws: std::runtime_error on failed parse
auto Parse(std::istream & in) -> Input
{
auto result = Input{};
auto const content = std::string{std::istreambuf_iterator{in}, {}};
auto b = content.begin(); // updated on successful parse
auto const e = content.end();
if (!qi::parse(b, e, Grammar<decltype(b)>{}, result) || b != e) {
throw std::runtime_error{"Bad input file: " + content};
}
return result;
}
struct Range { struct Range {
std::int64_t lo, hi; std::int64_t lo, hi;
auto operator<=>(Range const& rhs) const = default; auto operator<=>(Range const& rhs) const = default;
@ -235,7 +219,7 @@ TEST_SUITE("2022-15 examples") {
"Sensor at x=14, y=3: closest beacon is at x=15, y=3\n" "Sensor at x=14, y=3: closest beacon is at x=15, y=3\n"
"Sensor at x=20, y=1: closest beacon is at x=15, y=3\n" "Sensor at x=20, y=1: closest beacon is at x=15, y=3\n"
}; };
auto const input = Parse(in); auto const input = aocpp::ParseGrammar(Grammar{}, in);
CHECK(26 == Part1(input, 10)); CHECK(26 == Part1(input, 10));
CHECK(56000011 == Part2(input, 20)); CHECK(56000011 == Part2(input, 20));
} }
@ -243,7 +227,7 @@ TEST_SUITE("2022-15 examples") {
auto Main(std::istream & in, std::ostream & out) -> void auto Main(std::istream & in, std::ostream & out) -> void
{ {
auto const input = Parse(in); auto const input = aocpp::ParseGrammar(Grammar{}, in);
out << "Part 1: " << Part1(input, 2'000'000) << std::endl; out << "Part 1: " << Part1(input, 2'000'000) << std::endl;
out << "Part 2: " << Part2(input, 4'000'000) << std::endl; out << "Part 2: " << Part2(input, 4'000'000) << std::endl;
} }

View File

@ -28,6 +28,7 @@
#include <doctest.h> #include <doctest.h>
#include <aocpp/Parsing.hpp>
#include <aocpp/Startup.hpp> #include <aocpp/Startup.hpp>
namespace { namespace {
@ -80,25 +81,16 @@ struct Room {
std::vector<std::string> connections; std::vector<std::string> connections;
}; };
/// @brief Parse the input file class Grammar : public qi::grammar<std::string::const_iterator, std::vector<Room>()> {
/// @param[in,out] in input stream qi::rule<iterator_type, std::string()> name;
/// @return Vector of parsed rooms, one per input line qi::rule<iterator_type, Room()> room_description;
/// qi::rule<iterator_type, std::vector<Room>()> room_descriptions;
/// The parser will consume input until the end of the stream.
///
/// Input lines should follow the following pattern:
/// * Valve **name** has flow rate= **number** ; tunnels lead to valves **name**, **name** ...
/// * Valve **name** has flow rate= **number** ; tunnel leads to valve **name**
auto Parse(std::istream & in) -> std::vector<Room>
{
auto result = std::vector<Room>{};
auto line = std::string{};
while (std::getline(in, line)) {
using namespace qi::labels;
using It = std::string::const_iterator;
qi::rule<It, std::string()> const name = qi::as_string[+qi::alpha]; public:
qi::rule<It, Room()> const room_description = Grammar() : grammar::base_type{room_descriptions} {
using namespace qi::labels;
name = qi::as_string[+qi::alpha];
room_description =
"Valve " >> "Valve " >>
name [ phx::bind(&Room::name, _val) = _1 ] >> name [ phx::bind(&Room::name, _val) = _1 ] >>
" has flow rate=" >> " has flow rate=" >>
@ -107,18 +99,11 @@ auto Parse(std::istream & in) -> std::vector<Room>
" lead" >> -qi::string("s") >> " lead" >> -qi::string("s") >>
" to valve" >> -qi::string("s") >> " to valve" >> -qi::string("s") >>
" " >> " " >>
(name % ", ") [ phx::bind(&Room::connections, _val) = _1 ]; (name % ", ") [ phx::bind(&Room::connections, _val) = _1 ] >>
"\n";
auto b = line.cbegin(); room_descriptions = *room_description;
auto const e = line.cend();
result.emplace_back();
if (!qi::parse(b, e, room_description, result.back()) || b != e) {
throw std::runtime_error{"bad input line"};
}
} }
};
return result;
}
/// @brief Rearrange the rooms so that those with flows come first /// @brief Rearrange the rooms so that those with flows come first
/// @param[in,out] rooms vector of rooms that gets reordered /// @param[in,out] rooms vector of rooms that gets reordered
@ -287,7 +272,7 @@ auto Part2(
/// @param[in,out] out output text /// @param[in,out] out output text
auto Main(std::istream & in, std::ostream & out) -> void auto Main(std::istream & in, std::ostream & out) -> void
{ {
auto rooms = Parse(in); auto rooms = aocpp::ParseGrammar(Grammar{}, in);
auto const n = FlowsFirst(rooms); // reorders rooms auto const n = FlowsFirst(rooms); // reorders rooms
auto const [start, distances] = GenerateDistances(rooms); auto const [start, distances] = GenerateDistances(rooms);
rooms.resize(n); // forget about the rooms with no flow rooms.resize(n); // forget about the rooms with no flow

View File

@ -15,6 +15,7 @@
#include <z3++.h> #include <z3++.h>
#include <aocpp/Overloaded.hpp> #include <aocpp/Overloaded.hpp>
#include <aocpp/Parsing.hpp>
#include <aocpp/Startup.hpp> #include <aocpp/Startup.hpp>
namespace { namespace {
@ -38,14 +39,13 @@ struct Entry {
using Input = std::vector<Entry>; using Input = std::vector<Entry>;
// Input file grammar // Input file grammar
template <typename It> class Grammar : public qi::grammar<std::string::const_iterator, Input()> {
class Grammar : public qi::grammar<It, Input()> { qi::rule<iterator_type, std::string()> variable;
qi::rule<It, std::string()> variable; qi::rule<iterator_type, Op()> op;
qi::rule<It, Op()> op; qi::rule<iterator_type, Expr()> expr;
qi::rule<It, Expr()> expr; qi::rule<iterator_type, std::variant<std::int64_t, Expr>()> rhs;
qi::rule<It, std::variant<std::int64_t, Expr>()> rhs; qi::rule<iterator_type, Entry()> line;
qi::rule<It, Entry()> line; qi::rule<iterator_type, Input()> input;
qi::rule<It, Input()> input;
public: public:
Grammar() : Grammar::base_type{input} { Grammar() : Grammar::base_type{input} {
@ -69,22 +69,6 @@ public:
} }
}; };
/// Parse the complete input stream according to the rules defined in 'Grammar'.
/// Throws: std::runtime_error on failed parse
auto Parse(std::istream & in) -> Input
{
auto result = Input{};
auto const content = std::string{std::istreambuf_iterator{in}, {}};
auto b = content.begin(); // updated on successful parse
auto const e = content.end();
if (!qi::parse(b, e, Grammar<decltype(b)>{}, result) || b != e) {
throw std::runtime_error{"Bad input file: " + content};
}
return result;
}
auto Eval( auto Eval(
std::unordered_map<std::string, double> & values, std::unordered_map<std::string, double> & values,
std::unordered_map<std::string, Expr> const& exprs, std::unordered_map<std::string, Expr> const& exprs,
@ -191,15 +175,15 @@ TEST_SUITE("2022-21 examples") {
"drzm: hmdt - zczc\n" "drzm: hmdt - zczc\n"
"hmdt: 32\n" "hmdt: 32\n"
}; };
auto const input = Parse(in); auto const input = aocpp::ParseGrammar(Grammar(), in);
CHECK(152 == Part1(input)); CHECK(152 == Part1(input));
CHECK(301 == Part2(input)); CHECK(301 == Part2(input));
} }
} }
auto Main(std::istream & in, std::ostream & out) -> void auto Main(std::istream & in, std::ostream & out) -> void
{ {
auto const input = Parse(in); auto const input = aocpp::ParseGrammar(Grammar(), in);
out << "Part 1: " << Part1(input) << std::endl; out << "Part 1: " << Part1(input) << std::endl;
out << "Part 2: " << Part2(input) << std::endl; out << "Part 2: " << Part2(input) << std::endl;
} }

66
2022/24.cpp Normal file
View File

@ -0,0 +1,66 @@
#include <cstdint>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <boost/range/adaptor/indexed.hpp>
#include <doctest.h>
#include <aocpp/Coord.hpp>
#include <aocpp/Startup.hpp>
namespace {
struct World {
std::vector<aocpp::Coord> ups, downs, lefts, rights;
std::size_t height, width;
};
auto Parse(std::istream & in) -> World
{
auto result = World{};
auto y = std::size_t{0};
auto line = std::string{};
while (std::getline(in, line)) {
for (auto const [x,c] : boost::adaptors::index(line)) {
switch (c) {
case '>': result.rights.emplace_back(x-1,y-1); break;
case '<': result.lefts.emplace_back(x-1,y-1); break;
case '^': result.ups.emplace_back(x-1,y-1); break;
case 'v': result.downs.emplace_back(x-1,y-1); break;
}
}
++y;
}
result.height = y - 2;
result.width = line.size() - 2;
return result;
}
} // namespace
TEST_SUITE("2022-24 examples") {
TEST_CASE("example") {
std::istringstream in {
"#.#####\n"
"#.....#\n"
"#>....#\n"
"#.....#\n"
"#...v.#\n"
"#.....#\n"
"#####.#\n"
};
auto const world = Parse(in);
}
}
auto Main(std::istream & in, std::ostream & out) -> void
{
auto const input = Parse(in);
//out << "Part 1: " << Part1(in) << std::endl;
}

View File

@ -1,2 +1,3 @@
add_library(aocpp src/Startup.cpp src/Coord.cpp src/Parsing.cpp) add_library(aocpp src/Startup.cpp src/Coord.cpp src/Parsing.cpp)
target_include_directories(aocpp PUBLIC include) target_include_directories(aocpp PUBLIC include)
target_link_libraries(aocpp Boost::headers)

View File

@ -2,12 +2,34 @@
#define AOCPP_PARSING_HPP_ #define AOCPP_PARSING_HPP_
#include <string> #include <string>
#include <iostream>
#include <iterator>
#include <vector> #include <vector>
#include <boost/spirit/home/qi.hpp>
namespace aocpp { namespace aocpp {
auto SplitOn(std::string const& stuff, std::string const& sep) -> std::vector<std::string>; auto SplitOn(std::string const& stuff, std::string const& sep) -> std::vector<std::string>;
/// Parse the complete input stream according to the rules defined in 'Grammar'.
/// Throws: std::runtime_error on failed parse
template <typename G>
auto ParseGrammar(G const& grammar, std::istream & in) -> typename G::start_type::attr_type
{
namespace qi = boost::spirit::qi;
auto result = typename G::start_type::attr_type {};
auto const content = std::string{std::istreambuf_iterator{in}, {}};
auto b = content.begin(); // updated on successful parse
auto const e = content.end();
if (!qi::parse(b, e, grammar, result) || b != e) {
throw std::runtime_error{"Bad input file: " + content};
}
return result;
}
} }
#endif #endif