Merge branch 'develop' into feature/iterator_range_parsing
diff --git a/src/json.hpp b/src/json.hpp
index ddf37eb..03c1625 100644
--- a/src/json.hpp
+++ b/src/json.hpp
@@ -37,6 +37,7 @@
#include <cstddef>
#include <cstdint>
#include <cstdlib>
+#include <cstring>
#include <functional>
#include <initializer_list>
#include <iomanip>
@@ -3960,7 +3961,7 @@
@return Iterator following the last removed element. If the iterator @a
pos refers to the last element, the `end()` iterator is returned.
- @tparam InteratorType an @ref iterator or @ref const_iterator
+ @tparam IteratorType an @ref iterator or @ref const_iterator
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
@@ -3982,7 +3983,7 @@
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType}
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@@ -3991,13 +3992,13 @@
@since version 1.0.0
*/
- template <class InteratorType, typename
+ template <class IteratorType, typename
std::enable_if<
- std::is_same<InteratorType, typename basic_json_t::iterator>::value or
- std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value
, int>::type
= 0>
- InteratorType erase(InteratorType pos)
+ IteratorType erase(IteratorType pos)
{
// make sure iterator fits the current value
if (this != pos.m_object)
@@ -4005,7 +4006,7 @@
throw std::domain_error("iterator does not fit current value");
}
- InteratorType result = end();
+ IteratorType result = end();
switch (m_type)
{
@@ -4069,7 +4070,7 @@
@return Iterator following the last removed element. If the iterator @a
second refers to the last element, the `end()` iterator is returned.
- @tparam InteratorType an @ref iterator or @ref const_iterator
+ @tparam IteratorType an @ref iterator or @ref const_iterator
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
@@ -4092,7 +4093,7 @@
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType_IteratorType}
- @sa @ref erase(InteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType) -- removes the element at a given position
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@sa @ref erase(const size_type) -- removes the element from an array at
@@ -4100,13 +4101,13 @@
@since version 1.0.0
*/
- template <class InteratorType, typename
+ template <class IteratorType, typename
std::enable_if<
- std::is_same<InteratorType, typename basic_json_t::iterator>::value or
- std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value
, int>::type
= 0>
- InteratorType erase(InteratorType first, InteratorType last)
+ IteratorType erase(IteratorType first, IteratorType last)
{
// make sure iterator fits the current value
if (this != first.m_object or this != last.m_object)
@@ -4114,7 +4115,7 @@
throw std::domain_error("iterators do not fit current value");
}
- InteratorType result = end();
+ IteratorType result = end();
switch (m_type)
{
@@ -4186,8 +4187,8 @@
@liveexample{The example shows the effect of `erase()`.,erase__key_type}
- @sa @ref erase(InteratorType) -- removes the element at a given position
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const size_type) -- removes the element from an array at
the given index
@@ -4223,8 +4224,8 @@
@liveexample{The example shows the effect of `erase()`.,erase__size_type}
- @sa @ref erase(InteratorType) -- removes the element at a given position
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@@ -7504,32 +7505,25 @@
/// the char type to use in the lexer
using lexer_char_t = unsigned char;
- /// constructor with a given buffer
- explicit lexer(const string_t& s) noexcept
- : m_stream(nullptr), m_buffer(s)
+ /// a lexer from a buffer with given length
+ lexer(const lexer_char_t* buff, const size_t len) noexcept
+ : m_content(buff)
{
- m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
assert(m_content != nullptr);
m_start = m_cursor = m_content;
- m_limit = m_content + s.size();
+ m_limit = m_content + len;
}
- /// constructor with a given stream
- explicit lexer(std::istream* s) noexcept
- : m_stream(s), m_buffer()
+ /// a lexer from an input stream
+ explicit lexer(std::istream& s)
+ : m_stream(&s), m_line_buffer()
{
- assert(m_stream != nullptr);
- std::getline(*m_stream, m_buffer);
- m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
- assert(m_content != nullptr);
- m_start = m_cursor = m_content;
- m_limit = m_content + m_buffer.size();
+ // fill buffer
+ fill_line_buffer();
}
- /// default constructor
- lexer() = default;
-
// switch off unwanted functions
+ lexer() = delete;
lexer(const lexer&) = delete;
lexer operator=(const lexer&) = delete;
@@ -7682,7 +7676,7 @@
infinite sequence of whitespace or byte-order-marks. This contradicts
the assumption of finite input, q.e.d.
*/
- token_type scan() noexcept
+ token_type scan()
{
while (true)
{
@@ -7734,7 +7728,7 @@
};
if ((m_limit - m_cursor) < 5)
{
- yyfill(); // LCOV_EXCL_LINE;
+ fill_line_buffer(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yybm[0 + yych] & 32)
@@ -7868,7 +7862,7 @@
++m_cursor;
if (m_limit <= m_cursor)
{
- yyfill(); // LCOV_EXCL_LINE;
+ fill_line_buffer(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yybm[0 + yych] & 32)
@@ -7938,7 +7932,7 @@
m_marker = ++m_cursor;
if ((m_limit - m_cursor) < 3)
{
- yyfill(); // LCOV_EXCL_LINE;
+ fill_line_buffer(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yybm[0 + yych] & 64)
@@ -8031,7 +8025,7 @@
++m_cursor;
if (m_limit <= m_cursor)
{
- yyfill(); // LCOV_EXCL_LINE;
+ fill_line_buffer(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
basic_json_parser_32:
@@ -8068,7 +8062,7 @@
++m_cursor;
if (m_limit <= m_cursor)
{
- yyfill(); // LCOV_EXCL_LINE;
+ fill_line_buffer(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yych <= 'e')
@@ -8212,7 +8206,7 @@
++m_cursor;
if (m_limit <= m_cursor)
{
- yyfill(); // LCOV_EXCL_LINE;
+ fill_line_buffer(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yych <= '@')
@@ -8248,7 +8242,7 @@
m_marker = ++m_cursor;
if ((m_limit - m_cursor) < 3)
{
- yyfill(); // LCOV_EXCL_LINE;
+ fill_line_buffer(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yych <= 'D')
@@ -8289,7 +8283,7 @@
++m_cursor;
if (m_limit <= m_cursor)
{
- yyfill(); // LCOV_EXCL_LINE;
+ fill_line_buffer(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yych <= '/')
@@ -8331,7 +8325,7 @@
++m_cursor;
if (m_limit <= m_cursor)
{
- yyfill(); // LCOV_EXCL_LINE;
+ fill_line_buffer(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yych <= '@')
@@ -8385,7 +8379,7 @@
++m_cursor;
if (m_limit <= m_cursor)
{
- yyfill(); // LCOV_EXCL_LINE;
+ fill_line_buffer(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yych <= '@')
@@ -8426,7 +8420,7 @@
++m_cursor;
if (m_limit <= m_cursor)
{
- yyfill(); // LCOV_EXCL_LINE;
+ fill_line_buffer(); // LCOV_EXCL_LINE;
}
yych = *m_cursor;
if (yych <= '@')
@@ -8464,30 +8458,57 @@
return last_token_type;
}
- /// append data from the stream to the internal buffer
- void yyfill() noexcept
+ /*!
+ @brief append data from the stream to the line buffer
+
+ This function is called by the scan() function when the end of the
+ buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be
+ incremented without leaving the limits of the line buffer. Note re2c
+ decides when to call this function.
+
+ @pre
+ p p p p p p u u u u u x . . . . . .
+ ^ ^ ^ ^
+ m_content m_start | m_limit
+ m_cursor
+
+ @post
+ u u u u u x x x x x x x . . . . . .
+ ^ ^ ^
+ | m_cursor m_limit
+ m_start
+ m_content
+ */
+ void fill_line_buffer()
{
+ // no stream is used or end of file is reached
if (m_stream == nullptr or not * m_stream)
{
return;
}
+ // number of processed characters (p)
const auto offset_start = m_start - m_content;
+ // offset for m_marker wrt. to m_start
const auto offset_marker = m_marker - m_start;
+ // number of unprocessed characters (u)
const auto offset_cursor = m_cursor - m_start;
- m_buffer.erase(0, static_cast<size_t>(offset_start));
+ // delete processed characters from line buffer
+ m_line_buffer.erase(0, static_cast<size_t>(offset_start));
+ // read next line from input stream
std::string line;
- assert(m_stream != nullptr);
std::getline(*m_stream, line);
- m_buffer += "\n" + line; // add line with newline symbol
+ // add line with newline symbol to the line buffer
+ m_line_buffer += "\n" + line;
- m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
+ // set pointers
+ m_content = reinterpret_cast<const lexer_char_t*>(m_line_buffer.c_str());
assert(m_content != nullptr);
m_start = m_content;
m_marker = m_start + offset_marker;
m_cursor = m_start + offset_cursor;
- m_limit = m_start + m_buffer.size() - 1;
+ m_limit = m_start + m_line_buffer.size() - 1;
}
/// return string representation of last read token
@@ -8829,8 +8850,8 @@
private:
/// optional input stream
std::istream* m_stream = nullptr;
- /// the buffer
- string_t m_buffer;
+ /// line buffer buffer for m_stream
+ string_t m_line_buffer {};
/// the buffer pointer
const lexer_char_t* m_content = nullptr;
/// pointer to the beginning of the current symbol
@@ -8853,25 +8874,48 @@
class parser
{
public:
- /// constructor for strings
- parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(s)
- {
- // read first token
- get_token();
- }
+ /// a parser reading from a string literal
+ parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr)
+ : callback(cb),
+ m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(buff),
+ strlen(buff))
+ {}
+
+ /// a parser reading from a string container
+ parser(const string_t& s, const parser_callback_t cb = nullptr)
+ : callback(cb),
+ m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(s.c_str()), s.size())
+ {}
/// a parser reading from an input stream
- parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(&_is)
+ parser(std::istream& is, const parser_callback_t cb = nullptr)
+ : callback(cb), m_lexer(is)
+ {}
+
+ /// a parser reading from a container with contiguous storage
+ template <class IteratorType, typename
+ std::enable_if<
+ std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
+ , int>::type
+ = 0>
+ parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr)
+ : callback(cb),
+ m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(&(*first)),
+ static_cast<size_t>(std::distance(first, last)))
{
- // read first token
- get_token();
+ int i = 0;
+ assert(std::accumulate(first, last, true, [&i, &first](bool res, decltype(*first) val)
+ {
+ return res and (val == *(std::next(std::addressof(*first), i++)));
+ }));
}
/// public parser interface
basic_json parse()
{
+ // read first token
+ get_token();
+
basic_json result = parse_internal(true);
result.assert_invariant();
@@ -9076,7 +9120,7 @@
}
/// get next token from lexer
- typename lexer::token_type get_token() noexcept
+ typename lexer::token_type get_token()
{
last_token = m_lexer.scan();
return last_token;
diff --git a/src/json.hpp.re2c b/src/json.hpp.re2c
index 740c768..58724e9 100644
--- a/src/json.hpp.re2c
+++ b/src/json.hpp.re2c
@@ -37,6 +37,7 @@
#include <cstddef>
#include <cstdint>
#include <cstdlib>
+#include <cstring>
#include <functional>
#include <initializer_list>
#include <iomanip>
@@ -3960,7 +3961,7 @@
@return Iterator following the last removed element. If the iterator @a
pos refers to the last element, the `end()` iterator is returned.
- @tparam InteratorType an @ref iterator or @ref const_iterator
+ @tparam IteratorType an @ref iterator or @ref const_iterator
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
@@ -3982,7 +3983,7 @@
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType}
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@@ -3991,13 +3992,13 @@
@since version 1.0.0
*/
- template <class InteratorType, typename
+ template <class IteratorType, typename
std::enable_if<
- std::is_same<InteratorType, typename basic_json_t::iterator>::value or
- std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value
, int>::type
= 0>
- InteratorType erase(InteratorType pos)
+ IteratorType erase(IteratorType pos)
{
// make sure iterator fits the current value
if (this != pos.m_object)
@@ -4005,7 +4006,7 @@
throw std::domain_error("iterator does not fit current value");
}
- InteratorType result = end();
+ IteratorType result = end();
switch (m_type)
{
@@ -4069,7 +4070,7 @@
@return Iterator following the last removed element. If the iterator @a
second refers to the last element, the `end()` iterator is returned.
- @tparam InteratorType an @ref iterator or @ref const_iterator
+ @tparam IteratorType an @ref iterator or @ref const_iterator
@post Invalidates iterators and references at or after the point of the
erase, including the `end()` iterator.
@@ -4092,7 +4093,7 @@
@liveexample{The example shows the result of `erase()` for different JSON
types.,erase__IteratorType_IteratorType}
- @sa @ref erase(InteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType) -- removes the element at a given position
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@sa @ref erase(const size_type) -- removes the element from an array at
@@ -4100,13 +4101,13 @@
@since version 1.0.0
*/
- template <class InteratorType, typename
+ template <class IteratorType, typename
std::enable_if<
- std::is_same<InteratorType, typename basic_json_t::iterator>::value or
- std::is_same<InteratorType, typename basic_json_t::const_iterator>::value
+ std::is_same<IteratorType, typename basic_json_t::iterator>::value or
+ std::is_same<IteratorType, typename basic_json_t::const_iterator>::value
, int>::type
= 0>
- InteratorType erase(InteratorType first, InteratorType last)
+ IteratorType erase(IteratorType first, IteratorType last)
{
// make sure iterator fits the current value
if (this != first.m_object or this != last.m_object)
@@ -4114,7 +4115,7 @@
throw std::domain_error("iterators do not fit current value");
}
- InteratorType result = end();
+ IteratorType result = end();
switch (m_type)
{
@@ -4186,8 +4187,8 @@
@liveexample{The example shows the effect of `erase()`.,erase__key_type}
- @sa @ref erase(InteratorType) -- removes the element at a given position
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const size_type) -- removes the element from an array at
the given index
@@ -4223,8 +4224,8 @@
@liveexample{The example shows the effect of `erase()`.,erase__size_type}
- @sa @ref erase(InteratorType) -- removes the element at a given position
- @sa @ref erase(InteratorType, InteratorType) -- removes the elements in
+ @sa @ref erase(IteratorType) -- removes the element at a given position
+ @sa @ref erase(IteratorType, IteratorType) -- removes the elements in
the given range
@sa @ref erase(const typename object_t::key_type&) -- removes the element
from an object at the given key
@@ -7504,32 +7505,25 @@
/// the char type to use in the lexer
using lexer_char_t = unsigned char;
- /// constructor with a given buffer
- explicit lexer(const string_t& s) noexcept
- : m_stream(nullptr), m_buffer(s)
+ /// a lexer from a buffer with given length
+ lexer(const lexer_char_t* buff, const size_t len) noexcept
+ : m_content(buff)
{
- m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
assert(m_content != nullptr);
m_start = m_cursor = m_content;
- m_limit = m_content + s.size();
+ m_limit = m_content + len;
}
- /// constructor with a given stream
- explicit lexer(std::istream* s) noexcept
- : m_stream(s), m_buffer()
+ /// a lexer from an input stream
+ explicit lexer(std::istream& s)
+ : m_stream(&s), m_line_buffer()
{
- assert(m_stream != nullptr);
- std::getline(*m_stream, m_buffer);
- m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
- assert(m_content != nullptr);
- m_start = m_cursor = m_content;
- m_limit = m_content + m_buffer.size();
+ // fill buffer
+ fill_line_buffer();
}
- /// default constructor
- lexer() = default;
-
// switch off unwanted functions
+ lexer() = delete;
lexer(const lexer&) = delete;
lexer operator=(const lexer&) = delete;
@@ -7682,7 +7676,7 @@
infinite sequence of whitespace or byte-order-marks. This contradicts
the assumption of finite input, q.e.d.
*/
- token_type scan() noexcept
+ token_type scan()
{
while (true)
{
@@ -7698,7 +7692,7 @@
re2c:define:YYCURSOR = m_cursor;
re2c:define:YYLIMIT = m_limit;
re2c:define:YYMARKER = m_marker;
- re2c:define:YYFILL = "yyfill(); // LCOV_EXCL_LINE";
+ re2c:define:YYFILL = "fill_line_buffer(); // LCOV_EXCL_LINE";
re2c:yyfill:parameter = 0;
re2c:indent:string = " ";
re2c:indent:top = 1;
@@ -7761,30 +7755,57 @@
return last_token_type;
}
- /// append data from the stream to the internal buffer
- void yyfill() noexcept
+ /*!
+ @brief append data from the stream to the line buffer
+
+ This function is called by the scan() function when the end of the
+ buffer (`m_limit`) is reached and the `m_cursor` pointer cannot be
+ incremented without leaving the limits of the line buffer. Note re2c
+ decides when to call this function.
+
+ @pre
+ p p p p p p u u u u u x . . . . . .
+ ^ ^ ^ ^
+ m_content m_start | m_limit
+ m_cursor
+
+ @post
+ u u u u u x x x x x x x . . . . . .
+ ^ ^ ^
+ | m_cursor m_limit
+ m_start
+ m_content
+ */
+ void fill_line_buffer()
{
+ // no stream is used or end of file is reached
if (m_stream == nullptr or not * m_stream)
{
return;
}
+ // number of processed characters (p)
const auto offset_start = m_start - m_content;
+ // offset for m_marker wrt. to m_start
const auto offset_marker = m_marker - m_start;
+ // number of unprocessed characters (u)
const auto offset_cursor = m_cursor - m_start;
- m_buffer.erase(0, static_cast<size_t>(offset_start));
+ // delete processed characters from line buffer
+ m_line_buffer.erase(0, static_cast<size_t>(offset_start));
+ // read next line from input stream
std::string line;
- assert(m_stream != nullptr);
std::getline(*m_stream, line);
- m_buffer += "\n" + line; // add line with newline symbol
+ // add line with newline symbol to the line buffer
+ m_line_buffer += "\n" + line;
- m_content = reinterpret_cast<const lexer_char_t*>(m_buffer.c_str());
+ // set pointers
+ m_content = reinterpret_cast<const lexer_char_t*>(m_line_buffer.c_str());
assert(m_content != nullptr);
m_start = m_content;
m_marker = m_start + offset_marker;
m_cursor = m_start + offset_cursor;
- m_limit = m_start + m_buffer.size() - 1;
+ m_limit = m_start + m_line_buffer.size() - 1;
}
/// return string representation of last read token
@@ -8126,8 +8147,8 @@
private:
/// optional input stream
std::istream* m_stream = nullptr;
- /// the buffer
- string_t m_buffer;
+ /// line buffer buffer for m_stream
+ string_t m_line_buffer {};
/// the buffer pointer
const lexer_char_t* m_content = nullptr;
/// pointer to the beginning of the current symbol
@@ -8150,25 +8171,48 @@
class parser
{
public:
- /// constructor for strings
- parser(const string_t& s, const parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(s)
- {
- // read first token
- get_token();
- }
+ /// a parser reading from a string literal
+ parser(const typename string_t::value_type* buff, parser_callback_t cb = nullptr)
+ : callback(cb),
+ m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(buff),
+ strlen(buff))
+ {}
+
+ /// a parser reading from a string container
+ parser(const string_t& s, const parser_callback_t cb = nullptr)
+ : callback(cb),
+ m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(s.c_str()), s.size())
+ {}
/// a parser reading from an input stream
- parser(std::istream& _is, const parser_callback_t cb = nullptr) noexcept
- : callback(cb), m_lexer(&_is)
+ parser(std::istream& is, const parser_callback_t cb = nullptr)
+ : callback(cb), m_lexer(is)
+ {}
+
+ /// a parser reading from a container with contiguous storage
+ template <class IteratorType, typename
+ std::enable_if<
+ std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value
+ , int>::type
+ = 0>
+ parser(IteratorType first, IteratorType last, const parser_callback_t cb = nullptr)
+ : callback(cb),
+ m_lexer(reinterpret_cast<const typename lexer::lexer_char_t*>(&(*first)),
+ static_cast<size_t>(std::distance(first, last)))
{
- // read first token
- get_token();
+ int i = 0;
+ assert(std::accumulate(first, last, true, [&i, &first](bool res, decltype(*first) val)
+ {
+ return res and (val == *(std::next(std::addressof(*first), i++)));
+ }));
}
/// public parser interface
basic_json parse()
{
+ // read first token
+ get_token();
+
basic_json result = parse_internal(true);
result.assert_invariant();
@@ -8373,7 +8417,7 @@
}
/// get next token from lexer
- typename lexer::token_type get_token() noexcept
+ typename lexer::token_type get_token()
{
last_token = m_lexer.scan();
return last_token;
diff --git a/test/src/unit-class_lexer.cpp b/test/src/unit-class_lexer.cpp
index 708a8cb..7cece21 100644
--- a/test/src/unit-class_lexer.cpp
+++ b/test/src/unit-class_lexer.cpp
@@ -38,43 +38,67 @@
{
SECTION("structural characters")
{
- CHECK(json::lexer("[").scan() == json::lexer::token_type::begin_array);
- CHECK(json::lexer("]").scan() == json::lexer::token_type::end_array);
- CHECK(json::lexer("{").scan() == json::lexer::token_type::begin_object);
- CHECK(json::lexer("}").scan() == json::lexer::token_type::end_object);
- CHECK(json::lexer(",").scan() == json::lexer::token_type::value_separator);
- CHECK(json::lexer(":").scan() == json::lexer::token_type::name_separator);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("["),
+ 1).scan() == json::lexer::token_type::begin_array);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("]"),
+ 1).scan() == json::lexer::token_type::end_array);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("{"),
+ 1).scan() == json::lexer::token_type::begin_object);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("}"),
+ 1).scan() == json::lexer::token_type::end_object);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(","),
+ 1).scan() == json::lexer::token_type::value_separator);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(":"),
+ 1).scan() == json::lexer::token_type::name_separator);
}
SECTION("literal names")
{
- CHECK(json::lexer("null").scan() == json::lexer::token_type::literal_null);
- CHECK(json::lexer("true").scan() == json::lexer::token_type::literal_true);
- CHECK(json::lexer("false").scan() == json::lexer::token_type::literal_false);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("null"),
+ 4).scan() == json::lexer::token_type::literal_null);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("true"),
+ 4).scan() == json::lexer::token_type::literal_true);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("false"),
+ 5).scan() == json::lexer::token_type::literal_false);
}
SECTION("numbers")
{
- CHECK(json::lexer("0").scan() == json::lexer::token_type::value_number);
- CHECK(json::lexer("1").scan() == json::lexer::token_type::value_number);
- CHECK(json::lexer("2").scan() == json::lexer::token_type::value_number);
- CHECK(json::lexer("3").scan() == json::lexer::token_type::value_number);
- CHECK(json::lexer("4").scan() == json::lexer::token_type::value_number);
- CHECK(json::lexer("5").scan() == json::lexer::token_type::value_number);
- CHECK(json::lexer("6").scan() == json::lexer::token_type::value_number);
- CHECK(json::lexer("7").scan() == json::lexer::token_type::value_number);
- CHECK(json::lexer("8").scan() == json::lexer::token_type::value_number);
- CHECK(json::lexer("9").scan() == json::lexer::token_type::value_number);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("0"),
+ 1).scan() == json::lexer::token_type::value_number);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("1"),
+ 1).scan() == json::lexer::token_type::value_number);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("2"),
+ 1).scan() == json::lexer::token_type::value_number);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("3"),
+ 1).scan() == json::lexer::token_type::value_number);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("4"),
+ 1).scan() == json::lexer::token_type::value_number);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("5"),
+ 1).scan() == json::lexer::token_type::value_number);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("6"),
+ 1).scan() == json::lexer::token_type::value_number);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("7"),
+ 1).scan() == json::lexer::token_type::value_number);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("8"),
+ 1).scan() == json::lexer::token_type::value_number);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("9"),
+ 1).scan() == json::lexer::token_type::value_number);
}
SECTION("whitespace")
{
// result is end_of_input, because not token is following
- CHECK(json::lexer(" ").scan() == json::lexer::token_type::end_of_input);
- CHECK(json::lexer("\t").scan() == json::lexer::token_type::end_of_input);
- CHECK(json::lexer("\n").scan() == json::lexer::token_type::end_of_input);
- CHECK(json::lexer("\r").scan() == json::lexer::token_type::end_of_input);
- CHECK(json::lexer(" \t\n\r\n\t ").scan() == json::lexer::token_type::end_of_input);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" "),
+ 1).scan() == json::lexer::token_type::end_of_input);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\t"),
+ 1).scan() == json::lexer::token_type::end_of_input);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\n"),
+ 1).scan() == json::lexer::token_type::end_of_input);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>("\r"),
+ 1).scan() == json::lexer::token_type::end_of_input);
+ CHECK(json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(" \t\n\r\n\t "),
+ 7).scan() == json::lexer::token_type::end_of_input);
}
}
@@ -100,7 +124,11 @@
{
for (int c = 1; c < 128; ++c)
{
- auto s = std::string(1, c);
+ // create string from the ASCII code
+ const auto s = std::string(1, c);
+ // store scan() result
+ const auto res = json::lexer(reinterpret_cast<const json::lexer::lexer_char_t*>(s.c_str()),
+ 1).scan();
switch (c)
{
@@ -122,7 +150,7 @@
case ('8'):
case ('9'):
{
- CHECK(json::lexer(s.c_str()).scan() != json::lexer::token_type::parse_error);
+ CHECK(res != json::lexer::token_type::parse_error);
break;
}
@@ -132,14 +160,14 @@
case ('\n'):
case ('\r'):
{
- CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::end_of_input);
+ CHECK(res == json::lexer::token_type::end_of_input);
break;
}
// anything else is not expected
default:
{
- CHECK(json::lexer(s.c_str()).scan() == json::lexer::token_type::parse_error);
+ CHECK(res == json::lexer::token_type::parse_error);
break;
}
}
diff --git a/test/src/unit-class_parser.cpp b/test/src/unit-class_parser.cpp
index fe00550..b6d6a61 100644
--- a/test/src/unit-class_parser.cpp
+++ b/test/src/unit-class_parser.cpp
@@ -32,6 +32,8 @@
#include "json.hpp"
using nlohmann::json;
+#include <valarray>
+
TEST_CASE("parser class")
{
SECTION("parse")
@@ -743,11 +745,47 @@
}
}
- SECTION("copy constructor")
+ SECTION("constructing from contiguous containers")
{
- json::string_t* s = new json::string_t("[1,2,3,4]");
- json::parser p(*s);
- delete s;
- CHECK(p.parse() == json({1, 2, 3, 4}));
+ SECTION("from std::vector")
+ {
+ std::vector<uint8_t> v = {'t', 'r', 'u', 'e', '\0'};
+ CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+ }
+
+ SECTION("from std::array")
+ {
+ std::array<uint8_t, 5> v { {'t', 'r', 'u', 'e', '\0'} };
+ CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+ }
+
+ SECTION("from array")
+ {
+ uint8_t v[] = {'t', 'r', 'u', 'e'};
+ CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+ }
+
+ SECTION("from char literal")
+ {
+ CHECK(json::parser("true").parse() == json(true));
+ }
+
+ SECTION("from std::string")
+ {
+ std::string v = {'t', 'r', 'u', 'e'};
+ CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+ }
+
+ SECTION("from std::initializer_list")
+ {
+ std::initializer_list<uint8_t> v = {'t', 'r', 'u', 'e', '\0'};
+ CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+ }
+
+ SECTION("from std::valarray")
+ {
+ std::valarray<uint8_t> v = {'t', 'r', 'u', 'e', '\0'};
+ CHECK(json::parser(std::begin(v), std::end(v)).parse() == json(true));
+ }
}
}