|
|
@@ -9,8 +9,7 @@
|
|
|
#include "die.h"
|
|
|
#include "exception.h"
|
|
|
|
|
|
-#include <boost/variant/get.hpp>
|
|
|
-
|
|
|
+#include <cmath>
|
|
|
#include <iostream>
|
|
|
|
|
|
static void advance_over_whitespace(std::istream & in, char const * also = "") {
|
|
|
@@ -21,7 +20,7 @@ static void advance_over_whitespace(std::istream & in, char const * also = "") {
|
|
|
static std::string carrot(long long pos) {
|
|
|
if (pos == -1) return "<END>";
|
|
|
std::string out;
|
|
|
- out.resize(pos, '~');
|
|
|
+ out.resize(std::max(pos, 1ll), '~');
|
|
|
out.back() = '^';
|
|
|
return out;
|
|
|
}
|
|
|
@@ -44,10 +43,16 @@ namespace dice {
|
|
|
}
|
|
|
|
|
|
namespace dice {
|
|
|
- unexpected_token::unexpected_token(long long pos) :
|
|
|
- std::runtime_error(carrot(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 << '{';
|
|
|
@@ -60,59 +65,79 @@ namespace dice {
|
|
|
if (d.num != 1) out << '}';
|
|
|
return out;
|
|
|
}
|
|
|
+}
|
|
|
+namespace dice { namespace {
|
|
|
+ struct parser {
|
|
|
+ void parse(sign s);
|
|
|
+ void parse_dN(sign s, int value);
|
|
|
+ void parse_const(sign s, int value);
|
|
|
+
|
|
|
+ std::istream & in;
|
|
|
+ dice & d;
|
|
|
+ };
|
|
|
|
|
|
- static void parse(std::istream & in, dice & d, sign s);
|
|
|
-
|
|
|
- static void parse_dN(std::istream & in, dice & d, die di) {
|
|
|
+ void parser::parse_dN(sign s, int value) {
|
|
|
advance_over_whitespace(in, "dD");
|
|
|
- d.of.push_back(di);
|
|
|
+ d.of.push_back({s, value, 0});
|
|
|
+ if (!isnumber(in.peek())) {
|
|
|
+ throw unexpected_token("Expected a number of sides", in.tellg());
|
|
|
+ }
|
|
|
in >> d.of.back().sides;
|
|
|
- parse(in, d, ZERO);
|
|
|
+ parse(ZERO);
|
|
|
+ }
|
|
|
+
|
|
|
+ void parser::parse_const(sign s, int value) {
|
|
|
+ if (value) {
|
|
|
+ d.modifier.push_back({s, std::abs(value)});
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- static void parse(std::istream & in, dice & d, sign s) {
|
|
|
+ void parser::parse(sign s) {
|
|
|
// Allow implicit 1
|
|
|
advance_over_whitespace(in);
|
|
|
int value = 0;
|
|
|
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(in, d, {s, value, 0});
|
|
|
+ return parse_dN(s, value);
|
|
|
case '+': case '-':
|
|
|
- if (value) {
|
|
|
- d.modifier.push_back({s, std::abs(value)});
|
|
|
- }
|
|
|
- s = (in.get() == '+') ? PLUS : MINUS;
|
|
|
- parse(in, d, s);
|
|
|
- break;
|
|
|
- case EOF:
|
|
|
+ parse_const(s, value);
|
|
|
+ parse((in.get() == '+') ? PLUS : MINUS);
|
|
|
break;
|
|
|
default:
|
|
|
- d.modifier.push_back({s, std::abs(value)});
|
|
|
+ parse_const(s, value);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
+}}
|
|
|
|
|
|
+namespace dice {
|
|
|
std::istream & operator>>(std::istream & in, dice & d) {
|
|
|
int value{1};
|
|
|
advance_over_whitespace(in);
|
|
|
if (isnumber(in.peek())) { in >> value; }
|
|
|
switch (in.peek()) {
|
|
|
case 'd': case 'D':
|
|
|
- parse(in, d, ZERO);
|
|
|
+ parser{in, d}.parse(ZERO);
|
|
|
d.of.front().num = value;
|
|
|
break;
|
|
|
case '{':
|
|
|
in.get();
|
|
|
d.num = value;
|
|
|
- parse(in, d, ZERO);
|
|
|
+ parser{in, d}.parse(ZERO);
|
|
|
if (in.get() != '}') {
|
|
|
- throw unexpected_token(in.tellg());
|
|
|
+ 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(in.tellg());
|
|
|
+ throw unexpected_token("Unexpected token", in.tellg());
|
|
|
}
|
|
|
return in;
|
|
|
}
|