9#include "json_helpers.h"
17 struct wilson_parsing_error {
const char* where; std::string message; };
19 using wilson = nlohmann::json;
74 auto parse(std::string_view wilson_str) -> expected<wilson, wilson_parsing_error>;
76 auto parse_object(std::string_view wilson_str,
char closing_char =
'}') -> expected<wilson, wilson_parsing_error>;
78 auto parse_array(std::string_view wilson_str,
char closing_char =
']') -> expected<wilson, wilson_parsing_error>;
80 auto parse_word_or_string(std::string_view wilson_str) -> expected<wilson, wilson_parsing_error>;
82 auto parse_string_value(std::string_view wilson_str) -> expected<std::string, wilson_parsing_error>;
84 auto parse_string_literal(std::string_view wilson_str) -> expected<std::string, wilson_parsing_error>;
89 auto consume_object(std::string_view& wilson_str,
char closing_char =
'}') -> expected<wilson, wilson_parsing_error>;
90 auto consume_array(std::string_view& wilson_str,
char closing_char =
']') -> expected<wilson, wilson_parsing_error>;
91 auto consume_value(std::string_view& wilson_str) -> expected<wilson, wilson_parsing_error>;
93 struct output_parameters
97 std::string_view indent_str =
"\t";
101 template <
typename OUTFUNC>
102 void output(OUTFUNC&& out, wilson
const& value, output_parameters
const& parameters = {},
size_t indent = 0);
105 inline void output_to_stream(std::ostream& strm, wilson
const& value, output_parameters
const& parameters = {})
107 output([&](std::string_view val) { strm.write(val.data(), val.size()); }, value, parameters);
111 std::string
to_string(wilson
const& value, output_parameters
const& parameters = {});
114 expected<wilson, wilson_parsing_error>
load_file(std::filesystem::path
const& from);
117 wilson
try_load_file(std::filesystem::path
const& from, wilson or_json = json::empty_json);
120 inline void save_file(std::filesystem::path
const&
to, wilson
const& j, output_parameters
const& parameters = {})
123 std::ofstream out{
to };
125 throw std::runtime_error{
"could not open file for writing" };
135 inline void consume_whitespace(std::string_view& str)
137 string_ops::trim_whitespace_left(str);
138 while (str.starts_with(
'#'))
141 string_ops::trim_whitespace_left(str);
145 inline expected<wilson, wilson_parsing_error> consume_array(std::string_view& str,
char closing_char)
147 auto obj = wilson::array();
150 consume_whitespace(str);
154 auto element = formats::wilson::consume_value(str);
157 obj.push_back(std::move(
element.value()));
159 consume_whitespace(str);
161 if (!str.empty() && (str[0] ==
',' || str[0] ==
';'))
162 str.remove_prefix(1);
164 }
while (!str.empty());
171 std::pair<std::string_view, std::string> result;
173 result = parsing::consume_c_string<'\''>(_str);
175 result = parsing::consume_c_string<
'"'>(_str);
176 return std::move(result).second;
182 consume_whitespace(str);
186 const auto first = str[0];
187 if (first ==
'\'' || first ==
'"')
189 else if (string_ops::ascii::isidentstart(first))
193 return unexpected(wilson_parsing_error{ str.data(),
"expected quote character or identifier" });
199 if (string_ops::ascii::isalpha(str[0]))
202 if (
string ==
"true")
204 else if (
string ==
"false")
206 else if (
string ==
"null" ||
string ==
"nil")
213 inline expected<wilson, wilson_parsing_error> consume_object(std::string_view& str,
char closing_char)
215 auto obj = wilson::object();
219 consume_whitespace(str);
228 consume_whitespace(str);
232 obj[std::move(key.value())] =
true;
236 if (str[0] ==
',' || str[0] ==
';')
238 obj[std::move(key.value())] =
true;
242 if (str[0] ==
'=' || str[0] ==
':')
243 str.remove_prefix(1);
245 consume_whitespace(str);
247 auto val = formats::wilson::consume_value(str);
250 obj[std::move(key.value())] = std::move(val.value());
253 consume_whitespace(str);
255 if (!str.empty() && (str[0] ==
',' || str[0] ==
';'))
256 str.remove_prefix(1);
257 }
while (!str.empty());
262 inline expected<wilson, wilson_parsing_error> consume_value(std::string_view& str)
264 consume_whitespace(str);
266 return unexpected(wilson_parsing_error{str.data(),
"expected value"});
268 const auto first = str[0];
270 return consume_object(str);
272 return consume_array(str,
')');
274 return consume_array(str,
']');
275 else if (string_ops::ascii::isidentstart(first) || first ==
'\'' || first ==
'"')
277 else if (string_ops::ascii::isdigit(first) || first ==
'-')
281 if (fcresult.ec == std::errc{})
283 str = string_ops::make_sv(fcresult.ptr, str.end());
288 return unexpected(wilson_parsing_error{ str.data(),
"expected object, array, or valid scalar" });
291 inline auto parse(std::string_view wilson_str) -> expected<wilson, wilson_parsing_error>
293 const auto start = wilson_str.data();
294 return formats::wilson::consume_value(wilson_str);
297 inline auto parse_object(std::string_view wilson_str,
char closing_char) -> expected<wilson, wilson_parsing_error>
299 return formats::wilson::consume_object(wilson_str, closing_char);
302 inline auto parse_array(std::string_view wilson_str,
char closing_char) -> expected<wilson, wilson_parsing_error>
304 return formats::wilson::consume_array(wilson_str, closing_char);
312 inline auto parse_string_value(std::string_view wilson_str) -> expected<std::string, wilson_parsing_error>
324 template <
typename OUTFUNC,
typename VAL>
325 void output_value(OUTFUNC&& func, VAL
const& val)
327 char temp_buffer[32]{};
328 auto&& [end, ec] = std::to_chars(std::begin(temp_buffer), std::end(temp_buffer), val);
329 func(string_ops::make_sv(std::begin(temp_buffer), end));
332 template <
typename OUTFUNC>
333 void output_string(OUTFUNC&& func, std::string_view strval)
338 auto start = strval.begin();
339 for (
auto it = strval.begin(); it != strval.end(); ++it)
341 if (string_ops::ascii::isprint(*it) && *it !=
'"' && *it !=
'\\')
346 func(string_ops::make_sv(start, it));
352 case 'n': func(
"\\n");
goto reset_start_to_next_char;
353 case '"': func(
"\\\"");
goto reset_start_to_next_char;
354 case '\'': func(
"\\'");
goto reset_start_to_next_char;
355 case '\\': func(
"\\\\");
goto reset_start_to_next_char;
356 case 'b': func(
"\\b");
goto reset_start_to_next_char;
357 case 'r': func(
"\\r");
goto reset_start_to_next_char;
358 case 'f': func(
"\\f");
goto reset_start_to_next_char;
359 case 't': func(
"\\t");
goto reset_start_to_next_char;
360 case '0': func(
"\\0");
goto reset_start_to_next_char;
366 auto&& [end, ec] = std::to_chars(std::begin(temp), std::end(temp),
static_cast<uint8_t
>(*it), 16);
367 func(string_ops::make_sv(std::begin(temp), end));
370 reset_start_to_next_char:
371 start = std::next(it);
374 if (start != strval.end())
375 func(string_ops::make_sv(start, strval.end()));
382 template <
typename OUTFUNC>
383 void do_indent(OUTFUNC&& out, output_parameters
const& parameters,
size_t indent)
385 for (
size_t i=0; i<indent; ++i)
386 out(parameters.indent_str);
390 template <
typename OUTFUNC>
391 void output(OUTFUNC&& out, wilson
const& value, output_parameters
const& parameters,
size_t indent)
393 const auto pretty = parameters.pretty;
394 switch (value.type())
396 using enum nlohmann::detail::value_t;
397 case null: out(
"null");
return;
399 out(pretty ?
"{\n" :
"{ ");
400 for (
auto& [k, v] : value.get_ref<wilson::object_t
const&>())
403 detail::do_indent(out, parameters, indent + 1);
404 formats::wilson::detail::output_string(out, k);
407 out(pretty ?
",\n" :
", ");
410 detail::do_indent(out, parameters, indent);
414 out(pretty ?
"[\n" :
"[ ");
415 for (
auto& obj : value.get_ref<wilson::array_t
const&>())
418 detail::do_indent(out, parameters, indent + 1);
420 out(pretty ?
",\n" :
", ");
423 detail::do_indent(out, parameters, indent);
426 case string: formats::wilson::detail::output_string(out, value.get_ref<wilson::string_t
const&>());
return;
427 case boolean: out(value.get_ref<wilson::boolean_t
const&>() ?
"true" :
"false");
return;
428 case number_integer: formats::wilson::detail::output_value(out, value.get_ref<wilson::number_integer_t
const&>());
return;
429 case number_unsigned: formats::wilson::detail::output_value(out, value.get_ref<wilson::number_unsigned_t
const&>());
return;
430 case number_float: formats::wilson::detail::output_value(out, value.get_ref<wilson::number_float_t
const&>());
return;
432 out(pretty ?
"[\n" :
"[ ");
434 for (
auto byte : value.get_ref<wilson::binary_t
const&>())
436 if (pretty && (count % 16) == 0)
437 detail::do_indent(out, parameters, indent + 1);
438 formats::wilson::detail::output_value(out,
byte);
440 out((pretty && (count % 16) == 0) ?
",\n" :
", ");
445 detail::do_indent(out, parameters, indent);
450 case discarded:
return;
454 inline std::string
to_string(wilson
const& value, output_parameters
const& parameters)
461 inline expected<wilson, wilson_parsing_error>
load_file(std::filesystem::path
const& from)
464 const auto source = ghassanpl::make_mmap_source<char>(from, ec);
466 return unexpected(wilson_parsing_error{ {}, ec.message() });
470 inline wilson
try_load_file(std::filesystem::path
const& from, wilson or_json)
auto * element(T &cont, RANDOM &rng=::ghassanpl::random::default_random_engine)
Returns an pointer to a random element in cont.
constexpr CONTAINER to(RANGE &&range, TYPES &&... args)
to<container>();
char consume(std::string_view &str)
Consumes and returns the first character in the str, or \0 if no more characters.
constexpr auto from_chars(std::string_view str, T &value, const int base=10) noexcept
A version of std::from_chars that takes a std::string_view as the first argument.
std::string_view consume_until_delim(std::string_view &str, char c)
Consumes characters from the beginning of str until one is equal to c, consuming c and including it a...
std::string_view consume_while(std::string_view &str, FUNC &&pred)
Consumes characters from the beginning of str while they match pred(str[0]).
auto parse_string_value(std::string_view wilson_str) -> expected< std::string, wilson_parsing_error >
Parses a word or a string literal a string value.
auto parse(std::string_view wilson_str) -> expected< wilson, wilson_parsing_error >
Parses any Wilson value.
void output(OUTFUNC &&out, wilson const &value, output_parameters const ¶meters={}, size_t indent=0)
Outputs value formatted as a string using the out function which should take a string_view to append ...
auto consume_string_value(std::string_view &str) -> expected< std::string, wilson_parsing_error >
Consumes a word or a string literal returning a string value.
wilson try_load_file(std::filesystem::path const &from, wilson or_json=json::empty_json)
Loads a Wilson value from a file, or returns or_json on failure.
auto consume_word_or_string(std::string_view &str) -> expected< wilson, wilson_parsing_error >
Consumes: a word, returning a string/bool/null value; or a "string literal", returning a string value...
auto parse_array(std::string_view wilson_str, char closing_char=']') -> expected< wilson, wilson_parsing_error >
Parses an array of Wilson values, until end of string or encountering the closing_char.
expected< wilson, wilson_parsing_error > load_file(std::filesystem::path const &from)
Loads a Wilson value from a file.
std::string to_string(wilson const &value, output_parameters const ¶meters={})
Returns value formatted as a string.
auto parse_string_literal(std::string_view wilson_str) -> expected< std::string, wilson_parsing_error >
Parses a string literal as a string value.
void save_file(std::filesystem::path const &to, wilson const &j, output_parameters const ¶meters={})
Saves a Wilson value to a file formatted as a string.
void output_to_stream(std::ostream &strm, wilson const &value, output_parameters const ¶meters={})
Outputs value formatted as a string to the strm output stream.
auto parse_word_or_string(std::string_view wilson_str) -> expected< wilson, wilson_parsing_error >
Parses: a word as a string value or bool/null value; a "string literal" as a string value.
auto parse_object(std::string_view wilson_str, char closing_char='}') -> expected< wilson, wilson_parsing_error >
Parses a set of key-value pairs, until end of string or encountering the closing_char.
auto consume_string_literal(std::string_view &_str) -> expected< std::string, wilson_parsing_error >
Consumes a string literal returning a string value.
The below code is based on Sun's libm library code, which is licensed under the following license: