8#include "../parser/parser.hpp"
9#include "../utility/utility.hpp"
10#include "../algorithm/algorithm.hpp"
13#include "../macros.hpp"
19hi_export_module(hikogui.codec.JSON);
21hi_export
namespace hi::inline
v1 {
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());
242hi_export
constexpr void format_JSON_impl(datum
const& value,
std::string& result,
hi::indent indent = {})
244 if (holds_alternative<nullptr_t>(value)) {
246 }
else if (
auto const *b = get_if<bool>(value)) {
247 result += *b ?
"true" :
"false";
248 }
else if (
auto const *i = get_if<long long>(value)) {
249 result += hi::to_string(*i);
250 }
else if (
auto const *f = get_if<double>(value)) {
251 result += hi::to_string(*f);
252 }
else if (
auto const *s = get_if<std::string>(value)) {
254 for (
auto const c : *s) {
282 }
else if (
auto const *v = get_if<datum::vector_type>(value)) {
287 for (
auto it = v->begin(); it != v->end(); it++) {
288 if (it != v->begin()) {
292 result += indent + 1;
294 format_JSON_impl(*it, result, indent + 1);
300 }
else if (
auto const *m = get_if<datum::map_type>(value)) {
305 for (
auto it = m->begin(); it != m->end(); it++) {
306 if (it != m->begin()) {
310 result += indent + 1;
312 format_JSON_impl(it->first, result, indent + 1);
315 format_JSON_impl(it->second, result, indent + 1);
333 format_JSON_impl(root, r);
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:163
DOXYGEN BUG.
Definition algorithm_misc.hpp:20
hi_export constexpr std::string format_JSON(datum const &root)
Dump an datum object into a JSON string.
Definition JSON.hpp:330
Indentation for writing out text files.
Definition indent.hpp:20