Sfoglia il codice sorgente

Refactor I/O and Parser code into own files.

Sam Jaffe 4 anni fa
parent
commit
cdf59d865e
6 ha cambiato i file con 209 aggiunte e 140 eliminazioni
  1. 8 0
      dice-roll.xcodeproj/project.pbxproj
  2. 0 1
      include/dice-roll/die.h
  3. 60 0
      include/dice-roll/parser.h
  4. 0 139
      src/die.cxx
  5. 72 0
      src/io.cxx
  6. 69 0
      src/parser.cxx

+ 8 - 0
dice-roll.xcodeproj/project.pbxproj

@@ -22,6 +22,8 @@
 		CD38F56521C94A00007A732C /* libdice.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = CD38F50921C83912007A732C /* libdice.dylib */; };
 		CDC7489B25312DD6008D9D1D /* GoogleMock.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = CDC7489425312DBF008D9D1D /* GoogleMock.framework */; };
 		CDED6A2721B2F28A00AB91D0 /* main.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDED6A2621B2F28A00AB91D0 /* main.cxx */; };
+		CDEE78F725B336B000F195F9 /* parser.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDEE78F625B336B000F195F9 /* parser.cxx */; };
+		CDEE790225B336EC00F195F9 /* io.cxx in Sources */ = {isa = PBXBuildFile; fileRef = CDEE790125B336EC00F195F9 /* io.cxx */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXContainerItemProxy section */
@@ -116,6 +118,8 @@
 		CDED6A6021B2F89900AB91D0 /* libshared_random_generator.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; path = libshared_random_generator.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CDEE78D225B333A700F195F9 /* dice-roll */ = {isa = PBXFileReference; lastKnownFileType = folder; name = "dice-roll"; path = "include/dice-roll"; sourceTree = "<group>"; };
 		CDEE78ED25B3350B00F195F9 /* xcode_gtest_helper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = xcode_gtest_helper.h; sourceTree = "<group>"; };
+		CDEE78F625B336B000F195F9 /* parser.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = parser.cxx; sourceTree = "<group>"; };
+		CDEE790125B336EC00F195F9 /* io.cxx */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = io.cxx; sourceTree = "<group>"; };
 /* End PBXFileReference section */
 
 /* Begin PBXFrameworksBuildPhase section */
@@ -221,6 +225,8 @@
 			children = (
 				CD38F54E21C945C2007A732C /* terminal_helper.cxx */,
 				CDED6A3021B2F2DC00AB91D0 /* die.cxx */,
+				CDEE78F625B336B000F195F9 /* parser.cxx */,
+				CDEE790125B336EC00F195F9 /* io.cxx */,
 				CD38F53621C89493007A732C /* exception.cxx */,
 				CD8F1ABD21B31E9E00CBB3CA /* roll.cxx */,
 			);
@@ -426,6 +432,8 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CDEE790225B336EC00F195F9 /* io.cxx in Sources */,
+				CDEE78F725B336B000F195F9 /* parser.cxx in Sources */,
 				CD38F50E21C83929007A732C /* roll.cxx in Sources */,
 				CD38F50D21C83926007A732C /* die.cxx in Sources */,
 				CD38F53721C89493007A732C /* exception.cxx in Sources */,

+ 0 - 1
include/dice-roll/die.h

@@ -9,7 +9,6 @@
 #pragma once
 
 #include <iosfwd>
