Merge branch 'add_json_support' into develop
This commit is contained in:
@@ -19,6 +19,7 @@
|
|||||||
#include "dispatchkit/bootstrap_stl.hpp"
|
#include "dispatchkit/bootstrap_stl.hpp"
|
||||||
#include "dispatchkit/boxed_value.hpp"
|
#include "dispatchkit/boxed_value.hpp"
|
||||||
#include "language/chaiscript_prelude.chai"
|
#include "language/chaiscript_prelude.chai"
|
||||||
|
#include "utility/json_wrap.hpp"
|
||||||
|
|
||||||
#ifndef CHAISCRIPT_NO_THREADS
|
#ifndef CHAISCRIPT_NO_THREADS
|
||||||
#include <future>
|
#include <future>
|
||||||
@@ -51,6 +52,8 @@ namespace chaiscript
|
|||||||
lib->add(chaiscript::fun([](const std::function<chaiscript::Boxed_Value ()> &t_func){ return std::async(std::launch::async, t_func);}), "async");
|
lib->add(chaiscript::fun([](const std::function<chaiscript::Boxed_Value ()> &t_func){ return std::async(std::launch::async, t_func);}), "async");
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
lib->add(json_wrap::library());
|
||||||
|
|
||||||
lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ );
|
lib->eval(ChaiScript_Prelude::chaiscript_prelude() /*, "standard prelude"*/ );
|
||||||
|
|
||||||
return lib;
|
return lib;
|
||||||
|
@@ -468,6 +468,30 @@ namespace chaiscript
|
|||||||
m->add(fun(static_cast<elem_access>(&MapType::at)), "at");
|
m->add(fun(static_cast<elem_access>(&MapType::at)), "at");
|
||||||
m->add(fun(static_cast<const_elem_access>(&MapType::at)), "at");
|
m->add(fun(static_cast<const_elem_access>(&MapType::at)), "at");
|
||||||
|
|
||||||
|
if (typeid(MapType) == typeid(std::map<std::string, Boxed_Value>))
|
||||||
|
{
|
||||||
|
m->eval(R"(
|
||||||
|
def Map::`==`(Map rhs) {
|
||||||
|
if ( rhs.size() != this.size() ) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
auto r1 = range(this);
|
||||||
|
auto r2 = range(rhs);
|
||||||
|
while (!r1.empty())
|
||||||
|
{
|
||||||
|
if (!eq(r1.front().first, r2.front().first) || !eq(r1.front().second, r2.front().second))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
r1.pop_front();
|
||||||
|
r2.pop_front();
|
||||||
|
}
|
||||||
|
true;
|
||||||
|
}
|
||||||
|
} )"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
container_type<MapType>(type, m);
|
container_type<MapType>(type, m);
|
||||||
default_constructible_type<MapType>(type, m);
|
default_constructible_type<MapType>(type, m);
|
||||||
assignable_type<MapType>(type, m);
|
assignable_type<MapType>(type, m);
|
||||||
@@ -523,7 +547,7 @@ namespace chaiscript
|
|||||||
if (typeid(VectorType) == typeid(std::vector<Boxed_Value>))
|
if (typeid(VectorType) == typeid(std::vector<Boxed_Value>))
|
||||||
{
|
{
|
||||||
m->eval(R"(
|
m->eval(R"(
|
||||||
def Vector::`==`(rhs) : type_match(rhs, this) {
|
def Vector::`==`(Vector rhs) {
|
||||||
if ( rhs.size() != this.size() ) {
|
if ( rhs.size() != this.size() ) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
|
@@ -100,6 +100,7 @@ namespace chaiscript
|
|||||||
:(Common_Types::t_uint64);
|
:(Common_Types::t_uint64);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static Common_Types get_common_type(const Boxed_Value &t_bv)
|
static Common_Types get_common_type(const Boxed_Value &t_bv)
|
||||||
{
|
{
|
||||||
const Type_Info &inp_ = t_bv.get_type_info();
|
const Type_Info &inp_ = t_bv.get_type_info();
|
||||||
@@ -517,6 +518,21 @@ namespace chaiscript
|
|||||||
validate_boxed_number(bv);
|
validate_boxed_number(bv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool is_floating_point(const Boxed_Value &t_bv)
|
||||||
|
{
|
||||||
|
const Type_Info &inp_ = t_bv.get_type_info();
|
||||||
|
|
||||||
|
if (inp_ == typeid(double)) {
|
||||||
|
return true;
|
||||||
|
} else if (inp_ == typeid(long double)) {
|
||||||
|
return true;
|
||||||
|
} else if (inp_ == typeid(float)) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Boxed_Number get_as(const Type_Info &inp_) const
|
Boxed_Number get_as(const Type_Info &inp_) const
|
||||||
{
|
{
|
||||||
if (inp_.bare_equal_type_info(typeid(int))) {
|
if (inp_.bare_equal_type_info(typeid(int))) {
|
||||||
|
@@ -41,6 +41,16 @@ namespace chaiscript
|
|||||||
return m_type_name;
|
return m_type_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const Boxed_Value &operator[](const std::string &t_attr_name) const
|
||||||
|
{
|
||||||
|
return get_attr(t_attr_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
Boxed_Value &operator[](const std::string &t_attr_name)
|
||||||
|
{
|
||||||
|
return get_attr(t_attr_name);
|
||||||
|
}
|
||||||
|
|
||||||
const Boxed_Value &get_attr(const std::string &t_attr_name) const
|
const Boxed_Value &get_attr(const std::string &t_attr_name) const
|
||||||
{
|
{
|
||||||
auto a = m_attrs.find(t_attr_name);
|
auto a = m_attrs.find(t_attr_name);
|
||||||
|
@@ -40,7 +40,7 @@ namespace chaiscript
|
|||||||
{
|
{
|
||||||
const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(),
|
const bool has_arity_match = std::any_of(funcs.begin(), funcs.end(),
|
||||||
[](const Const_Proxy_Function &f) {
|
[](const Const_Proxy_Function &f) {
|
||||||
return f->get_arity() == -1 || f->get_arity() == chaiscript::dispatch::detail::Arity<FunctionType>::arity;
|
return f->get_arity() == -1 || size_t(f->get_arity()) == chaiscript::dispatch::detail::Arity<FunctionType>::arity;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!has_arity_match) {
|
if (!has_arity_match) {
|
||||||
|
647
include/chaiscript/utility/json.hpp
Normal file
647
include/chaiscript/utility/json.hpp
Normal file
@@ -0,0 +1,647 @@
|
|||||||
|
// From github.com/nbsdx/SimpleJSON.
|
||||||
|
// Released under the DWTFYW PL
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#ifndef SIMPLEJSON_HPP
|
||||||
|
#define SIMPLEJSON_HPP
|
||||||
|
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cctype>
|
||||||
|
#include <string>
|
||||||
|
#include <deque>
|
||||||
|
#include <map>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <initializer_list>
|
||||||
|
#include <ostream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace json {
|
||||||
|
|
||||||
|
using std::map;
|
||||||
|
using std::deque;
|
||||||
|
using std::string;
|
||||||
|
using std::enable_if;
|
||||||
|
using std::initializer_list;
|
||||||
|
using std::is_same;
|
||||||
|
using std::is_convertible;
|
||||||
|
using std::is_integral;
|
||||||
|
using std::is_floating_point;
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
string json_escape( const string &str ) {
|
||||||
|
string output;
|
||||||
|
for( unsigned i = 0; i < str.length(); ++i )
|
||||||
|
switch( str[i] ) {
|
||||||
|
case '\"': output += "\\\""; break;
|
||||||
|
case '\\': output += "\\\\"; break;
|
||||||
|
case '\b': output += "\\b"; break;
|
||||||
|
case '\f': output += "\\f"; break;
|
||||||
|
case '\n': output += "\\n"; break;
|
||||||
|
case '\r': output += "\\r"; break;
|
||||||
|
case '\t': output += "\\t"; break;
|
||||||
|
default : output += str[i]; break;
|
||||||
|
}
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class JSON
|
||||||
|
{
|
||||||
|
union BackingData {
|
||||||
|
BackingData( double d ) : Float( d ){}
|
||||||
|
BackingData( long l ) : Int( l ){}
|
||||||
|
BackingData( bool b ) : Bool( b ){}
|
||||||
|
BackingData( string s ) : String( new string( s ) ){}
|
||||||
|
BackingData() : Int( 0 ){}
|
||||||
|
|
||||||
|
deque<JSON> *List;
|
||||||
|
map<string,JSON> *Map;
|
||||||
|
string *String;
|
||||||
|
double Float;
|
||||||
|
long Int;
|
||||||
|
bool Bool;
|
||||||
|
} Internal;
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class Class {
|
||||||
|
Null,
|
||||||
|
Object,
|
||||||
|
Array,
|
||||||
|
String,
|
||||||
|
Floating,
|
||||||
|
Integral,
|
||||||
|
Boolean
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
class JSONWrapper {
|
||||||
|
Container *object;
|
||||||
|
|
||||||
|
public:
|
||||||
|
JSONWrapper( Container *val ) : object( val ) {}
|
||||||
|
JSONWrapper( std::nullptr_t ) : object( nullptr ) {}
|
||||||
|
|
||||||
|
typename Container::iterator begin() { return object ? object->begin() : typename Container::iterator(); }
|
||||||
|
typename Container::iterator end() { return object ? object->end() : typename Container::iterator(); }
|
||||||
|
typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::iterator(); }
|
||||||
|
typename Container::const_iterator end() const { return object ? object->end() : typename Container::iterator(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
class JSONConstWrapper {
|
||||||
|
const Container *object;
|
||||||
|
|
||||||
|
public:
|
||||||
|
JSONConstWrapper( const Container *val ) : object( val ) {}
|
||||||
|
JSONConstWrapper( std::nullptr_t ) : object( nullptr ) {}
|
||||||
|
|
||||||
|
typename Container::const_iterator begin() const { return object ? object->begin() : typename Container::const_iterator(); }
|
||||||
|
typename Container::const_iterator end() const { return object ? object->end() : typename Container::const_iterator(); }
|
||||||
|
};
|
||||||
|
|
||||||
|
JSON() : Internal(), Type( Class::Null ){}
|
||||||
|
|
||||||
|
explicit JSON(Class type)
|
||||||
|
: Internal(), Type(Class::Null)
|
||||||
|
{
|
||||||
|
SetType( type );
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON( initializer_list<JSON> list )
|
||||||
|
: Internal(), Type(Class::Null)
|
||||||
|
{
|
||||||
|
SetType( Class::Object );
|
||||||
|
for( auto i = list.begin(), e = list.end(); i != e; ++i, ++i )
|
||||||
|
operator[]( i->ToString() ) = *std::next( i );
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON( JSON&& other )
|
||||||
|
: Internal( other.Internal )
|
||||||
|
, Type( other.Type )
|
||||||
|
{ other.Type = Class::Null; other.Internal.Map = nullptr; }
|
||||||
|
|
||||||
|
JSON& operator=( JSON&& other ) {
|
||||||
|
Internal = other.Internal;
|
||||||
|
Type = other.Type;
|
||||||
|
other.Internal.Map = nullptr;
|
||||||
|
other.Type = Class::Null;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON( const JSON &other ) {
|
||||||
|
switch( other.Type ) {
|
||||||
|
case Class::Object:
|
||||||
|
Internal.Map =
|
||||||
|
new map<string,JSON>( other.Internal.Map->begin(),
|
||||||
|
other.Internal.Map->end() );
|
||||||
|
break;
|
||||||
|
case Class::Array:
|
||||||
|
Internal.List =
|
||||||
|
new deque<JSON>( other.Internal.List->begin(),
|
||||||
|
other.Internal.List->end() );
|
||||||
|
break;
|
||||||
|
case Class::String:
|
||||||
|
Internal.String =
|
||||||
|
new string( *other.Internal.String );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Internal = other.Internal;
|
||||||
|
}
|
||||||
|
Type = other.Type;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON& operator=( const JSON &other ) {
|
||||||
|
if (&other == this) return *this;
|
||||||
|
|
||||||
|
switch( other.Type ) {
|
||||||
|
case Class::Object:
|
||||||
|
Internal.Map =
|
||||||
|
new map<string,JSON>( other.Internal.Map->begin(),
|
||||||
|
other.Internal.Map->end() );
|
||||||
|
break;
|
||||||
|
case Class::Array:
|
||||||
|
Internal.List =
|
||||||
|
new deque<JSON>( other.Internal.List->begin(),
|
||||||
|
other.Internal.List->end() );
|
||||||
|
break;
|
||||||
|
case Class::String:
|
||||||
|
Internal.String =
|
||||||
|
new string( *other.Internal.String );
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
Internal = other.Internal;
|
||||||
|
}
|
||||||
|
Type = other.Type;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~JSON() {
|
||||||
|
switch( Type ) {
|
||||||
|
case Class::Array:
|
||||||
|
delete Internal.List;
|
||||||
|
break;
|
||||||
|
case Class::Object:
|
||||||
|
delete Internal.Map;
|
||||||
|
break;
|
||||||
|
case Class::String:
|
||||||
|
delete Internal.String;
|
||||||
|
break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
JSON( T b, typename enable_if<is_same<T,bool>::value>::type* = 0 ) : Internal( b ), Type( Class::Boolean ){}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
JSON( T i, typename enable_if<is_integral<T>::value && !is_same<T,bool>::value>::type* = 0 ) : Internal( long(i) ), Type( Class::Integral ){}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
JSON( T f, typename enable_if<is_floating_point<T>::value>::type* = 0 ) : Internal( double(f) ), Type( Class::Floating ){}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
JSON( T s, typename enable_if<is_convertible<T,string>::value>::type* = 0 ) : Internal( string( s ) ), Type( Class::String ){}
|
||||||
|
|
||||||
|
JSON( std::nullptr_t ) : Internal(), Type( Class::Null ){}
|
||||||
|
|
||||||
|
static JSON Make( Class type ) {
|
||||||
|
return JSON(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSON Load( const string & );
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void append( T arg ) {
|
||||||
|
SetType( Class::Array ); Internal.List->emplace_back( arg );
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, typename... U>
|
||||||
|
void append( T arg, U... args ) {
|
||||||
|
append( arg ); append( args... );
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename enable_if<is_same<T,bool>::value, JSON&>::type operator=( T b ) {
|
||||||
|
SetType( Class::Boolean ); Internal.Bool = b; return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename enable_if<is_integral<T>::value && !is_same<T,bool>::value, JSON&>::type operator=( T i ) {
|
||||||
|
SetType( Class::Integral ); Internal.Int = i; return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename enable_if<is_floating_point<T>::value, JSON&>::type operator=( T f ) {
|
||||||
|
SetType( Class::Floating ); Internal.Float = f; return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
typename enable_if<is_convertible<T,string>::value, JSON&>::type operator=( T s ) {
|
||||||
|
SetType( Class::String ); *Internal.String = string( s ); return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON& operator[]( const string &key ) {
|
||||||
|
SetType( Class::Object ); return Internal.Map->operator[]( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON& operator[]( unsigned index ) {
|
||||||
|
SetType( Class::Array );
|
||||||
|
if( index >= Internal.List->size() ) Internal.List->resize( index + 1 );
|
||||||
|
return Internal.List->operator[]( index );
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON &at( const string &key ) {
|
||||||
|
return operator[]( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
const JSON &at( const string &key ) const {
|
||||||
|
return Internal.Map->at( key );
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON &at( unsigned index ) {
|
||||||
|
return operator[]( index );
|
||||||
|
}
|
||||||
|
|
||||||
|
const JSON &at( unsigned index ) const {
|
||||||
|
return Internal.List->at( index );
|
||||||
|
}
|
||||||
|
|
||||||
|
int length() const {
|
||||||
|
if( Type == Class::Array )
|
||||||
|
return static_cast<int>(Internal.List->size());
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool hasKey( const string &key ) const {
|
||||||
|
if( Type == Class::Object )
|
||||||
|
return Internal.Map->find( key ) != Internal.Map->end();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
int size() const {
|
||||||
|
if( Type == Class::Object )
|
||||||
|
return static_cast<int>(Internal.Map->size());
|
||||||
|
else if( Type == Class::Array )
|
||||||
|
return static_cast<int>(Internal.List->size());
|
||||||
|
else
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Class JSONType() const { return Type; }
|
||||||
|
|
||||||
|
/// Functions for getting primitives from the JSON object.
|
||||||
|
bool IsNull() const { return Type == Class::Null; }
|
||||||
|
|
||||||
|
string ToString() const { bool b; return ToString( b ); }
|
||||||
|
string ToString( bool &ok ) const {
|
||||||
|
ok = (Type == Class::String);
|
||||||
|
return ok ? *Internal.String : string("");
|
||||||
|
}
|
||||||
|
|
||||||
|
double ToFloat() const { bool b; return ToFloat( b ); }
|
||||||
|
double ToFloat( bool &ok ) const {
|
||||||
|
ok = (Type == Class::Floating);
|
||||||
|
return ok ? Internal.Float : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
long ToInt() const { bool b; return ToInt( b ); }
|
||||||
|
long ToInt( bool &ok ) const {
|
||||||
|
ok = (Type == Class::Integral);
|
||||||
|
return ok ? Internal.Int : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ToBool() const { bool b; return ToBool( b ); }
|
||||||
|
bool ToBool( bool &ok ) const {
|
||||||
|
ok = (Type == Class::Boolean);
|
||||||
|
return ok ? Internal.Bool : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONWrapper<map<string,JSON>> ObjectRange() {
|
||||||
|
if( Type == Class::Object )
|
||||||
|
return JSONWrapper<map<string,JSON>>( Internal.Map );
|
||||||
|
return JSONWrapper<map<string,JSON>>( nullptr );
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONWrapper<deque<JSON>> ArrayRange() {
|
||||||
|
if( Type == Class::Array )
|
||||||
|
return JSONWrapper<deque<JSON>>( Internal.List );
|
||||||
|
return JSONWrapper<deque<JSON>>( nullptr );
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONConstWrapper<map<string,JSON>> ObjectRange() const {
|
||||||
|
if( Type == Class::Object )
|
||||||
|
return JSONConstWrapper<map<string,JSON>>( Internal.Map );
|
||||||
|
return JSONConstWrapper<map<string,JSON>>( nullptr );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSONConstWrapper<deque<JSON>> ArrayRange() const {
|
||||||
|
if( Type == Class::Array )
|
||||||
|
return JSONConstWrapper<deque<JSON>>( Internal.List );
|
||||||
|
return JSONConstWrapper<deque<JSON>>( nullptr );
|
||||||
|
}
|
||||||
|
|
||||||
|
string dump( int depth = 1, string tab = " ") const {
|
||||||
|
switch( Type ) {
|
||||||
|
case Class::Null:
|
||||||
|
return "null";
|
||||||
|
case Class::Object: {
|
||||||
|
string pad = "";
|
||||||
|
for( int i = 0; i < depth; ++i, pad += tab );
|
||||||
|
|
||||||
|
string s = "{\n";
|
||||||
|
bool skip = true;
|
||||||
|
for( auto &p : *Internal.Map ) {
|
||||||
|
if( !skip ) s += ",\n";
|
||||||
|
s += ( pad + "\"" + p.first + "\" : " + p.second.dump( depth + 1, tab ) );
|
||||||
|
skip = false;
|
||||||
|
}
|
||||||
|
s += ( "\n" + pad.erase( 0, 2 ) + "}" ) ;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
case Class::Array: {
|
||||||
|
string s = "[";
|
||||||
|
bool skip = true;
|
||||||
|
for( auto &p : *Internal.List ) {
|
||||||
|
if( !skip ) s += ", ";
|
||||||
|
s += p.dump( depth + 1, tab );
|
||||||
|
skip = false;
|
||||||
|
}
|
||||||
|
s += "]";
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
case Class::String:
|
||||||
|
return "\"" + json_escape( *Internal.String ) + "\"";
|
||||||
|
case Class::Floating:
|
||||||
|
return std::to_string( Internal.Float );
|
||||||
|
case Class::Integral:
|
||||||
|
return std::to_string( Internal.Int );
|
||||||
|
case Class::Boolean:
|
||||||
|
return Internal.Bool ? "true" : "false";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
friend std::ostream& operator<<( std::ostream&, const JSON & );
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetType( Class type ) {
|
||||||
|
if( type == Type )
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch( Type ) {
|
||||||
|
case Class::Object: delete Internal.Map; break;
|
||||||
|
case Class::Array: delete Internal.List; break;
|
||||||
|
case Class::String: delete Internal.String; break;
|
||||||
|
default:;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch( type ) {
|
||||||
|
case Class::Null: Internal.Map = nullptr; break;
|
||||||
|
case Class::Object: Internal.Map = new map<string,JSON>(); break;
|
||||||
|
case Class::Array: Internal.List = new deque<JSON>(); break;
|
||||||
|
case Class::String: Internal.String = new string(); break;
|
||||||
|
case Class::Floating: Internal.Float = 0.0; break;
|
||||||
|
case Class::Integral: Internal.Int = 0; break;
|
||||||
|
case Class::Boolean: Internal.Bool = false; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
Class Type;
|
||||||
|
};
|
||||||
|
|
||||||
|
JSON Array() {
|
||||||
|
return JSON::Make( JSON::Class::Array );
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... T>
|
||||||
|
JSON Array( T... args ) {
|
||||||
|
JSON arr = JSON::Make( JSON::Class::Array );
|
||||||
|
arr.append( args... );
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON Object() {
|
||||||
|
return JSON::Make( JSON::Class::Object );
|
||||||
|
}
|
||||||
|
|
||||||
|
std::ostream& operator<<( std::ostream &os, const JSON &json ) {
|
||||||
|
os << json.dump();
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
JSON parse_next( const string &, size_t & );
|
||||||
|
|
||||||
|
void consume_ws( const string &str, size_t &offset ) {
|
||||||
|
while( isspace( str[offset] ) ) ++offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON parse_object( const string &str, size_t &offset ) {
|
||||||
|
JSON Object = JSON::Make( JSON::Class::Object );
|
||||||
|
|
||||||
|
++offset;
|
||||||
|
consume_ws( str, offset );
|
||||||
|
if( str[offset] == '}' ) {
|
||||||
|
++offset; return Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;offset<str.size();) {
|
||||||
|
JSON Key = parse_next( str, offset );
|
||||||
|
consume_ws( str, offset );
|
||||||
|
if( str[offset] != ':' ) {
|
||||||
|
throw std::runtime_error(std::string("JSON ERROR: Object: Expected colon, found '") + str[offset] + "'\n");
|
||||||
|
}
|
||||||
|
consume_ws( str, ++offset );
|
||||||
|
JSON Value = parse_next( str, offset );
|
||||||
|
Object[Key.ToString()] = Value;
|
||||||
|
|
||||||
|
consume_ws( str, offset );
|
||||||
|
if( str[offset] == ',' ) {
|
||||||
|
++offset; continue;
|
||||||
|
}
|
||||||
|
else if( str[offset] == '}' ) {
|
||||||
|
++offset; break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error(std::string("JSON ERROR: Object: Expected comma, found '") + str[offset] + "'\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON parse_array( const string &str, size_t &offset ) {
|
||||||
|
JSON Array = JSON::Make( JSON::Class::Array );
|
||||||
|
unsigned index = 0;
|
||||||
|
|
||||||
|
++offset;
|
||||||
|
consume_ws( str, offset );
|
||||||
|
if( str[offset] == ']' ) {
|
||||||
|
++offset; return Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;offset < str.size();) {
|
||||||
|
Array[index++] = parse_next( str, offset );
|
||||||
|
consume_ws( str, offset );
|
||||||
|
|
||||||
|
if( str[offset] == ',' ) {
|
||||||
|
++offset; continue;
|
||||||
|
}
|
||||||
|
else if( str[offset] == ']' ) {
|
||||||
|
++offset; break;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
throw std::runtime_error(std::string("JSON ERROR: Array: Expected ',' or ']', found '") + str[offset] + "'\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Array;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON parse_string( const string &str, size_t &offset ) {
|
||||||
|
string val;
|
||||||
|
for( char c = str[++offset]; c != '\"' ; c = str[++offset] ) {
|
||||||
|
if( c == '\\' ) {
|
||||||
|
switch( str[ ++offset ] ) {
|
||||||
|
case '\"': val += '\"'; break;
|
||||||
|
case '\\': val += '\\'; break;
|
||||||
|
case '/' : val += '/' ; break;
|
||||||
|
case 'b' : val += '\b'; break;
|
||||||
|
case 'f' : val += '\f'; break;
|
||||||
|
case 'n' : val += '\n'; break;
|
||||||
|
case 'r' : val += '\r'; break;
|
||||||
|
case 't' : val += '\t'; break;
|
||||||
|
case 'u' : {
|
||||||
|
val += "\\u" ;
|
||||||
|
for( unsigned i = 1; i <= 4; ++i ) {
|
||||||
|
c = str[offset+i];
|
||||||
|
if( (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') )
|
||||||
|
val += c;
|
||||||
|
else {
|
||||||
|
throw std::runtime_error(std::string("JSON ERROR: String: Expected hex character in unicode escape, found '") + c + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offset += 4;
|
||||||
|
} break;
|
||||||
|
default : val += '\\'; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
val += c;
|
||||||
|
}
|
||||||
|
++offset;
|
||||||
|
return JSON(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON parse_number( const string &str, size_t &offset ) {
|
||||||
|
JSON Number;
|
||||||
|
string val, exp_str;
|
||||||
|
char c;
|
||||||
|
bool isDouble = false;
|
||||||
|
long exp = 0;
|
||||||
|
for (; offset < str.size() ;) {
|
||||||
|
c = str[offset++];
|
||||||
|
if( (c == '-') || (c >= '0' && c <= '9') )
|
||||||
|
val += c;
|
||||||
|
else if( c == '.' ) {
|
||||||
|
val += c;
|
||||||
|
isDouble = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if( offset < str.size() && (c == 'E' || c == 'e' )) {
|
||||||
|
c = str[ offset++ ];
|
||||||
|
if( c == '-' ) { exp_str += '-';}
|
||||||
|
else if( c == '+' ) { }
|
||||||
|
else --offset;
|
||||||
|
|
||||||
|
for (; offset < str.size() ;) {
|
||||||
|
c = str[ offset++ ];
|
||||||
|
if( c >= '0' && c <= '9' )
|
||||||
|
exp_str += c;
|
||||||
|
else if( !isspace( c ) && c != ',' && c != ']' && c != '}' ) {
|
||||||
|
throw std::runtime_error(std::string("JSON ERROR: Number: Expected a number for exponent, found '") + c + "'");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
exp = std::stol( exp_str );
|
||||||
|
}
|
||||||
|
else if( offset < str.size() && (!isspace( c ) && c != ',' && c != ']' && c != '}' )) {
|
||||||
|
throw std::runtime_error(std::string("JSON ERROR: Number: unexpected character '") + c + "'");
|
||||||
|
}
|
||||||
|
--offset;
|
||||||
|
|
||||||
|
if( isDouble )
|
||||||
|
Number = std::stod( val ) * std::pow( 10, exp );
|
||||||
|
else {
|
||||||
|
if( !exp_str.empty() )
|
||||||
|
Number = std::stol( val ) * std::pow( 10, exp );
|
||||||
|
else
|
||||||
|
Number = std::stol( val );
|
||||||
|
}
|
||||||
|
return Number;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON parse_bool( const string &str, size_t &offset ) {
|
||||||
|
JSON Bool;
|
||||||
|
if( str.substr( offset, 4 ) == "true" ) {
|
||||||
|
offset += 4;
|
||||||
|
Bool = true;
|
||||||
|
} else if( str.substr( offset, 5 ) == "false" ) {
|
||||||
|
offset += 5;
|
||||||
|
Bool = false;
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error(std::string("JSON ERROR: Bool: Expected 'true' or 'false', found '") + str.substr( offset, 5 ) + "'");
|
||||||
|
}
|
||||||
|
return Bool;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON parse_null( const string &str, size_t &offset ) {
|
||||||
|
if( str.substr( offset, 4 ) != "null" ) {
|
||||||
|
throw std::runtime_error(std::string("JSON ERROR: Null: Expected 'null', found '") + str.substr( offset, 4 ) + "'");
|
||||||
|
}
|
||||||
|
offset += 4;
|
||||||
|
return JSON();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON parse_next( const string &str, size_t &offset ) {
|
||||||
|
char value;
|
||||||
|
consume_ws( str, offset );
|
||||||
|
value = str[offset];
|
||||||
|
switch( value ) {
|
||||||
|
case '[' : return parse_array( str, offset );
|
||||||
|
case '{' : return parse_object( str, offset );
|
||||||
|
case '\"': return parse_string( str, offset );
|
||||||
|
case 't' :
|
||||||
|
case 'f' : return parse_bool( str, offset );
|
||||||
|
case 'n' : return parse_null( str, offset );
|
||||||
|
default : if( ( value <= '9' && value >= '0' ) || value == '-' )
|
||||||
|
return parse_number( str, offset );
|
||||||
|
}
|
||||||
|
throw std::runtime_error(std::string("JSON ERROR: Parse: Unexpected starting character '") + value + "'");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSON JSON::Load( const string &str ) {
|
||||||
|
size_t offset = 0;
|
||||||
|
return parse_next( str, offset );
|
||||||
|
}
|
||||||
|
|
||||||
|
} // End Namespace json
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
158
include/chaiscript/utility/json_wrap.hpp
Normal file
158
include/chaiscript/utility/json_wrap.hpp
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
#ifndef CHAISCRIPT_SIMPLEJSON_WRAP_HPP
|
||||||
|
#define CHAISCRIPT_SIMPLEJSON_WRAP_HPP
|
||||||
|
|
||||||
|
#include "json.hpp"
|
||||||
|
|
||||||
|
namespace chaiscript
|
||||||
|
{
|
||||||
|
class json_wrap
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
static ModulePtr library(ModulePtr m = std::make_shared<Module>())
|
||||||
|
{
|
||||||
|
|
||||||
|
m->add(chaiscript::fun([](const std::string &t_str) { return json_wrap::from_json(t_str); }), "from_json");
|
||||||
|
m->add(chaiscript::fun(&json_wrap::to_json), "to_json");
|
||||||
|
|
||||||
|
return m;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static Boxed_Value from_json(const json::JSON &t_json)
|
||||||
|
{
|
||||||
|
switch( t_json.JSONType() ) {
|
||||||
|
case json::JSON::Class::Null:
|
||||||
|
return Boxed_Value();
|
||||||
|
case json::JSON::Class::Object:
|
||||||
|
{
|
||||||
|
std::map<std::string, Boxed_Value> m;
|
||||||
|
|
||||||
|
for (const auto &p : t_json.ObjectRange())
|
||||||
|
{
|
||||||
|
m.insert(std::make_pair(p.first, from_json(p.second)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Boxed_Value(m);
|
||||||
|
}
|
||||||
|
case json::JSON::Class::Array:
|
||||||
|
{
|
||||||
|
std::vector<Boxed_Value> vec;
|
||||||
|
|
||||||
|
for (const auto &p : t_json.ArrayRange())
|
||||||
|
{
|
||||||
|
vec.emplace_back(from_json(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Boxed_Value(vec);
|
||||||
|
}
|
||||||
|
case json::JSON::Class::String:
|
||||||
|
return Boxed_Value(t_json.ToString());
|
||||||
|
case json::JSON::Class::Floating:
|
||||||
|
return Boxed_Value(t_json.ToFloat());
|
||||||
|
case json::JSON::Class::Integral:
|
||||||
|
return Boxed_Value(t_json.ToInt());
|
||||||
|
case json::JSON::Class::Boolean:
|
||||||
|
return Boxed_Value(t_json.ToBool());
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Unknown JSON type");
|
||||||
|
}
|
||||||
|
|
||||||
|
static Boxed_Value from_json(const std::string &t_json)
|
||||||
|
{
|
||||||
|
return from_json( json::JSON::Load(t_json) );
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string to_json(const Boxed_Value &t_bv)
|
||||||
|
{
|
||||||
|
return to_json_object(t_bv).dump();
|
||||||
|
}
|
||||||
|
|
||||||
|
static json::JSON to_json_object(const Boxed_Value &t_bv)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
const std::map<std::string, Boxed_Value> m = chaiscript::boxed_cast<const std::map<std::string, Boxed_Value> &>(t_bv);
|
||||||
|
|
||||||
|
json::JSON obj;
|
||||||
|
for (const auto &o : m)
|
||||||
|
{
|
||||||
|
obj[o.first] = to_json_object(o.second);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
} catch (const chaiscript::exception::bad_boxed_cast &) {
|
||||||
|
// not a map
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const std::vector<Boxed_Value> v = chaiscript::boxed_cast<const std::vector<Boxed_Value> &>(t_bv);
|
||||||
|
|
||||||
|
json::JSON obj;
|
||||||
|
for (size_t i = 0; i < v.size(); ++i)
|
||||||
|
{
|
||||||
|
obj[i] = to_json_object(v[i]);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
} catch (const chaiscript::exception::bad_boxed_cast &) {
|
||||||
|
// not a vector
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
Boxed_Number bn(t_bv);
|
||||||
|
json::JSON obj;
|
||||||
|
if (Boxed_Number::is_floating_point(t_bv))
|
||||||
|
{
|
||||||
|
obj = bn.get_as<double>();
|
||||||
|
} else {
|
||||||
|
obj = bn.get_as<long>();
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
} catch (const chaiscript::detail::exception::bad_any_cast &) {
|
||||||
|
// not a number
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
bool b = boxed_cast<bool>(t_bv);
|
||||||
|
json::JSON obj;
|
||||||
|
obj = b;
|
||||||
|
return obj;
|
||||||
|
} catch (const chaiscript::exception::bad_boxed_cast &) {
|
||||||
|
// not a bool
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::string s = boxed_cast<std::string>(t_bv);
|
||||||
|
json::JSON obj;
|
||||||
|
obj = s;
|
||||||
|
return obj;
|
||||||
|
} catch (const chaiscript::exception::bad_boxed_cast &) {
|
||||||
|
// not a string
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
const chaiscript::dispatch::Dynamic_Object &o = boxed_cast<const dispatch::Dynamic_Object &>(t_bv);
|
||||||
|
|
||||||
|
json::JSON obj;
|
||||||
|
for (const auto &attr : o.get_attrs())
|
||||||
|
{
|
||||||
|
obj[attr.first] = to_json_object(attr.second);
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
} catch (const chaiscript::exception::bad_boxed_cast &) {
|
||||||
|
// not a dynamic object
|
||||||
|
}
|
||||||
|
|
||||||
|
throw std::runtime_error("Unknown object type to convert to JSON");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
3
unittests/json_1.chai
Normal file
3
unittests/json_1.chai
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
|
assert_true(from_json("null").is_var_null())
|
1
unittests/json_10.chai
Normal file
1
unittests/json_10.chai
Normal file
@@ -0,0 +1 @@
|
|||||||
|
assert_equal(from_json("\"This is a\\n\\nMultiline string\""), "This is a\n\nMultiline string")
|
14
unittests/json_11.chai
Normal file
14
unittests/json_11.chai
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
assert_equal(from_json(
|
||||||
|
"{\n" +
|
||||||
|
" \"T1\" : \"Value With a Quote : \\\"\",\n" +
|
||||||
|
" \"T2\" : \"Value With a Rev Solidus : \\/\",\n" +
|
||||||
|
" \"T3\" : \"Value with a Solidus : \\\\\",\n" +
|
||||||
|
" \"T4\" : \"Value with a Backspace : \\b\",\n" +
|
||||||
|
" \"T5\" : \"Value with a Formfeed : \\f\",\n" +
|
||||||
|
" \"T6\" : \"Value with a Newline : \\n\",\n" +
|
||||||
|
" \"T7\" : \"Value with a Carriage Return : \\r\",\n" +
|
||||||
|
" \"T8\" : \"Value with a Horizontal Tab : \\t\"\n" +
|
||||||
|
"}"), [ "T1" : "Value With a Quote : \"", "T2" : "Value With a Rev Solidus : /", "T3" : "Value with a Solidus : \\", "T4" : "Value with a Backspace : \b", "T5" : "Value with a Formfeed : \f", "T6" : "Value with a Newline : \n", "T7" : "Value with a Carriage Return : \r", "T8" : "Value with a Horizontal Tab : \t" ]);
|
||||||
|
|
||||||
|
|
||||||
|
|
1
unittests/json_12.chai
Normal file
1
unittests/json_12.chai
Normal file
@@ -0,0 +1 @@
|
|||||||
|
assert_equal(from_json("\"\""), "")
|
1
unittests/json_13.chai
Normal file
1
unittests/json_13.chai
Normal file
@@ -0,0 +1 @@
|
|||||||
|
assert_equal(from_json("1.20E+2"), 1.20e2)
|
3
unittests/json_2.chai
Normal file
3
unittests/json_2.chai
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
|
||||||
|
assert_true(from_json("true"))
|
||||||
|
|
1
unittests/json_3.chai
Normal file
1
unittests/json_3.chai
Normal file
@@ -0,0 +1 @@
|
|||||||
|
assert_equal(from_json("100"), 100)
|
1
unittests/json_4.chai
Normal file
1
unittests/json_4.chai
Normal file
@@ -0,0 +1 @@
|
|||||||
|
assert_equal(from_json("1.234"), 1.234)
|
1
unittests/json_5.chai
Normal file
1
unittests/json_5.chai
Normal file
@@ -0,0 +1 @@
|
|||||||
|
assert_equal(from_json("\"StringTest\""), "StringTest")
|
1
unittests/json_6.chai
Normal file
1
unittests/json_6.chai
Normal file
@@ -0,0 +1 @@
|
|||||||
|
assert_equal(from_json("{}"), Map())
|
4
unittests/json_7.chai
Normal file
4
unittests/json_7.chai
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
assert_equal(from_json("\n" +
|
||||||
|
"{\n" +
|
||||||
|
" \"Key\" : \"Value\"\n" +
|
||||||
|
"}\n"), ["Key":"Value"])
|
1
unittests/json_8.chai
Normal file
1
unittests/json_8.chai
Normal file
@@ -0,0 +1 @@
|
|||||||
|
assert_equal(from_json("[]"), [])
|
2
unittests/json_9.chai
Normal file
2
unittests/json_9.chai
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
assert_equal(from_json("[1,2,3]"), [1,2,3])
|
||||||
|
|
Reference in New Issue
Block a user