|
|
@@ -13,7 +13,8 @@
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
-static void advance_over_whitespace(std::istream & in) {
|
|
|
+static void advance_over_whitespace(std::istream & in, char const * also = "") {
|
|
|
+ if (strchr(also, in.peek())) { in.get(); }
|
|
|
while (isspace(in.peek())) { in.get(); }
|
|
|
}
|
|
|
|
|
|
@@ -25,18 +26,21 @@ static std::string carrot(long long pos) {
|
|
|
return out;
|
|
|
}
|
|
|
|
|
|
-void print(std::ostream & out, int i) { out << 'd' << i; }
|
|
|
-void print(std::ostream & out, std::shared_ptr<dice::dice> d) {
|
|
|
- out << '{' << *d << '}';
|
|
|
-}
|
|
|
-
|
|
|
-void modifier(std::ostream & out, int i) {
|
|
|
- if (i > 0) out << '+';
|
|
|
- if (i) out << i;
|
|
|
-}
|
|
|
-void modifier(std::ostream & out, std::shared_ptr<dice::dice> d) {
|
|
|
- if (d->num > 0) out << '+';
|
|
|
- out << *d;
|
|
|
+namespace dice {
|
|
|
+ int sgn(sign s) {
|
|
|
+ return s == MINUS ? -1 : 1;
|
|
|
+ }
|
|
|
+ std::string str(sign s) {
|
|
|
+ switch (s) {
|
|
|
+ case PLUS: return "+";
|
|
|
+ case MINUS: return "-";
|
|
|
+ default: return "";
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ mod::operator int() const {
|
|
|
+ return sgn(sign) * value;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
namespace dice {
|
|
|
@@ -46,67 +50,70 @@ namespace dice {
|
|
|
}
|
|
|
|
|
|
std::ostream & operator<<(std::ostream & out, dice const & d) {
|
|
|
- out << d.num;
|
|
|
- boost::apply_visitor([&out](auto v) { print(out, v); }, d.of);
|
|
|
- boost::apply_visitor([&out](auto v) { modifier(out, v); }, d.modifier);
|
|
|
+ if (d.num != 1) out << d.num << '{';
|
|
|
+ for (die const & di : d.of) {
|
|
|
+ out << str(di.sgn) << di.num << 'd' << di.sides;
|
|
|
+ }
|
|
|
+ for (mod m : d.modifier) {
|
|
|
+ out << str(m.sign) << m.value;
|
|
|
+ }
|
|
|
+ if (d.num != 1) out << '}';
|
|
|
return out;
|
|
|
}
|
|
|
|
|
|
- static void parse(std::istream & in, dice & d, bool allow_nest = false);
|
|
|
+ static void parse(std::istream & in, dice & d, sign s);
|
|
|
+
|
|
|
+ static void parse_dN(std::istream & in, dice & d, die di) {
|
|
|
+ advance_over_whitespace(in, "dD");
|
|
|
+ d.of.push_back(di);
|
|
|
+ in >> d.of.back().sides;
|
|
|
+ parse(in, d, ZERO);
|
|
|
+ }
|
|
|
|
|
|
- static void parse_inner_expr(std::istream & in, dice & d, bool allow_nest) {
|
|
|
- using sp_dice = std::shared_ptr<dice>;
|
|
|
+ static void parse(std::istream & in, dice & d, sign s) {
|
|
|
+ // Allow implicit 1
|
|
|
+ advance_over_whitespace(in);
|
|
|
+ int value = 0;
|
|
|
+ if (isnumber(in.peek())) { in >> value; }
|
|
|
advance_over_whitespace(in);
|
|
|
- switch (in.get()) {
|
|
|
+ switch (in.peek()) {
|
|
|
case 'd': case 'D':
|
|
|
- in >> boost::get<int>(d.of);
|
|
|
- break;
|
|
|
- case '{':
|
|
|
- if (!allow_nest) { throw unexpected_token(in.tellg()); }
|
|
|
- d.of = std::make_shared<dice>();
|
|
|
- parse(in, *boost::get<std::shared_ptr<dice>>(d.of));
|
|
|
- if (in.get() != '}') { throw unexpected_token(in.tellg()); }
|
|
|
+ return parse_dN(in, d, {s, value, 0});
|
|
|
+ case '+': case '-':
|
|
|
+ if (value) {
|
|
|
+ d.modifier.push_back({s, std::abs(value)});
|
|
|
+ }
|
|
|
+ s = (in.get() == '+') ? PLUS : MINUS;
|
|
|
+ parse(in, d, s);
|
|
|
break;
|
|
|
case EOF:
|
|
|
break;
|
|
|
default:
|
|
|
- throw unexpected_token(in.tellg());
|
|
|
+ d.modifier.push_back({s, std::abs(value)});
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- static void parse_modifier_expr(std::istream & in, dice & d) {
|
|
|
+
|
|
|
+ std::istream & operator>>(std::istream & in, dice & d) {
|
|
|
+ int value{1};
|
|
|
advance_over_whitespace(in);
|
|
|
- int modifier = 0;
|
|
|
+ if (isnumber(in.peek())) { in >> value; }
|
|
|
switch (in.peek()) {
|
|
|
- case '+':
|
|
|
+ case 'd': case 'D':
|
|
|
+ parse(in, d, ZERO);
|
|
|
+ d.of.front().num = value;
|
|
|
+ break;
|
|
|
+ case '{':
|
|
|
in.get();
|
|
|
- case '-':
|
|
|
- in >> modifier;
|
|
|
- advance_over_whitespace(in);
|
|
|
- if (in.peek() != 'd' && in.peek() != 'D') {
|
|
|
- d.modifier = modifier;
|
|
|
- } else {
|
|
|
- d.modifier = std::make_shared<dice>();
|
|
|
- parse(in, *boost::get<std::shared_ptr<dice>>(d.modifier));
|
|
|
- boost::get<std::shared_ptr<dice>>(d.modifier)->num = modifier;
|
|
|
+ d.num = value;
|
|
|
+ parse(in, d, ZERO);
|
|
|
+ if (in.get() != '}') {
|
|
|
+ throw unexpected_token(in.tellg());
|
|
|
}
|
|
|
- default:
|
|
|
break;
|
|
|
+ default:
|
|
|
+ throw unexpected_token(in.tellg());
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- static void parse(std::istream & in, dice & d, bool allow_nest) {
|
|
|
- // Allow implicit 1
|
|
|
- advance_over_whitespace(in);
|
|
|
- if (isnumber(in.peek())) { in >> d.num; }
|
|
|
- parse_inner_expr(in, d, allow_nest);
|
|
|
- if (!d.of.which()) {
|
|
|
- parse_modifier_expr(in, d);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- std::istream & operator>>(std::istream & in, dice & d) {
|
|
|
- parse(in, d, true);
|
|
|
- return in;
|
|
|
+ return in;
|
|
|
}
|
|
|
}
|