Browse Source

Moved parsing utilities to json_parser.cpp.
Moved json_parser.hpp parts to json/json_binder_parser.hpp.
Moved general json_parser.hpp parts to json_common.hpp

Samuel Jaffe 9 years ago
parent
commit
3f4d671e87
11 changed files with 287 additions and 179 deletions
  1. 19 65
      json.cpp
  2. 22 13
      json.h
  3. 32 12
      json.xcodeproj/project.pbxproj
  4. 0 19
      json_parser.hpp
  5. 2 10
      json/json_direct_scalar_binder.hpp
  6. 2 1
      json_binder.hpp
  7. 2 2
      json_common.cpp
  8. 0 56
      json_common.h
  9. 84 0
      json_common.hpp
  10. 124 0
      json_parser.cpp
  11. 0 1
      json_test.cpp

+ 19 - 65
json.cpp

@@ -6,74 +6,10 @@
 //  Copyright © 2016 Sam Jaffe. All rights reserved.
 //
 
-#include "json.h"
-#include "json_parser.hpp"
-#include "json_binder.hpp"
+#include "json.hpp"
 
 const json::value json::value::null_value{};
 
-namespace {
-  void parse_object(json::value& json, char const*& data);
-  void parse_array(json::value& json, char const*& data);
-  void parse_one_token(json::value& json, char const*& data);
-  
-  void parse_one_token(json::value& json, char const*& data) {
-    const char ch = json::helper::get_next_element(data);
-    if (ch == '{') {
-      parse_object(json, ++data);
-    } else if (ch == '[') {
-      parse_array(json, ++data);
-    } else if (ch == '"') {
-      json::helper::parse_string(json, ++data);
-    } else if (isnumber(ch)) {
-      json::helper::parse_numeric(json, data);
-    } else if (!strncmp(data, "true", 4)) {
-      json = true;
-    } else if (!strncmp(data, "false", 5)) {
-      json = false;
-    } else {
-      throw json::malformed_json_exception(std::string("Expected the start of a JSON element, found character '") + ch + "' instead");
-    }
-  }
-  
-  void parse_object(json::value& json, char const*& data) {
-    std::string key;
-    while (*data && *data != '}') {
-      json::helper::parse_string(key, data);
-      if (json::helper::get_next_element(data) != ':') {
-        throw json::malformed_json_exception(std::string("Expected key:value pair delimited by ':', got '") + *data + "' instead");
-      }
-      parse_one_token(json[key], ++data);
-      json::helper::advance_to_boundary<'}'>(data);
-    }
-    if (*data) ++data;
-    else throw json::malformed_json_exception("Reached end of parse string without finding object end");
-  }
-  
-  void parse_array(json::value& json, char const*& data) {
-    size_t current_idx = 0;
-    while (*data && *data != ']') {
-      parse_one_token(json[current_idx++], data);
-      json::helper::advance_to_boundary<']'>(data);
-    }
-    if (*data) ++data;
-    else throw json::malformed_json_exception("Reached end of parse string without finding array end");
-  }
-}
-
-namespace json {
-  namespace parser {
-    void parse(json::value& json, char const* data) {
-      parse_one_token(json, data);
-      if (*data) throw json::malformed_json_exception("Expected a single json token in top-level parse");
-    }
-  }
-}
-
-void json::value::parse(char const* data)  { parser::parse(*this, data); }
-void json::value::parse(std::string const& str) { parser::parse(*this, str); }
-void json::value::parse(std::istream & in)  { parser::parse(*this, in); }
-
 json::value& json::value::operator[](const size_t idx) {
   if (!is_array()) {
     data.set<array_jt>();
@@ -129,6 +65,22 @@ json::value::int_jt json::value::as_int() const {
     return static_cast<int_jt>(data.get<double_jt>());
   } else if (data.is<int_jt>()) {
     return data.get<int_jt>();
+  } else if (data.is<uint_jt>() && data.get<uint_jt>() <= uint_jt(INT_JT_MAX)) {
+    return static_cast<int_jt>(data.get<uint_jt>());
+  } else if (data.is<bool_jt>()) {
+    return data.get<bool_jt>() ? 1 : 0;
+  } else {
+    return 0;
+  }
+}
+
+json::value::uint_jt json::value::as_uint() const {
+  if (data.is<double_jt>()) {
+    return static_cast<int_jt>(data.get<double_jt>());
+  } else if (data.is<int_jt>() && data.get<int_jt>() >= 0) {
+    return data.get<int_jt>();
+  } else if (data.is<uint_jt>()) {
+    return data.get<uint_jt>();
   } else if (data.is<bool_jt>()) {
     return data.get<bool_jt>() ? 1 : 0;
   } else {
@@ -139,6 +91,8 @@ json::value::int_jt json::value::as_int() const {
 json::value::bool_jt json::value::as_bool() const {
   if (data.is<double_jt>()) {
     return data.get<double_jt>() != 0;
+  } else if (data.is<uint_jt>()) {
+    return data.get<uint_jt>() != 0;
   } else if (data.is<int_jt>()) {
     return data.get<int_jt>() != 0;
   } else if (data.is<bool_jt>()) {

+ 22 - 13
json.h

@@ -9,12 +9,10 @@
 #ifndef json_hpp
 #define json_hpp
 
-#include <map>
-#include <vector>
-#include <utility>
-
 #include "../variant/variant.hpp"
-#include "json_common.h"
+#include "json_common.hpp"
+
+#include <utility>
 
 #define JSON_TYPE_LIST \
   X(object) \
@@ -22,28 +20,37 @@
   X(string) \
   X(double) \
   X(int) \
+  X(uint) \
   X(bool)
 
 namespace json {
+  namespace {
+    const constexpr json::int_jt INT_JT_MAX_LAST_DIGIT = (0x7FFFFFFF % 10);
+    const constexpr json::int_jt INT_JT_MAX = json::uint_jt(-1) - 1;
+    const constexpr json::int_jt INT_JT_MIN = json::int_jt(~(json::uint_jt(-1) / 2));
+    const constexpr json::uint_jt INT_JT_OVER = json::uint_jt(INT_JT_MAX) + 1;
+    const constexpr json::uint_jt UINT_JT_MAX = json::uint_jt(0) - 1;
+    //  const constexpr json::uint_jt UINT_JT_MIN = 0;
+  }
+  
   class value;
   namespace parser {
-    template <typename T> void parse(T& json, char const* data);
-    template <typename T> void parse(T& json, std::string const& str);
-    template <typename T> void parse(T& json, std::istream & in);
+    void parse(value&, char const*);
   }
   
   class value {
   public:
     using object_jt = std::map<std::string, value>;
     using array_jt = std::vector<value>;
-    using string_jt = std::string;
-    using double_jt = double;
-    using int_jt = int32_t;
-    using bool_jt = bool;
+    using string_jt = json::string_jt;
+    using double_jt = json::double_jt;
+    using int_jt = json::int_jt;
+    using uint_jt = json::uint_jt;
+    using bool_jt = json::bool_jt;
   private:
     static const value null_value;
     
-    using data_t = variant<object_jt, array_jt, string_jt, double_jt, int_jt, bool_jt>;
+    using data_t = variant<object_jt, array_jt, string_jt, double_jt, int_jt, uint_jt, bool_jt>;
     data_t data;
   public:
 #define X(type) bool is_##type() const { return data.is<type##_jt>(); }
@@ -80,11 +87,13 @@ namespace json {
     string_jt const& as_string() const;
     double_jt as_double() const;
     int_jt as_int() const;
+    uint_jt as_uint() const;
     bool_jt as_bool() const;
     
     operator string_jt const&() const { return as_string(); }
     operator double_jt() const { return as_double(); }
     operator int_jt() const { return as_int(); }
+    operator uint_jt() const { return as_uint(); }
     operator bool_jt() const { return as_bool(); }
     
   };

+ 32 - 12
json.xcodeproj/project.pbxproj

@@ -10,8 +10,9 @@
 		CD217D911CCAD587007C50C6 /* json_test.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD217D8F1CCAD587007C50C6 /* json_test.cpp */; };
 		CD472C761CCC1ABD0084C8D6 /* json_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C751CCC1ABD0084C8D6 /* json_common.cpp */; };
 		CD472C771CCC1ABD0084C8D6 /* json_common.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C751CCC1ABD0084C8D6 /* json_common.cpp */; };
+		CD472C801CCDA4B00084C8D6 /* json_parser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CD472C7F1CCDA4B00084C8D6 /* json_parser.cpp */; };
 		CDB2F7431C5D48090067C2EC /* json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDB2F7411C5D48090067C2EC /* json.cpp */; };
-		CDB2F7441C5D48090067C2EC /* json.h in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7421C5D48090067C2EC /* json.h */; };
+		CDB2F7441C5D48090067C2EC /* json.hpp in Headers */ = {isa = PBXBuildFile; fileRef = CDB2F7421C5D48090067C2EC /* json.hpp */; };
 /* End PBXBuildFile section */
 
 /* Begin PBXCopyFilesBuildPhase section */
@@ -28,7 +29,7 @@
 
 /* Begin PBXFileReference section */
 		CD217D8F1CCAD587007C50C6 /* json_test.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_test.cpp; sourceTree = "<group>"; };
-		CD217D921CCAD885007C50C6 /* json_common.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = json_common.h; sourceTree = "<group>"; };
+		CD217D921CCAD885007C50C6 /* json_common.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_common.hpp; sourceTree = "<group>"; };
 		CD472C751CCC1ABD0084C8D6 /* json_common.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_common.cpp; sourceTree = "<group>"; };
 		CD472C791CCC1CD80084C8D6 /* json_tuple_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_tuple_binder.hpp; sourceTree = "<group>"; };
 		CD472C7A1CCC1D440084C8D6 /* json_object_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_object_binder.hpp; sourceTree = "<group>"; };
@@ -36,11 +37,12 @@
 		CD472C7C1CCC1DDF0084C8D6 /* json_direct_vector_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_direct_vector_binder.hpp; sourceTree = "<group>"; };
 		CD472C7D1CCC1E120084C8D6 /* json_direct_scalar_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_direct_scalar_binder.hpp; sourceTree = "<group>"; };
 		CD472C7E1CCC498C0084C8D6 /* json_direct_binder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = json_direct_binder.hpp; sourceTree = "<group>"; };
+		CD472C7F1CCDA4B00084C8D6 /* json_parser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = json_parser.cpp; sourceTree = "<group>"; };
 		CDB2F7331C5D47F70067C2EC /* libjson.dylib */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.dylib"; includeInIndex = 0; path = libjson.dylib; sourceTree = BUILT_PRODUCTS_DIR; };
 		CDB2F7411C5D48090067C2EC /* json.cpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.cpp; path = json.cpp; sourceTree = "<group>"; tabWidth = 2; };
-		CDB2F7421C5D48090067C2EC /* json.h */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.c.h; path = json.h; sourceTree = "<group>"; tabWidth = 2; };
-		CDB2F7451C5E9BEB0067C2EC /* json_parser.hpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = json_parser.hpp; sourceTree = "<group>"; tabWidth = 2; };
-		CDB2F7461C5EA2E80067C2EC /* json_binder.hpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = json_binder.hpp; sourceTree = "<group>"; tabWidth = 2; };
+		CDB2F7421C5D48090067C2EC /* json.hpp */ = {isa = PBXFileReference; fileEncoding = 4; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = json.hpp; sourceTree = "<group>"; tabWidth = 2; };
+		CDB2F7451C5E9BEB0067C2EC /* json_binder_parser.hpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = json_binder_parser.hpp; sourceTree = "<group>"; tabWidth = 2; };
+		CDB2F7461C5EA2E80067C2EC /* json_binder.hpp */ = {isa = PBXFileReference; indentWidth = 2; lastKnownFileType = sourcecode.cpp.h; path = json_binder.hpp; sourceTree = SOURCE_ROOT; tabWidth = 2; };
 		CDF643321C6E9A8B0016A475 /* json-test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = "json-test"; sourceTree = BUILT_PRODUCTS_DIR; };
 /* End PBXFileReference section */
 
@@ -65,6 +67,7 @@
 		CD472C781CCC1CB00084C8D6 /* impl */ = {
 			isa = PBXGroup;
 			children = (
+				CDB2F7451C5E9BEB0067C2EC /* json_binder_parser.hpp */,
 				CD472C791CCC1CD80084C8D6 /* json_tuple_binder.hpp */,
 				CD472C7A1CCC1D440084C8D6 /* json_object_binder.hpp */,
 				CD472C7E1CCC498C0084C8D6 /* json_direct_binder.hpp */,
@@ -73,20 +76,36 @@
 				CD472C7C1CCC1DDF0084C8D6 /* json_direct_vector_binder.hpp */,
 			);
 			name = impl;
+			sourceTree = "<group>";
+		};
+		CD472C821CCDA5830084C8D6 /* binder */ = {
+			isa = PBXGroup;
+			children = (
+				CDB2F7461C5EA2E80067C2EC /* json_binder.hpp */,
+				CD472C781CCC1CB00084C8D6 /* impl */,
+			);
+			name = binder;
 			path = json;
 			sourceTree = "<group>";
 		};
+		CD472C831CCDA5990084C8D6 /* json */ = {
+			isa = PBXGroup;
+			children = (
+				CDB2F7411C5D48090067C2EC /* json.cpp */,
+				CDB2F7421C5D48090067C2EC /* json.hpp */,
+				CD472C7F1CCDA4B00084C8D6 /* json_parser.cpp */,
+			);
+			name = json;
+			sourceTree = "<group>";
+		};
 		CDB2F72A1C5D47F70067C2EC = {
 			isa = PBXGroup;
 			children = (
 				CD217D8F1CCAD587007C50C6 /* json_test.cpp */,
-				CDB2F7411C5D48090067C2EC /* json.cpp */,
-				CDB2F7421C5D48090067C2EC /* json.h */,
-				CD217D921CCAD885007C50C6 /* json_common.h */,
+				CD217D921CCAD885007C50C6 /* json_common.hpp */,
 				CD472C751CCC1ABD0084C8D6 /* json_common.cpp */,
-				CDB2F7451C5E9BEB0067C2EC /* json_parser.hpp */,
-				CDB2F7461C5EA2E80067C2EC /* json_binder.hpp */,
-				CD472C781CCC1CB00084C8D6 /* impl */,
+				CD472C831CCDA5990084C8D6 /* json */,
+				CD472C821CCDA5830084C8D6 /* binder */,
 				CDB2F7341C5D47F70067C2EC /* Products */,
 			);
 			sourceTree = "<group>";
@@ -107,7 +126,7 @@
 			isa = PBXHeadersBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
-				CDB2F7441C5D48090067C2EC /* json.h in Headers */,
+				CDB2F7441C5D48090067C2EC /* json.hpp in Headers */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -188,6 +207,7 @@
 			isa = PBXSourcesBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				CD472C801CCDA4B00084C8D6 /* json_parser.cpp in Sources */,
 				CDB2F7431C5D48090067C2EC /* json.cpp in Sources */,
 				CD472C761CCC1ABD0084C8D6 /* json_common.cpp in Sources */,
 			);

+ 0 - 19
json_parser.hpp

@@ -10,34 +10,15 @@
 #define json_parser_h
 #pragma once
 
-#include <iostream>
-
 namespace json {
   class value;
   
   namespace binder { template <typename, typename> class visitor; }
   
   namespace parser {
-    void parse(value& json, char const* data);
-    
     template <typename T, typename S>
     void parse(binder::visitor<T, S>&, char const*);
     
-    template <typename T>
-    void parse(T& json, std::string const& str) {
-      parse(json, str.c_str());
-    }
-    
-    template <typename T>
-    void parse(T& json, std::istream & in) {
-      in.seekg(0, std::ios_base::end);
-      size_t end = in.tellg();
-      char data[end];
-      in.seekg(0);
-      in.read(data, end);
-      parse(json, data);
-    }
-    
     template <typename T, typename S, typename V>
     void parse(binder::visitor<T, S>&& v, V& s) {
       parse(v, s);

+ 2 - 10
json/json_direct_scalar_binder.hpp

@@ -39,11 +39,7 @@ namespace json { namespace binder {
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
     virtual void parse(T& val, char const*& data) const override {
-      //        if (false) {
-      json::helper::parse_numeric(val.*ptr, data);
-      //        } else {
-      //          throw json::malformed_json_exception("Expected an integral type here");
-      //        }
+      json::helper::parse_integer(val.*ptr, data);
     }
     
     virtual void write(T const& val, std::string & data) const override {
@@ -62,11 +58,7 @@ namespace json { namespace binder {
     virtual binder_impl<T>* clone() const override { return new direct_binder(*this); }
     
     virtual void parse(T& val, char const*& data) const override {
-      //        if (false) {
-      json::helper::parse_numeric(val.*ptr, data);
-      //        } else {
-      //          throw json::malformed_json_exception("Expected a floating point type here");
-      //        }
+      json::helper::parse_double(val.*ptr, data);
     }
     
     virtual void write(T const& val, std::string & data) const override {

+ 2 - 1
json_binder.hpp

@@ -10,7 +10,7 @@
 #define json_binder_h
 #pragma once
 
-#include "json_common.h"
+#include "json_common.hpp"
 
 #include <map>
 #include <string>
@@ -103,6 +103,7 @@ namespace json {
   }
 }
 
+#include "json/json_binder_parser.hpp"
 #include "json/json_direct_binder.hpp"
 #include "json/json_tuple_binder.hpp"
 #include "json/json_object_binder.hpp"

+ 2 - 2
json_common.cpp

@@ -5,7 +5,7 @@
 //  Created by Sam Jaffe on 4/23/16.
 //
 
-#include "json_common.h"
+#include "json_common.hpp"
 
 namespace json {
   namespace helper {
@@ -41,6 +41,6 @@ namespace json {
         }
       }
       throw json::malformed_json_exception("Could not locate end of string");
-    }
+    }    
   }
 }

+ 0 - 56
json_common.h

@@ -1,56 +0,0 @@
-//
-//  json_common.h
-//  json
-//
-//  Created by Sam Jaffe on 4/22/16.
-//
-
-#pragma once
-
-#include <cstdlib>
-#include <stdexcept>
-#include <string>
-
-namespace json {
-  class malformed_json_exception : public std::domain_error {
-    using std::domain_error::domain_error;
-  };
-}
-
-namespace json {
-  namespace helper {
-    const char get_next_element(char const*& data);
-    
-    /**
-     * @throws json::malformed_json_exception
-     */
-    void advance_to_boundary(char const endtok, char const*& data);
-    
-    /**
-     * @throws json::malformed_json_exception
-     */
-    std::string parse_string(char const * & data);
-    
-    template <typename T>
-    void parse_string(T& json, char const*& data) {
-      json = parse_string(data);
-    }
-    
-    template <typename T>
-    void parse_numeric(T& json, char const*& data) {
-      // TODO: more sophisticated float detection?
-      char const* start = data;
-      while (*++data) {
-        if (*data == '.') {
-          while (isnumber(*++data));
-          json = atof(start);
-          break;
-        } else if (!isnumber(*data)) {
-          json = atoi(start);
-          break;
-        }
-      }
-    }
-    
-  }
-}

+ 84 - 0
json_common.hpp

@@ -0,0 +1,84 @@
+//
+//  json_common.h
+//  json
+//
+//  Created by Sam Jaffe on 4/22/16.
+//
+
+#pragma once
+
+#include "../variant/variant.hpp"
+
+#include <cstdlib>
+#include <iostream>
+#include <map>
+#include <stdexcept>
+#include <string>
+#include <vector>
+
+namespace json {
+  
+  using string_jt = std::string;
+  using double_jt = double;
+  using int_jt = int32_t;
+  using uint_jt = uint32_t;
+  using bool_jt = bool;
+  
+  using numeric_jt = variant<int_jt, uint_jt, double_jt>;
+  
+  class value;
+
+  class malformed_json_exception :
+  public std::domain_error {
+    using std::domain_error::domain_error;
+  };
+}
+
+namespace json {
+  namespace helper {
+    const char get_next_element(char const*& data);
+    
+    /**
+     * @throws json::malformed_json_exception
+     */
+    void advance_to_boundary(char const endtok, char const *& data);
+    
+    /**
+     * @throws json::malformed_json_exception
+     */
+    std::string parse_string(char const * & data);
+        
+    template <typename T>
+    void parse_string(T& json, char const * & data) {
+      json = parse_string(data);
+    }
+    
+    template <typename T>
+    void parse_double(T& json, char const * & data) {
+      json = atof(data);
+    }
+    
+    template <typename T>
+    void parse_integer(T& json, char const * & data) {
+      json = atoi(data);
+    }
+    
+  }
+  
+  namespace parser {
+    template <typename T>
+    void parse(T& json, std::string const& str) {
+      parse(json, str.c_str());
+    }
+    
+    template <typename T>
+    void parse(T& json, std::istream & in) {
+      in.seekg(0, std::ios_base::end);
+      size_t end = in.tellg();
+      char data[end];
+      in.seekg(0);
+      in.read(data, end);
+      parse(json, data);
+    }
+  }
+}

+ 124 - 0
json_parser.cpp

@@ -0,0 +1,124 @@
+//
+//  json_parser.cpp
+//  json
+//
+//  Created by Sam Jaffe on 4/24/16.
+//
+
+#include "json.hpp"
+
+namespace json { namespace {
+  char const * get_numeric_token_end(char const * start);
+  void parse_numeric(value& json, char const*& data);
+  void parse_object(value& json, char const*& data);
+  void parse_array(value& json, char const*& data);
+  void parse_one_token(value& json, char const*& data);
+  
+  void parse_one_token(value& json, char const*& data) {
+    const char ch = helper::get_next_element(data);
+    if (ch == '{') {
+      parse_object(json, ++data);
+    } else if (ch == '[') {
+      parse_array(json, ++data);
+    } else if (ch == '"') {
+      helper::parse_string(json, ++data);
+    } else if (!strncmp(data, "true", 4)) {
+      json = true;
+    } else if (!strncmp(data, "false", 5)) {
+      json = false;
+    } else {
+      parse_numeric(json, data);
+    }
+  }
+  
+  char const * get_numeric_token_end(char const * start) {
+    while (strchr(",]}", *start) == NULL &&
+           !isspace(*start)) {
+      ++start;
+    }
+    return start;
+  }
+  
+  void parse_numeric(value & json, char const * & data) {
+    char const * start = data;
+    char const * const end = get_numeric_token_end(start);
+    
+    if (end == start) {
+      throw malformed_json_exception("Expected any token, got nothing");
+    }
+    
+    bool const negative = *start == '-';
+    if (negative) ++start;
+    
+    uint_jt const threshold = (UINT_JT_MAX / 10);
+    uint_jt val = 0;
+    for (; start != end; ++start) {
+      if (!isdigit(*start)) {
+        helper::parse_double(json, start);
+        return;
+      }
+      int_jt digit = static_cast<int_jt>(*start - '\0');
+      if (val >= threshold) {
+        if (val > threshold || (start + 1) < end || digit > INT_JT_MAX_LAST_DIGIT) {
+          helper::parse_double(json, start);
+          return;
+        }
+      }
+      val = (10 * val) + digit;
+    }
+    if (negative && val == INT_JT_OVER) {
+      json = INT_JT_MIN;
+    } else if (negative) {
+      json = -int_jt(val);
+    } else if (val <= uint_jt(INT_JT_MAX)) {
+      json = int_jt(val);
+    } else {
+      json = val;
+    }
+  }
+  
+  void parse_object(value& json, char const*& data) {
+    std::string key;
+    while (*data && *data != '}') {
+      helper::parse_string(key, data);
+      if (helper::get_next_element(data) != ':') {
+        throw malformed_json_exception(std::string("Expected key:value pair delimited by ':', got '") + *data + "' instead");
+      }
+      parse_one_token(json[key], ++data);
+      helper::advance_to_boundary('}', data);
+    }
+    if (*data) ++data;
+    else throw malformed_json_exception("Reached end of parse string without finding object end");
+  }
+  
+  void parse_array(value& json, char const*& data) {
+    size_t current_idx = 0;
+    while (*data && *data != ']') {
+      parse_one_token(json[current_idx++], data);
+      helper::advance_to_boundary(']', data);
+    }
+    if (*data) ++data;
+    else throw malformed_json_exception("Reached end of parse string without finding array end");
+  }
+} }
+
+namespace json {
+  namespace parser {
+    void parse(value& json, char const* data) {
+      parse_one_token(json, data);
+      if (*data) throw malformed_json_exception("Expected a single json token in top-level parse");
+    }
+  }
+}
+
+void json::value::parse(char const* data) {
+  parser::parse(*this, data);
+}
+
+void json::value::parse(std::string const& str) {
+  parser::parse(*this, str);
+}
+
+void json::value::parse(std::istream & in) {
+  parser::parse(*this, in);
+}

+ 0 - 1
json_test.cpp

@@ -6,7 +6,6 @@
 //
 
 #include "json_binder.hpp"
-#include "json_parser.hpp"
 
 struct test_t {
   int a, b;