From c221dc9bf2fa241ffc7a1d13c81d9106336ad25e Mon Sep 17 00:00:00 2001 From: Eric Mertens Date: Sat, 8 Apr 2023 12:08:51 -0700 Subject: [PATCH] extract parser driver --- 2022/13.cpp | 34 +++++------------- 2022/14.cpp | 30 ++++------------ 2022/15.cpp | 30 ++++------------ 2022/16.cpp | 45 ++++++++---------------- 2022/21.cpp | 38 ++++++-------------- 2022/24.cpp | 66 +++++++++++++++++++++++++++++++++++ lib/CMakeLists.txt | 3 +- lib/include/aocpp/Parsing.hpp | 22 ++++++++++++ 8 files changed, 139 insertions(+), 129 deletions(-) create mode 100644 2022/24.cpp diff --git a/2022/13.cpp b/2022/13.cpp index 9386918..1ff6232 100644 --- a/2022/13.cpp +++ b/2022/13.cpp @@ -12,6 +12,7 @@ #include +#include #include 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 -class Grammar : public qi::grammar { - qi::rule integer; - qi::rule packets; - qi::rule packet; - qi::rule packetPair; - qi::rule packetPairs; +class Grammar : public qi::grammar { + qi::rule integer; + qi::rule packets; + qi::rule packet; + qi::rule packetPair; + qi::rule 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{}, 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; } diff --git a/2022/14.cpp b/2022/14.cpp index c0e4d90..f25884c 100644 --- a/2022/14.cpp +++ b/2022/14.cpp @@ -15,6 +15,7 @@ #include +#include #include #include @@ -28,11 +29,10 @@ using Input = std::vector>; const aocpp::Coord TOP {500, 0}; // Input file grammar -template -class Grammar : public qi::grammar { - qi::rule coord; - qi::rule()> line; - qi::rule input; +class Grammar : public qi::grammar { + qi::rule coord; + qi::rule()> line; + qi::rule 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{}, result) || b != e) { - throw std::runtime_error{"Bad input file: " + content}; - } - - return result; -} - auto ExpandLines(Input const& lines) -> std::unordered_set { auto result = std::unordered_set{}; @@ -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; diff --git a/2022/15.cpp b/2022/15.cpp index 48b6fae..efb14e7 100644 --- a/2022/15.cpp +++ b/2022/15.cpp @@ -12,6 +12,7 @@ #include #include +#include #include namespace { @@ -27,11 +28,10 @@ struct Entry { using Input = std::vector; // Input file grammar -template -class Grammar : public qi::grammar { - qi::rule coord; - qi::rule entry; - qi::rule input; +class Grammar : public qi::grammar { + qi::rule coord; + qi::rule entry; + qi::rule 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{}, 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; } diff --git a/2022/16.cpp b/2022/16.cpp index fb80757..e308034 100644 --- a/2022/16.cpp +++ b/2022/16.cpp @@ -28,6 +28,7 @@ #include +#include #include namespace { @@ -80,25 +81,16 @@ struct Room { std::vector 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 -{ - auto result = std::vector{}; - auto line = std::string{}; - while (std::getline(in, line)) { - using namespace qi::labels; - using It = std::string::const_iterator; +class Grammar : public qi::grammar()> { + qi::rule name; + qi::rule room_description; + qi::rule()> room_descriptions; - qi::rule const name = qi::as_string[+qi::alpha]; - qi::rule 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 " 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 diff --git a/2022/21.cpp b/2022/21.cpp index babe1e2..b8f7181 100644 --- a/2022/21.cpp +++ b/2022/21.cpp @@ -15,6 +15,7 @@ #include #include +#include #include namespace { @@ -38,14 +39,13 @@ struct Entry { using Input = std::vector; // Input file grammar -template -class Grammar : public qi::grammar { - qi::rule variable; - qi::rule op; - qi::rule expr; - qi::rule()> rhs; - qi::rule line; - qi::rule input; +class Grammar : public qi::grammar { + qi::rule variable; + qi::rule op; + qi::rule expr; + qi::rule()> rhs; + qi::rule line; + qi::rule 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{}, result) || b != e) { - throw std::runtime_error{"Bad input file: " + content}; - } - - return result; -} - auto Eval( std::unordered_map & values, std::unordered_map 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; } diff --git a/2022/24.cpp b/2022/24.cpp new file mode 100644 index 0000000..2038297 --- /dev/null +++ b/2022/24.cpp @@ -0,0 +1,66 @@ +#include +#include +#include +#include +#include + +#include + +#include + +#include +#include + +namespace { + +struct World { + std::vector 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; +} diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 0ca5734..f21d974 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,2 +1,3 @@ add_library(aocpp src/Startup.cpp src/Coord.cpp src/Parsing.cpp) -target_include_directories(aocpp PUBLIC include) \ No newline at end of file +target_include_directories(aocpp PUBLIC include) +target_link_libraries(aocpp Boost::headers) \ No newline at end of file diff --git a/lib/include/aocpp/Parsing.hpp b/lib/include/aocpp/Parsing.hpp index 02b89ec..80d3188 100644 --- a/lib/include/aocpp/Parsing.hpp +++ b/lib/include/aocpp/Parsing.hpp @@ -2,12 +2,34 @@ #define AOCPP_PARSING_HPP_ #include +#include +#include #include +#include + namespace aocpp { auto SplitOn(std::string const& stuff, std::string const& sep) -> std::vector; +/// Parse the complete input stream according to the rules defined in 'Grammar'. +/// Throws: std::runtime_error on failed parse +template +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 \ No newline at end of file