8#include "../parser/parser.hpp"
9#include "../utility/utility.hpp"
10#include "../algorithm/module.hpp"
13#include "../macros.hpp"
19hi_export_module(hikogui.codec.JSON);
24template<std::input_iterator It, std::sentinel_for<It> ItEnd>
25[[nodiscard]]
constexpr std::optional<datum> json_parse_value(It &it, ItEnd last, std::string_view path);
27template<std::input_iterator It, std::sentinel_for<It> ItEnd>
28[[nodiscard]]
constexpr std::optional<datum> json_parse_array(It &it, ItEnd last, std::string_view path)
30 auto r = datum::make_vector();
39 auto comma_after_value =
true;
47 }
else if (
auto result = json_parse_value(it, last, path)) {
48 if (not comma_after_value) {
49 throw parse_error(std::format(
"{}: Expecting ',', found {}",
token_location(it, last, path), *it));
56 comma_after_value =
true;
58 comma_after_value =
false;
62 throw parse_error(std::format(
"{}: Expecting a JSON value, found {}",
token_location(it, last, path), *it));
69template<std::input_iterator It, std::sentinel_for<It> ItEnd>
70[[nodiscard]]
constexpr std::optional<datum> json_parse_object(It& it, ItEnd last, std::string_view path)
72 auto r = datum::make_map();
82 auto comma_after_value =
true;
90 }
else if (*it == token::dstr) {
91 if (not comma_after_value) {
92 throw parse_error(std::format(
"{}: Expecting ',', found {}.",
token_location(it, last, path), *it));
100 throw parse_error(std::format(
"{}: Expecting ':', found {}.",
token_location(it, last, path), *it));
103 if (
auto result = json_parse_value(it, last, path)) {
108 std::format(
"{}: Expecting a JSON value, found {}.",
token_location(it, last, path), *it));
113 comma_after_value =
true;
115 comma_after_value =
false;
119 throw parse_error(std::format(
120 "{}: Unexpected token {}, expected a key or close-brace.",
token_location(it, last, path), *it));
127template<std::input_iterator It, std::sentinel_for<It> ItEnd>
128[[nodiscard]]
constexpr std::optional<datum> json_parse_value(It& it, ItEnd last, std::string_view path)
130 hi_assert(it != last);
132 if (*it == token::dstr) {
135 }
else if (*it == token::integer) {
136 return datum{
static_cast<long long>(*it++)};
138 }
else if (*it == token::real) {
139 return datum{
static_cast<double>(*it++)};
141 }
else if (*it ==
'-') {
143 if (*it == token::integer) {
144 return datum{-
static_cast<long long>(*it++)};
146 }
else if (*it == token::real) {
147 return datum{-
static_cast<double>(*it++)};
150 throw parse_error(std::format(
151 "{}: Unexpected token '{}' after '-', expected integer or floating point literal.",
156 }
else if (*it == token::id and *it ==
"true") {
160 }
else if (*it == token::id and *it ==
"false") {
164 }
else if (*it == token::id and *it ==
"null") {
166 return datum{
nullptr};
168 }
else if (
auto object = json_parse_object(it, last, path)) {
171 }
else if (
auto array = json_parse_array(it, last, path)) {
181hi_export
template<std::input_iterator It, std::sentinel_for<It> ItEnd>
182[[
nodiscard]]
constexpr datum parse_JSON(
It it, ItEnd last, std::string_view path = std::string_view{
"<none>"})
184 auto token_it = lexer<lexer_config::json_style()>.parse(
it, last);
186 if (
token_it == std::default_sentinel) {
187 throw parse_error(std::format(
"{}: No tokens found",
token_location(
token_it, std::default_sentinel, path)));
191 if (
auto result = json_parse_value(
token_it, std::default_sentinel, path)) {
195 throw parse_error(std::format(
"{}: Missing JSON object",
token_location(
token_it, std::default_sentinel, path)));
198 if (
token_it != std::default_sentinel) {
200 std::format(
"{}: Unexpected text after JSON root object",
token_location(
token_it, std::default_sentinel, path)));
210hi_export [[
nodiscard]]
constexpr datum parse_JSON(std::string_view text, std::string_view path = std::string_view{
"<none>"})
212 return parse_JSON(text.cbegin(), text.cend(), path);
219hi_export [[
nodiscard]]
constexpr datum parse_JSON(
std::string const& text, std::string_view path = std::string_view{
"<none>"})
221 return parse_JSON(std::string_view{text}, path);
228hi_export [[
nodiscard]]
constexpr datum parse_JSON(
char const *text, std::string_view path = std::string_view{
"<none>"})
230 return parse_JSON(std::string_view{text}, path);
237hi_export [[
nodiscard]]
inline datum parse_JSON(std::filesystem::path
const& path)
239 return parse_JSON(as_string_view(file_view(path)), path.string());
247 result += *b ?
"true" :
"false";
249 result += hi::to_string(*i);
251 result += hi::to_string(*f);
287 for (
auto it = v->begin();
it != v->end();
it++) {
288 if (
it != v->begin()) {
294 format_JSON_impl(*
it,
result, indent + 1);
305 for (
auto it = m->begin();
it != m->end();
it++) {
306 if (
it != m->begin()) {
312 format_JSON_impl(
it->first,
result, indent + 1);
315 format_JSON_impl(
it->second,
result, indent + 1);
333 format_JSON_impl(root, r);
DOXYGEN BUG.
Definition algorithm.hpp:16
hi_export constexpr std::string format_JSON(datum const &root)
Dump an datum object into a JSON string.
Definition JSON.hpp:330
hi_export constexpr std::string token_location(It &it, ItEnd last, std::string_view path) noexcept
Create a location string for error messages.
Definition token.hpp:160
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
Indentation for writing out text files.
Definition indent.hpp:18