| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119 |
- //
- // die.cxx
- // dice-roll
- //
- // Created by Sam Jaffe on 12/1/18.
- // Copyright © 2018 Sam Jaffe. All rights reserved.
- //
- #include "die.h"
- #include "exception.h"
- #include <boost/variant/get.hpp>
- #include <iostream>
- static void advance_over_whitespace(std::istream & in, char const * also = "") {
- if (strchr(also, in.peek())) { in.get(); }
- while (isspace(in.peek())) { in.get(); }
- }
- static std::string carrot(long long pos) {
- if (pos == -1) return "<END>";
- std::string out;
- out.resize(pos, '~');
- out.back() = '^';
- return out;
- }
- 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 {
- unexpected_token::unexpected_token(long long pos) :
- std::runtime_error(carrot(pos)) {
-
- }
-
- std::ostream & operator<<(std::ostream & out, dice const & d) {
- 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, 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(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.peek()) {
- case 'd': case 'D':
- 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:
- d.modifier.push_back({s, std::abs(value)});
- break;
- }
- }
- 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);
- d.of.front().num = value;
- break;
- case '{':
- in.get();
- d.num = value;
- parse(in, d, ZERO);
- if (in.get() != '}') {
- throw unexpected_token(in.tellg());
- }
- break;
- default:
- throw unexpected_token(in.tellg());
- }
- return in;
- }
- }
|