json: split json impl files into tree and flat

This commit is contained in:
Danny Robson 2015-02-03 00:07:09 +11:00
parent 81a7ff5db5
commit 60787655b3
15 changed files with 650 additions and 564 deletions

View File

@ -71,8 +71,12 @@ UTIL_FILES = \
ip.cpp \ ip.cpp \
ip.hpp \ ip.hpp \
iterator.hpp \ iterator.hpp \
json.cpp \ json/except.cpp \
json.hpp \ json/except.hpp \
json/flat.cpp \
json/flat.hpp \
json/tree.cpp \
json/tree.hpp \
lerp.cpp \ lerp.cpp \
lerp.hpp \ lerp.hpp \
log.cpp \ log.cpp \

View File

@ -22,7 +22,7 @@
#include "detail/coord.hpp" #include "detail/coord.hpp"
#include "json.hpp" #include "json/tree.hpp"
#include <iostream> #include <iostream>

359
json.hpp
View File

@ -1,359 +0,0 @@
/*
* This file is part of libgim.
*
* libgim is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libgim is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with libgim. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010-2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_JSON_HPP
#define __UTIL_JSON_HPP
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <boost/filesystem.hpp>
#include "iterator.hpp"
namespace json {
namespace tree {
class node;
class object;
class array;
class string;
class number;
class boolean;
class null;
/// Parse an encoded form of JSON into a tree structure
extern std::unique_ptr<node> parse (const boost::filesystem::path &path);
extern std::unique_ptr<node> parse (const char *start, const char *stop);
extern std::unique_ptr<node> parse (const char *start);
extern std::unique_ptr<node> parse (const std::string&);
extern void write (const json::tree::node&, std::ostream&);
/// Abstract base for all JSON values
class node {
public:
virtual ~node () { ; }
virtual const object& as_object (void) const;
virtual const array& as_array (void) const;
virtual const string& as_string (void) const;
virtual const number& as_number (void) const;
virtual const boolean& as_boolean (void) const;
virtual const null& as_null (void) const;
virtual bool is_object (void) const { return false; }
virtual bool is_array (void) const { return false; }
virtual bool is_string (void) const { return false; }
virtual bool is_number (void) const { return false; }
virtual bool is_boolean (void) const { return false; }
virtual bool is_null (void) const { return false; }
virtual bool operator==(const node &rhs) const = 0;
virtual bool operator!=(const node &rhs) const;
virtual bool operator==(const object &) const { return false; }
virtual bool operator==(const array &) const { return false; }
virtual bool operator==(const string &) const { return false; }
virtual bool operator==(const number &) const { return false; }
virtual bool operator==(const boolean &) const { return false; }
virtual bool operator==(const null &) const { return false; }
virtual bool operator==(const char *rhs) const;
virtual bool operator!=(const char *rhs) const { return !(*this == rhs); }
virtual const node& operator[] (const std::string&) const;
virtual const node& operator[] (unsigned int) const;
virtual std::ostream& write (std::ostream &os) const = 0;
};
/// Represents a JSON object, and contains its children.
class object : public node {
protected:
typedef std::map<std::string, std::unique_ptr<node>> value_store;
public:
typedef value_store::iterator iterator;
typedef value_store::const_iterator const_iterator;
protected:
value_store m_values;
public:
virtual ~object ();
virtual const object& as_object (void) const { return *this; }
virtual bool is_object (void) const { return true; }
virtual bool operator==(const object &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
virtual void insert (const std::string &key, std::unique_ptr<node>&& value);
virtual const node& operator[](const std::string &key) const;
virtual bool has (const std::string&) const;
virtual void clear (void);
virtual void erase (const std::string &key);
virtual const_iterator begin (void) const;
virtual const_iterator end (void) const;
virtual std::ostream& write (std::ostream &os) const;
};
/// Represents a JSON array, and contains its children.
class array : public node {
protected:
typedef std::vector<std::unique_ptr<node>>::iterator pointer_array_iterator;
typedef std::vector<std::unique_ptr<node>>::const_iterator const_pointer_array_iterator;
public:
typedef referencing_iterator<pointer_array_iterator> iterator;
typedef referencing_iterator<const_pointer_array_iterator> const_iterator;
protected:
std::vector<std::unique_ptr<node>> m_values;
public:
virtual ~array();
virtual const array& as_array (void) const { return *this; }
virtual bool is_array (void) const { return true; }
virtual bool operator==(const array &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
virtual size_t size (void) const
{ return m_values.size (); }
virtual node& operator [](unsigned int idx)
{ return *m_values[idx]; }
virtual const node& operator [](unsigned int idx) const
{ return *m_values[idx]; }
virtual const_iterator begin (void) const { return const_iterator (m_values.begin ()); }
virtual const_iterator end (void) const { return const_iterator (m_values.end ()); }
virtual void insert (std::unique_ptr<json::tree::node> &&_value);
virtual std::ostream& write (std::ostream &os) const;
};
/// Represents a JSON string literal.
class string : public node {
protected:
std::string m_value;
public:
explicit string (const std::string &_value): m_value (_value) { ; }
explicit string (const char *_value): m_value (_value) { ; }
string (const char *_first, const char *_last): m_value (_first, _last) { ; }
virtual const string& as_string (void) const { return *this; }
virtual bool is_string (void) const { return true; }
virtual bool operator==(const char *rhs) const;
virtual bool operator==(const string &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
virtual size_t size (void) const { return m_value.size (); }
operator const std::string&(void) const { return m_value; }
const std::string& native (void) const { return m_value; }
virtual std::ostream& write (std::ostream &os) const;
};
/// Represents a JSON integer/float literal.
class number : public node {
protected:
double m_value;
public:
explicit number (double _value): m_value (_value) { ; }
explicit number (int _value): m_value (_value) { ; }
explicit number (size_t _value): m_value (_value) { ; }
virtual const number& as_number (void) const { return *this; }
virtual bool is_number (void) const { return true; }
virtual bool operator==(const number &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
operator double(void) const { return m_value; }
double native (void) const { return m_value; }
virtual std::ostream& write (std::ostream &os) const;
};
/// Represents a JSON boolean literal.
class boolean : public node {
protected:
bool m_value;
public:
explicit boolean (bool _value): m_value (_value) { ; }
virtual const boolean& as_boolean (void) const { return *this; }
virtual bool is_boolean (void) const { return true; }
virtual bool operator==(const boolean &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
operator bool (void) const { return m_value; }
bool native (void) const { return m_value; }
virtual std::ostream& write (std::ostream &os) const;
};
/// Represents a JSON null value.
class null : public node {
public:
virtual bool operator==(const null&) const { return true; }
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
virtual bool is_null (void) const { return true; }
virtual const null& as_null (void) const { return *this; }
virtual std::ostream& write (std::ostream &os) const;
};
/// The base class for all exceptions throw directly by the json namespace.
class error : public std::runtime_error {
public:
error (const std::string &_what):
std::runtime_error (_what)
{ ; }
};
/// Base class for all type conversion errors
class type_error : public error {
public:
type_error (const std::string &_what):
error (_what)
{ ; }
};
/// Base class for errors thrown during parsing
class parse_error : public error {
public:
parse_error (const std::string &_what):
error (_what)
{ ; }
};
/// Base class for errors thrown during schema validation
class schema_error : public error {
public:
schema_error (const std::string &_what):
error (_what)
{ ; }
};
std::ostream&
operator <<(std::ostream &os, const json::tree::node &n);
// Instantiate this template for the type you wish to output. We use a
// helper class here to avoid partial template specialisation of a
// function (eg, for templated types).
template <typename T>
struct io {
static std::unique_ptr<json::tree::node> serialise (const T&);
static T deserialise (const json::tree::node&);
};
}
namespace flat {
enum class type {
UNKNOWN,
NUL,
BOOLEAN,
STRING,
INTEGER,
REAL,
OBJECT_BEGIN,
OBJECT_END,
ARRAY_BEGIN,
ARRAY_END
};
struct item {
type tag;
const char *first;
const char *last;
template <typename T>
T as (void) const;
};
std::vector<item> parse (const char *first, const char *last);
std::vector<item> parse (const char *first);
std::vector<item> parse (const boost::filesystem::path&);
//-----------------------------------------------------------------------------
struct error : public std::runtime_error {
error (const std::string &&_what):
runtime_error (std::move (_what))
{ ; }
};
struct parse_error : public error {
parse_error (size_t _line, const std::string &&_what):
error (std::move (_what)),
line (_line)
{ ; }
size_t line;
};
struct value_error : public error {
value_error (const std::string &&_what):
error (std::move (_what))
{ ; }
};
}
}
template <typename T, class ...Args>
std::unique_ptr<json::tree::node> to_json (const T &t, Args&&... args) {
return json::tree::io<T>::serialise (t, std::forward<Args>(args)...);
}
template <typename T, class ...Args>
T from_json (const json::tree::node &n, Args&&... args) {
return json::tree::io<T>::deserialise (n, std::forward<Args>(args)...);
}
#endif

28
json/except.cpp Normal file
View File

@ -0,0 +1,28 @@
/*
* This file is part of libgim.
*
* libgim is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libgim is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with libgim. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#include "except.hpp"
//-----------------------------------------------------------------------------
json::parse_error::parse_error (const std::string &_what, size_t _line):
error (_what),
line (_line)
{ ; }

54
json/except.hpp Normal file
View File

@ -0,0 +1,54 @@
/*
* This file is part of libgim.
*
* libgim is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libgim is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with libgim. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_JSON_EXCEPT_HPP
#define __UTIL_JSON_EXCEPT_HPP
#include <stdexcept>
#include <string>
#include <cstdlib>
namespace json {
/// The base class for all exceptions throw directly by the json namespace.
struct error : public std::runtime_error {
using runtime_error::runtime_error;
};
/// Base class for all type conversion errors
struct type_error : public error {
using error::error;
};
/// Base class for errors thrown during parsing
struct parse_error : public error {
using error::error;
parse_error (const std::string &_what, size_t _line = 0);
size_t line;
};
/// Base class for errors thrown during schema validation
struct schema_error : public error {
using error::error;
};
}
#endif

199
json/flat.cpp.rl Normal file
View File

@ -0,0 +1,199 @@
/*
* This file is part of libgim.
*
* libgim is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libgim is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with libgim. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010-2015 Danny Robson <danny@nerdcruft.net>
*/
#include "json/flat.hpp"
#include "json/except.hpp"
#include "io.hpp"
#include <deque>
//-----------------------------------------------------------------------------
%%{
# JSON (rfc7159)
machine json;
action trace { if (false) std::cerr << *p; }
action success { __success = true; }
action failure { }
action new_line { ++line; }
action first { parsed.push_back ({ type::UNKNOWN, p, p}); }
action last { parsed.back ().last = p; }
action tag_nul { parsed.back ().tag = type::NUL; }
action tag_boolean { parsed.back ().tag = type::BOOLEAN; }
action tag_string { parsed.back ().tag = type::STRING; }
action tag_integer { parsed.back ().tag = type::INTEGER; }
action tag_real { parsed.back ().tag = type::REAL; }
action tag_object_begin { parsed.push_back ({ type::OBJECT_BEGIN, p, p + 1 }); }
action tag_object_end { parsed.push_back ({ type::OBJECT_END, p, p + 1 }); }
action tag_array_begin { parsed.push_back ({ type::ARRAY_BEGIN, p, p + 1 }); }
action tag_array_end { parsed.push_back ({ type::ARRAY_END, p, p + 1 }); }
# Line counter
lines = (
any | '\n' @new_line
)*;
# UTF-8 (rfc3629)
utf8_tail = 0x80..0xbf;
utf8_1 = 0x00..0x7f;
utf8_2 = 0xc2..0xdf utf8_tail;
utf8_3 = 0xe0 0xa0..0xbf utf8_tail |
0xe1..0xec utf8_tail{2} |
0xed 0x80..0x9f utf8_tail |
0xee..0xef utf8_tail{2};
utf8_4 = 0xf0 0x90..0xbf utf8_tail{2} |
0xf1..0xf3 utf8_tail{3} |
0xf4 0x80..0x8f utf8_tail{2};
utf8 = utf8_1 | utf8_2 | utf8_3 | utf8_4;
# Utility
ws = 0x20 | 0x09 | 0x0A | 0x0D;
array_start = '[';
array_end = ']';
object_start = '{';
object_end = '}';
# Strings
char =
(utf8 - ["\\])
| "\\" (
[\\"/bfnrt]
| "u" xdigit{4}
)
;
string = ('"' char* '"') >first >tag_string %*last;
# numbers
int = '0' | [1-9] digit*;
frac = '.' digit+;
e = 'e'i[+\-]?;
exp = e digit+;
number = (
'-'?
int
(frac >tag_real)?
exp?
) >tag_integer;
# wrapper types
array = array_start @{ fhold; fcall array_members; } array_end;
object = object_start @{ fhold; fcall object_members; } object_end;
# simple types; case sensitive literals
bool = ("true" | "false") >tag_boolean;
nul = "null" >tag_nul;
literal = bool | nul;
value = object | array | (number | string | literal) >first %last;
# Complex
member = string ws* ':' ws* value;
array_members := ((
array_start >tag_array_begin ws* (value ws* (',' ws* value ws*)*)? array_end >tag_array_end
) & lines)
@{ fhold; fret; } $trace $!failure;
object_members := ((
object_start >tag_object_begin ws* (member ws* (',' ws* member ws*)*)? object_end >tag_object_end
) & lines)
@{ fhold; fret; } $trace $!failure;
# meta types
document := ((ws* value ws*) & lines)
%success
$!failure
$trace;
variable stack ragelstack;
prepush { ragelstack.push_back (0); }
postpop { ragelstack.pop_back (); }
write data;
}%%
//-----------------------------------------------------------------------------
std::vector<json::flat::item>
json::flat::parse (const char *first, const char *last)
{
const char *p = first;
const char *pe = last;
const char *eof = pe;
std::deque<int> ragelstack;
std::vector<item> parsed;
size_t line = 0;
int cs, top;
bool __success = false;
%%write init;
%%write exec;
if (!__success)
throw json::parse_error ("parse error", line);
return parsed;
}
//-----------------------------------------------------------------------------
std::vector<json::flat::item>
json::flat::parse (const boost::filesystem::path &path)
{
util::mapped_file f (path);
return parse ((const char *)f.cbegin (), (const char*)f.cend ());
}
//-----------------------------------------------------------------------------
std::ostream&
json::flat::operator<< (std::ostream &os, json::flat::type t)
{
switch (t) {
case json::flat::type::STRING: os << "STRING"; break;
case json::flat::type::NUL: os << "NUL"; break;
case json::flat::type::BOOLEAN: os << "BOOLEAN"; break;
case json::flat::type::INTEGER: os << "INTEGER"; break;
case json::flat::type::REAL: os << "REAL"; break;
case json::flat::type::OBJECT_BEGIN: os << "OBJECT_BEGIN"; break;
case json::flat::type::OBJECT_END: os << "OBJECT_END"; break;
case json::flat::type::ARRAY_BEGIN: os << "ARRAY_BEGIN"; break;
case json::flat::type::ARRAY_END: os << "ARRAY_END"; break;
default:
unreachable ();
}
return os;
}

59
json/flat.hpp Normal file
View File

@ -0,0 +1,59 @@
/*
* This file is part of libgim.
*
* libgim is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libgim is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with libgim. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010-2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_JSON_FLAT_HPP
#define __UTIL_JSON_FLAT_HPP
#include <boost/filesystem/path.hpp>
#include <iostream>
namespace json { namespace flat {
enum class type {
UNKNOWN,
NUL,
BOOLEAN,
STRING,
INTEGER,
REAL,
OBJECT_BEGIN,
OBJECT_END,
ARRAY_BEGIN,
ARRAY_END
};
struct item {
type tag;
const char *first;
const char *last;
template <typename T>
T as (void) const;
};
std::vector<item> parse (const char *first, const char *last);
std::vector<item> parse (const char *first);
std::vector<item> parse (const boost::filesystem::path&);
std::ostream& operator<< (std::ostream&, type);
} }
#endif

View File

@ -18,7 +18,10 @@
*/ */
#include "json.hpp" #include "json/tree.hpp"
#include "json/except.hpp"
#include "json/flat.hpp"
#include "debug.hpp" #include "debug.hpp"
#include "io.hpp" #include "io.hpp"
@ -40,157 +43,6 @@
using namespace std; using namespace std;
using namespace util; using namespace util;
//-----------------------------------------------------------------------------
%%{
# JSON (rfc7159)
machine json;
action trace { if (false) std::cerr << *p; }
action success { __success = true; }
action failure { }
action new_line { ++line; }
action first { parsed.push_back ({ type::UNKNOWN, p, p}); }
action last { parsed.back ().last = p; }
action tag_nul { parsed.back ().tag = type::NUL; }
action tag_boolean { parsed.back ().tag = type::BOOLEAN; }
action tag_string { parsed.back ().tag = type::STRING; }
action tag_integer { parsed.back ().tag = type::INTEGER; }
action tag_real { parsed.back ().tag = type::REAL; }
action tag_object_begin { parsed.push_back ({ type::OBJECT_BEGIN, p, p + 1 }); }
action tag_object_end { parsed.push_back ({ type::OBJECT_END, p, p + 1 }); }
action tag_array_begin { parsed.push_back ({ type::ARRAY_BEGIN, p, p + 1 }); }
action tag_array_end { parsed.push_back ({ type::ARRAY_END, p, p + 1 }); }
# Line counter
lines = (
any | '\n' @new_line
)*;
# UTF-8 (rfc3629)
utf8_tail = 0x80..0xbf;
utf8_1 = 0x00..0x7f;
utf8_2 = 0xc2..0xdf utf8_tail;
utf8_3 = 0xe0 0xa0..0xbf utf8_tail |
0xe1..0xec utf8_tail{2} |
0xed 0x80..0x9f utf8_tail |
0xee..0xef utf8_tail{2};
utf8_4 = 0xf0 0x90..0xbf utf8_tail{2} |
0xf1..0xf3 utf8_tail{3} |
0xf4 0x80..0x8f utf8_tail{2};
utf8 = utf8_1 | utf8_2 | utf8_3 | utf8_4;
# Utility
ws = 0x20 | 0x09 | 0x0A | 0x0D;
array_start = '[';
array_end = ']';
object_start = '{';
object_end = '}';
# Strings
char =
(utf8 - ["\\])
| "\\" (
[\\"/bfnrt]
| "u" xdigit{4}
)
;
string = ('"' char* '"') >first >tag_string %*last;
# numbers
int = '0' | [1-9] digit*;
frac = '.' digit+;
e = 'e'i[+\-]?;
exp = e digit+;
number = (
'-'?
int
(frac >tag_real)?
exp?
) >tag_integer;
# wrapper types
array = array_start @{ fhold; fcall array_members; } array_end;
object = object_start @{ fhold; fcall object_members; } object_end;
# simple types; case sensitive literals
bool = ("true" | "false") >tag_boolean;
nul = "null" >tag_nul;
literal = bool | nul;
value = object | array | (number | string | literal) >first %last;
# Complex
member = string ws* ':' ws* value;
array_members := ((
array_start >tag_array_begin ws* (value ws* (',' ws* value ws*)*)? array_end >tag_array_end
) & lines)
@{ fhold; fret; } $trace $!failure;
object_members := ((
object_start >tag_object_begin ws* (member ws* (',' ws* member ws*)*)? object_end >tag_object_end
) & lines)
@{ fhold; fret; } $trace $!failure;
# meta types
document := ((ws* value ws*) & lines)
%success
$!failure
$trace;
variable stack ragelstack;
prepush { ragelstack.push_back (0); }
postpop { ragelstack.pop_back (); }
write data;
}%%
std::ostream& operator<< (std::ostream &os, json::flat::type);
//-----------------------------------------------------------------------------
std::vector<json::flat::item>
json::flat::parse (const char *first, const char *last)
{
const char *p = first;
const char *pe = last;
const char *eof = pe;
std::deque<int> ragelstack;
std::vector<item> parsed;
size_t line = 0;
int cs, top;
bool __success = false;
%%write init;
%%write exec;
if (!__success)
throw parse_error (line, "parse error");
return parsed;
}
std::vector<json::flat::item>
json::flat::parse (const boost::filesystem::path &path)
{
util::mapped_file f (path);
return parse ((const char *)f.cbegin (), (const char*)f.cend ());
}
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// External support // External support
@ -355,32 +207,32 @@ json::tree::parse (const char *first, const char *last)
const json::tree::object& const json::tree::object&
json::tree::node::as_object (void) const json::tree::node::as_object (void) const
{ throw type_error ("node is not an object"); } { throw json::type_error ("node is not an object"); }
const json::tree::array& const json::tree::array&
json::tree::node::as_array (void) const json::tree::node::as_array (void) const
{ throw type_error ("node is not an array"); } { throw json::type_error ("node is not an array"); }
const json::tree::string& const json::tree::string&
json::tree::node::as_string (void) const json::tree::node::as_string (void) const
{ throw type_error ("node is not a string"); } { throw json::type_error ("node is not a string"); }
const json::tree::number& const json::tree::number&
json::tree::node::as_number (void) const json::tree::node::as_number (void) const
{ throw type_error ("node is not a number"); } { throw json::type_error ("node is not a number"); }
const json::tree::boolean& const json::tree::boolean&
json::tree::node::as_boolean (void) const json::tree::node::as_boolean (void) const
{ throw type_error ("node is not a boolean"); } { throw json::type_error ("node is not a boolean"); }
const json::tree::null& const json::tree::null&
json::tree::node::as_null (void) const json::tree::node::as_null (void) const
{ throw type_error ("node is not a null"); } { throw json::type_error ("node is not a null"); }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
@ -394,7 +246,7 @@ json::tree::node::operator!= (const node &rhs) const
bool json::tree::node::operator==(const char *rhs) const { bool json::tree::node::operator==(const char *rhs) const {
try { try {
return as_string ().native () == rhs; return as_string ().native () == rhs;
} catch (const json::tree::type_error&) { } catch (const json::type_error&) {
return false; return false;
} }
} }
@ -444,7 +296,7 @@ json::tree::object::operator[](const std::string &key) const {
if (value == m_values.end ()) { if (value == m_values.end ()) {
ostringstream ss; ostringstream ss;
ss << "no key: " << key; ss << "no key: " << key;
throw json::tree::error (ss.str()); throw json::error (ss.str());
} }
return *value->second; return *value->second;
@ -466,7 +318,7 @@ void
json::tree::object::erase (const std::string &key) { json::tree::object::erase (const std::string &key) {
auto pos = m_values.find (key); auto pos = m_values.find (key);
if (pos == m_values.end ()) if (pos == m_values.end ())
throw json::tree::error ("erasing invalid key"); throw json::error ("erasing invalid key");
m_values.erase (key); m_values.erase (key);
} }
@ -649,27 +501,3 @@ namespace json { namespace tree {
return std::unique_ptr<node> (new number (f)); return std::unique_ptr<node> (new number (f));
} }
} } } }
//-----------------------------------------------------------------------------
std::ostream&
operator<< (std::ostream &os, json::flat::type t)
{
switch (t) {
case json::flat::type::STRING: os << "STRING"; break;
case json::flat::type::NUL: os << "NUL"; break;
case json::flat::type::BOOLEAN: os << "BOOLEAN"; break;
case json::flat::type::INTEGER: os << "INTEGER"; break;
case json::flat::type::REAL: os << "REAL"; break;
case json::flat::type::OBJECT_BEGIN: os << "OBJECT_BEGIN"; break;
case json::flat::type::OBJECT_END: os << "OBJECT_END"; break;
case json::flat::type::ARRAY_BEGIN: os << "ARRAY_BEGIN"; break;
case json::flat::type::ARRAY_END: os << "ARRAY_END"; break;
default:
unreachable ();
}
return os;
}

271
json/tree.hpp Normal file
View File

@ -0,0 +1,271 @@
/*
* This file is part of libgim.
*
* libgim is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later
* version.
*
* libgim is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License
* along with libgim. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright 2010-2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_JSON_TREE_HPP
#define __UTIL_JSON_TREE_HPP
#include <iostream>
#include <map>
#include <memory>
#include <string>
#include <vector>
#include <boost/filesystem.hpp>
#include "iterator.hpp"
#include "json/flat.hpp"
namespace json { namespace tree {
class node;
class object;
class array;
class string;
class number;
class boolean;
class null;
/// Parse an encoded form of JSON into a tree structure
extern std::unique_ptr<node> parse (const boost::filesystem::path &path);
extern std::unique_ptr<node> parse (const char *start, const char *stop);
extern std::unique_ptr<node> parse (const char *start);
extern std::unique_ptr<node> parse (const std::string&);
extern void write (const json::tree::node&, std::ostream&);
/// Abstract base for all JSON values
class node {
public:
virtual ~node () { ; }
virtual const object& as_object (void) const;
virtual const array& as_array (void) const;
virtual const string& as_string (void) const;
virtual const number& as_number (void) const;
virtual const boolean& as_boolean (void) const;
virtual const null& as_null (void) const;
virtual bool is_object (void) const { return false; }
virtual bool is_array (void) const { return false; }
virtual bool is_string (void) const { return false; }
virtual bool is_number (void) const { return false; }
virtual bool is_boolean (void) const { return false; }
virtual bool is_null (void) const { return false; }
virtual bool operator==(const node &rhs) const = 0;
virtual bool operator!=(const node &rhs) const;
virtual bool operator==(const object &) const { return false; }
virtual bool operator==(const array &) const { return false; }
virtual bool operator==(const string &) const { return false; }
virtual bool operator==(const number &) const { return false; }
virtual bool operator==(const boolean &) const { return false; }
virtual bool operator==(const null &) const { return false; }
virtual bool operator==(const char *rhs) const;
virtual bool operator!=(const char *rhs) const { return !(*this == rhs); }
virtual const node& operator[] (const std::string&) const;
virtual const node& operator[] (unsigned int) const;
virtual std::ostream& write (std::ostream &os) const = 0;
};
/// Represents a JSON object, and contains its children.
class object : public node {
protected:
typedef std::map<std::string, std::unique_ptr<node>> value_store;
public:
typedef value_store::iterator iterator;
typedef value_store::const_iterator const_iterator;
protected:
value_store m_values;
public:
virtual ~object ();
virtual const object& as_object (void) const { return *this; }
virtual bool is_object (void) const { return true; }
virtual bool operator==(const object &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
virtual void insert (const std::string &key, std::unique_ptr<node>&& value);
virtual const node& operator[](const std::string &key) const;
virtual bool has (const std::string&) const;
virtual void clear (void);
virtual void erase (const std::string &key);
virtual const_iterator begin (void) const;
virtual const_iterator end (void) const;
virtual std::ostream& write (std::ostream &os) const;
};
/// Represents a JSON array, and contains its children.
class array : public node {
protected:
typedef std::vector<std::unique_ptr<node>>::iterator pointer_array_iterator;
typedef std::vector<std::unique_ptr<node>>::const_iterator const_pointer_array_iterator;
public:
typedef referencing_iterator<pointer_array_iterator> iterator;
typedef referencing_iterator<const_pointer_array_iterator> const_iterator;
protected:
std::vector<std::unique_ptr<node>> m_values;
public:
virtual ~array();
virtual const array& as_array (void) const { return *this; }
virtual bool is_array (void) const { return true; }
virtual bool operator==(const array &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
virtual size_t size (void) const
{ return m_values.size (); }
virtual node& operator [](unsigned int idx)
{ return *m_values[idx]; }
virtual const node& operator [](unsigned int idx) const
{ return *m_values[idx]; }
virtual const_iterator begin (void) const { return const_iterator (m_values.begin ()); }
virtual const_iterator end (void) const { return const_iterator (m_values.end ()); }
virtual void insert (std::unique_ptr<json::tree::node> &&_value);
virtual std::ostream& write (std::ostream &os) const;
};
/// Represents a JSON string literal.
class string : public node {
protected:
std::string m_value;
public:
explicit string (const std::string &_value): m_value (_value) { ; }
explicit string (const char *_value): m_value (_value) { ; }
string (const char *_first, const char *_last): m_value (_first, _last) { ; }
virtual const string& as_string (void) const { return *this; }
virtual bool is_string (void) const { return true; }
virtual bool operator==(const char *rhs) const;
virtual bool operator==(const string &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
virtual size_t size (void) const { return m_value.size (); }
operator const std::string&(void) const { return m_value; }
const std::string& native (void) const { return m_value; }
virtual std::ostream& write (std::ostream &os) const;
};
/// Represents a JSON integer/float literal.
class number : public node {
protected:
double m_value;
public:
explicit number (double _value): m_value (_value) { ; }
explicit number (int _value): m_value (_value) { ; }
explicit number (size_t _value): m_value (_value) { ; }
virtual const number& as_number (void) const { return *this; }
virtual bool is_number (void) const { return true; }
virtual bool operator==(const number &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
operator double(void) const { return m_value; }
double native (void) const { return m_value; }
virtual std::ostream& write (std::ostream &os) const;
};
/// Represents a JSON boolean literal.
class boolean : public node {
protected:
bool m_value;
public:
explicit boolean (bool _value): m_value (_value) { ; }
virtual const boolean& as_boolean (void) const { return *this; }
virtual bool is_boolean (void) const { return true; }
virtual bool operator==(const boolean &rhs) const;
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
operator bool (void) const { return m_value; }
bool native (void) const { return m_value; }
virtual std::ostream& write (std::ostream &os) const;
};
/// Represents a JSON null value.
class null : public node {
public:
virtual bool operator==(const null&) const { return true; }
virtual bool operator==(const node &rhs) const
{ return rhs == *this; }
virtual bool is_null (void) const { return true; }
virtual const null& as_null (void) const { return *this; }
virtual std::ostream& write (std::ostream &os) const;
};
std::ostream&
operator <<(std::ostream &os, const json::tree::node &n);
// Instantiate this template for the type you wish to output. We use a
// helper class here to avoid partial template specialisation of a
// function (eg, for templated types).
template <typename T>
struct io {
static std::unique_ptr<json::tree::node> serialise (const T&);
static T deserialise (const json::tree::node&);
};
} }
template <typename T, class ...Args>
std::unique_ptr<json::tree::node> to_json (const T &t, Args&&... args) {
return json::tree::io<T>::serialise (t, std::forward<Args>(args)...);
}
template <typename T, class ...Args>
T from_json (const json::tree::node &n, Args&&... args) {
return json::tree::io<T>::deserialise (n, std::forward<Args>(args)...);
}
#endif

View File

@ -21,7 +21,7 @@
#include "range.hpp" #include "range.hpp"
#include "debug.hpp" #include "debug.hpp"
#include "json.hpp" #include "json/tree.hpp"
#include "maths.hpp" #include "maths.hpp"
#include <limits> #include <limits>

View File

@ -1,6 +1,6 @@
#include "../debug.hpp" #include "../debug.hpp"
#include "../json.hpp" #include "../json/tree.hpp"
#include "../maths.hpp" #include "../maths.hpp"
#include <memory> #include <memory>

View File

@ -17,7 +17,8 @@
* Copyright 2012 Danny Robson <danny@nerdcruft.net> * Copyright 2012 Danny Robson <danny@nerdcruft.net>
*/ */
#include "../json.hpp" #include "json/except.hpp"
#include "json/tree.hpp"
#include <cstdlib> #include <cstdlib>
#include <iostream> #include <iostream>
@ -49,7 +50,7 @@ main (int argc, char **argv) {
try { try {
boost::filesystem::path input (argv[ARG_INPUT]); boost::filesystem::path input (argv[ARG_INPUT]);
std::cout << *json::tree::parse (input) << "\n"; std::cout << *json::tree::parse (input) << "\n";
} catch (const json::tree::error& err) { } catch (const json::error& err) {
std::cerr << err.what () << "\n"; std::cerr << err.what () << "\n";
return EXIT_FAILURE; return EXIT_FAILURE;
} }

View File

@ -18,10 +18,11 @@
*/ */
#include "../json.hpp" #include "json/except.hpp"
#include "json/tree.hpp"
#include "../debug.hpp" #include "debug.hpp"
#include "../maths.hpp" #include "maths.hpp"
#include <cmath> #include <cmath>
#include <cstdlib> #include <cstdlib>
@ -82,7 +83,7 @@ is_type_valid (const json::tree::node &node,
} }
if (!type.is_string ()) if (!type.is_string ())
throw json::tree::schema_error ("schema type requires array, string, or object"); throw json::schema_error ("schema type requires array, string, or object");
static const auto ANY_VALIDATOR = [] (const json::tree::node &) { return true; }; static const auto ANY_VALIDATOR = [] (const json::tree::node &) { return true; };
static const auto INT_VALIDATOR = [] (const json::tree::node &n) { static const auto INT_VALIDATOR = [] (const json::tree::node &n) {
@ -120,7 +121,7 @@ bool
is_enum_valid (const json::tree::node &node, is_enum_valid (const json::tree::node &node,
const json::tree::node &constraint) { const json::tree::node &constraint) {
if (!constraint.is_array ()) if (!constraint.is_array ())
throw json::tree::schema_error ("enum validation requires an array"); throw json::schema_error ("enum validation requires an array");
const json::tree::array &valids = constraint.as_array (); const json::tree::array &valids = constraint.as_array ();
return valids.end () != std::find (valids.begin (), return valids.end () != std::find (valids.begin (),
@ -318,7 +319,7 @@ bool
is_max_items_valid (const json::tree::array &node, is_max_items_valid (const json::tree::array &node,
const json::tree::node &constraint) { const json::tree::node &constraint) {
if (!constraint.is_number () && is_integer (constraint.as_number ())) if (!constraint.is_number () && is_integer (constraint.as_number ()))
throw json::tree::schema_error ("max_items should be an integer"); throw json::schema_error ("max_items should be an integer");
return node.size () <= constraint.as_number (); return node.size () <= constraint.as_number ();
} }
@ -328,7 +329,7 @@ bool
is_min_items_valid (const json::tree::array &node, is_min_items_valid (const json::tree::array &node,
const json::tree::node &constraint) { const json::tree::node &constraint) {
if (!constraint.is_number () && is_integer (constraint.as_number ())) if (!constraint.is_number () && is_integer (constraint.as_number ()))
throw json::tree::schema_error ("min_items should be an integer"); throw json::schema_error ("min_items should be an integer");
return node.size () >= constraint.as_number (); return node.size () >= constraint.as_number ();
} }
@ -338,7 +339,7 @@ bool
is_unique_items_valid (const json::tree::array &node, is_unique_items_valid (const json::tree::array &node,
const json::tree::node &constraint) { const json::tree::node &constraint) {
if (!constraint.is_boolean ()) if (!constraint.is_boolean ())
throw json::tree::schema_error ("uniqueItems must be a boolean"); throw json::schema_error ("uniqueItems must be a boolean");
if (node.size () < 2) if (node.size () < 2)
return true; return true;
@ -357,7 +358,7 @@ bool
is_items_valid (const json::tree::array &node, is_items_valid (const json::tree::array &node,
const json::tree::node &_schema) { const json::tree::node &_schema) {
if (!_schema.is_object ()) if (!_schema.is_object ())
throw json::tree::schema_error ("array_items constraint must be an object"); throw json::schema_error ("array_items constraint must be an object");
const json::tree::object &schema = _schema.as_object (); const json::tree::object &schema = _schema.as_object ();
for (const json::tree::node &i: node) for (const json::tree::node &i: node)
@ -445,7 +446,7 @@ is_properties_valid (const json::tree::object &node,
CHECK (node.is_object ()); CHECK (node.is_object ());
if (!constraint.is_object ()) if (!constraint.is_object ())
throw json::tree::schema_error ("properties needs an object"); throw json::schema_error ("properties needs an object");
return is_properties_valid (node, constraint.as_object ()); return is_properties_valid (node, constraint.as_object ());
} }
@ -583,7 +584,7 @@ main (int argc, char **argv) {
try { try {
schema = json::tree::parse (boost::filesystem::path (argv[ARG_SCHEMA])); schema = json::tree::parse (boost::filesystem::path (argv[ARG_SCHEMA]));
input = json::tree::parse (boost::filesystem::path (argv[ARG_INPUT])); input = json::tree::parse (boost::filesystem::path (argv[ARG_INPUT]));
} catch (const json::tree::parse_error &err) { } catch (const json::parse_error &err) {
std::cerr << "malformed json for schema or input. " << err.what () << "\n"; std::cerr << "malformed json for schema or input. " << err.what () << "\n";
return EXIT_FAILURE; return EXIT_FAILURE;
} }

View File

@ -1,4 +1,4 @@
#include "../json.hpp" #include "json/flat.hpp"
#include <iostream> #include <iostream>
#include <cstdlib> #include <cstdlib>

View File

@ -20,7 +20,7 @@
#ifndef __UTIL_VECTOR_HPP #ifndef __UTIL_VECTOR_HPP
#define __UTIL_VECTOR_HPP #define __UTIL_VECTOR_HPP
#include "json.hpp" #include "json/tree.hpp"
#include "detail/coord.hpp" #include "detail/coord.hpp"
#include <array> #include <array>