-#include <memory>
 #include <vector>
 
 namespace dice {

+ 60 - 0
include/dice-roll/parser.h

@@ -0,0 +1,60 @@
+//
+//  parser.h
+//  dice-roll
+//
+//  Created by Sam Jaffe on 1/16/21.
+//  Copyright © 2021 Sam Jaffe. All rights reserved.
+//
+
+#pragma once
+
+#include <iosfwd>
+
+#include "die.h"
+
+namespace dice {
+
+struct parser {
+  /**
+   * 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 parse(sign s);
+  /**
+   * @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 parse_dN(sign s, int value);
+  /**
+   * @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 parse_const(sign s, int value);
+
+  std::istream & in;
+  dice & d;
+};
+
+}

+ 0 - 139
src/die.cxx

@@ -34,143 +34,4 @@ namespace dice {
   }
 
   mod::operator int() const { return sgn(sign) * value; }
-
-  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;
-  }
-}
-
-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;
-  };
-
-  /**
-   * @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) {
-    advance_over_whitespace(in, "dD");
-    // Disallow 0dM, as that is not a real thing...
-    d.of.push_back({s, std::max(value, 1), 0});
-    if (!isnumber(in.peek())) {
-      throw unexpected_token("Expected a number of sides", in.tellg());
-    }
-    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.
-   * @param value The value associated with this modifier term.
-   * Domain: value >= 0
-   */
-  void parser::parse_const(sign s, int value) {
-    if (value) { // Zero is not a modifier we care about
-      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) {
-    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;
-    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;
-    }
-  }
-}}
-
-namespace dice {
-  std::istream & operator>>(std::istream & in, dice & d) {
-    int value{1};
-    advance_over_whitespace(in);
-    if (isnumber(in.peek())) { in >> value; }
-    advance_over_whitespace(in);
-    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());
-    }
-    return in;
-  }
-  
-  dice from_string(std::string const & str) {
-    std::stringstream ss(str);
-    dice d;
-    ss >> d;
-    return d;
-  }
 }

+ 72 - 0
src/io.cxx

@@ -0,0 +1,72 @@
+//
+//  io.cxx
+//  dice
+//
+//  Created by Sam Jaffe on 1/16/21.
+//  Copyright © 2021 Sam Jaffe. All rights reserved.
+//
+
+#include <iostream>
+#include <sstream>
+
+#include "dice-roll/die.h"
+#include "dice-roll/exception.h"
+#include "dice-roll/parser.h"
+
+static void advance_over_whitespace(std::istream & in, char const * also = "") {
+  if (strchr(also, in.peek())) { in.get(); }
+  while (isspace(in.peek())) {
+    in.get();
+  }
+}
+
+namespace dice {
+
+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;
+}
+
+std::istream & operator>>(std::istream & in, dice & d) {
+  int value{1};
+  advance_over_whitespace(in);
+  if (isnumber(in.peek())) { in >> value; }
+  advance_over_whitespace(in);
+  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());
+  }
+  return in;
+}
+
+dice from_string(std::string const & str) {
+  std::stringstream ss(str);
+  dice d;
+  ss >> d;
+  return d;
+}
+
+}

+ 69 - 0
src/parser.cxx

@@ -0,0 +1,69 @@
+//
+//  parser.cxx
+//  dice
+//
+//  Created by Sam Jaffe on 1/16/21.
+//  Copyright © 2021 Sam Jaffe. All rights reserved.
+//
+
+#include "dice-roll/parser.h"
+
+#include <iostream>
+
+#include "dice-roll/exception.h"
+
+static void advance_over_whitespace(std::istream & in, char const * also = "") {
+  if (strchr(also, in.peek())) { in.get(); }
+  while (isspace(in.peek())) {
+    in.get();
+  }
+}
+
+namespace dice {
+
+void parser::parse_dN(sign s, int value) {
+  advance_over_whitespace(in, "dD");
+  // Disallow 0dM, as that is not a real thing...
+  d.of.push_back({s, std::max(value, 1), 0});
+  if (!isnumber(in.peek())) {
+    throw unexpected_token("Expected a number of sides", in.tellg());
+  }
+  in >> d.of.back().sides;
+  parse(ZERO);
+}
+
+void parser::parse_const(sign s, int value) {
+  if (value) { // Zero is not a modifier we care about
+    d.modifier.push_back({s, std::abs(value)});
+  }
+}
+
+void parser::parse(sign s) {
+  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;
+  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;
+  }
+}
+
+}