json2: initial import
This commit is contained in:
parent
d64e3d244e
commit
340981dac2
@ -318,6 +318,18 @@ list (
|
|||||||
json/schema.hpp
|
json/schema.hpp
|
||||||
json/tree.cpp
|
json/tree.cpp
|
||||||
json/tree.hpp
|
json/tree.hpp
|
||||||
|
json2/fwd.hpp
|
||||||
|
json2/event.hpp
|
||||||
|
json2/event.cpp
|
||||||
|
json2/except.hpp
|
||||||
|
json2/personality/base.cpp
|
||||||
|
json2/personality/base.hpp
|
||||||
|
json2/personality/jsonish.cpp
|
||||||
|
json2/personality/jsonish.hpp
|
||||||
|
json2/personality/rfc7519.cpp
|
||||||
|
json2/personality/rfc7519.hpp
|
||||||
|
json2/tree.cpp
|
||||||
|
json2/tree.hpp
|
||||||
library.hpp
|
library.hpp
|
||||||
log.cpp
|
log.cpp
|
||||||
log.hpp
|
log.hpp
|
||||||
@ -509,6 +521,7 @@ if (TESTS)
|
|||||||
iterator
|
iterator
|
||||||
job/queue
|
job/queue
|
||||||
json_types
|
json_types
|
||||||
|
json2/event
|
||||||
maths
|
maths
|
||||||
matrix
|
matrix
|
||||||
memory/deleter
|
memory/deleter
|
||||||
|
90
json2/event.cpp
Normal file
90
json2/event.cpp
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "./event.hpp"
|
||||||
|
#include "./except.hpp"
|
||||||
|
|
||||||
|
#include "../debug.hpp"
|
||||||
|
|
||||||
|
using util::json2::event::packet;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
util::json2::event::type_t
|
||||||
|
packet::type (void) const noexcept
|
||||||
|
{
|
||||||
|
CHECK_NEQ (first, last);
|
||||||
|
|
||||||
|
const auto &c = *first;
|
||||||
|
|
||||||
|
switch (c) {
|
||||||
|
case '{': return type_t::OBJECT_BEGIN;
|
||||||
|
case '}': return type_t::OBJECT_END;
|
||||||
|
case '[': return type_t::ARRAY_BEGIN;
|
||||||
|
case ']': return type_t::ARRAY_END;
|
||||||
|
case '"': return type_t::STRING;
|
||||||
|
case 'n': return type_t::NONE;
|
||||||
|
|
||||||
|
case 't':
|
||||||
|
case 'f':
|
||||||
|
return type_t::BOOLEAN;
|
||||||
|
|
||||||
|
case '-':
|
||||||
|
case '0'...'9':
|
||||||
|
return type_t::NUMBER;
|
||||||
|
}
|
||||||
|
|
||||||
|
unhandled (c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
#include "./personality/rfc7519.hpp"
|
||||||
|
#include "./personality/jsonish.hpp"
|
||||||
|
|
||||||
|
template <typename PersonalityT>
|
||||||
|
const char*
|
||||||
|
util::json2::event::parse (const std::function<util::json2::callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last)
|
||||||
|
{
|
||||||
|
auto cursor = first;
|
||||||
|
|
||||||
|
PersonalityT p {};
|
||||||
|
cursor = p.consume_whitespace (cursor, last);
|
||||||
|
cursor = p.parse_value (cb, cursor, last);
|
||||||
|
cursor = p.consume_whitespace (cursor, last);
|
||||||
|
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
template
|
||||||
|
const char* util::json2::event::parse<util::json2::personality::rfc7159> (
|
||||||
|
const std::function<util::json2::callback_t> &,
|
||||||
|
const char*,
|
||||||
|
const char*
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
template
|
||||||
|
const char* util::json2::event::parse<util::json2::personality::jsonish> (
|
||||||
|
const std::function<util::json2::callback_t> &,
|
||||||
|
const char*,
|
||||||
|
const char*
|
||||||
|
);
|
55
json2/event.hpp
Normal file
55
json2/event.hpp
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
#ifndef CRUFT_JSON2_EVENT_HPP
|
||||||
|
#define CRUFT_JSON2_EVENT_HPP
|
||||||
|
|
||||||
|
#include "./fwd.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
|
namespace util::json2::event {
|
||||||
|
// It is important that scalars come after compound types because it
|
||||||
|
// simplifies categorisation as we can use a simple '>' to classify the
|
||||||
|
// types.
|
||||||
|
//
|
||||||
|
// the value of the enumerants isn't important, but they might make it
|
||||||
|
// fractionally easier to visualise in a debugger on some occasions.
|
||||||
|
enum class type_t {
|
||||||
|
OBJECT_BEGIN = '{',
|
||||||
|
OBJECT_END = '}',
|
||||||
|
ARRAY_BEGIN = '[',
|
||||||
|
ARRAY_END = ']',
|
||||||
|
|
||||||
|
STRING = '"',
|
||||||
|
NUMBER = '1',
|
||||||
|
BOOLEAN = 't',
|
||||||
|
NONE = 'n',
|
||||||
|
};
|
||||||
|
|
||||||
|
struct packet {
|
||||||
|
type_t type (void) const noexcept;
|
||||||
|
|
||||||
|
const char *first;
|
||||||
|
const char *last;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename PersonalityT = personality::rfc7159>
|
||||||
|
const char*
|
||||||
|
parse (const std::function<callback_t>&, const char *first, const char *last);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
40
json2/except.hpp
Normal file
40
json2/except.hpp
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CRUFT_UTIL_JSON2_EXCEPT_HPP
|
||||||
|
#define CRUFT_UTIL_JSON2_EXCEPT_HPP
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
|
||||||
|
namespace util::json2 {
|
||||||
|
struct error : public std::exception {};
|
||||||
|
|
||||||
|
|
||||||
|
struct parse_error : public error {
|
||||||
|
parse_error (const char *_position):
|
||||||
|
position (_position)
|
||||||
|
{ ; }
|
||||||
|
|
||||||
|
const char *position;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
struct overrun_error : public parse_error {
|
||||||
|
using parse_error::parse_error;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
#endif
|
47
json2/fwd.hpp
Normal file
47
json2/fwd.hpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CRUFT_UTIL_JSON2_FWD_HPP
|
||||||
|
#define CRUFT_UTIL_JSON2_FWD_HPP
|
||||||
|
|
||||||
|
#include "../preprocessor.hpp"
|
||||||
|
|
||||||
|
namespace util::json2 {
|
||||||
|
namespace personality {
|
||||||
|
template <typename> struct base;
|
||||||
|
|
||||||
|
struct rfc7159;
|
||||||
|
struct jsonish;
|
||||||
|
|
||||||
|
#define MAP_JSON2_PERSONALITY_TYPES(FUNC) \
|
||||||
|
MAP0(FUNC, \
|
||||||
|
util::json2::personality::rfc7159,\
|
||||||
|
util::json2::personality::jsonish)
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace event {
|
||||||
|
enum class type_t;
|
||||||
|
struct packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
using callback_t = void(const event::packet&);
|
||||||
|
|
||||||
|
struct error;
|
||||||
|
struct parse_error;
|
||||||
|
struct overrun_error;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
319
json2/personality/base.cpp
Normal file
319
json2/personality/base.cpp
Normal file
@ -0,0 +1,319 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
#include "./base.hpp"
|
||||||
|
|
||||||
|
#include "../event.hpp"
|
||||||
|
#include "../except.hpp"
|
||||||
|
#include "../../debug.hpp"
|
||||||
|
|
||||||
|
#include "./rfc7519.hpp"
|
||||||
|
#include "./jsonish.hpp"
|
||||||
|
|
||||||
|
using util::json2::personality::base;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
static const char*
|
||||||
|
expect [[nodiscard]] (const char *first, const char *last, const char value)
|
||||||
|
{
|
||||||
|
if (first == last || *first != value)
|
||||||
|
throw util::json2::parse_error {first};
|
||||||
|
return first + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template <typename ParentT>
|
||||||
|
const char*
|
||||||
|
base<ParentT>::consume_whitespace (const char *first, const char *last) noexcept
|
||||||
|
{
|
||||||
|
auto cursor = first;
|
||||||
|
|
||||||
|
while (cursor != last) {
|
||||||
|
switch (*cursor) {
|
||||||
|
case 0x20:
|
||||||
|
case 0x09:
|
||||||
|
case 0x0A:
|
||||||
|
case 0x0D:
|
||||||
|
++cursor;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template <typename ParentT>
|
||||||
|
const char*
|
||||||
|
base<ParentT>::parse_number (const std::function<util::json2::callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last)
|
||||||
|
{
|
||||||
|
(void)last;
|
||||||
|
|
||||||
|
// number: minus? int frac? exp?
|
||||||
|
auto cursor = first;
|
||||||
|
|
||||||
|
// minus: '-'
|
||||||
|
if (*cursor == '-')
|
||||||
|
++cursor;
|
||||||
|
|
||||||
|
// int: '0' | [1-9] DIGIT*
|
||||||
|
switch (*cursor) {
|
||||||
|
case '1'...'9':
|
||||||
|
{
|
||||||
|
++cursor;
|
||||||
|
while ('0' <= *cursor && *cursor <= '9')
|
||||||
|
++cursor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case '0':
|
||||||
|
// leading zero means we _must_ be parsing a fractional value so we
|
||||||
|
// look ahead to ensure we're about to do so. note that we don't use
|
||||||
|
// `expect' here because it implies consumption of '.'
|
||||||
|
++cursor;
|
||||||
|
if (*cursor != '.')
|
||||||
|
throw util::json2::parse_error { cursor };
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw util::json2::parse_error { cursor };
|
||||||
|
}
|
||||||
|
|
||||||
|
// frac: '.' digit+
|
||||||
|
if (*cursor == '.') {
|
||||||
|
++cursor;
|
||||||
|
|
||||||
|
auto frac_start = cursor;
|
||||||
|
while ('0' <= *cursor && *cursor <= '9')
|
||||||
|
++cursor;
|
||||||
|
if (frac_start == cursor)
|
||||||
|
throw util::json2::parse_error { cursor };
|
||||||
|
}
|
||||||
|
|
||||||
|
// exp: [eE] [-+]? digit+
|
||||||
|
if (*cursor == 'e' || *cursor == 'E') {
|
||||||
|
++cursor;
|
||||||
|
|
||||||
|
if (*cursor == '-' || *cursor == '+')
|
||||||
|
++cursor;
|
||||||
|
|
||||||
|
auto exp_digits = cursor;
|
||||||
|
while ('0' <= *cursor && *cursor <= '9')
|
||||||
|
++cursor;
|
||||||
|
if (exp_digits == cursor)
|
||||||
|
throw util::json2::parse_error { cursor };
|
||||||
|
}
|
||||||
|
|
||||||
|
cb ({ first, cursor });
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
template <typename ParentT>
|
||||||
|
template <int N>
|
||||||
|
const char*
|
||||||
|
base<ParentT>::parse_literal (const std::function<util::json2::callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last,
|
||||||
|
const char (&value)[N])
|
||||||
|
{
|
||||||
|
CHECK_LE (first, last);
|
||||||
|
|
||||||
|
if (last - first < N - 1)
|
||||||
|
throw util::json2::overrun_error { first };
|
||||||
|
|
||||||
|
if (!std::equal (first, first + N - 1, value))
|
||||||
|
throw util::json2::parse_error { first };
|
||||||
|
|
||||||
|
cb ({ first, first + N - 1 });
|
||||||
|
return first + N - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
template <typename ParentT>
|
||||||
|
const char*
|
||||||
|
base<ParentT>::parse_string (const std::function<util::json2::callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last)
|
||||||
|
{
|
||||||
|
CHECK_LE (first, last);
|
||||||
|
|
||||||
|
auto cursor = first;
|
||||||
|
|
||||||
|
cursor = expect (cursor, last, '"');
|
||||||
|
|
||||||
|
for ( ; cursor != last && *cursor != '"'; ) {
|
||||||
|
// advance the simple case first; unescaped character
|
||||||
|
if (*cursor++ != '\\') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*cursor++ == 'u') {
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
switch (*cursor) {
|
||||||
|
case 'a'...'f':
|
||||||
|
case 'A'...'F':
|
||||||
|
++cursor;
|
||||||
|
continue;
|
||||||
|
default:
|
||||||
|
throw util::json2::parse_error { cursor };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = expect (cursor, last, '"');
|
||||||
|
cb ({ first, cursor });
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
template <typename ParentT>
|
||||||
|
const char*
|
||||||
|
base<ParentT>::parse_array (const std::function<util::json2::callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last)
|
||||||
|
{
|
||||||
|
CHECK_LE (first, last);
|
||||||
|
|
||||||
|
auto cursor = first;
|
||||||
|
|
||||||
|
if (*cursor != '[')
|
||||||
|
throw util::json2::parse_error {cursor};
|
||||||
|
cb ({ cursor, cursor + 1 });
|
||||||
|
++cursor;
|
||||||
|
|
||||||
|
cursor = ParentT::consume_whitespace (cursor, last);
|
||||||
|
|
||||||
|
if (*cursor == ']') {
|
||||||
|
cb ({cursor, cursor + 1});
|
||||||
|
return ++cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor = ParentT::parse_value (cb, cursor, last);
|
||||||
|
|
||||||
|
if (*cursor == ']') {
|
||||||
|
cb ({cursor, cursor + 1});
|
||||||
|
return ++cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
cursor = ParentT::consume_whitespace (cursor, last);
|
||||||
|
cursor = expect (cursor, last, ',');
|
||||||
|
cursor = ParentT::consume_whitespace (cursor, last);
|
||||||
|
cursor = ParentT::parse_value (cb, cursor, last);
|
||||||
|
} while (*cursor != ']');
|
||||||
|
|
||||||
|
cb ({cursor, cursor + 1});
|
||||||
|
++cursor;
|
||||||
|
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
template <typename ParentT>
|
||||||
|
const char*
|
||||||
|
base<ParentT>::parse_object (const std::function<util::json2::callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last)
|
||||||
|
{
|
||||||
|
CHECK_LE (first, last);
|
||||||
|
|
||||||
|
auto cursor = first;
|
||||||
|
cursor = expect (cursor, last, '{');
|
||||||
|
cb ({ cursor - 1, cursor });
|
||||||
|
|
||||||
|
cursor = ParentT::consume_whitespace (cursor, last);
|
||||||
|
|
||||||
|
if (*cursor == '}') {
|
||||||
|
cb ({cursor, cursor + 1});
|
||||||
|
return ++cursor;
|
||||||
|
};
|
||||||
|
|
||||||
|
auto parse_member = [] (auto _cb, auto _cursor, auto _last) {
|
||||||
|
_cursor = parse_string (_cb, _cursor, _last);
|
||||||
|
|
||||||
|
_cursor = ParentT::consume_whitespace (_cursor, _last);
|
||||||
|
_cursor = expect (_cursor, _last, ':');
|
||||||
|
_cursor = ParentT::consume_whitespace (_cursor, _last);
|
||||||
|
|
||||||
|
_cursor = ParentT::parse_value (_cb, _cursor, _last);
|
||||||
|
_cursor = ParentT::consume_whitespace (_cursor, _last);
|
||||||
|
|
||||||
|
return _cursor;
|
||||||
|
};
|
||||||
|
|
||||||
|
cursor = parse_member (cb, cursor, last);
|
||||||
|
|
||||||
|
if (*cursor == '}') {
|
||||||
|
cb ({cursor, cursor + 1});
|
||||||
|
return ++cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
do {
|
||||||
|
cursor = expect (cursor, last, ',');
|
||||||
|
cursor = ParentT::consume_whitespace (cursor, last);
|
||||||
|
cursor = parse_member (cb, cursor, last);
|
||||||
|
} while (*cursor != '}');
|
||||||
|
|
||||||
|
cursor = expect (cursor, last, '}');
|
||||||
|
cb ({cursor - 1, cursor});
|
||||||
|
return cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
template <typename ParentT>
|
||||||
|
const char*
|
||||||
|
base<ParentT>::parse_value (const std::function<util::json2::callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last)
|
||||||
|
{
|
||||||
|
switch (*first) {
|
||||||
|
case '-':
|
||||||
|
case '0'...'9':
|
||||||
|
return ParentT::parse_number (cb, first, last);
|
||||||
|
|
||||||
|
case '"':
|
||||||
|
return ParentT::parse_string (cb, first, last);
|
||||||
|
|
||||||
|
case 't': return ParentT::parse_literal (cb, first, last, "true");
|
||||||
|
case 'f': return ParentT::parse_literal (cb, first, last, "false");
|
||||||
|
case 'n': return ParentT::parse_literal (cb, first, last, "null");
|
||||||
|
|
||||||
|
case '[': return ParentT::parse_array (cb, first, last);
|
||||||
|
case '{': return ParentT::parse_object (cb, first, last);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw util::json2::parse_error (first);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
#define INSTANTIATE(KLASS) template struct util::json2::personality::base<KLASS>;
|
||||||
|
MAP_JSON2_PERSONALITY_TYPES (INSTANTIATE)
|
83
json2/personality/base.hpp
Normal file
83
json2/personality/base.hpp
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CRUFT_UTIL_JSON2_PERSONALITY_BASE_HPP
|
||||||
|
#define CRUFT_UTIL_JSON2_PERSONALITY_BASE_HPP
|
||||||
|
|
||||||
|
#include "../fwd.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
|
namespace util::json2::personality {
|
||||||
|
template <typename T>
|
||||||
|
struct base {
|
||||||
|
static const char*
|
||||||
|
consume_whitespace [[nodiscard]] (const char *first, const char *last) noexcept;
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_number [[nodiscard]] (
|
||||||
|
const std::function<callback_t>&,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
static const char*
|
||||||
|
parse_literal [[nodiscard]] (
|
||||||
|
const std::function<callback_t>&,
|
||||||
|
const char *first,
|
||||||
|
const char *last,
|
||||||
|
const char (&value)[N]
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_string [[nodiscard]] (
|
||||||
|
const std::function<callback_t>&,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_array [[nodiscard]] (
|
||||||
|
const std::function<callback_t>&,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_object [[nodiscard]] (
|
||||||
|
const std::function<callback_t>&,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_value [[nodiscard]] (
|
||||||
|
const std::function<callback_t>&,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
24
json2/personality/jsonish.cpp
Normal file
24
json2/personality/jsonish.cpp
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "./jsonish.hpp"
|
||||||
|
|
||||||
|
#include "./base.hpp"
|
||||||
|
|
||||||
|
using util::json2::personality::jsonish;
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
84
json2/personality/jsonish.hpp
Normal file
84
json2/personality/jsonish.hpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CRUFT_UTIL_JSON2_PERSONALITY_JSONISH_HPP
|
||||||
|
#define CRUFT_UTIL_JSON2_PERSONALITY_JSONISH_HPP
|
||||||
|
|
||||||
|
#include "../fwd.hpp"
|
||||||
|
|
||||||
|
#include "./base.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
|
namespace util::json2::personality {
|
||||||
|
struct jsonish {
|
||||||
|
static const char*
|
||||||
|
consume_whitespace [[nodiscard]] (const char *first, const char *last) noexcept
|
||||||
|
{ return base<jsonish>::consume_whitespace (first, last); }
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_value [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first, const char *last
|
||||||
|
) { return base<jsonish>::parse_value (cb, first, last); }
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_number [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
) { return base<jsonish>::parse_number (cb, first, last); }
|
||||||
|
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
static const char*
|
||||||
|
parse_literal [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last,
|
||||||
|
const char (&value)[N]
|
||||||
|
) { return base<jsonish>::parse_literal (cb, first, last, value); }
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_string [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
) { return base<jsonish>::parse_string (cb, first, last); }
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_array [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
) { return base<jsonish>::parse_array (cb, first, last); }
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_object [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
) { return base<jsonish>::parse_object (cb, first, last); }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
21
json2/personality/rfc7519.cpp
Normal file
21
json2/personality/rfc7519.cpp
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "./rfc7519.hpp"
|
||||||
|
|
||||||
|
#include "./base.hpp"
|
||||||
|
|
||||||
|
using util::json2::personality::rfc7159;
|
84
json2/personality/rfc7519.hpp
Normal file
84
json2/personality/rfc7519.hpp
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CRUFT_UTIL_JSON2_PERSONALITY_RFC7159_HPP
|
||||||
|
#define CRUFT_UTIL_JSON2_PERSONALITY_RFC7159_HPP
|
||||||
|
|
||||||
|
#include "./base.hpp"
|
||||||
|
|
||||||
|
#include "../fwd.hpp"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace util::json2::personality {
|
||||||
|
struct rfc7159 {
|
||||||
|
static const char*
|
||||||
|
consume_whitespace [[nodiscard]] (const char *first, const char *last) noexcept
|
||||||
|
{ return base<rfc7159>::consume_whitespace (first, last); }
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_value [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first, const char *last
|
||||||
|
) { return base<rfc7159>::parse_value (cb, first, last); }
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_number [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
) { return base<rfc7159>::parse_number (cb, first, last); }
|
||||||
|
|
||||||
|
|
||||||
|
template <int N>
|
||||||
|
static const char*
|
||||||
|
parse_literal [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last,
|
||||||
|
const char (&value)[N]
|
||||||
|
) { return base<rfc7159>::parse_literal (cb, first, last, value); }
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_string [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
) { return base<rfc7159>::parse_string (cb, first, last); }
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_array [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
) { return base<rfc7159>::parse_array (cb, first, last); }
|
||||||
|
|
||||||
|
|
||||||
|
static const char*
|
||||||
|
parse_object [[nodiscard]] (
|
||||||
|
const std::function<callback_t> &cb,
|
||||||
|
const char *first,
|
||||||
|
const char *last
|
||||||
|
) { return base<rfc7159>::parse_object (cb, first, last); }
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
1
json2/tree.cpp
Normal file
1
json2/tree.cpp
Normal file
@ -0,0 +1 @@
|
|||||||
|
#include "./tree.hpp"
|
22
json2/tree.hpp
Normal file
22
json2/tree.hpp
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
/*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*
|
||||||
|
* Copyright 2017 Danny Robson <danny@nerdcruft.net>
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef CRUFT_JSON2_TREE_HPP
|
||||||
|
#define CRUFT_JSON2_TREE_HPP
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif
|
276
test/json2/event.cpp
Normal file
276
test/json2/event.cpp
Normal file
@ -0,0 +1,276 @@
|
|||||||
|
#include "json2/event.hpp"
|
||||||
|
#include "json2/except.hpp"
|
||||||
|
#include "tap.hpp"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
test_numbers (util::TAP::logger &tap)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
const char *data;
|
||||||
|
bool good;
|
||||||
|
const char *message;
|
||||||
|
} TESTS[] = {
|
||||||
|
{ "1", true, "single digit" },
|
||||||
|
{ "01", false, "leading zero" },
|
||||||
|
{ "-1", true, "leading minus" },
|
||||||
|
{ "1.", false, "truncated fraction" },
|
||||||
|
{ "1.0", true, "fraction" },
|
||||||
|
{ "1.0e", false, "truncated exponential" },
|
||||||
|
{ "1.0e1", true, "lower exponential" },
|
||||||
|
{ "1.0E1", true, "upper exponential" },
|
||||||
|
{ "1.0e+1", true, "positive exponential" },
|
||||||
|
{ "1.0e+", false, "truncated positive exponential" },
|
||||||
|
{ "1.0e-1", true, "negative exponential" },
|
||||||
|
{ "1.0e-", false, "truncated negative exponential" },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &t: TESTS) {
|
||||||
|
auto first = t.data;
|
||||||
|
auto last = first + strlen (first);
|
||||||
|
|
||||||
|
if (!t.good) {
|
||||||
|
tap.expect_throw<util::json2::error> (
|
||||||
|
[&] () {
|
||||||
|
util::json2::event::parse ([] (auto) {}, first, last);
|
||||||
|
},
|
||||||
|
"number, %s",
|
||||||
|
t.message
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
util::json2::event::type_t type;
|
||||||
|
auto end = util::json2::event::parse (
|
||||||
|
[&type] (auto p) { type = p.type (); }, first, last
|
||||||
|
);
|
||||||
|
|
||||||
|
tap.expect (
|
||||||
|
last == end && type == util::json2::event::type_t::NUMBER,
|
||||||
|
"number, %s",
|
||||||
|
t.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
test_literals (util::TAP::logger &tap)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
const char *data;
|
||||||
|
bool good;
|
||||||
|
util::json2::event::type_t type;
|
||||||
|
const char *message;
|
||||||
|
} TESTS[] = {
|
||||||
|
{ "true", true, util::json2::event::type_t::BOOLEAN, "lower true" },
|
||||||
|
{ "TRUE", false, util::json2::event::type_t::BOOLEAN, "upper true" },
|
||||||
|
{ "truE", false, util::json2::event::type_t::BOOLEAN, "mixed true" },
|
||||||
|
{ "tru", false, util::json2::event::type_t::BOOLEAN, "truncated true" },
|
||||||
|
|
||||||
|
{ "false", true, util::json2::event::type_t::BOOLEAN, "lower false" },
|
||||||
|
{ "FALSE", false, util::json2::event::type_t::BOOLEAN, "upper false" },
|
||||||
|
{ "falSe", false, util::json2::event::type_t::BOOLEAN, "mixed false" },
|
||||||
|
{ "fals", false, util::json2::event::type_t::BOOLEAN, "truncated false" },
|
||||||
|
|
||||||
|
{ "null", true, util::json2::event::type_t::NONE, "lower null" },
|
||||||
|
{ "NULL", false, util::json2::event::type_t::NONE, "upper null" },
|
||||||
|
{ "nUll", false, util::json2::event::type_t::NONE, "mixed null" },
|
||||||
|
{ "nul", false, util::json2::event::type_t::NONE, "truncated null" },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &t: TESTS) {
|
||||||
|
auto first = t.data;
|
||||||
|
auto last = first + strlen (first);
|
||||||
|
|
||||||
|
if (t.good) {
|
||||||
|
util::json2::event::type_t type;
|
||||||
|
util::json2::event::parse ([&type] (auto p) { type = p.type (); }, first, last);
|
||||||
|
tap.expect_eq (type, t.type, "literal, %s", t.message);
|
||||||
|
} else {
|
||||||
|
tap.expect_throw<util::json2::parse_error> (
|
||||||
|
[first,last] () {
|
||||||
|
util::json2::event::parse ([] (auto) {}, first, last);
|
||||||
|
},
|
||||||
|
"literal, %s",
|
||||||
|
t.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
test_strings (util::TAP::logger &tap)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
const char *data;
|
||||||
|
bool good;
|
||||||
|
const char *message;
|
||||||
|
} TESTS[] = {
|
||||||
|
{ "\"abc\"", true, "abc" },
|
||||||
|
{ "\"\"", true, "empty" },
|
||||||
|
{ "\"", false, "unbalanced quote" },
|
||||||
|
{ "\"\\n\"", true, "newline escape" },
|
||||||
|
{ "\"\\a\"", true, "valid unnecessary escape" },
|
||||||
|
{ "\"\\uABCD\"", true, "upper unicode hex escape" },
|
||||||
|
{ "\"\\uabcd\"", true, "lower unicode hex escape" },
|
||||||
|
{ "\"\\uab\"", false, "truncated unicode hex escape" },
|
||||||
|
{ "\"\\uabxy\"", false, "invalid unicode hex escape" },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &t: TESTS) {
|
||||||
|
auto first = t.data;
|
||||||
|
auto last = first + strlen (first);
|
||||||
|
|
||||||
|
if (t.good) {
|
||||||
|
util::json2::event::type_t type;
|
||||||
|
util::json2::event::parse ([&type] (auto p) { type = p.type (); }, first, last);
|
||||||
|
tap.expect_eq (type, util::json2::event::type_t::STRING, "string, %s", t.message);
|
||||||
|
} else {
|
||||||
|
tap.expect_throw<util::json2::parse_error> (
|
||||||
|
[first,last] () {
|
||||||
|
util::json2::event::parse ([] (auto) {}, first, last);
|
||||||
|
},
|
||||||
|
"string, %s",
|
||||||
|
t.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
void
|
||||||
|
test_arrays (util::TAP::logger &tap)
|
||||||
|
{
|
||||||
|
using util::json2::event::type_t;
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
const char *data;
|
||||||
|
bool good;
|
||||||
|
std::vector<util::json2::event::type_t> types;
|
||||||
|
const char *message;
|
||||||
|
} TESTS[] = {
|
||||||
|
{ "[]", true, { type_t::ARRAY_BEGIN, type_t::ARRAY_END }, "empty" },
|
||||||
|
{ "[1]", true, { type_t::ARRAY_BEGIN, type_t::NUMBER, type_t::ARRAY_END }, "single number" },
|
||||||
|
{ "[1true]", false, { type_t::ARRAY_BEGIN, type_t::NUMBER }, "contatenated number/bool" },
|
||||||
|
{ "[1,2]", true, { type_t::ARRAY_BEGIN, type_t::NUMBER, type_t::NUMBER, type_t::ARRAY_END }, "two numbers" },
|
||||||
|
{ "[1,]", false, { type_t::ARRAY_BEGIN, type_t::NUMBER }, "single trailing comma" },
|
||||||
|
{ "[1,2,]", false, { type_t::ARRAY_BEGIN, type_t::NUMBER, type_t::NUMBER }, "double trailing comma" },
|
||||||
|
{ "[,]", false, { type_t::ARRAY_BEGIN }, "only comma" },
|
||||||
|
{ "[", false, { type_t::ARRAY_BEGIN }, "missing terminator" },
|
||||||
|
{ "[[]]", true, { type_t::ARRAY_BEGIN, type_t::ARRAY_BEGIN, type_t::ARRAY_END, type_t::ARRAY_END }, "nested array" },
|
||||||
|
{ "[[]", false, { type_t::ARRAY_BEGIN, type_t::ARRAY_END }, "unbalanced nested array" },
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &t: TESTS) {
|
||||||
|
auto first = t.data;
|
||||||
|
auto last = first + strlen (first);
|
||||||
|
|
||||||
|
if (t.good) {
|
||||||
|
std::vector<type_t> types;
|
||||||
|
util::json2::event::parse ([&types] (auto p) { types.push_back (p.type ()); }, first, last);
|
||||||
|
tap.expect_eq (types, t.types, "array, %s", t.message);
|
||||||
|
} else {
|
||||||
|
tap.expect_throw<util::json2::parse_error> (
|
||||||
|
[&] () {
|
||||||
|
util::json2::event::parse ([] (auto) { }, first, last);
|
||||||
|
}, "array, %s", t.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
void
|
||||||
|
test_objects (util::TAP::logger &tap)
|
||||||
|
{
|
||||||
|
using util::json2::event::type_t;
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
const char *data;
|
||||||
|
bool good;
|
||||||
|
std::vector<type_t> types;
|
||||||
|
std::vector<std::string> strings;
|
||||||
|
const char *message;
|
||||||
|
} TESTS[] = {
|
||||||
|
{ "{}", true, { type_t::OBJECT_BEGIN, type_t::OBJECT_END }, {}, "empty" },
|
||||||
|
{ "{", false, { type_t::OBJECT_BEGIN }, {}, "missing terminator" },
|
||||||
|
{ "{\"a\":1}", true, { type_t::OBJECT_BEGIN, type_t::STRING, type_t::NUMBER, type_t::OBJECT_END }, {"\"a\""}, "empty" },
|
||||||
|
{ "{1:1}", false, { type_t::OBJECT_BEGIN }, {}, "integer key" },
|
||||||
|
{ "{:1}", false, { type_t::OBJECT_BEGIN }, {}, "no key" },
|
||||||
|
{ "{\"a\":}", false, { type_t::OBJECT_BEGIN, type_t::STRING }, {}, "no value" },
|
||||||
|
{
|
||||||
|
R"json({"a":[]})json",
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
type_t::OBJECT_BEGIN,
|
||||||
|
type_t::STRING,
|
||||||
|
type_t::ARRAY_BEGIN,
|
||||||
|
type_t::ARRAY_END,
|
||||||
|
type_t::OBJECT_END
|
||||||
|
},
|
||||||
|
{ "\"a\"" },
|
||||||
|
"array value" },
|
||||||
|
{
|
||||||
|
R"json({ "a": { "b": null } })json",
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
type_t::OBJECT_BEGIN,
|
||||||
|
type_t::STRING,
|
||||||
|
type_t::OBJECT_BEGIN,
|
||||||
|
type_t::STRING,
|
||||||
|
type_t::NONE,
|
||||||
|
type_t::OBJECT_END,
|
||||||
|
type_t::OBJECT_END
|
||||||
|
},
|
||||||
|
{ "\"a\"", "\"b\"" },
|
||||||
|
"recursive object"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const auto &t: TESTS) {
|
||||||
|
auto first = t.data;
|
||||||
|
auto last = first + strlen (first);
|
||||||
|
|
||||||
|
if (t.good) {
|
||||||
|
std::vector<type_t> types;
|
||||||
|
std::vector<std::string> strings;
|
||||||
|
|
||||||
|
util::json2::event::parse ([&] (auto p) {
|
||||||
|
types.push_back (p.type ());
|
||||||
|
if (p.type () == type_t::STRING)
|
||||||
|
strings.push_back (std::string {p.first, p.last});
|
||||||
|
}, first, last);
|
||||||
|
|
||||||
|
tap.expect (types == t.types && strings == t.strings, "object, %s", t.message);
|
||||||
|
} else {
|
||||||
|
tap.expect_throw<util::json2::parse_error> (
|
||||||
|
[&] () {
|
||||||
|
util::json2::event::parse ([] (auto) {}, first, last);
|
||||||
|
}, "object, %s", t.message
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////
|
||||||
|
int
|
||||||
|
main (void)
|
||||||
|
{
|
||||||
|
util::TAP::logger tap;
|
||||||
|
|
||||||
|
test_numbers (tap);
|
||||||
|
test_literals (tap);
|
||||||
|
test_strings (tap);
|
||||||
|
test_arrays (tap);
|
||||||
|
test_objects (tap);
|
||||||
|
|
||||||
|
return tap.status ();
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user