|
@@ -18,8 +18,9 @@ static void advance_over_whitespace(std::istream & in, char const * also = "") {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static std::string carrot(long long pos) {
|
|
static std::string carrot(long long pos) {
|
|
|
- if (pos == -1) return "<END>";
|
|
|
|
|
|
|
+ if (pos == -1) return "<END>"; // For safety
|
|
|
std::string out;
|
|
std::string out;
|
|
|
|
|
+ // resize(0) followed by back() will cause UB
|
|
|
out.resize(std::max(pos, 1ll), '~');
|
|
out.resize(std::max(pos, 1ll), '~');
|
|
|
out.back() = '^';
|
|
out.back() = '^';
|
|
|
return out;
|
|
return out;
|
|
@@ -66,6 +67,7 @@ namespace dice {
|
|
|
return out;
|
|
return out;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
namespace dice { namespace {
|
|
namespace dice { namespace {
|
|
|
struct parser {
|
|
struct parser {
|
|
|
void parse(sign s);
|
|
void parse(sign s);
|
|
@@ -76,6 +78,20 @@ namespace dice { namespace {
|
|
|
dice & d;
|
|
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.
|
|
|
|
|
+ * This function appends a new die into {@see d.of}, representing (+/-)NdM,
|
|
|
|
|
+ * where N is the input parameter 'value', and M is retrieved internally.
|
|
|
|
|
+ * @param s The arithmatic sign attached to this roll, denoting whether this
|
|
|
|
|
+ * die increases or decreases the total result of the roll. For example, the
|
|
|
|
|
+ * 5E spell Bane imposes a -1d4 modifier on attack rolls, an attack roll might
|
|
|
|
|
+ * go from '1d20+2+3' (1d20 + Proficiency + Ability) to '1d20+2+3-1d4'.
|
|
|
|
|
+ * @param value The number of dice to be rolled.
|
|
|
|
|
+ * Domain: value >= 0
|
|
|
|
|
+ * @throw dice::unexpected_token if we somehow call parse_dN while the first
|
|
|
|
|
+ * non-whitespace token after the 'd' char is not a number.
|
|
|
|
|
+ */
|
|
|
void parser::parse_dN(sign s, int value) {
|
|
void parser::parse_dN(sign s, int value) {
|
|
|
advance_over_whitespace(in, "dD");
|
|
advance_over_whitespace(in, "dD");
|
|
|
d.of.push_back({s, value, 0});
|
|
d.of.push_back({s, value, 0});
|
|
@@ -86,15 +102,36 @@ namespace dice { namespace {
|
|
|
parse(ZERO);
|
|
parse(ZERO);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * @param s The arithmatic sign attached to this numeric constant. Because
|
|
|
|
|
+ * value is non-negative, this token contains the +/- effect.
|
|
|
|
|
+ * @param value The value associated with this modifier term.
|
|
|
|
|
+ * Domain: value >= 0
|
|
|
|
|
+ */
|
|
|
void parser::parse_const(sign s, int value) {
|
|
void parser::parse_const(sign s, int value) {
|
|
|
- if (value) {
|
|
|
|
|
|
|
+ if (value) { // Zero is not a modifier we care about
|
|
|
d.modifier.push_back({s, std::abs(value)});
|
|
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
|
|
|
|
|
+ * when parsing the first token, or after parsing a die. This means an
|
|
|
|
|
+ * expression like '1d4+5+1d6+2d8' is evaluated as a sequence like so:
|
|
|
|
|
+ * [1d4][+][5+][1d6][+][2d8]. This produces the following states of (SIGN,
|
|
|
|
|
+ * input stream):
|
|
|
|
|
+ * 1) ZERO, 1d4+5+1d6+2d8
|
|
|
|
|
+ * 2) ZERO, +5+1d6+2d8
|
|
|
|
|
+ * 3) PLUS, 5+1d6+2d8
|
|
|
|
|
+ * 4) PLUS, 1d6+2d8
|
|
|
|
|
+ * 5) ZERO, +2d8
|
|
|
|
|
+ * 6) PLUS, 2d8
|
|
|
|
|
+ */
|
|
|
void parser::parse(sign s) {
|
|
void parser::parse(sign s) {
|
|
|
- // Allow implicit 1
|
|
|
|
|
advance_over_whitespace(in);
|
|
advance_over_whitespace(in);
|
|
|
|
|
+ // By defaulting this to zero, we can write a more elegant handling of
|
|
|
|
|
+ // expressions like 1d4+1d6+5+1
|
|
|
int value = 0;
|
|
int value = 0;
|
|
|
if (isnumber(in.peek())) { in >> value; }
|
|
if (isnumber(in.peek())) { in >> value; }
|
|
|
else if (in.peek() == EOF && s != ZERO) {
|
|
else if (in.peek() == EOF && s != ZERO) {
|
|
@@ -105,7 +142,9 @@ namespace dice { namespace {
|
|
|
case 'd': case 'D':
|
|
case 'd': case 'D':
|
|
|
return parse_dN(s, value);
|
|
return parse_dN(s, value);
|
|
|
case '+': case '-':
|
|
case '+': case '-':
|
|
|
|
|
+ // Handle 5+... cases
|
|
|
parse_const(s, value);
|
|
parse_const(s, value);
|
|
|
|
|
+ // Add another token
|
|
|
parse((in.get() == '+') ? PLUS : MINUS);
|
|
parse((in.get() == '+') ? PLUS : MINUS);
|
|
|
break;
|
|
break;
|
|
|
default:
|
|
default:
|