header_utils
Loading...
Searching...
No Matches
wilson.h
1
4
5#pragma once
6
7#include "parsing.h"
8#include "mmap.h"
9#include "json_helpers.h"
10#include "expected.h"
11#include <ostream>
12
13// TODO: https://dev.stenway.com/SML/Examples.html
14
16{
17 struct wilson_parsing_error { const char* where; std::string message; };
18
19 using wilson = nlohmann::json;
20
72
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>;
85
86 auto consume_string_literal(std::string_view& _str) -> expected<std::string, wilson_parsing_error>;
87 auto consume_string_value(std::string_view& str) -> expected<std::string, wilson_parsing_error>;
88 auto consume_word_or_string(std::string_view& str) -> expected<wilson, 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>;
92
93 struct output_parameters
94 {
95 bool pretty = false;
97 std::string_view indent_str = "\t";
98 };
99
101 template <typename OUTFUNC>
102 void output(OUTFUNC&& out, wilson const& value, output_parameters const& parameters = {}, size_t indent = 0);
103
105 inline void output_to_stream(std::ostream& strm, wilson const& value, output_parameters const& parameters = {})
106 {
107 output([&](std::string_view val) { strm.write(val.data(), val.size()); }, value, parameters);
108 }
109
111 std::string to_string(wilson const& value, output_parameters const& parameters = {});
112
114 expected<wilson, wilson_parsing_error> load_file(std::filesystem::path const& from);
115
117 wilson try_load_file(std::filesystem::path const& from, wilson or_json = json::empty_json);
118
120 inline void save_file(std::filesystem::path const& to, wilson const& j, output_parameters const& parameters = {})
121 {
123 std::ofstream out{ to };
124 if (!out)
125 throw std::runtime_error{ "could not open file for writing" };
126 formats::wilson::output_to_stream(out, j, parameters);
127 }
128
130}
131
133{
134
135 inline void consume_whitespace(std::string_view& str)
136 {
137 string_ops::trim_whitespace_left(str);
138 while (str.starts_with('#'))
139 {
140 std::ignore = string_ops::consume_until_delim(str, '\n');
141 string_ops::trim_whitespace_left(str);
142 }
143 }
144
145 inline expected<wilson, wilson_parsing_error> consume_array(std::string_view& str, char closing_char)
146 {
147 auto obj = wilson::array();
148 do
149 {
150 consume_whitespace(str);
151 if (string_ops::consume(str, closing_char))
152 return obj;
153
154 auto element = formats::wilson::consume_value(str);
155 if (!element)
156 return element;
157 obj.push_back(std::move(element.value()));
158
159 consume_whitespace(str);
160
161 if (!str.empty() && (str[0] == ',' || str[0] == ';'))
162 str.remove_prefix(1);
163
164 } while (!str.empty());
165 return obj;
166 }
167
169 inline expected<std::string, wilson_parsing_error> consume_string_literal(std::string_view& _str)
170 {
171 std::pair<std::string_view, std::string> result;
172 if (_str[0] == '\'')
173 result = parsing::consume_c_string<'\''>(_str);
174 else
175 result = parsing::consume_c_string<'"'>(_str);
176 return std::move(result).second;
177 }
178
180 inline expected<std::string, wilson_parsing_error> consume_string_value(std::string_view& str)
181 {
182 consume_whitespace(str);
183
184 if (!str.empty())
185 {
186 const auto first = str[0];
187 if (first == '\'' || first == '"')
188 return consume_string_literal(str);
189 else if (string_ops::ascii::isidentstart(first))
190 return std::string{ string_ops::consume_while(str, &string_ops::ascii::isident) };
191 }
192
193 return unexpected(wilson_parsing_error{ str.data(), "expected quote character or identifier" });
194 }
195
197 inline expected<wilson, wilson_parsing_error> consume_word_or_string(std::string_view& str)
198 {
199 if (string_ops::ascii::isalpha(str[0]))
200 {
201 auto string = consume_string_value(str);
202 if (string == "true")
203 return true;
204 else if (string == "false")
205 return false;
206 else if (string == "null" || string == "nil")
207 return nullptr;
208 return string;
209 }
210 return consume_string_value(str);
211 }
212
213 inline expected<wilson, wilson_parsing_error> consume_object(std::string_view& str, char closing_char)
214 {
215 auto obj = wilson::object();
216
217 do
218 {
219 consume_whitespace(str);
220
221 if (string_ops::consume(str, closing_char))
222 return obj;
223
224 auto key = consume_string_value(str);
225 if (!key)
226 return key;
227
228 consume_whitespace(str);
229
230 if (str.empty() || string_ops::consume(str, closing_char))
231 {
232 obj[std::move(key.value())] = true;
233 break;
234 }
235
236 if (str[0] == ',' || str[0] == ';')
237 {
238 obj[std::move(key.value())] = true;
239 }
240 else
241 {
242 if (str[0] == '=' || str[0] == ':')
243 str.remove_prefix(1);
244
245 consume_whitespace(str);
246
247 auto val = formats::wilson::consume_value(str);
248 if (!val)
249 return val;
250 obj[std::move(key.value())] = std::move(val.value());
251 }
252
253 consume_whitespace(str);
254
255 if (!str.empty() && (str[0] == ',' || str[0] == ';'))
256 str.remove_prefix(1);
257 } while (!str.empty());
258
259 return obj;
260 }
261
262 inline expected<wilson, wilson_parsing_error> consume_value(std::string_view& str)
263 {
264 consume_whitespace(str);
265 if (str.empty())
266 return unexpected(wilson_parsing_error{str.data(), "expected value"});
267
268 const auto first = str[0];
269 if (string_ops::consume(str, '{'))
270 return consume_object(str);
271 else if (string_ops::consume(str, '('))
272 return consume_array(str, ')');
273 else if (string_ops::consume(str, '['))
274 return consume_array(str, ']');
275 else if (string_ops::ascii::isidentstart(first) || first == '\'' || first == '"')
276 return consume_word_or_string(str);
277 else if (string_ops::ascii::isdigit(first) || first == '-')
278 {
279 double result = 0;
280 const auto fcresult = string_ops::from_chars(str, result);
281 if (fcresult.ec == std::errc{})
282 {
283 str = string_ops::make_sv(fcresult.ptr, str.end());
284 return result;
285 }
286 }
287
288 return unexpected(wilson_parsing_error{ str.data(), "expected object, array, or valid scalar" });
289 }
290
291 inline auto parse(std::string_view wilson_str) -> expected<wilson, wilson_parsing_error>
292 {
293 const auto start = wilson_str.data();
294 return formats::wilson::consume_value(wilson_str);
295 }
296
297 inline auto parse_object(std::string_view wilson_str, char closing_char) -> expected<wilson, wilson_parsing_error>
298 {
299 return formats::wilson::consume_object(wilson_str, closing_char);
300 }
301
302 inline auto parse_array(std::string_view wilson_str, char closing_char) -> expected<wilson, wilson_parsing_error>
303 {
304 return formats::wilson::consume_array(wilson_str, closing_char);
305 }
306
307 inline auto parse_word_or_string(std::string_view wilson_str) -> expected<wilson, wilson_parsing_error>
308 {
310 }
311
312 inline auto parse_string_value(std::string_view wilson_str) -> expected<std::string, wilson_parsing_error>
313 {
314 return formats::wilson::consume_string_value(wilson_str);
315 }
316
317 inline auto parse_string_literal(std::string_view wilson_str) -> expected<std::string, wilson_parsing_error>
318 {
320 }
321
322 namespace detail
323 {
324 template <typename OUTFUNC, typename VAL>
325 void output_value(OUTFUNC&& func, VAL const& val)
326 {
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));
330 }
331
332 template <typename OUTFUNC>
333 void output_string(OUTFUNC&& func, std::string_view strval)
334 {
335 func("\"");
336 std::string buf;
337 char temp[8]{};
338 auto start = strval.begin();
339 for (auto it = strval.begin(); it != strval.end(); ++it)
340 {
341 if (string_ops::ascii::isprint(*it) && *it != '"' && *it != '\\')
342 continue;
343 else
344 {
345 if (start != it)
346 func(string_ops::make_sv(start, it));
347
350 switch (*it)
351 {
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;
361 default: break;
362 }
363
364 {
365 func("\\x");
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));
368 }
369
370 reset_start_to_next_char:
371 start = std::next(it);
372 }
373 }
374 if (start != strval.end())
375 func(string_ops::make_sv(start, strval.end()));
376 func("\"");
377 }
378 }
379
380 namespace detail
381 {
382 template <typename OUTFUNC>
383 void do_indent(OUTFUNC&& out, output_parameters const& parameters, size_t indent)
384 {
385 for (size_t i=0; i<indent; ++i)
386 out(parameters.indent_str);
387 }
388 }
389
390 template <typename OUTFUNC>
391 void output(OUTFUNC&& out, wilson const& value, output_parameters const& parameters, size_t indent)
392 {
393 const auto pretty = parameters.pretty;
394 switch (value.type())
395 {
396 using enum nlohmann::detail::value_t;
397 case null: out("null"); return;
398 case object:
399 out(pretty ? "{\n" : "{ ");
400 for (auto& [k, v] : value.get_ref<wilson::object_t const&>())
401 {
402 if (pretty)
403 detail::do_indent(out, parameters, indent + 1);
404 formats::wilson::detail::output_string(out, k);
405 out(": ");
406 formats::wilson::output(out, v, parameters, indent + 1);
407 out(pretty ? ",\n" : ", ");
408 }
409 if (pretty)
410 detail::do_indent(out, parameters, indent);
411 out("}");
412 return;
413 case array:
414 out(pretty ? "[\n" : "[ ");
415 for (auto& obj : value.get_ref<wilson::array_t const&>())
416 {
417 if (pretty)
418 detail::do_indent(out, parameters, indent + 1);
419 formats::wilson::output(out, obj, parameters, indent + 1);
420 out(pretty ? ",\n" : ", ");
421 }
422 if (pretty)
423 detail::do_indent(out, parameters, indent);
424 out("]");
425 return;
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;
431 case binary: {
432 out(pretty ? "[\n" : "[ ");
433 size_t count = 0;
434 for (auto byte : value.get_ref<wilson::binary_t const&>())
435 {
436 if (pretty && (count % 16) == 0)
437 detail::do_indent(out, parameters, indent + 1);
438 formats::wilson::detail::output_value(out, byte);
439 ++count;
440 out((pretty && (count % 16) == 0) ? ",\n" : ", ");
441 }
442 if (pretty)
443 {
444 out("\n");
445 detail::do_indent(out, parameters, indent);
446 }
447 out("]");
448 return;
449 }
450 case discarded: return;
451 }
452 }
453
454 inline std::string to_string(wilson const& value, output_parameters const& parameters)
455 {
456 std::string result;
457 formats::wilson::output(op::append_to(result), value, parameters);
458 return result;
459 }
460
461 inline expected<wilson, wilson_parsing_error> load_file(std::filesystem::path const& from)
462 {
463 std::error_code ec;
464 const auto source = ghassanpl::make_mmap_source<char>(from, ec);
465 if (ec)
466 return unexpected(wilson_parsing_error{ {}, ec.message() });
467 return formats::wilson::parse(std::string_view{source.begin(), source.end()});
468 }
469
470 inline wilson try_load_file(std::filesystem::path const& from, wilson or_json)
471 {
472 return formats::wilson::load_file(from).value_or(std::move(or_json));
473 }
474
475}
auto * element(T &cont, RANDOM &rng=::ghassanpl::random::default_random_engine)
Returns an pointer to a random element in cont.
Definition random.h:345
constexpr CONTAINER to(RANGE &&range, TYPES &&... args)
to<container>();
Definition ranges.h:386
char consume(std::string_view &str)
Consumes and returns the first character in the str, or \0 if no more characters.
Definition string_ops.h:702
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...
Definition string_ops.h:894
std::string_view consume_while(std::string_view &str, FUNC &&pred)
Consumes characters from the beginning of str while they match pred(str[0]).
Definition string_ops.h:805
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.
Definition wilson.h:312
auto parse(std::string_view wilson_str) -> expected< wilson, wilson_parsing_error >
Parses any Wilson value.
Definition wilson.h:291
void output(OUTFUNC &&out, wilson const &value, output_parameters const &parameters={}, size_t indent=0)
Outputs value formatted as a string using the out function which should take a string_view to append ...
Definition wilson.h:391
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.
Definition wilson.h:180
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.
Definition wilson.h:470
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...
Definition wilson.h:197
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.
Definition wilson.h:302
expected< wilson, wilson_parsing_error > load_file(std::filesystem::path const &from)
Loads a Wilson value from a file.
Definition wilson.h:461
std::string to_string(wilson const &value, output_parameters const &parameters={})
Returns value formatted as a string.
Definition wilson.h:454
auto parse_string_literal(std::string_view wilson_str) -> expected< std::string, wilson_parsing_error >
Parses a string literal as a string value.
Definition wilson.h:317
void save_file(std::filesystem::path const &to, wilson const &j, output_parameters const &parameters={})
Saves a Wilson value to a file formatted as a string.
Definition wilson.h:120
void output_to_stream(std::ostream &strm, wilson const &value, output_parameters const &parameters={})
Outputs value formatted as a string to the strm output stream.
Definition wilson.h:105
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.
Definition wilson.h:307
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.
Definition wilson.h:297
auto consume_string_literal(std::string_view &_str) -> expected< std::string, wilson_parsing_error >
Consumes a string literal returning a string value.
Definition wilson.h:169
The below code is based on Sun's libm library code, which is licensed under the following license: