error handling

This commit is contained in:
Eric Mertens 2022-11-21 10:34:59 -08:00
parent 4e3e5f0584
commit 43cf5942bc

View File

@ -23,30 +23,73 @@ auto Eval(
std::vector<std::int64_t> vals; std::vector<std::int64_t> vals;
auto const eval1 = [&](){ auto const eval1 = [&](){
char o = ops.back(); ops.pop_back(); if (ops.size() < 1 || vals.size() < 2) {
std::int64_t y = vals.back(); vals.pop_back(); throw std::runtime_error{"parse error"};
std::int64_t x = vals.back(); vals.pop_back(); }
vals.push_back(cfg[o].second(x,y)); if (auto opit = cfg.find(ops.back()); opit != cfg.end()) {
std::int64_t y = vals.back(); vals.pop_back();
std::int64_t x = vals.back(); vals.pop_back();
vals.push_back(opit->second.second(x,y));
ops.pop_back();
} else {
throw std::runtime_error{"parse error"};
}
}; };
for (auto it = input.begin(); it != input.end(); it++) { bool expect_operator = false;
char const c = *it;
auto it = input.begin();
while (it != input.end()) {
char const c = *it++;
// skip whitespace
if (c == ' ') continue; if (c == ' ') continue;
// number literal
if (std::isdigit(c)) { if (std::isdigit(c)) {
vals.push_back(c - '0'); if (expect_operator) { throw std::runtime_error{"unexpected literal"}; }
} else if ('(' == c) { expect_operator = true;
std::int64_t acc = c - '0';
while (it != input.end() && std::isdigit(*it)) {
acc = acc*10 + (*it++ - '0');
}
vals.push_back(acc);
continue;
}
// begin subexpression
if ('(' == c) {
if (expect_operator) { throw std::runtime_error{"unexpected '('"}; }
ops.push_back('('); ops.push_back('(');
} else if (')' == c) { continue;
while (ops.back() != '(') eval1(); }
ops.pop_back();
} else if (auto opit = cfg.find(c); opit != cfg.end()) { // end subexpression
if (')' == c) {
if (!expect_operator) { throw std::runtime_error{"unexpected ')'"}; }
while (!ops.empty() && ops.back() != '(') eval1();
if (ops.empty()) throw std::runtime_error{"parse error"};
ops.pop_back(); // '('
continue;
}
// binary operator
if (auto opit = cfg.find(c); opit != cfg.end()) {
if (!expect_operator) { throw std::runtime_error{"unexpected operator"}; }
expect_operator = false;
while (!ops.empty() && cfg[ops.back()].first >= opit->second.first) eval1(); while (!ops.empty() && cfg[ops.back()].first >= opit->second.first) eval1();
ops.push_back(c); ops.push_back(c);
continue;
} }
throw std::runtime_error{"unknown character"};
} }
while (!ops.empty()) eval1(); while (!ops.empty()) eval1();
if (vals.empty()) { throw std::runtime_error{"empty input"}; }
return vals.back(); return vals.back();
} }
@ -63,21 +106,36 @@ auto Part2(std::string const& line) {
TEST_SUITE("documented examples") { TEST_SUITE("documented examples") {
TEST_CASE("part 1") { TEST_CASE("part 1") {
REQUIRE(Part1("1 + 2 * 3 + 4 * 5 + 6") == 71); CHECK(Part1("1 + 2 * 3 + 4 * 5 + 6") == 71);
REQUIRE(Part1("1 + (2 * 3) + (4 * (5 + 6))") == 51); CHECK(Part1("1 + (2 * 3) + (4 * (5 + 6))") == 51);
REQUIRE(Part1("2 * 3 + (4 * 5)") == 26); CHECK(Part1("2 * 3 + (4 * 5)") == 26);
REQUIRE(Part1("5 + (8 * 3 + 9 + 3 * 4 * 3)") == 437); CHECK(Part1("5 + (8 * 3 + 9 + 3 * 4 * 3)") == 437);
REQUIRE(Part1("5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))") == 12240); CHECK(Part1("5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))") == 12240);
REQUIRE(Part1("((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2") == 13632); CHECK(Part1("((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2") == 13632);
} }
TEST_CASE("part 2") { TEST_CASE("part 2") {
REQUIRE(Part2("1 + 2 * 3 + 4 * 5 + 6") == 231); CHECK(Part2("1 + 2 * 3 + 4 * 5 + 6") == 231);
REQUIRE(Part2("1 + (2 * 3) + (4 * (5 + 6))") == 51); CHECK(Part2("1 + (2 * 3) + (4 * (5 + 6))") == 51);
REQUIRE(Part2("2 * 3 + (4 * 5)") == 46); CHECK(Part2("2 * 3 + (4 * 5)") == 46);
REQUIRE(Part2("5 + (8 * 3 + 9 + 3 * 4 * 3)") == 1445); CHECK(Part2("5 + (8 * 3 + 9 + 3 * 4 * 3)") == 1445);
REQUIRE(Part2("5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))") == 669060); CHECK(Part2("5 * 9 * (7 * 3 * 3 + 9 * 3 + (8 + 6 * 4))") == 669060);
REQUIRE(Part2("((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2") == 23340); CHECK(Part2("((2 + 4 * 9) * (6 + 9 * 8 + 6) + 6) + 2 + 4 * 2") == 23340);
}
TEST_CASE("errors") {
CHECK_THROWS_AS(Part1(""), std::runtime_error);
CHECK_THROWS_AS(Part1("1 + ()"), std::runtime_error);
CHECK_THROWS_AS(Part1("(1"), std::runtime_error);
CHECK_THROWS_AS(Part1("1 (2)"), std::runtime_error);
CHECK_THROWS_AS(Part1(")"), std::runtime_error);
CHECK_THROWS_AS(Part1("1)"), std::runtime_error);
CHECK_THROWS_AS(Part1("1 2"), std::runtime_error);
CHECK_THROWS_AS(Part1("1 +"), std::runtime_error);
CHECK_THROWS_AS(Part1("+ 1"), std::runtime_error);
CHECK_THROWS_AS(Part1("1 /"), std::runtime_error);
CHECK_THROWS_AS(Part1("1 / 2"), std::runtime_error);
CHECK_THROWS_AS(Part1("1 + * 2"), std::runtime_error);
} }
} }