AP_JSON: added JSON parsing library
This commit is contained in:
parent
cb01789b9f
commit
c0beda3235
718
libraries/AP_JSON/AP_JSON.cpp
Normal file
718
libraries/AP_JSON/AP_JSON.cpp
Normal file
@ -0,0 +1,718 @@
|
||||
/*
|
||||
ArduPilot JSON parser, based on picojson.h cloned from:
|
||||
https://github.com/kazuho/picojson
|
||||
*/
|
||||
/*
|
||||
Picojson copyright:
|
||||
|
||||
* Copyright 2009-2010 Cybozu Labs, Inc.
|
||||
* Copyright 2011-2014 Kazuho Oku
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#pragma GCC optimize("Os")
|
||||
|
||||
#define ALLOW_DOUBLE_MATH_FUNCTIONS
|
||||
|
||||
#include "AP_JSON.h"
|
||||
#include <AP_Filesystem/AP_Filesystem.h>
|
||||
#include <AP_Math/AP_Math.h>
|
||||
#include <stdio.h>
|
||||
|
||||
/*
|
||||
load JSON file, returning a value object or nullptr on failure
|
||||
*/
|
||||
AP_JSON::value *AP_JSON::load_json(const char *filename)
|
||||
{
|
||||
struct stat st;
|
||||
if (AP::FS().stat(filename, &st) != 0) {
|
||||
::printf("No such json file %s\n", filename);
|
||||
return nullptr;
|
||||
}
|
||||
int fd = AP::FS().open(filename, O_RDONLY);
|
||||
if (fd == -1) {
|
||||
::printf("failed to open json %s\n", filename);
|
||||
return nullptr;
|
||||
}
|
||||
char *buf = new char[st.st_size+1];
|
||||
if (buf == nullptr) {
|
||||
AP::FS().close(fd);
|
||||
::printf("failed to allocate json %s\n", filename);
|
||||
return nullptr;
|
||||
}
|
||||
if (AP::FS().read(fd, buf, st.st_size) != st.st_size) {
|
||||
::printf("failed to read json %s\n", filename);
|
||||
delete[] buf;
|
||||
AP::FS().close(fd);
|
||||
return nullptr;
|
||||
}
|
||||
AP::FS().close(fd);
|
||||
|
||||
char *start = strchr(buf, '{');
|
||||
if (!start) {
|
||||
::printf("Invalid json %s", filename);
|
||||
delete[] buf;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/*
|
||||
remove comments, as not allowed by the parser
|
||||
*/
|
||||
for (char *p = strchr(start,'#'); p; p=strchr(p+1, '#')) {
|
||||
// clear to end of line
|
||||
do {
|
||||
*p++ = ' ';
|
||||
} while (*p != '\n' && *p != '\r' && *p);
|
||||
}
|
||||
|
||||
AP_JSON::value *obj = new AP_JSON::value;
|
||||
if (obj == nullptr) {
|
||||
::printf("Invalid allocate json for %s", filename);
|
||||
delete[] buf;
|
||||
return nullptr;
|
||||
}
|
||||
std::string err = AP_JSON::parse(*obj, start);
|
||||
if (!err.empty()) {
|
||||
::printf("parse failed for json %s", filename);
|
||||
delete obj;
|
||||
delete[] buf;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
delete[] buf;
|
||||
return obj;
|
||||
}
|
||||
|
||||
typedef AP_JSON::value::array array;
|
||||
typedef AP_JSON::value::object object;
|
||||
typedef AP_JSON::value value;
|
||||
typedef AP_JSON::null null;
|
||||
|
||||
enum {
|
||||
null_type,
|
||||
boolean_type,
|
||||
number_type,
|
||||
string_type,
|
||||
array_type,
|
||||
object_type
|
||||
};
|
||||
|
||||
|
||||
AP_JSON::value::value() : type_(null_type), u_()
|
||||
{
|
||||
}
|
||||
|
||||
AP_JSON::value::value(int type, bool) : type_(type), u_()
|
||||
{
|
||||
switch (type) {
|
||||
#define INIT(p, v) \
|
||||
case p##type: \
|
||||
u_.p = v; \
|
||||
break
|
||||
INIT(boolean_, false);
|
||||
INIT(number_, 0.0);
|
||||
INIT(string_, new std::string());
|
||||
INIT(array_, new array());
|
||||
INIT(object_, new object());
|
||||
#undef INIT
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AP_JSON::value::value(bool b) : type_(boolean_type), u_()
|
||||
{
|
||||
u_.boolean_ = b;
|
||||
}
|
||||
|
||||
AP_JSON::value::value(double n) : type_(number_type), u_()
|
||||
{
|
||||
u_.number_ = n;
|
||||
}
|
||||
|
||||
AP_JSON::value::value(const std::string &s) : type_(string_type), u_()
|
||||
{
|
||||
u_.string_ = new std::string(s);
|
||||
}
|
||||
|
||||
AP_JSON::value::value(const array &a) : type_(array_type), u_()
|
||||
{
|
||||
u_.array_ = new array(a);
|
||||
}
|
||||
|
||||
AP_JSON::value::value(const object &o) : type_(object_type), u_()
|
||||
{
|
||||
u_.object_ = new object(o);
|
||||
}
|
||||
|
||||
AP_JSON::value::value(std::string &&s) : type_(string_type), u_()
|
||||
{
|
||||
u_.string_ = new std::string(std::move(s));
|
||||
}
|
||||
|
||||
AP_JSON::value::value(array &&a) : type_(array_type), u_()
|
||||
{
|
||||
u_.array_ = new array(std::move(a));
|
||||
}
|
||||
|
||||
AP_JSON::value::value(object &&o) : type_(object_type), u_()
|
||||
{
|
||||
u_.object_ = new object(std::move(o));
|
||||
}
|
||||
|
||||
AP_JSON::value::value(const char *s) : type_(string_type), u_()
|
||||
{
|
||||
u_.string_ = new std::string(s);
|
||||
}
|
||||
|
||||
AP_JSON::value::value(const char *s, size_t len) : type_(string_type), u_()
|
||||
{
|
||||
u_.string_ = new std::string(s, len);
|
||||
}
|
||||
|
||||
void AP_JSON::value::clear()
|
||||
{
|
||||
switch (type_) {
|
||||
#define DEINIT(p) \
|
||||
case p##type: \
|
||||
delete u_.p; \
|
||||
break
|
||||
DEINIT(string_);
|
||||
DEINIT(array_);
|
||||
DEINIT(object_);
|
||||
#undef DEINIT
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
AP_JSON::value::~value()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
AP_JSON::value::value(const value &x) : type_(x.type_), u_()
|
||||
{
|
||||
switch (type_) {
|
||||
#define INIT(p, v) \
|
||||
case p##type: \
|
||||
u_.p = v; \
|
||||
break
|
||||
INIT(string_, new std::string(*x.u_.string_));
|
||||
INIT(array_, new array(*x.u_.array_));
|
||||
INIT(object_, new object(*x.u_.object_));
|
||||
#undef INIT
|
||||
default:
|
||||
u_ = x.u_;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
value &AP_JSON::value::operator=(const value &x)
|
||||
{
|
||||
if (this != &x) {
|
||||
value t(x);
|
||||
swap(t);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
AP_JSON::value::value(value &&x) : type_(null_type), u_()
|
||||
{
|
||||
swap(x);
|
||||
}
|
||||
value &AP_JSON::value::operator=(value &&x)
|
||||
{
|
||||
swap(x);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void AP_JSON::value::swap(value &x)
|
||||
{
|
||||
std::swap(type_, x.type_);
|
||||
std::swap(u_, x.u_);
|
||||
}
|
||||
|
||||
#define IS(ctype, jtype) \
|
||||
template <> bool AP_JSON::value::is<ctype>() const { \
|
||||
return type_ == jtype##_type; \
|
||||
}
|
||||
IS(null, null)
|
||||
IS(bool, boolean)
|
||||
IS(std::string, string)
|
||||
IS(array, array)
|
||||
IS(object, object)
|
||||
#undef IS
|
||||
template <> bool AP_JSON::value::is<double>() const
|
||||
{
|
||||
return type_ == number_type;
|
||||
}
|
||||
|
||||
#define GET(ctype, var) \
|
||||
template <> const ctype &AP_JSON::value::get<ctype>() const { \
|
||||
return var; \
|
||||
} \
|
||||
template <> ctype &AP_JSON::value::get<ctype>() { \
|
||||
return var; \
|
||||
}
|
||||
GET(bool, u_.boolean_)
|
||||
GET(std::string, *u_.string_)
|
||||
GET(array, *u_.array_)
|
||||
GET(object, *u_.object_)
|
||||
GET(double, u_.number_)
|
||||
#undef GET
|
||||
|
||||
#define SET(ctype, jtype, setter) \
|
||||
template <> void AP_JSON::value::set<ctype>(const ctype &_val) { \
|
||||
clear(); \
|
||||
type_ = jtype##_type; \
|
||||
setter \
|
||||
}
|
||||
SET(bool, boolean, u_.boolean_ = _val;)
|
||||
SET(std::string, string, u_.string_ = new std::string(_val);)
|
||||
SET(array, array, u_.array_ = new array(_val);)
|
||||
SET(object, object, u_.object_ = new object(_val);)
|
||||
SET(double, number, u_.number_ = _val;)
|
||||
#undef SET
|
||||
|
||||
#define MOVESET(ctype, jtype, setter) \
|
||||
template <> void AP_JSON::value::set<ctype>(ctype && _val) { \
|
||||
clear(); \
|
||||
type_ = jtype##_type; \
|
||||
setter \
|
||||
}
|
||||
MOVESET(std::string, string, u_.string_ = new std::string(std::move(_val));)
|
||||
MOVESET(array, array, u_.array_ = new array(std::move(_val));)
|
||||
MOVESET(object, object, u_.object_ = new object(std::move(_val));)
|
||||
#undef MOVESET
|
||||
|
||||
bool AP_JSON::value::evaluate_as_boolean() const
|
||||
{
|
||||
switch (type_) {
|
||||
case null_type:
|
||||
return false;
|
||||
case boolean_type:
|
||||
return u_.boolean_;
|
||||
case number_type:
|
||||
return !is_zero(u_.number_);
|
||||
case string_type:
|
||||
return !u_.string_->empty();
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
const value &AP_JSON::value::get(const size_t idx) const
|
||||
{
|
||||
static value s_null;
|
||||
return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
|
||||
}
|
||||
|
||||
value &AP_JSON::value::get(const size_t idx)
|
||||
{
|
||||
static value s_null;
|
||||
return idx < u_.array_->size() ? (*u_.array_)[idx] : s_null;
|
||||
}
|
||||
|
||||
const value &AP_JSON::value::get(const std::string &key) const
|
||||
{
|
||||
static value s_null;
|
||||
object::const_iterator i = u_.object_->find(key);
|
||||
return i != u_.object_->end() ? i->second : s_null;
|
||||
}
|
||||
|
||||
value &AP_JSON::value::get(const std::string &key)
|
||||
{
|
||||
static value s_null;
|
||||
object::iterator i = u_.object_->find(key);
|
||||
return i != u_.object_->end() ? i->second : s_null;
|
||||
}
|
||||
|
||||
bool AP_JSON::value::contains(const size_t idx) const
|
||||
{
|
||||
return idx < u_.array_->size();
|
||||
}
|
||||
|
||||
bool AP_JSON::value::contains(const std::string &key) const
|
||||
{
|
||||
object::const_iterator i = u_.object_->find(key);
|
||||
return i != u_.object_->end();
|
||||
}
|
||||
|
||||
std::string AP_JSON::value::to_str() const
|
||||
{
|
||||
switch (type_) {
|
||||
case null_type:
|
||||
return "null";
|
||||
case boolean_type:
|
||||
return u_.boolean_ ? "true" : "false";
|
||||
case number_type: {
|
||||
char buf[256];
|
||||
double tmp;
|
||||
snprintf(buf, sizeof(buf), fabs(u_.number_) < (1ULL << 53) && is_zero(modf(u_.number_, &tmp)) ? "%.f" : "%.17g", u_.number_);
|
||||
return buf;
|
||||
}
|
||||
case string_type:
|
||||
return *u_.string_;
|
||||
case array_type:
|
||||
return "array";
|
||||
case object_type:
|
||||
return "object";
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
template <typename Iter> void copy(const std::string &s, Iter oi)
|
||||
{
|
||||
std::copy(s.begin(), s.end(), oi);
|
||||
}
|
||||
|
||||
template <typename Iter> class input
|
||||
{
|
||||
protected:
|
||||
Iter cur_, end_;
|
||||
bool consumed_;
|
||||
int line_;
|
||||
|
||||
public:
|
||||
input(const Iter &first, const Iter &last) : cur_(first), end_(last), consumed_(false), line_(1)
|
||||
{
|
||||
}
|
||||
int getc()
|
||||
{
|
||||
if (consumed_) {
|
||||
if (*cur_ == '\n') {
|
||||
++line_;
|
||||
}
|
||||
++cur_;
|
||||
}
|
||||
if (cur_ == end_) {
|
||||
consumed_ = false;
|
||||
return -1;
|
||||
}
|
||||
consumed_ = true;
|
||||
return *cur_ & 0xff;
|
||||
}
|
||||
void ungetc()
|
||||
{
|
||||
consumed_ = false;
|
||||
}
|
||||
Iter cur() const
|
||||
{
|
||||
if (consumed_) {
|
||||
input<Iter> *self = const_cast<input<Iter> *>(this);
|
||||
self->consumed_ = false;
|
||||
++self->cur_;
|
||||
}
|
||||
return cur_;
|
||||
}
|
||||
int line() const
|
||||
{
|
||||
return line_;
|
||||
}
|
||||
void skip_ws()
|
||||
{
|
||||
while (1) {
|
||||
int ch = getc();
|
||||
if (!(ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r')) {
|
||||
ungetc();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
bool expect(const int expected)
|
||||
{
|
||||
skip_ws();
|
||||
if (getc() != expected) {
|
||||
ungetc();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool match(const std::string &pattern)
|
||||
{
|
||||
for (std::string::const_iterator pi(pattern.begin()); pi != pattern.end(); ++pi) {
|
||||
if (getc() != *pi) {
|
||||
ungetc();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Iter> int _parse_quadhex(input<Iter> &in)
|
||||
{
|
||||
int uni_ch = 0, hex;
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if ((hex = in.getc()) == -1) {
|
||||
return -1;
|
||||
}
|
||||
if ('0' <= hex && hex <= '9') {
|
||||
hex -= '0';
|
||||
} else if ('A' <= hex && hex <= 'F') {
|
||||
hex -= 'A' - 0xa;
|
||||
} else if ('a' <= hex && hex <= 'f') {
|
||||
hex -= 'a' - 0xa;
|
||||
} else {
|
||||
in.ungetc();
|
||||
return -1;
|
||||
}
|
||||
uni_ch = uni_ch * 16 + hex;
|
||||
}
|
||||
return uni_ch;
|
||||
}
|
||||
|
||||
template <typename String, typename Iter> bool _parse_string(String &out, input<Iter> &in)
|
||||
{
|
||||
while (1) {
|
||||
int ch = in.getc();
|
||||
if (ch < ' ') {
|
||||
in.ungetc();
|
||||
return false;
|
||||
} else if (ch == '"') {
|
||||
return true;
|
||||
} else if (ch == '\\') {
|
||||
if ((ch = in.getc()) == -1) {
|
||||
return false;
|
||||
}
|
||||
switch (ch) {
|
||||
#define MAP(sym, val) \
|
||||
case sym: \
|
||||
out.push_back(val); \
|
||||
break
|
||||
MAP('"', '\"');
|
||||
MAP('\\', '\\');
|
||||
MAP('/', '/');
|
||||
MAP('b', '\b');
|
||||
MAP('f', '\f');
|
||||
MAP('n', '\n');
|
||||
MAP('r', '\r');
|
||||
MAP('t', '\t');
|
||||
#undef MAP
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
out.push_back(static_cast<char>(ch));
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename Context, typename Iter> bool _parse_array(Context &ctx, input<Iter> &in)
|
||||
{
|
||||
if (!ctx.parse_array_start()) {
|
||||
return false;
|
||||
}
|
||||
size_t idx = 0;
|
||||
if (in.expect(']')) {
|
||||
return ctx.parse_array_stop(idx);
|
||||
}
|
||||
do {
|
||||
if (!ctx.parse_array_item(in, idx)) {
|
||||
return false;
|
||||
}
|
||||
idx++;
|
||||
} while (in.expect(','));
|
||||
return in.expect(']') && ctx.parse_array_stop(idx);
|
||||
}
|
||||
|
||||
template <typename Context, typename Iter> bool _parse_object(Context &ctx, input<Iter> &in)
|
||||
{
|
||||
if (!ctx.parse_object_start()) {
|
||||
return false;
|
||||
}
|
||||
if (in.expect('}')) {
|
||||
return true;
|
||||
}
|
||||
do {
|
||||
std::string key;
|
||||
if (!in.expect('"') || !_parse_string(key, in) || !in.expect(':')) {
|
||||
return false;
|
||||
}
|
||||
if (!ctx.parse_object_item(in, key)) {
|
||||
return false;
|
||||
}
|
||||
} while (in.expect(','));
|
||||
return in.expect('}');
|
||||
}
|
||||
|
||||
template <typename Iter> std::string _parse_number(input<Iter> &in)
|
||||
{
|
||||
std::string num_str;
|
||||
while (1) {
|
||||
int ch = in.getc();
|
||||
if (('0' <= ch && ch <= '9') || ch == '+' || ch == '-' || ch == 'e' || ch == 'E') {
|
||||
num_str.push_back(static_cast<char>(ch));
|
||||
} else if (ch == '.') {
|
||||
num_str.push_back('.');
|
||||
} else {
|
||||
in.ungetc();
|
||||
break;
|
||||
}
|
||||
}
|
||||
return num_str;
|
||||
}
|
||||
|
||||
template <typename Context, typename Iter> bool _parse(Context &ctx, input<Iter> &in)
|
||||
{
|
||||
in.skip_ws();
|
||||
int ch = in.getc();
|
||||
switch (ch) {
|
||||
#define IS(ch, text, op) \
|
||||
case ch: \
|
||||
if (in.match(text) && op) { \
|
||||
return true; \
|
||||
} else { \
|
||||
return false; \
|
||||
}
|
||||
IS('n', "ull", ctx.set_null());
|
||||
IS('f', "alse", ctx.set_bool(false));
|
||||
IS('t', "rue", ctx.set_bool(true));
|
||||
#undef IS
|
||||
case '"':
|
||||
return ctx.parse_string(in);
|
||||
case '[':
|
||||
return _parse_array(ctx, in);
|
||||
case '{':
|
||||
return _parse_object(ctx, in);
|
||||
default:
|
||||
if (('0' <= ch && ch <= '9') || ch == '-') {
|
||||
double f;
|
||||
char *endp;
|
||||
in.ungetc();
|
||||
std::string num_str(_parse_number(in));
|
||||
if (num_str.empty()) {
|
||||
return false;
|
||||
}
|
||||
f = strtod(num_str.c_str(), &endp);
|
||||
if (endp == num_str.c_str() + num_str.size()) {
|
||||
ctx.set_number(f);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
in.ungetc();
|
||||
return false;
|
||||
}
|
||||
|
||||
class default_parse_context
|
||||
{
|
||||
protected:
|
||||
value *out_;
|
||||
|
||||
public:
|
||||
default_parse_context(value *out) : out_(out)
|
||||
{
|
||||
}
|
||||
bool set_null()
|
||||
{
|
||||
*out_ = value();
|
||||
return true;
|
||||
}
|
||||
bool set_bool(bool b)
|
||||
{
|
||||
*out_ = value(b);
|
||||
return true;
|
||||
}
|
||||
bool set_number(double f)
|
||||
{
|
||||
*out_ = value(f);
|
||||
return true;
|
||||
}
|
||||
template <typename Iter> bool parse_string(input<Iter> &in)
|
||||
{
|
||||
*out_ = value(string_type, false);
|
||||
return _parse_string(out_->get<std::string>(), in);
|
||||
}
|
||||
bool parse_array_start()
|
||||
{
|
||||
*out_ = value(array_type, false);
|
||||
return true;
|
||||
}
|
||||
template <typename Iter> bool parse_array_item(input<Iter> &in, size_t)
|
||||
{
|
||||
array &a = out_->get<array>();
|
||||
a.push_back(value());
|
||||
default_parse_context ctx(&a.back());
|
||||
return _parse(ctx, in);
|
||||
}
|
||||
bool parse_array_stop(size_t)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
bool parse_object_start()
|
||||
{
|
||||
*out_ = value(object_type, false);
|
||||
return true;
|
||||
}
|
||||
template <typename Iter> bool parse_object_item(input<Iter> &in, const std::string &key)
|
||||
{
|
||||
object &o = out_->get<object>();
|
||||
default_parse_context ctx(&o[key]);
|
||||
return _parse(ctx, in);
|
||||
}
|
||||
|
||||
private:
|
||||
default_parse_context(const default_parse_context &);
|
||||
default_parse_context &operator=(const default_parse_context &);
|
||||
};
|
||||
|
||||
template <typename Context, typename Iter> Iter _parse(Context &ctx, const Iter &first, const Iter &last, std::string *err)
|
||||
{
|
||||
input<Iter> in(first, last);
|
||||
if (!_parse(ctx, in) && err != NULL) {
|
||||
char buf[64];
|
||||
snprintf(buf, sizeof(buf), "syntax error at line %d near: ", in.line());
|
||||
*err = buf;
|
||||
while (1) {
|
||||
int ch = in.getc();
|
||||
if (ch == -1 || ch == '\n') {
|
||||
break;
|
||||
} else if (ch >= ' ') {
|
||||
err->push_back(static_cast<char>(ch));
|
||||
}
|
||||
}
|
||||
}
|
||||
return in.cur();
|
||||
}
|
||||
|
||||
template <typename Iter> Iter parse(value &out, const Iter &first, const Iter &last, std::string *err)
|
||||
{
|
||||
default_parse_context ctx(&out);
|
||||
return _parse(ctx, first, last, err);
|
||||
}
|
||||
|
||||
std::string AP_JSON::parse(value &out, const std::string &s)
|
||||
{
|
||||
std::string err;
|
||||
::parse(out, s.begin(), s.end(), &err);
|
||||
return err;
|
||||
}
|
86
libraries/AP_JSON/AP_JSON.h
Normal file
86
libraries/AP_JSON/AP_JSON.h
Normal file
@ -0,0 +1,86 @@
|
||||
/*
|
||||
ArduPilot JSON parser
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
this avoids an issue on stm32 with std::string
|
||||
*/
|
||||
#undef _GLIBCXX_USE_C99_STDIO
|
||||
#include <string>
|
||||
#define _GLIBCXX_USE_C99_STDIO 1
|
||||
|
||||
class AP_JSON
|
||||
{
|
||||
public:
|
||||
struct null {};
|
||||
|
||||
class value
|
||||
{
|
||||
public:
|
||||
typedef std::vector<value> array;
|
||||
typedef std::map<std::string, value> object;
|
||||
union _storage {
|
||||
bool boolean_;
|
||||
double number_;
|
||||
std::string *string_;
|
||||
array *array_;
|
||||
object *object_;
|
||||
};
|
||||
|
||||
|
||||
protected:
|
||||
int type_;
|
||||
_storage u_;
|
||||
|
||||
public:
|
||||
value();
|
||||
value(int type, bool);
|
||||
explicit value(bool b);
|
||||
explicit value(double n);
|
||||
explicit value(const std::string &s);
|
||||
explicit value(const array &a);
|
||||
explicit value(const object &o);
|
||||
explicit value(std::string &&s);
|
||||
explicit value(array &&a);
|
||||
explicit value(object &&o);
|
||||
|
||||
explicit value(const char *s);
|
||||
value(const char *s, size_t len);
|
||||
~value();
|
||||
value(const value &x);
|
||||
value &operator=(const value &x);
|
||||
value(value &&x);
|
||||
value &operator=(value &&x);
|
||||
void swap(value &x);
|
||||
template <typename T> bool is() const;
|
||||
template <typename T> const T &get() const;
|
||||
template <typename T> T &get();
|
||||
template <typename T> void set(const T &);
|
||||
template <typename T> void set(T &&);
|
||||
bool evaluate_as_boolean() const;
|
||||
const value &get(const size_t idx) const;
|
||||
const value &get(const std::string &key) const;
|
||||
value &get(const size_t idx);
|
||||
value &get(const std::string &key);
|
||||
|
||||
bool contains(const size_t idx) const;
|
||||
bool contains(const std::string &key) const;
|
||||
std::string to_str() const;
|
||||
|
||||
private:
|
||||
template <typename T> value(const T *); // intentionally defined to block implicit conversion of pointer to bool
|
||||
void clear();
|
||||
};
|
||||
|
||||
static std::string parse(value &out, const std::string &s);
|
||||
|
||||
// load a json file, returning a value object
|
||||
static value *load_json(const char *filename);
|
||||
};
|
||||
|
49
libraries/AP_JSON/examples/XPlane/XPlane.cpp
Normal file
49
libraries/AP_JSON/examples/XPlane/XPlane.cpp
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// tests for the AP_JSON parser
|
||||
//
|
||||
|
||||
#include <AP_HAL/AP_HAL.h>
|
||||
#include <AP_JSON/AP_JSON.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
void setup();
|
||||
void loop();
|
||||
|
||||
const AP_HAL::HAL& hal = AP_HAL::get_HAL();
|
||||
|
||||
static void test_xplane(void)
|
||||
{
|
||||
const uint32_t m1 = hal.util->available_memory();
|
||||
auto *obj = AP_JSON::load_json("@ROMFS/models/xplane_plane.json");
|
||||
if (obj == nullptr) {
|
||||
::printf("Failed to parse json\n");
|
||||
}
|
||||
const uint32_t m2 = hal.util->available_memory();
|
||||
::printf("Used %u bytes\n", unsigned(m1-m2));
|
||||
|
||||
const AP_JSON::value::object& o = obj->get<AP_JSON::value::object>();
|
||||
for (AP_JSON::value::object::const_iterator i = o.begin();
|
||||
i != o.end();
|
||||
++i) {
|
||||
const char *label = i->first.c_str();
|
||||
::printf("Label: %s\n", label);
|
||||
}
|
||||
delete obj;
|
||||
}
|
||||
|
||||
/*
|
||||
* euler angle tests
|
||||
*/
|
||||
void setup(void)
|
||||
{
|
||||
hal.console->printf("AP_JSON tests\n");
|
||||
}
|
||||
|
||||
void loop(void)
|
||||
{
|
||||
::printf("Memory: %u\n", (unsigned)hal.util->available_memory());
|
||||
test_xplane();
|
||||
}
|
||||
|
||||
AP_HAL_MAIN();
|
7
libraries/AP_JSON/examples/XPlane/wscript
Normal file
7
libraries/AP_JSON/examples/XPlane/wscript
Normal file
@ -0,0 +1,7 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
def build(bld):
|
||||
bld.ap_example(
|
||||
use='ap',
|
||||
)
|
Loading…
Reference in New Issue
Block a user