json2: initial import
This commit is contained in:
parent
d64e3d244e
commit
340981dac2
@ -318,6 +318,18 @@ list (
|
||||
json/schema.hpp
|
||||
json/tree.cpp
|
||||
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
|
||||
log.cpp
|
||||
log.hpp
|
||||
@ -509,6 +521,7 @@ if (TESTS)
|
||||
iterator
|
||||
job/queue
|
||||
json_types
|
||||
json2/event
|
||||
maths
|
||||
matrix
|
||||
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