libcruft-util/json.cpp.rl

604 lines
14 KiB
Plaintext
Raw Normal View History

/*
* 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.
2014-05-20 14:11:28 +10:00
*
* 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.
2014-05-20 14:11:28 +10:00
*
* You should have received a copy of the GNU General Public License
* along with libgim. If not, see <http://www.gnu.org/licenses/>.
*
2012-04-23 13:06:41 +10:00
* Copyright 2010-2012 Danny Robson <danny@nerdcruft.net>
*/
2011-05-23 17:18:52 +10:00
#include "json.hpp"
#include "debug.hpp"
2011-05-23 17:18:52 +10:00
#include "io.hpp"
#include "maths.hpp"
2011-05-23 17:18:52 +10:00
#include <algorithm>
#include <cstdlib>
#include <deque>
#include <iomanip>
2011-05-23 17:18:52 +10:00
#include <sstream>
#include <stdexcept>
2011-05-23 17:18:52 +10:00
#include <fcntl.h>
2011-05-23 17:18:52 +10:00
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
2011-05-23 17:18:52 +10:00
using namespace std;
using namespace util;
2011-05-23 17:18:52 +10:00
2013-08-05 20:12:04 +10:00
//-----------------------------------------------------------------------------
2012-04-19 16:41:10 +10:00
// Parsing
2011-05-23 17:18:52 +10:00
struct parse_context {
parse_context(json::node *_root):
root (_root),
value (NULL),
key (NULL),
start (NULL),
stop (NULL)
{ ; }
json::node *root,
*value,
*key;
const char *start,
*stop;
};
%%{
machine json;
## Record whether parsing was successful for future use
action success
{ __success = true; }
action failure {
__success = false;
/*std::cerr << std::endl
<< "Failure on: '" << fc << "' in level " << top << " at " << fpc - p
<< std::endl;
*/
}
action new_object { nodestack.push_back (parse_context(new json::object)); }
action new_array { nodestack.push_back (parse_context(new json::array)); }
action new_object_value {
2012-05-11 12:34:21 +10:00
CHECK_HARD (nodestack.back ().root->is_object ());
CHECK (nodestack.back ().key);
CHECK (nodestack.back ().value);
2011-05-23 17:18:52 +10:00
if (!nodestack.back ().key->is_string ())
throw parse_error ("object keys must be strings");
2011-05-23 17:18:52 +10:00
json::object *object = (json::object*)nodestack.back ().root;
object->insert (nodestack.back ().key->as_string (),
unique_ptr<json::node> (nodestack.back ().value));
2011-05-23 17:18:52 +10:00
nodestack.back ().key = NULL;
nodestack.back ().value = NULL;
}
action new_array_value {
2012-05-11 12:34:21 +10:00
CHECK_HARD (nodestack.back ().root->is_array ());
CHECK (nodestack.back ().value);
2011-05-23 17:18:52 +10:00
json::array *array = (json::array *)nodestack.back ().root;
array->insert (unique_ptr<json::node> (nodestack.back ().value));
2011-05-23 17:18:52 +10:00
nodestack.back ().value = NULL;
}
action new_string {
2012-05-11 12:34:21 +10:00
CHECK_HARD (!nodestack.empty ());
CHECK (!nodestack.back ().value);
2011-05-23 17:18:52 +10:00
std::string value (std::string (nodestack.back ().start,
nodestack.back ().stop));
nodestack.back ().value = new json::string(value);
}
action new_boolean {
2012-05-11 12:34:21 +10:00
CHECK_HARD (!nodestack.empty ());
CHECK (!nodestack.back ().value);
2011-05-23 17:18:52 +10:00
throw parse_error ("unable to parse boolean");
2011-05-23 17:18:52 +10:00
}
action new_number {
2012-05-11 12:34:21 +10:00
CHECK_HARD (!nodestack.empty ());
2012-05-11 12:17:40 +10:00
parse_context &back = nodestack.back ();
2012-05-11 12:34:21 +10:00
CHECK (!back.value);
CHECK_HARD (back.start);
CHECK_HARD (back.stop);
CHECK_HARD (back.start <= back.stop);
2012-05-11 12:17:40 +10:00
2011-05-23 17:18:52 +10:00
errno = 0;
2012-05-11 12:17:40 +10:00
char *end;
double value = strtod (back.start, &end);
if (end == back.start || errno)
throw parse_error ("unable to parse number");
2012-05-11 12:17:40 +10:00
back.value = new json::number (value);
2011-05-23 17:18:52 +10:00
}
action new_null {
2012-05-11 12:34:21 +10:00
CHECK_HARD (!nodestack.empty ());
CHECK (!nodestack.back ().value);
2011-05-23 17:18:52 +10:00
nodestack.back().value = new json::null ();
}
action new_object_key {
2012-05-11 12:34:21 +10:00
CHECK_HARD (!nodestack.empty ());
CHECK_HARD (nodestack.back ().root->is_object ());
CHECK (nodestack.back ().value);
CHECK (!nodestack.back ().key);
2011-05-23 17:18:52 +10:00
nodestack.back ().key = nodestack.back ().value;
nodestack.back ().value = NULL;
}
prepush {
fsmstack.push_back (0);
}
postpop {
fsmstack.pop_back ();
__root = nodestack.back ().root;
if (nodestack.size () > 1)
(nodestack.rbegin () + 1)->value = nodestack.back ().root;
nodestack.pop_back ();
}
variable stack fsmstack;
alphtype char;
## numerical
exp = [eE]('-' | '+')? digit+;
frac = '.' digit+;
int = '-'? [1-9]? digit+;
number = int ( frac
| exp
| frac exp)?;
## textual
char =
any - (cntrl | '\"' | '\\')
| '\\\"'
| '\\\\'
| '\\/'
| '\\b'
| '\\f'
| '\\n'
| '\\r'
| '\\t'
| '\\u' xdigit{4};
2014-05-20 14:11:28 +10:00
string = ('"'
2011-05-23 17:18:52 +10:00
char* >{ nodestack.back ().start = fpc; }
%{ nodestack.back ().stop = fpc; })
2014-05-20 14:11:28 +10:00
'"'
2011-05-23 17:18:52 +10:00
@new_string;
## other
boolean =
'true' @{ nodestack.back ().value = new json::boolean ( true); }
| 'false' @{ nodestack.back ().value = new json::boolean (false); };
## components
object = '{' @{ fhold; fcall _object; } '}';
array = '[' @{ fhold; fcall _array; } ']';
value =
2014-05-20 14:11:28 +10:00
string
| boolean
2011-05-23 17:18:52 +10:00
| number >{ nodestack.back ().start = fpc; } %{ nodestack.back ().stop = fpc; } %new_number
2014-05-20 14:11:28 +10:00
| object
| array
2011-05-23 17:18:52 +10:00
| 'null' %new_null;
## compound data types
_array := ('[' @new_array
space* ((value %new_array_value space* ',' space*)* value %new_array_value space*)?
']')
$!failure
@{ fhold; fret; };
pair = string %new_object_key space* ':' space* value %new_object_value;
_object := ('{' @new_object
space* ((pair space* ',' space*)* pair space*)?
'}')
$!failure
@{ fhold; fret; };
2015-01-18 15:36:24 +11:00
json := (space* (object | array) space*)
2011-05-23 17:18:52 +10:00
$!failure
%success
>{ __success = false; };
write data;
2011-05-23 17:18:52 +10:00
}%%
2013-08-05 20:12:04 +10:00
//-----------------------------------------------------------------------------
// External support
template <>
bool
is_integer (const json::number &node) {
return is_integer (node.native ());
}
template <>
bool
is_integer (const json::node &node) {
return node.is_number () &&
is_integer (node.as_number ());
}
2011-05-23 17:18:52 +10:00
2013-08-05 20:12:04 +10:00
//-----------------------------------------------------------------------------
2012-04-19 16:41:10 +10:00
// Node
2011-05-23 17:18:52 +10:00
std::unique_ptr<json::node>
2012-04-26 18:18:09 +10:00
json::parse (const boost::filesystem::path &path) {
auto data = slurp (path);
return parse (static_cast <const char *> (data.get ()));
}
std::unique_ptr<json::node>
json::parse (const std::string &path)
{ return parse (path.c_str (), path.c_str () + path.size ()); }
std::unique_ptr<json::node>
json::parse (const char *start,
const char *stop) {
bool __success = true;
2014-03-03 14:34:45 +11:00
json::node *__root = nullptr;
size_t top = 0;
int cs;
deque <int> fsmstack;
deque <parse_context> nodestack;
const char *p = start,
*pe = stop,
*eof = stop;
%%write init;
%%write exec;
if (!__success) {
std::ostringstream os;
os << "unable to parse json at char " << (p - start);
throw parse_error (os.str ());
}
return std::unique_ptr<json::node> (__root);
}
2011-05-23 17:18:52 +10:00
std::unique_ptr<json::node>
json::parse (const char *start)
{ return parse (start, start + strlen (start)); }
2011-05-23 17:18:52 +10:00
void
json::write (const json::node &node, std::ostream &os)
{ node.write (os); }
2013-08-05 20:12:04 +10:00
//-----------------------------------------------------------------------------
2012-04-19 16:41:10 +10:00
// Type conversion
2011-05-23 17:18:52 +10:00
const json::object&
json::node::as_object (void) const
{ throw type_error ("node is not an object"); }
2011-05-23 17:18:52 +10:00
const json::array&
json::node::as_array (void) const
{ throw type_error ("node is not an array"); }
2011-05-23 17:18:52 +10:00
const json::string&
json::node::as_string (void) const
{ throw type_error ("node is not a string"); }
2011-05-23 17:18:52 +10:00
const json::number&
json::node::as_number (void) const
{ throw type_error ("node is not a number"); }
2011-05-23 17:18:52 +10:00
const json::boolean&
json::node::as_boolean (void) const
{ throw type_error ("node is not a boolean"); }
2011-05-23 17:18:52 +10:00
2012-04-13 11:15:08 +10:00
const json::null&
json::node::as_null (void) const
{ throw type_error ("node is not a null"); }
2012-04-13 11:15:08 +10:00
2013-08-05 20:12:04 +10:00
//-----------------------------------------------------------------------------
2012-04-19 16:41:10 +10:00
// Global operatoers
2011-05-23 17:18:52 +10:00
bool
json::node::operator!= (const node &rhs) const
2011-05-23 17:18:52 +10:00
{ return !(*this == rhs); }
bool json::node::operator==(const char *rhs) const {
try {
2012-04-20 18:07:42 +10:00
return as_string ().native () == rhs;
} catch (const json::type_error&) {
return false;
}
}
2012-01-04 17:05:40 +11:00
const json::node&
json::node::operator[] (const std::string &key) const
{ return as_object ()[key]; }
const json::node&
json::node::operator[] (unsigned int idx) const
{ return as_array()[idx]; }
2012-04-19 16:41:10 +10:00
2013-08-05 20:12:04 +10:00
//-----------------------------------------------------------------------------
2012-04-19 16:41:10 +10:00
// Object
2011-05-23 17:18:52 +10:00
json::object::~object ()
{ ; }
2011-05-23 17:18:52 +10:00
bool
json::object::operator ==(const json::object &rhs) const {
for (auto i = rhs.m_values.begin (), j = m_values.begin ();
i != rhs.m_values.end () && j != m_values.end ();
++i, ++j)
{
if (i->first != j->first)
return false;
if ((*i->second) != (*j->second))
return false;
}
return true;
}
void
json::object::insert (const std::string &_key, unique_ptr<json::node> &&value)
{ m_values[_key] = move(value); }
2011-05-23 17:18:52 +10:00
const json::node&
json::object::operator[](const std::string &key) const {
auto value = m_values.find (key);
if (value == m_values.end ()) {
ostringstream ss;
ss << "no key: " << key;
throw json::error (ss.str());
2011-05-23 17:18:52 +10:00
}
return *value->second;
}
2012-04-20 18:09:23 +10:00
bool
json::object::has (const std::string &key) const {
return m_values.find (key) != m_values.end ();
}
2012-04-19 16:44:35 +10:00
void
json::object::clear (void)
{ m_values.clear (); }
void
json::object::erase (const std::string &key) {
auto pos = m_values.find (key);
if (pos == m_values.end ())
throw json::error ("erasing invalid key");
m_values.erase (key);
}
json::object::const_iterator
json::object::begin (void) const
{ return m_values.begin (); }
json::object::const_iterator
json::object::end (void) const
{ return m_values.end (); }
2011-05-23 17:18:52 +10:00
std::ostream&
2012-04-12 14:09:33 +10:00
json::object::write (std::ostream &os) const {
os << "{\n";
{
indenter raii(os);
2011-05-23 17:18:52 +10:00
2012-04-12 14:09:33 +10:00
for (auto i = m_values.begin (); i != m_values.end ();) {
os << '"' << i->first << "\" : " << *i->second;
2011-05-23 17:18:52 +10:00
2012-04-12 14:09:33 +10:00
if (++i != m_values.end ())
os << ",\n";
}
2011-05-23 17:18:52 +10:00
}
2012-04-12 14:09:33 +10:00
os << "\n}";
2011-05-23 17:18:52 +10:00
return os;
}
2013-08-05 20:12:04 +10:00
//-----------------------------------------------------------------------------
2012-04-19 16:41:10 +10:00
// Array
2011-05-23 17:18:52 +10:00
json::array::~array()
{ ; }
2011-05-23 17:18:52 +10:00
void
json::array::insert (unique_ptr<json::node> &&_value)
{ m_values.push_back (move (_value)); }
2011-05-23 17:18:52 +10:00
bool
json::array::operator ==(const json::array &rhs) const {
for (auto i = rhs.m_values.begin (), j = m_values.begin ();
i != rhs.m_values.end () && j != m_values.end ();
++i, ++j)
{ if ((**i) != (**j)) return false; }
return true;
}
std::ostream&
2012-04-12 14:09:33 +10:00
json::array::write (std::ostream &os) const {
2014-05-20 14:11:28 +10:00
os << "[\n";
2012-04-12 14:09:33 +10:00
{
2014-05-20 14:11:28 +10:00
indenter raii(os);
2011-05-23 17:18:52 +10:00
2012-04-12 14:09:33 +10:00
for (auto i = m_values.begin (); i != m_values.end (); ++i) {
(*i)->write (os);
2011-05-23 17:18:52 +10:00
2012-04-12 14:09:33 +10:00
if (i != m_values.end () - 1)
os << ",\n";
}
2011-05-23 17:18:52 +10:00
}
2012-04-12 14:09:33 +10:00
os << "\n]";
2011-05-23 17:18:52 +10:00
return os;
}
2013-08-05 20:12:04 +10:00
//-----------------------------------------------------------------------------
2012-04-19 16:41:10 +10:00
// String
2011-05-23 17:18:52 +10:00
std::ostream&
2012-04-12 14:09:33 +10:00
json::string::write (std::ostream &os) const {
2011-05-23 17:18:52 +10:00
os << '"' << m_value << '"';
return os;
}
bool
json::string::operator ==(const json::string &rhs) const
{ return rhs.m_value == m_value; }
2012-04-20 18:18:47 +10:00
bool
json::string::operator ==(const char *rhs) const
{ return rhs == m_value; }
2013-08-05 20:12:04 +10:00
//-----------------------------------------------------------------------------
2012-04-19 16:41:10 +10:00
// Number
2011-05-23 17:18:52 +10:00
std::ostream&
2012-04-12 14:09:33 +10:00
json::number::write (std::ostream &os) const {
os << setprecision (numeric_limits<double>::digits10) << m_value;
2011-05-23 17:18:52 +10:00
return os;
}
bool
2014-05-20 14:11:28 +10:00
json::number::operator ==(const json::number &rhs) const
2011-05-23 17:18:52 +10:00
{ return almost_equal (rhs.m_value, m_value); }
2013-08-05 20:12:04 +10:00
//-----------------------------------------------------------------------------
2012-04-19 16:41:10 +10:00
// Boolean
2011-05-23 17:18:52 +10:00
std::ostream&
2012-04-12 14:09:33 +10:00
json::boolean::write (std::ostream &os) const {
os << (m_value ? "true" : "false");
2011-05-23 17:18:52 +10:00
return os;
}
bool
2014-05-20 14:11:28 +10:00
json::boolean::operator ==(const json::boolean &rhs) const
2011-05-23 17:18:52 +10:00
{ return rhs.m_value == m_value; }
2013-08-05 20:12:04 +10:00
//-----------------------------------------------------------------------------
2012-04-19 16:41:10 +10:00
// Null
2011-05-23 17:18:52 +10:00
std::ostream&
2012-04-12 14:09:33 +10:00
json::null::write (std::ostream &os) const {
2011-05-23 17:18:52 +10:00
os << "null";
return os;
}
ostream&
json::operator <<(ostream &os, const json::node &n)
2012-04-12 14:09:33 +10:00
{ return n.write (os); }
2013-08-05 20:54:19 +10:00
//-----------------------------------------------------------------------------
// to_json
namespace json {
template <>
std::unique_ptr<node>
io<bool>::serialise (const bool &b) {
return std::unique_ptr<node> (new boolean (b));
}
template <>
std::unique_ptr<node>
io<nullptr_t>::serialise (const nullptr_t &) {
return std::unique_ptr<node> (new null ());
}
template <>
std::unique_ptr<node>
io<std::string>::serialise (const std::string &s) {
return std::unique_ptr<node> (new string (s));
}
2013-08-05 22:39:34 +10:00
template <>
std::unique_ptr<node>
io<int>::serialise (const int &i) {
return std::unique_ptr<node> (new number (i));
}
template <>
std::unique_ptr<node>
io<size_t>::serialise (const size_t &i) {
return std::unique_ptr<node> (new number (i));
}
2013-08-05 22:39:34 +10:00
template <>
std::unique_ptr<node>
io<float>::serialise (const float &f) {
return std::unique_ptr<node> (new number (f));
}
2013-08-05 20:54:19 +10:00
}