extract parser driver
This commit is contained in:
parent
a38a105e6f
commit
c221dc9bf2
34
2022/13.cpp
34
2022/13.cpp
@ -12,6 +12,7 @@
|
||||
|
||||
#include <doctest.h>
|
||||
|
||||
#include <aocpp/Parsing.hpp>
|
||||
#include <aocpp/Startup.hpp>
|
||||
|
||||
namespace {
|
||||
@ -69,13 +70,12 @@ auto operator<(Packet const& lhs, Packet const& rhs) -> bool {
|
||||
// Packets can be integer literals or a list of packets.
|
||||
// A list of packets is bracketed with square brackets and is comma-separated.
|
||||
// No additional whitespace is tolerated in the input stream.
|
||||
template <typename It>
|
||||
class Grammar : public qi::grammar<It, PacketPairs()> {
|
||||
qi::rule<It, std::int64_t> integer;
|
||||
qi::rule<It, Packets()> packets;
|
||||
qi::rule<It, Packet()> packet;
|
||||
qi::rule<It, PacketPair()> packetPair;
|
||||
qi::rule<It, PacketPairs()> packetPairs;
|
||||
class Grammar : public qi::grammar<std::string::const_iterator, PacketPairs()> {
|
||||
qi::rule<iterator_type, std::int64_t> integer;
|
||||
qi::rule<iterator_type, Packets()> packets;
|
||||
qi::rule<iterator_type, Packet()> packet;
|
||||
qi::rule<iterator_type, PacketPair()> packetPair;
|
||||
qi::rule<iterator_type, PacketPairs()> packetPairs;
|
||||
|
||||
public:
|
||||
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.
|
||||
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,0]]]],8,9]\n"
|
||||
};
|
||||
auto const packetPairs = Parse(in);
|
||||
auto const packetPairs = aocpp::ParseGrammar(Grammar{}, in);
|
||||
CHECK(13 == Part1(packetPairs));
|
||||
CHECK(140 == Part2(packetPairs));
|
||||
}
|
||||
@ -191,7 +175,7 @@ TEST_SUITE("2022-13 examples") {
|
||||
|
||||
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 2: " << Part2(packetPairs) << std::endl;
|
||||
}
|
||||
|
30
2022/14.cpp
30
2022/14.cpp
@ -15,6 +15,7 @@
|
||||
|
||||
#include <doctest.h>
|
||||
|
||||
#include <aocpp/Parsing.hpp>
|
||||
#include <aocpp/Startup.hpp>
|
||||
#include <aocpp/Coord.hpp>
|
||||
|
||||
@ -28,11 +29,10 @@ using Input = std::vector<std::vector<aocpp::Coord>>;
|
||||
const aocpp::Coord TOP {500, 0};
|
||||
|
||||
// Input file grammar
|
||||
template <typename It>
|
||||
class Grammar : public qi::grammar<It, Input()> {
|
||||
qi::rule<It, aocpp::Coord> coord;
|
||||
qi::rule<It, std::vector<aocpp::Coord>()> line;
|
||||
qi::rule<It, Input()> input;
|
||||
class Grammar : public qi::grammar<std::string::const_iterator, Input()> {
|
||||
qi::rule<iterator_type, aocpp::Coord> coord;
|
||||
qi::rule<iterator_type, std::vector<aocpp::Coord>()> line;
|
||||
qi::rule<iterator_type, Input()> input;
|
||||
|
||||
public:
|
||||
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 result = std::unordered_set<aocpp::Coord>{};
|
||||
@ -175,7 +159,7 @@ TEST_SUITE("2022-14 examples") {
|
||||
"498,4 -> 498,6 -> 496,6\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 world = ExpandLines(input);
|
||||
CHECK(24 == Part1(bottom, world));
|
||||
@ -185,7 +169,7 @@ TEST_SUITE("2022-14 examples") {
|
||||
|
||||
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 world = ExpandLines(input);
|
||||
out << "Part 1: " << Part1(bottom, world) << std::endl;
|
||||
|
30
2022/15.cpp
30
2022/15.cpp
@ -12,6 +12,7 @@
|
||||
#include <doctest.h>
|
||||
|
||||
#include <aocpp/Coord.hpp>
|
||||
#include <aocpp/Parsing.hpp>
|
||||
#include <aocpp/Startup.hpp>
|
||||
|
||||
namespace {
|
||||
@ -27,11 +28,10 @@ struct Entry {
|
||||
using Input = std::vector<Entry>;
|
||||
|
||||
// Input file grammar
|
||||
template <typename It>
|
||||
class Grammar : public qi::grammar<It, Input()> {
|
||||
qi::rule<It, aocpp::Coord> coord;
|
||||
qi::rule<It, Entry()> entry;
|
||||
qi::rule<It, Input()> input;
|
||||
class Grammar : public qi::grammar<std::string::const_iterator, Input()> {
|
||||
qi::rule<iterator_type, aocpp::Coord> coord;
|
||||
qi::rule<iterator_type, Entry()> entry;
|
||||
qi::rule<iterator_type, Input()> input;
|
||||
|
||||
public:
|
||||
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 {
|
||||
std::int64_t lo, hi;
|
||||
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=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(56000011 == Part2(input, 20));
|
||||
}
|
||||
@ -243,7 +227,7 @@ TEST_SUITE("2022-15 examples") {
|
||||
|
||||
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 2: " << Part2(input, 4'000'000) << std::endl;
|
||||
}
|
||||
|
45
2022/16.cpp
45
2022/16.cpp
@ -28,6 +28,7 @@
|
||||
|
||||
#include <doctest.h>
|
||||
|
||||
#include <aocpp/Parsing.hpp>
|
||||
#include <aocpp/Startup.hpp>
|
||||
|
||||
namespace {
|
||||
@ -80,25 +81,16 @@ struct Room {
|
||||
std::vector<std::string> connections;
|
||||
};
|
||||
|
||||
/// @brief Parse the input file
|
||||
/// @param[in,out] in input stream
|
||||
/// @return Vector of parsed rooms, one per input line
|
||||
///
|
||||
/// 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;
|
||||
class Grammar : public qi::grammar<std::string::const_iterator, std::vector<Room>()> {
|
||||
qi::rule<iterator_type, std::string()> name;
|
||||
qi::rule<iterator_type, Room()> room_description;
|
||||
qi::rule<iterator_type, std::vector<Room>()> room_descriptions;
|
||||
|
||||
qi::rule<It, std::string()> const name = qi::as_string[+qi::alpha];
|
||||
qi::rule<It, Room()> const room_description =
|
||||
public:
|
||||
Grammar() : grammar::base_type{room_descriptions} {
|
||||
using namespace qi::labels;
|
||||
name = qi::as_string[+qi::alpha];
|
||||
room_description =
|
||||
"Valve " >>
|
||||
name [ phx::bind(&Room::name, _val) = _1 ] >>
|
||||
" has flow rate=" >>
|
||||
@ -107,18 +99,11 @@ auto Parse(std::istream & in) -> std::vector<Room>
|
||||
" lead" >> -qi::string("s") >>
|
||||
" to valve" >> -qi::string("s") >>
|
||||
" " >>
|
||||
(name % ", ") [ phx::bind(&Room::connections, _val) = _1 ];
|
||||
|
||||
auto b = line.cbegin();
|
||||
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"};
|
||||
}
|
||||
(name % ", ") [ phx::bind(&Room::connections, _val) = _1 ] >>
|
||||
"\n";
|
||||
room_descriptions = *room_description;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
/// @brief Rearrange the rooms so that those with flows come first
|
||||
/// @param[in,out] rooms vector of rooms that gets reordered
|
||||
@ -287,7 +272,7 @@ auto Part2(
|
||||
/// @param[in,out] out output text
|
||||
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 [start, distances] = GenerateDistances(rooms);
|
||||
rooms.resize(n); // forget about the rooms with no flow
|
||||
|
38
2022/21.cpp
38
2022/21.cpp
@ -15,6 +15,7 @@
|
||||
#include <z3++.h>
|
||||
|
||||
#include <aocpp/Overloaded.hpp>
|
||||
#include <aocpp/Parsing.hpp>
|
||||
#include <aocpp/Startup.hpp>
|
||||
|
||||
namespace {
|
||||
@ -38,14 +39,13 @@ struct Entry {
|
||||
using Input = std::vector<Entry>;
|
||||
|
||||
// Input file grammar
|
||||
template <typename It>
|
||||
class Grammar : public qi::grammar<It, Input()> {
|
||||
qi::rule<It, std::string()> variable;
|
||||
qi::rule<It, Op()> op;
|
||||
qi::rule<It, Expr()> expr;
|
||||
qi::rule<It, std::variant<std::int64_t, Expr>()> rhs;
|
||||
qi::rule<It, Entry()> line;
|
||||
qi::rule<It, Input()> input;
|
||||
class Grammar : public qi::grammar<std::string::const_iterator, Input()> {
|
||||
qi::rule<iterator_type, std::string()> variable;
|
||||
qi::rule<iterator_type, Op()> op;
|
||||
qi::rule<iterator_type, Expr()> expr;
|
||||
qi::rule<iterator_type, std::variant<std::int64_t, Expr>()> rhs;
|
||||
qi::rule<iterator_type, Entry()> line;
|
||||
qi::rule<iterator_type, Input()> input;
|
||||
|
||||
public:
|
||||
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(
|
||||
std::unordered_map<std::string, double> & values,
|
||||
std::unordered_map<std::string, Expr> const& exprs,
|
||||
@ -191,15 +175,15 @@ TEST_SUITE("2022-21 examples") {
|
||||
"drzm: hmdt - zczc\n"
|
||||
"hmdt: 32\n"
|
||||
};
|
||||
auto const input = Parse(in);
|
||||
auto const input = aocpp::ParseGrammar(Grammar(), in);
|
||||
CHECK(152 == Part1(input));
|
||||
CHECK(301 == Part2(input));
|
||||
}
|
||||
}
|
||||
|
||||
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 2: " << Part2(input) << std::endl;
|
||||
}
|
||||
|
66
2022/24.cpp
Normal file
66
2022/24.cpp
Normal 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;
|
||||
}
|
@ -1,2 +1,3 @@
|
||||
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)
|
@ -2,12 +2,34 @@
|
||||
#define AOCPP_PARSING_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
#include <boost/spirit/home/qi.hpp>
|
||||
|
||||
namespace aocpp {
|
||||
|
||||
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
|
Loading…
Reference in New Issue
Block a user