|
|
@@ -14,7 +14,9 @@
|
|
|
|
|
|
static void advance_over_whitespace(std::istream & in, char const * also = "") {
|
|
|
if (strchr(also, in.peek())) { in.get(); }
|
|
|
- while (isspace(in.peek())) { in.get(); }
|
|
|
+ while (isspace(in.peek())) {
|
|
|
+ in.get();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
static std::string carrot(long long pos) {
|
|
|
@@ -27,34 +29,29 @@ static std::string carrot(long long pos) {
|
|
|
}
|
|
|
|
|
|
namespace dice {
|
|
|
- int sgn(sign s) {
|
|
|
- return s == MINUS ? -1 : 1;
|
|
|
- }
|
|
|
+ int sgn(sign s) { return s == MINUS ? -1 : 1; }
|
|
|
std::string str(sign s) {
|
|
|
switch (s) {
|
|
|
- case PLUS: return "+";
|
|
|
- case MINUS: return "-";
|
|
|
- default: return "";
|
|
|
+ case PLUS:
|
|
|
+ return "+";
|
|
|
+ case MINUS:
|
|
|
+ return "-";
|
|
|
+ default:
|
|
|
+ return "";
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
- mod::operator int() const {
|
|
|
- return sgn(sign) * value;
|
|
|
- }
|
|
|
+
|
|
|
+ mod::operator int() const { return sgn(sign) * value; }
|
|
|
}
|
|
|
|
|
|
namespace dice {
|
|
|
- unexpected_token::unexpected_token(std::string const & reason,
|
|
|
- long long pos) :
|
|
|
- std::runtime_error(reason),
|
|
|
- position(pos) {
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
+ unexpected_token::unexpected_token(std::string const & reason, long long pos)
|
|
|
+ : std::runtime_error(reason), position(pos) {}
|
|
|
+
|
|
|
std::string unexpected_token::pointer(long long backup_length) const {
|
|
|
return carrot(position == -1 ? backup_length : position);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
std::ostream & operator<<(std::ostream & out, dice const & d) {
|
|
|
if (d.num != 1) out << d.num << '{';
|
|
|
for (die const & di : d.of) {
|
|
|
@@ -73,11 +70,11 @@ namespace dice { namespace {
|
|
|
void parse(sign s);
|
|
|
void parse_dN(sign s, int value);
|
|
|
void parse_const(sign s, int value);
|
|
|
-
|
|
|
+
|
|
|
std::istream & in;
|
|
|
dice & d;
|
|
|
};
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* @sideeffect This function advances the input stream over a single numeric
|
|
|
* token. This token represents the number of sides in the die roll.
|
|
|
@@ -102,7 +99,7 @@ namespace dice { namespace {
|
|
|
in >> d.of.back().sides;
|
|
|
parse(ZERO);
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* @param s The arithmatic sign attached to this numeric constant. Because
|
|
|
* value is non-negative, this token contains the +/- effect.
|
|
|
@@ -114,7 +111,7 @@ namespace dice { namespace {
|
|
|
d.modifier.push_back({s, std::abs(value)});
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
/**
|
|
|
* Main dispatch function for parsing a dice roll.
|
|
|
* @param s The current +/- sign attached to the parse sequence. s is ZERO
|
|
|
@@ -134,23 +131,26 @@ namespace dice { namespace {
|
|
|
// By defaulting this to zero, we can write a more elegant handling of
|
|
|
// expressions like 1d4+1d6+5+1
|
|
|
int value = 0;
|
|
|
- if (isnumber(in.peek())) { in >> value; }
|
|
|
- else if (in.peek() == EOF && s != ZERO) {
|
|
|
+ if (isnumber(in.peek())) {
|
|
|
+ in >> value;
|
|
|
+ } else if (in.peek() == EOF && s != ZERO) {
|
|
|
throw unexpected_token("Unexpected EOF while parsing", -1);
|
|
|
}
|
|
|
advance_over_whitespace(in);
|
|
|
switch (in.peek()) {
|
|
|
- case 'd': case 'D':
|
|
|
- return parse_dN(s, value);
|
|
|
- case '+': case '-':
|
|
|
- // Handle 5+... cases
|
|
|
- parse_const(s, value);
|
|
|
- // Add another token
|
|
|
- parse((in.get() == '+') ? PLUS : MINUS);
|
|
|
- break;
|
|
|
- default:
|
|
|
- parse_const(s, value);
|
|
|
- break;
|
|
|
+ case 'd':
|
|
|
+ case 'D':
|
|
|
+ return parse_dN(s, value);
|
|
|
+ case '+':
|
|
|
+ case '-':
|
|
|
+ // Handle 5+... cases
|
|
|
+ parse_const(s, value);
|
|
|
+ // Add another token
|
|
|
+ parse((in.get() == '+') ? PLUS : MINUS);
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ parse_const(s, value);
|
|
|
+ break;
|
|
|
}
|
|
|
}
|
|
|
}}
|
|
|
@@ -161,24 +161,25 @@ namespace dice {
|
|
|
advance_over_whitespace(in);
|
|
|
if (isnumber(in.peek())) { in >> value; }
|
|
|
switch (in.peek()) {
|
|
|
- case 'd': case 'D':
|
|
|
- parser{in, d}.parse(ZERO);
|
|
|
- d.of.front().num = value;
|
|
|
- break;
|
|
|
- case '{':
|
|
|
- in.get();
|
|
|
- d.num = value;
|
|
|
- parser{in, d}.parse(ZERO);
|
|
|
- if (in.get() != '}') {
|
|
|
- throw unexpected_token("Expected closing '}' in repeated roll",
|
|
|
- in.tellg());
|
|
|
- }
|
|
|
- break;
|
|
|
- case EOF:
|
|
|
- throw unexpected_token("No dice in expression", in.tellg());
|
|
|
- default:
|
|
|
- throw unexpected_token("Unexpected token", in.tellg());
|
|
|
+ case 'd':
|
|
|
+ case 'D':
|
|
|
+ parser{in, d}.parse(ZERO);
|
|
|
+ d.of.front().num = value;
|
|
|
+ break;
|
|
|
+ case '{':
|
|
|
+ in.get();
|
|
|
+ d.num = value;
|
|
|
+ parser{in, d}.parse(ZERO);
|
|
|
+ if (in.get() != '}') {
|
|
|
+ throw unexpected_token("Expected closing '}' in repeated roll",
|
|
|
+ in.tellg());
|
|
|
+ }
|
|
|
+ break;
|
|
|
+ case EOF:
|
|
|
+ throw unexpected_token("No dice in expression", in.tellg());
|
|
|
+ default:
|
|
|
+ throw unexpected_token("Unexpected token", in.tellg());
|
|
|
}
|
|
|
- return in;
|
|
|
+ return in;
|
|
|
}
|
|
|
}
|