From 60787655b35bdaaf7a7b58167db3d2d0dd87a01b Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Tue, 3 Feb 2015 00:07:09 +1100 Subject: [PATCH] json: split json impl files into tree and flat --- Makefile.am | 8 +- colour.hpp | 2 +- json.hpp | 359 ----------------------------------- json/except.cpp | 28 +++ json/except.hpp | 54 ++++++ json/flat.cpp.rl | 199 +++++++++++++++++++ json/flat.hpp | 59 ++++++ json.cpp.rl => json/tree.cpp | 198 ++----------------- json/tree.hpp | 271 ++++++++++++++++++++++++++ range.cpp | 2 +- test/json_types.cpp | 2 +- tools/json-clean.cpp | 5 +- tools/json-schema.cpp | 23 +-- tools/json-validate.cpp | 2 +- vector.hpp | 2 +- 15 files changed, 650 insertions(+), 564 deletions(-) delete mode 100644 json.hpp create mode 100644 json/except.cpp create mode 100644 json/except.hpp create mode 100644 json/flat.cpp.rl create mode 100644 json/flat.hpp rename json.cpp.rl => json/tree.cpp (69%) create mode 100644 json/tree.hpp diff --git a/Makefile.am b/Makefile.am index 211614b2..4ae58f1f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -71,8 +71,12 @@ UTIL_FILES = \ ip.cpp \ ip.hpp \ iterator.hpp \ - json.cpp \ - json.hpp \ + json/except.cpp \ + json/except.hpp \ + json/flat.cpp \ + json/flat.hpp \ + json/tree.cpp \ + json/tree.hpp \ lerp.cpp \ lerp.hpp \ log.cpp \ diff --git a/colour.hpp b/colour.hpp index 9adb6453..1c57ee5e 100644 --- a/colour.hpp +++ b/colour.hpp @@ -22,7 +22,7 @@ #include "detail/coord.hpp" -#include "json.hpp" +#include "json/tree.hpp" #include diff --git a/json.hpp b/json.hpp deleted file mode 100644 index a15f995e..00000000 --- a/json.hpp +++ /dev/null @@ -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 . - * - * Copyright 2010-2015 Danny Robson - */ - -#ifndef __UTIL_JSON_HPP -#define __UTIL_JSON_HPP - -#include -#include -#include -#include -#include - -#include - -#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 parse (const boost::filesystem::path &path); - extern std::unique_ptr parse (const char *start, const char *stop); - extern std::unique_ptr parse (const char *start); - extern std::unique_ptr 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> 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&& 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>::iterator pointer_array_iterator; - typedef std::vector>::const_iterator const_pointer_array_iterator; - - public: - typedef referencing_iterator iterator; - typedef referencing_iterator const_iterator; - - protected: - std::vector> 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 &&_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 - struct io { - static std::unique_ptr 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 - T as (void) const; - }; - - std::vector parse (const char *first, const char *last); - std::vector parse (const char *first); - std::vector 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 -std::unique_ptr to_json (const T &t, Args&&... args) { - return json::tree::io::serialise (t, std::forward(args)...); -} - -template -T from_json (const json::tree::node &n, Args&&... args) { - return json::tree::io::deserialise (n, std::forward(args)...); -} - -#endif - diff --git a/json/except.cpp b/json/except.cpp new file mode 100644 index 00000000..430e597e --- /dev/null +++ b/json/except.cpp @@ -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 . + * + * Copyright 2015 Danny Robson + */ + +#include "except.hpp" + + +//----------------------------------------------------------------------------- +json::parse_error::parse_error (const std::string &_what, size_t _line): + error (_what), + line (_line) +{ ; } diff --git a/json/except.hpp b/json/except.hpp new file mode 100644 index 00000000..ec9be0b9 --- /dev/null +++ b/json/except.hpp @@ -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 . + * + * Copyright 2015 Danny Robson + */ + + +#ifndef __UTIL_JSON_EXCEPT_HPP +#define __UTIL_JSON_EXCEPT_HPP + +#include +#include +#include + +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 diff --git a/json/flat.cpp.rl b/json/flat.cpp.rl new file mode 100644 index 00000000..95190681 --- /dev/null +++ b/json/flat.cpp.rl @@ -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 . + * + * Copyright 2010-2015 Danny Robson + */ + +#include "json/flat.hpp" + +#include "json/except.hpp" +#include "io.hpp" + +#include + +//----------------------------------------------------------------------------- +%%{ + # 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::parse (const char *first, const char *last) +{ + const char *p = first; + const char *pe = last; + const char *eof = pe; + + std::deque ragelstack; + std::vector 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::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; +} + diff --git a/json/flat.hpp b/json/flat.hpp new file mode 100644 index 00000000..25f7ed4b --- /dev/null +++ b/json/flat.hpp @@ -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 . + * + * Copyright 2010-2015 Danny Robson + */ + +#ifndef __UTIL_JSON_FLAT_HPP +#define __UTIL_JSON_FLAT_HPP + +#include +#include + +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 + T as (void) const; + }; + + std::vector parse (const char *first, const char *last); + std::vector parse (const char *first); + std::vector parse (const boost::filesystem::path&); + + std::ostream& operator<< (std::ostream&, type); +} } + +#endif diff --git a/json.cpp.rl b/json/tree.cpp similarity index 69% rename from json.cpp.rl rename to json/tree.cpp index 336e01f9..fe15118e 100644 --- a/json.cpp.rl +++ b/json/tree.cpp @@ -18,7 +18,10 @@ */ -#include "json.hpp" +#include "json/tree.hpp" + +#include "json/except.hpp" +#include "json/flat.hpp" #include "debug.hpp" #include "io.hpp" @@ -40,157 +43,6 @@ using namespace std; 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::parse (const char *first, const char *last) -{ - const char *p = first; - const char *pe = last; - const char *eof = pe; - - std::deque ragelstack; - std::vector 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::parse (const boost::filesystem::path &path) -{ - util::mapped_file f (path); - return parse ((const char *)f.cbegin (), (const char*)f.cend ()); -} - //----------------------------------------------------------------------------- // External support @@ -355,32 +207,32 @@ json::tree::parse (const char *first, const char *last) const json::tree::object& 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& 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& 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& 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& 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& 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 { try { return as_string ().native () == rhs; - } catch (const json::tree::type_error&) { + } catch (const json::type_error&) { return false; } } @@ -444,7 +296,7 @@ json::tree::object::operator[](const std::string &key) const { if (value == m_values.end ()) { ostringstream ss; ss << "no key: " << key; - throw json::tree::error (ss.str()); + throw json::error (ss.str()); } return *value->second; @@ -466,7 +318,7 @@ void json::tree::object::erase (const std::string &key) { auto pos = m_values.find (key); if (pos == m_values.end ()) - throw json::tree::error ("erasing invalid key"); + throw json::error ("erasing invalid key"); m_values.erase (key); } @@ -649,27 +501,3 @@ namespace json { namespace tree { return std::unique_ptr (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; -} diff --git a/json/tree.hpp b/json/tree.hpp new file mode 100644 index 00000000..2a4ecf7f --- /dev/null +++ b/json/tree.hpp @@ -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 . + * + * Copyright 2010-2015 Danny Robson + */ + +#ifndef __UTIL_JSON_TREE_HPP +#define __UTIL_JSON_TREE_HPP + +#include +#include +#include +#include +#include + +#include + +#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 parse (const boost::filesystem::path &path); + extern std::unique_ptr parse (const char *start, const char *stop); + extern std::unique_ptr parse (const char *start); + extern std::unique_ptr 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> 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&& 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>::iterator pointer_array_iterator; + typedef std::vector>::const_iterator const_pointer_array_iterator; + + public: + typedef referencing_iterator iterator; + typedef referencing_iterator const_iterator; + + protected: + std::vector> 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 &&_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 + struct io { + static std::unique_ptr serialise (const T&); + static T deserialise (const json::tree::node&); + }; +} } + +template +std::unique_ptr to_json (const T &t, Args&&... args) { + return json::tree::io::serialise (t, std::forward(args)...); +} + +template +T from_json (const json::tree::node &n, Args&&... args) { + return json::tree::io::deserialise (n, std::forward(args)...); +} + +#endif + + diff --git a/range.cpp b/range.cpp index b82916a6..3da2a58c 100644 --- a/range.cpp +++ b/range.cpp @@ -21,7 +21,7 @@ #include "range.hpp" #include "debug.hpp" -#include "json.hpp" +#include "json/tree.hpp" #include "maths.hpp" #include diff --git a/test/json_types.cpp b/test/json_types.cpp index d5e3c616..d32100cf 100644 --- a/test/json_types.cpp +++ b/test/json_types.cpp @@ -1,6 +1,6 @@ #include "../debug.hpp" -#include "../json.hpp" +#include "../json/tree.hpp" #include "../maths.hpp" #include diff --git a/tools/json-clean.cpp b/tools/json-clean.cpp index 92934152..bb8d6da7 100644 --- a/tools/json-clean.cpp +++ b/tools/json-clean.cpp @@ -17,7 +17,8 @@ * Copyright 2012 Danny Robson */ -#include "../json.hpp" +#include "json/except.hpp" +#include "json/tree.hpp" #include #include @@ -49,7 +50,7 @@ main (int argc, char **argv) { try { boost::filesystem::path input (argv[ARG_INPUT]); std::cout << *json::tree::parse (input) << "\n"; - } catch (const json::tree::error& err) { + } catch (const json::error& err) { std::cerr << err.what () << "\n"; return EXIT_FAILURE; } diff --git a/tools/json-schema.cpp b/tools/json-schema.cpp index 18754fb6..71d8f203 100644 --- a/tools/json-schema.cpp +++ b/tools/json-schema.cpp @@ -18,10 +18,11 @@ */ -#include "../json.hpp" +#include "json/except.hpp" +#include "json/tree.hpp" -#include "../debug.hpp" -#include "../maths.hpp" +#include "debug.hpp" +#include "maths.hpp" #include #include @@ -82,7 +83,7 @@ is_type_valid (const json::tree::node &node, } 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 INT_VALIDATOR = [] (const json::tree::node &n) { @@ -120,7 +121,7 @@ bool is_enum_valid (const json::tree::node &node, const json::tree::node &constraint) { 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 (); return valids.end () != std::find (valids.begin (), @@ -318,7 +319,7 @@ bool is_max_items_valid (const json::tree::array &node, const json::tree::node &constraint) { 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 (); } @@ -328,7 +329,7 @@ bool is_min_items_valid (const json::tree::array &node, const json::tree::node &constraint) { 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 (); } @@ -338,7 +339,7 @@ bool is_unique_items_valid (const json::tree::array &node, const json::tree::node &constraint) { 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) return true; @@ -357,7 +358,7 @@ bool is_items_valid (const json::tree::array &node, const json::tree::node &_schema) { 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 (); for (const json::tree::node &i: node) @@ -445,7 +446,7 @@ is_properties_valid (const json::tree::object &node, CHECK (node.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 ()); } @@ -583,7 +584,7 @@ main (int argc, char **argv) { try { schema = json::tree::parse (boost::filesystem::path (argv[ARG_SCHEMA])); 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"; return EXIT_FAILURE; } diff --git a/tools/json-validate.cpp b/tools/json-validate.cpp index c7f9db8e..b3c91c44 100644 --- a/tools/json-validate.cpp +++ b/tools/json-validate.cpp @@ -1,4 +1,4 @@ -#include "../json.hpp" +#include "json/flat.hpp" #include #include diff --git a/vector.hpp b/vector.hpp index 9383d0b3..0506ac3a 100644 --- a/vector.hpp +++ b/vector.hpp @@ -20,7 +20,7 @@ #ifndef __UTIL_VECTOR_HPP #define __UTIL_VECTOR_HPP -#include "json.hpp" +#include "json/tree.hpp" #include "detail/coord.hpp" #include