Commit d12398ad authored by Dominik Charousset's avatar Dominik Charousset

Allow parsing JSON directly from file

parent e8db2cb1
...@@ -682,7 +682,19 @@ const array* empty_array() noexcept; ...@@ -682,7 +682,19 @@ const array* empty_array() noexcept;
// -- parsing ------------------------------------------------------------------ // -- parsing ------------------------------------------------------------------
// Parses the input and makes a deep copy of all strings. // Specialization for parsers operating on mutable character sequences.
using mutable_string_parser_state = parser_state<char*>;
// Specialization for parsers operating on files.
using file_parser_state = parser_state<std::istreambuf_iterator<char>>;
// Parses the input string and makes a deep copy of all strings.
value* parse(string_parser_state& ps, monotonic_buffer_resource* storage);
// Parses the input string and makes a deep copy of all strings.
value* parse(file_parser_state& ps, monotonic_buffer_resource* storage);
// Parses the input file and makes a deep copy of all strings.
value* parse(string_parser_state& ps, monotonic_buffer_resource* storage); value* parse(string_parser_state& ps, monotonic_buffer_resource* storage);
// Parses the input and makes a shallow copy of strings whenever possible. // Parses the input and makes a shallow copy of strings whenever possible.
......
...@@ -133,6 +133,17 @@ public: ...@@ -133,6 +133,17 @@ public:
/// @note Implicitly calls `reset`. /// @note Implicitly calls `reset`.
bool load(std::string_view json_text); bool load(std::string_view json_text);
/// Parses the content of the file under the given @p path. After loading the
/// content of the JSON file, the reader is ready for attempting to
/// deserialize inspectable objects.
/// @note Implicitly calls `reset`.
bool load_file(const char* path);
/// @copydoc load_file
bool load_file(const std::string& path) {
return load_file(path.c_str());
}
/// Reverts the state of the reader back to where it was after calling `load`. /// Reverts the state of the reader back to where it was after calling `load`.
/// @post The reader is ready for attempting to deserialize another /// @post The reader is ready for attempting to deserialize another
/// inspectable object. /// inspectable object.
......
...@@ -134,6 +134,13 @@ public: ...@@ -134,6 +134,13 @@ public:
/// objects created from that value. /// objects created from that value.
static expected<json_value> parse_in_situ(std::string& str); static expected<json_value> parse_in_situ(std::string& str);
/// Attempts to parse the content of the file at @p path as JSON input into a
/// self-contained value.
static expected<json_value> parse_file(const char* path);
/// @copydoc parse_file
static expected<json_value> parse_file(const std::string& path);
// -- printing --------------------------------------------------------------- // -- printing ---------------------------------------------------------------
template <class Buffer> template <class Buffer>
......
...@@ -141,7 +141,4 @@ auto make_error(const parser_state<Iterator, Sentinel>& ps, Ts&&... xs) ...@@ -141,7 +141,4 @@ auto make_error(const parser_state<Iterator, Sentinel>& ps, Ts&&... xs)
/// Specialization for parsers operating on string views. /// Specialization for parsers operating on string views.
using string_parser_state = parser_state<std::string_view::iterator>; using string_parser_state = parser_state<std::string_view::iterator>;
/// Specialization for parsers operating on mutable character sequences.
using mutable_string_parser_state = parser_state<char*>;
} // namespace caf } // namespace caf
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <iterator> #include <iterator>
#include <memory> #include <memory>
#include <numeric> #include <numeric>
#include <streambuf>
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/detail/parser/chars.hpp" #include "caf/detail/parser/chars.hpp"
...@@ -78,64 +79,63 @@ size_t do_unescape(const char* i, const char* e, char* out) { ...@@ -78,64 +79,63 @@ size_t do_unescape(const char* i, const char* e, char* out) {
return new_size; return new_size;
} }
std::string_view as_str_view(const char* first, const char* last) {
return {first, static_cast<size_t>(last - first)};
}
struct regular_unescaper { struct regular_unescaper {
std::string_view unescape(caf::detail::monotonic_buffer_resource* storage, std::string_view operator()(caf::detail::monotonic_buffer_resource* storage,
std::string_view str, bool is_escaped) const { const char* first, const char* last,
bool is_escaped) const {
auto len = static_cast<size_t>(last - first);
caf::detail::monotonic_buffer_resource::allocator<char> alloc{storage}; caf::detail::monotonic_buffer_resource::allocator<char> alloc{storage};
auto* str_buf = alloc.allocate(str.size()); auto* str_buf = alloc.allocate(len);
if (!is_escaped) { if (!is_escaped) {
strncpy(str_buf, str.data(), str.size()); strncpy(str_buf, first, len);
return std::string_view{str_buf, str.size()}; return std::string_view{str_buf, len};
} }
auto unescaped_size = do_unescape(str.data(), str.data() + str.size(), auto unescaped_size = do_unescape(first, last, str_buf);
str_buf);
return std::string_view{str_buf, unescaped_size}; return std::string_view{str_buf, unescaped_size};
} }
template <class Consumer>
void assign(Consumer& f, const char* str, size_t len, bool is_escaped) const {
auto val = std::string_view{str, len};
f.value(unescape(f.storage, val, is_escaped));
}
}; };
struct shallow_unescaper { struct shallow_unescaper {
std::string_view unescape(caf::detail::monotonic_buffer_resource* storage, std::string_view operator()(caf::detail::monotonic_buffer_resource* storage,
std::string_view str, bool is_escaped) const { const char* first, const char* last,
if (!is_escaped) { bool is_escaped) const {
return str; if (!is_escaped)
} return as_str_view(first, last);
caf::detail::monotonic_buffer_resource::allocator<char> alloc{storage}; caf::detail::monotonic_buffer_resource::allocator<char> alloc{storage};
auto* str_buf = alloc.allocate(str.size()); auto* str_buf = alloc.allocate(static_cast<size_t>(last - first));
auto unescaped_size = do_unescape(str.data(), str.data() + str.size(), auto unescaped_size = do_unescape(first, last, str_buf);
str_buf);
return std::string_view{str_buf, unescaped_size}; return std::string_view{str_buf, unescaped_size};
} }
template <class Consumer>
void assign(Consumer& f, const char* str, size_t len, bool is_escaped) const {
auto val = std::string_view{str, len};
f.value(unescape(f.storage, val, is_escaped));
}
}; };
struct in_situ_unescaper { struct in_situ_unescaper {
std::string_view unescape(caf::span<char> str, bool is_escaped) const { std::string_view operator()(caf::detail::monotonic_buffer_resource*,
if (!is_escaped) { char* first, char* last, bool is_escaped) const {
return std::string_view{str.data(), str.size()}; if (!is_escaped)
} return as_str_view(first, last);
auto unescaped_size = do_unescape(str.data(), str.data() + str.size(), auto unescaped_size = do_unescape(first, last, first);
str.data()); return std::string_view{first, unescaped_size};
return std::string_view{str.data(), unescaped_size};
}
template <class Consumer>
void assign(Consumer& f, char* str, size_t len, bool is_escaped) const {
auto val = caf::span<char>{str, len};
f.value(unescape(val, is_escaped));
} }
}; };
template <class Escaper, class Consumer, class Iterator>
void assign_value(Escaper escaper, Consumer& consumer, Iterator first,
Iterator last, bool is_escaped) {
auto iter2ptr = [](Iterator iter) {
if constexpr (std::is_pointer_v<Iterator>)
return iter;
else
return std::addressof(*iter);
};
auto str = escaper(consumer.storage, iter2ptr(first), iter2ptr(last),
is_escaped);
consumer.value(str);
}
} // namespace } // namespace
namespace caf::detail::parser { namespace caf::detail::parser {
...@@ -211,9 +211,6 @@ obj_consumer val_consumer::begin_object() { ...@@ -211,9 +211,6 @@ obj_consumer val_consumer::begin_object() {
return {&obj}; return {&obj};
} }
void read_value(string_parser_state& ps, size_t nesting_level,
val_consumer consumer);
template <class ParserState, class Consumer> template <class ParserState, class Consumer>
void read_json_null_or_nan(ParserState& ps, Consumer consumer) { void read_json_null_or_nan(ParserState& ps, Consumer consumer) {
enum { nil, is_null, is_nan }; enum { nil, is_null, is_nan };
...@@ -253,9 +250,16 @@ void read_json_null_or_nan(ParserState& ps, Consumer consumer) { ...@@ -253,9 +250,16 @@ void read_json_null_or_nan(ParserState& ps, Consumer consumer) {
// clang-format on // clang-format on
} }
// If we have an iterator into a contiguous memory block, we simply store the
// iterator position and use the escaper to decide whether we make regular,
// shallow or in-situ copies. Otherwise, we use the scratch-space and decode the
// string while parsing.
template <class ParserState, class Unescaper, class Consumer> template <class ParserState, class Unescaper, class Consumer>
void read_json_string(ParserState& ps, Unescaper escaper, Consumer consumer) { void read_json_string(ParserState& ps, unit_t, Unescaper escaper,
typename ParserState::iterator_type first; Consumer consumer) {
using iterator_t = typename ParserState::iterator_type;
iterator_t first;
// clang-format off // clang-format off
start(); start();
state(init) { state(init) {
...@@ -264,21 +268,17 @@ void read_json_string(ParserState& ps, Unescaper escaper, Consumer consumer) { ...@@ -264,21 +268,17 @@ void read_json_string(ParserState& ps, Unescaper escaper, Consumer consumer) {
} }
state(read_chars) { state(read_chars) {
transition(escape, '\\') transition(escape, '\\')
transition(done, '"', transition(done, '"', assign_value(escaper, consumer, first, ps.i, false))
escaper.assign(consumer, std::addressof(*first),
static_cast<size_t>(ps.i - first), false))
transition(read_chars, any_char) transition(read_chars, any_char)
} }
state(read_chars_after_escape) { state(read_chars_after_escape) {
transition(escape, '\\') transition(escape, '\\')
transition(done, '"', transition(done, '"', assign_value(escaper, consumer, first, ps.i, true))
escaper.assign(consumer, std::addressof(*first),
static_cast<size_t>(ps.i - first), true))
transition(read_chars_after_escape, any_char) transition(read_chars_after_escape, any_char)
} }
state(escape) { state(escape) {
// TODO: Add support for JSON's \uXXXX escaping. // TODO: Add support for JSON's \uXXXX escaping.
transition(read_chars_after_escape, "\"\\/bfnrt") transition(read_chars_after_escape, "\"\\/bfnrtv")
} }
term_state(done) { term_state(done) {
transition(done, " \t\n") transition(done, " \t\n")
...@@ -287,19 +287,56 @@ void read_json_string(ParserState& ps, Unescaper escaper, Consumer consumer) { ...@@ -287,19 +287,56 @@ void read_json_string(ParserState& ps, Unescaper escaper, Consumer consumer) {
// clang-format on // clang-format on
} }
template <class ParserState, class Unescaper> template <class ParserState, class Unescaper, class Consumer>
void read_member(ParserState& ps, Unescaper unescaper, size_t nesting_level, void read_json_string(ParserState& ps, std::vector<char>& scratch_space,
Unescaper escaper, Consumer consumer) {
scratch_space.clear();
// clang-format off
start();
state(init) {
transition(init, " \t\n")
transition(read_chars, '"')
}
state(read_chars) {
transition(escape, '\\')
transition(done, '"',
assign_value(escaper, consumer, scratch_space.begin(),
scratch_space.end(), false))
transition(read_chars, any_char, scratch_space.push_back(ch))
}
state(escape) {
// TODO: Add support for JSON's \uXXXX escaping.
transition(read_chars, '"', scratch_space.push_back('"'))
transition(read_chars, '\\', scratch_space.push_back('\\'))
transition(read_chars, 'b', scratch_space.push_back('\b'))
transition(read_chars, 'f', scratch_space.push_back('\f'))
transition(read_chars, 'n', scratch_space.push_back('\n'))
transition(read_chars, 'r', scratch_space.push_back('\r'))
transition(read_chars, 't', scratch_space.push_back('\t'))
transition(read_chars, 'v', scratch_space.push_back('\v'))
}
term_state(done) {
transition(done, " \t\n")
}
fin();
// clang-format on
}
template <class ParserState, class ScratchSpace, class Unescaper>
void read_member(ParserState& ps, ScratchSpace& scratch_space,
Unescaper unescaper, size_t nesting_level,
member_consumer consumer) { member_consumer consumer) {
// clang-format off // clang-format off
start(); start();
state(init) { state(init) {
transition(init, " \t\n") transition(init, " \t\n")
fsm_epsilon(read_json_string(ps, unescaper, consumer.begin_key()), fsm_epsilon(read_json_string(ps, scratch_space, unescaper,
consumer.begin_key()),
after_key, '"') after_key, '"')
} }
state(after_key) { state(after_key) {
transition(after_key, " \t\n") transition(after_key, " \t\n")
fsm_transition(read_value(ps, unescaper, nesting_level, fsm_transition(read_value(ps, scratch_space, unescaper, nesting_level,
consumer.begin_val()), consumer.begin_val()),
done, ':') done, ':')
} }
...@@ -310,9 +347,10 @@ void read_member(ParserState& ps, Unescaper unescaper, size_t nesting_level, ...@@ -310,9 +347,10 @@ void read_member(ParserState& ps, Unescaper unescaper, size_t nesting_level,
// clang-format on // clang-format on
} }
template <class ParserState, class Unescaper> template <class ParserState, class ScratchSpace, class Unescaper>
void read_json_object(ParserState& ps, Unescaper unescaper, void read_json_object(ParserState& ps, ScratchSpace& scratch_space,
size_t nesting_level, obj_consumer consumer) { Unescaper unescaper, size_t nesting_level,
obj_consumer consumer) {
if (nesting_level >= max_nesting_level) { if (nesting_level >= max_nesting_level) {
ps.code = pec::nested_too_deeply; ps.code = pec::nested_too_deeply;
return; return;
...@@ -325,7 +363,7 @@ void read_json_object(ParserState& ps, Unescaper unescaper, ...@@ -325,7 +363,7 @@ void read_json_object(ParserState& ps, Unescaper unescaper,
} }
state(has_open_brace) { state(has_open_brace) {
transition(has_open_brace, " \t\n") transition(has_open_brace, " \t\n")
fsm_epsilon(read_member(ps, unescaper, nesting_level + 1, fsm_epsilon(read_member(ps, scratch_space, unescaper, nesting_level + 1,
consumer.begin_member()), consumer.begin_member()),
after_member, '"') after_member, '"')
transition(done, '}') transition(done, '}')
...@@ -337,7 +375,7 @@ void read_json_object(ParserState& ps, Unescaper unescaper, ...@@ -337,7 +375,7 @@ void read_json_object(ParserState& ps, Unescaper unescaper,
} }
state(after_comma) { state(after_comma) {
transition(after_comma, " \t\n") transition(after_comma, " \t\n")
fsm_epsilon(read_member(ps, unescaper, nesting_level + 1, fsm_epsilon(read_member(ps, scratch_space, unescaper, nesting_level + 1,
consumer.begin_member()), consumer.begin_member()),
after_member, '"') after_member, '"')
} }
...@@ -348,8 +386,9 @@ void read_json_object(ParserState& ps, Unescaper unescaper, ...@@ -348,8 +386,9 @@ void read_json_object(ParserState& ps, Unescaper unescaper,
// clang-format on // clang-format on
} }
template <class ParserState, class Unescaper> template <class ParserState, class ScratchSpace, class Unescaper>
void read_json_array(ParserState& ps, Unescaper unescaper, size_t nesting_level, void read_json_array(ParserState& ps, ScratchSpace& scratch_space,
Unescaper unescaper, size_t nesting_level,
arr_consumer consumer) { arr_consumer consumer) {
if (nesting_level >= max_nesting_level) { if (nesting_level >= max_nesting_level) {
ps.code = pec::nested_too_deeply; ps.code = pec::nested_too_deeply;
...@@ -364,7 +403,7 @@ void read_json_array(ParserState& ps, Unescaper unescaper, size_t nesting_level, ...@@ -364,7 +403,7 @@ void read_json_array(ParserState& ps, Unescaper unescaper, size_t nesting_level,
state(has_open_brace) { state(has_open_brace) {
transition(has_open_brace, " \t\n") transition(has_open_brace, " \t\n")
transition(done, ']') transition(done, ']')
fsm_epsilon(read_value(ps, unescaper, nesting_level + 1, fsm_epsilon(read_value(ps, scratch_space, unescaper, nesting_level + 1,
consumer.begin_value()), consumer.begin_value()),
after_value) after_value)
} }
...@@ -375,7 +414,7 @@ void read_json_array(ParserState& ps, Unescaper unescaper, size_t nesting_level, ...@@ -375,7 +414,7 @@ void read_json_array(ParserState& ps, Unescaper unescaper, size_t nesting_level,
} }
state(after_comma) { state(after_comma) {
transition(after_comma, " \t\n") transition(after_comma, " \t\n")
fsm_epsilon(read_value(ps, unescaper, nesting_level + 1, fsm_epsilon(read_value(ps, scratch_space, unescaper, nesting_level + 1,
consumer.begin_value()), consumer.begin_value()),
after_value) after_value)
} }
...@@ -386,21 +425,23 @@ void read_json_array(ParserState& ps, Unescaper unescaper, size_t nesting_level, ...@@ -386,21 +425,23 @@ void read_json_array(ParserState& ps, Unescaper unescaper, size_t nesting_level,
// clang-format on // clang-format on
} }
template <class ParserState, class Unescaper> template <class ParserState, class ScratchSpace, class Unescaper>
void read_value(ParserState& ps, Unescaper unescaper, size_t nesting_level, void read_value(ParserState& ps, ScratchSpace& scratch_space,
Unescaper unescaper, size_t nesting_level,
val_consumer consumer) { val_consumer consumer) {
// clang-format off // clang-format off
start(); start();
state(init) { state(init) {
transition(init, " \t\n") transition(init, " \t\n")
fsm_epsilon(read_json_string(ps, unescaper, consumer), done, '"') fsm_epsilon(read_json_string(ps, scratch_space, unescaper, consumer),
done, '"')
fsm_epsilon(read_bool(ps, consumer), done, "ft") fsm_epsilon(read_bool(ps, consumer), done, "ft")
fsm_epsilon(read_json_null_or_nan(ps, consumer), done, "n") fsm_epsilon(read_json_null_or_nan(ps, consumer), done, "n")
fsm_epsilon(read_number(ps, consumer), done, "+-.0123456789") fsm_epsilon(read_number(ps, consumer), done, "+-.0123456789")
fsm_epsilon(read_json_object(ps, unescaper, nesting_level, fsm_epsilon(read_json_object(ps, scratch_space, unescaper, nesting_level,
consumer.begin_object()), consumer.begin_object()),
done, '{') done, '{')
fsm_epsilon(read_json_array(ps, unescaper, nesting_level, fsm_epsilon(read_json_array(ps, scratch_space, unescaper, nesting_level,
consumer.begin_array()), consumer.begin_array()),
done, '[') done, '[')
} }
...@@ -499,28 +540,41 @@ const array* empty_array() noexcept { ...@@ -499,28 +540,41 @@ const array* empty_array() noexcept {
} }
value* parse(string_parser_state& ps, monotonic_buffer_resource* storage) { value* parse(string_parser_state& ps, monotonic_buffer_resource* storage) {
unit_t scratch_space;
regular_unescaper unescaper;
monotonic_buffer_resource::allocator<value> alloc{storage};
auto result = new (alloc.allocate(1)) value();
parser::read_value(ps, scratch_space, unescaper, 0, {storage, result});
return result;
}
value* parse(file_parser_state& ps, monotonic_buffer_resource* storage) {
std::vector<char> scratch_space;
scratch_space.reserve(64);
regular_unescaper unescaper; regular_unescaper unescaper;
monotonic_buffer_resource::allocator<value> alloc{storage}; monotonic_buffer_resource::allocator<value> alloc{storage};
auto result = new (alloc.allocate(1)) value(); auto result = new (alloc.allocate(1)) value();
parser::read_value(ps, unescaper, 0, {storage, result}); parser::read_value(ps, scratch_space, unescaper, 0, {storage, result});
return result; return result;
} }
value* parse_shallow(string_parser_state& ps, value* parse_shallow(string_parser_state& ps,
monotonic_buffer_resource* storage) { monotonic_buffer_resource* storage) {
unit_t scratch_space;
shallow_unescaper unescaper; shallow_unescaper unescaper;
monotonic_buffer_resource::allocator<value> alloc{storage}; monotonic_buffer_resource::allocator<value> alloc{storage};
auto result = new (alloc.allocate(1)) value(); auto result = new (alloc.allocate(1)) value();
parser::read_value(ps, unescaper, 0, {storage, result}); parser::read_value(ps, scratch_space, unescaper, 0, {storage, result});
return result; return result;
} }
value* parse_in_situ(mutable_string_parser_state& ps, value* parse_in_situ(mutable_string_parser_state& ps,
monotonic_buffer_resource* storage) { monotonic_buffer_resource* storage) {
unit_t scratch_space;
in_situ_unescaper unescaper; in_situ_unescaper unescaper;
monotonic_buffer_resource::allocator<value> alloc{storage}; monotonic_buffer_resource::allocator<value> alloc{storage};
auto result = new (alloc.allocate(1)) value(); auto result = new (alloc.allocate(1)) value();
parser::read_value(ps, unescaper, 0, {storage, result}); parser::read_value(ps, scratch_space, unescaper, 0, {storage, result});
return result; return result;
} }
......
...@@ -8,6 +8,8 @@ ...@@ -8,6 +8,8 @@
#include "caf/detail/print.hpp" #include "caf/detail/print.hpp"
#include "caf/string_algorithms.hpp" #include "caf/string_algorithms.hpp"
#include <fstream>
namespace { namespace {
static constexpr const char class_name[] = "caf::json_reader"; static constexpr const char class_name[] = "caf::json_reader";
...@@ -150,14 +152,36 @@ bool json_reader::load(std::string_view json_text) { ...@@ -150,14 +152,36 @@ bool json_reader::load(std::string_view json_text) {
set_error(make_error(ps)); set_error(make_error(ps));
st_ = nullptr; st_ = nullptr;
return false; return false;
} else { }
err_.reset(); err_.reset();
detail::monotonic_buffer_resource::allocator<stack_type> alloc{&buf_}; detail::monotonic_buffer_resource::allocator<stack_type> alloc{&buf_};
st_ = new (alloc.allocate(1)) stack_type(stack_allocator{&buf_}); st_ = new (alloc.allocate(1)) stack_type(stack_allocator{&buf_});
st_->reserve(16); st_->reserve(16);
st_->emplace_back(root_); st_->emplace_back(root_);
return true; return true;
}
bool json_reader::load_file(const char* path) {
using iterator_t = std::istreambuf_iterator<char>;
reset();
std::ifstream input{path};
if (!input.is_open()) {
emplace_error(sec::cannot_open_file);
return false;
}
detail::json::file_parser_state ps{iterator_t{input}, iterator_t{}};
root_ = detail::json::parse(ps, &buf_);
if (ps.code != pec::success) {
set_error(make_error(ps));
st_ = nullptr;
return false;
} }
err_.reset();
detail::monotonic_buffer_resource::allocator<stack_type> alloc{&buf_};
st_ = new (alloc.allocate(1)) stack_type(stack_allocator{&buf_});
st_->reserve(16);
st_->emplace_back(root_);
return true;
} }
void json_reader::revert() { void json_reader::revert() {
......
...@@ -10,6 +10,8 @@ ...@@ -10,6 +10,8 @@
#include "caf/make_counted.hpp" #include "caf/make_counted.hpp"
#include "caf/parser_state.hpp" #include "caf/parser_state.hpp"
#include <fstream>
namespace caf { namespace caf {
// -- conversion --------------------------------------------------------------- // -- conversion ---------------------------------------------------------------
...@@ -92,35 +94,49 @@ expected<json_value> json_value::parse(std::string_view str) { ...@@ -92,35 +94,49 @@ expected<json_value> json_value::parse(std::string_view str) {
auto storage = make_counted<detail::json::storage>(); auto storage = make_counted<detail::json::storage>();
string_parser_state ps{str.begin(), str.end()}; string_parser_state ps{str.begin(), str.end()};
auto root = detail::json::parse(ps, &storage->buf); auto root = detail::json::parse(ps, &storage->buf);
if (ps.code != pec::success) { if (ps.code == pec::success)
return {make_error(ps)};
} else {
return {json_value{root, std::move(storage)}}; return {json_value{root, std::move(storage)}};
} return {make_error(ps)};
} }
expected<json_value> json_value::parse_shallow(std::string_view str) { expected<json_value> json_value::parse_shallow(std::string_view str) {
auto storage = make_counted<detail::json::storage>(); auto storage = make_counted<detail::json::storage>();
string_parser_state ps{str.begin(), str.end()}; string_parser_state ps{str.begin(), str.end()};
auto root = detail::json::parse_shallow(ps, &storage->buf); auto root = detail::json::parse_shallow(ps, &storage->buf);
if (ps.code != pec::success) { if (ps.code == pec::success)
return {make_error(ps)};
} else {
return {json_value{root, std::move(storage)}}; return {json_value{root, std::move(storage)}};
} return {make_error(ps)};
} }
expected<json_value> json_value::parse_in_situ(std::string& str) { expected<json_value> json_value::parse_in_situ(std::string& str) {
auto storage = make_counted<detail::json::storage>(); auto storage = make_counted<detail::json::storage>();
mutable_string_parser_state ps{str.data(), str.data() + str.size()}; detail::json::mutable_string_parser_state ps{str.data(),
str.data() + str.size()};
auto root = detail::json::parse_in_situ(ps, &storage->buf); auto root = detail::json::parse_in_situ(ps, &storage->buf);
if (ps.code != pec::success) { if (ps.code == pec::success)
return {json_value{root, std::move(storage)}};
return {make_error(ps)}; return {make_error(ps)};
} else { }
expected<json_value> json_value::parse_file(const char* path) {
using iterator_t = std::istreambuf_iterator<char>;
std::ifstream input{path};
if (!input.is_open())
return make_error(sec::cannot_open_file);
auto storage = make_counted<detail::json::storage>();
detail::json::file_parser_state ps{iterator_t{input}, iterator_t{}};
auto root = detail::json::parse(ps, &storage->buf);
if (ps.code == pec::success)
return {json_value{root, std::move(storage)}}; return {json_value{root, std::move(storage)}};
} return {make_error(ps)};
}
expected<json_value> json_value::parse_file(const std::string& path) {
return parse_file(path.c_str());
} }
// -- free functions -----------------------------------------------------------
std::string to_string(const json_value& val) { std::string to_string(const json_value& val) {
std::string result; std::string result;
val.print_to(result); val.print_to(result);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment