diff options
| author | Lexi Winter <lexi@le-fay.org> | 2025-06-23 18:34:18 +0100 |
|---|---|---|
| committer | Lexi Winter <lexi@le-fay.org> | 2025-06-23 18:34:18 +0100 |
| commit | 32b4443ba2ec5c3f7c09221ab9b21911a3126ef9 (patch) | |
| tree | cb6346997078626dc512e5e46e95796e375690ee /nihil.ucl | |
| parent | d5963532328ce5f1c9f266bf7e760b7d18a60c15 (diff) | |
| download | nihil-32b4443ba2ec5c3f7c09221ab9b21911a3126ef9.tar.gz nihil-32b4443ba2ec5c3f7c09221ab9b21911a3126ef9.tar.bz2 | |
add separate module implementation files
Diffstat (limited to 'nihil.ucl')
| -rw-r--r-- | nihil.ucl/CMakeLists.txt | 17 | ||||
| -rw-r--r-- | nihil.ucl/boolean.cc | 77 | ||||
| -rw-r--r-- | nihil.ucl/boolean.ccm | 60 | ||||
| -rw-r--r-- | nihil.ucl/emit.cc | 21 | ||||
| -rw-r--r-- | nihil.ucl/emit.ccm | 15 | ||||
| -rw-r--r-- | nihil.ucl/error.cc | 19 | ||||
| -rw-r--r-- | nihil.ucl/error.ccm | 5 | ||||
| -rw-r--r-- | nihil.ucl/integer.cc | 76 | ||||
| -rw-r--r-- | nihil.ucl/integer.ccm | 65 | ||||
| -rw-r--r-- | nihil.ucl/object.cc | 123 | ||||
| -rw-r--r-- | nihil.ucl/object.ccm | 198 | ||||
| -rw-r--r-- | nihil.ucl/parser.cc | 75 | ||||
| -rw-r--r-- | nihil.ucl/parser.ccm | 58 | ||||
| -rw-r--r-- | nihil.ucl/real.cc | 79 | ||||
| -rw-r--r-- | nihil.ucl/real.ccm | 65 | ||||
| -rw-r--r-- | nihil.ucl/string.cc | 139 | ||||
| -rw-r--r-- | nihil.ucl/string.ccm | 122 | ||||
| -rw-r--r-- | nihil.ucl/type.cc | 62 | ||||
| -rw-r--r-- | nihil.ucl/type.ccm | 48 |
19 files changed, 775 insertions, 549 deletions
diff --git a/nihil.ucl/CMakeLists.txt b/nihil.ucl/CMakeLists.txt index cb051a3..0ee024c 100644 --- a/nihil.ucl/CMakeLists.txt +++ b/nihil.ucl/CMakeLists.txt @@ -4,8 +4,9 @@ pkg_check_modules(LIBUCL REQUIRED libucl) add_library(nihil.ucl STATIC) target_link_libraries(nihil.ucl PUBLIC nihil) -target_sources(nihil.ucl PUBLIC - FILE_SET modules TYPE CXX_MODULES FILES + +target_sources(nihil.ucl + PUBLIC FILE_SET modules TYPE CXX_MODULES FILES nihil.ucl.ccm emit.ccm error.ccm @@ -20,6 +21,18 @@ target_sources(nihil.ucl PUBLIC map.ccm real.ccm string.ccm + + PRIVATE + emit.cc + error.cc + parser.cc + type.cc + + object.cc + boolean.cc + integer.cc + real.cc + string.cc ) target_compile_options(nihil.ucl PUBLIC ${LIBUCL_CFLAGS_OTHER}) diff --git a/nihil.ucl/boolean.cc b/nihil.ucl/boolean.cc new file mode 100644 index 0000000..95b4e2f --- /dev/null +++ b/nihil.ucl/boolean.cc @@ -0,0 +1,77 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <compare> +#include <cstdlib> + +#include <ucl.h> + +module nihil.ucl; + +namespace nihil::ucl { + +boolean::boolean() : boolean(false) +{ +} + +boolean::boolean(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, uobj) +{ + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); +} + +boolean::boolean(noref_t, ::ucl_object_t *uobj) + : object(noref, uobj) +{ + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); +} + +boolean::boolean(contained_type value) + : object(noref, ::ucl_object_frombool(value)) +{ + if (_object == nullptr) + throw error("failed to create UCL object"); +} + +auto boolean::value(this boolean const &self) +-> contained_type +{ + auto v = contained_type{}; + auto const *uobj = self.get_ucl_object(); + + if (::ucl_object_toboolean_safe(uobj, &v)) + return v; + + std::abort(); +} + +auto operator== (boolean const &a, boolean const &b) +-> bool +{ + return a.value() == b.value(); +} + +auto operator<=> (boolean const &a, boolean const &b) +-> std::strong_ordering +{ + return a.value() <=> b.value(); +} + +auto operator== (boolean const &a, boolean::contained_type b) +-> bool +{ + return a.value() == b; +} + +auto operator<=> (boolean const &a, boolean::contained_type b) +-> std::strong_ordering +{ + return a.value() <=> b; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/boolean.ccm b/nihil.ucl/boolean.ccm index 6a93867..78ede17 100644 --- a/nihil.ucl/boolean.ccm +++ b/nihil.ucl/boolean.ccm @@ -23,70 +23,28 @@ export struct boolean final : object { inline static constexpr object_type ucl_type = object_type::boolean; // Create a new boolean from a UCL object. - boolean(ref_t, ::ucl_object_t const *uobj) - : object(nihil::ucl::ref, uobj) - { - if (type() != ucl_type) - throw type_mismatch(ucl_type, type()); - } - - boolean(noref_t, ::ucl_object_t *uobj) - : object(noref, uobj) - { - if (type() != ucl_type) - throw type_mismatch(ucl_type, type()); - } + boolean(ref_t, ::ucl_object_t const *uobj); + boolean(noref_t, ::ucl_object_t *uobj); // Create a new default-initialised boolean. - boolean() : boolean(false) {} + boolean(); // Create a new boolean from a value. - explicit boolean(contained_type value) - : object(noref, ::ucl_object_frombool(value)) - { - if (_object == nullptr) - throw error("failed to create UCL object"); - } + explicit boolean(contained_type value); // Return this object's value. - auto value(this boolean const &self) -> contained_type - { - auto v = contained_type{}; - auto const *uobj = self.get_ucl_object(); - - if (::ucl_object_toboolean_safe(uobj, &v)) - return v; - - std::abort(); - } + auto value(this boolean const &self) -> contained_type; }; /* * Comparison operators. */ -export auto operator== (boolean const &a, boolean const &b) - -> bool -{ - return a.value() == b.value(); -} - +export auto operator== (boolean const &a, boolean const &b) -> bool; +export auto operator== (boolean const &a, boolean::contained_type b) -> bool; export auto operator<=> (boolean const &a, boolean const &b) - -> std::strong_ordering -{ - return a.value() <=> b.value(); -} - -export auto operator== (boolean const &a, boolean::contained_type b) - -> bool -{ - return a.value() == b; -} - + -> std::strong_ordering; export auto operator<=> (boolean const &a, boolean::contained_type b) - -> std::strong_ordering -{ - return a.value() <=> b; -} + -> std::strong_ordering; } // namespace nihil::ucl diff --git a/nihil.ucl/emit.cc b/nihil.ucl/emit.cc new file mode 100644 index 0000000..480ddd8 --- /dev/null +++ b/nihil.ucl/emit.cc @@ -0,0 +1,21 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <iostream> +#include <iterator> + +module nihil.ucl; + +namespace nihil::ucl { + +auto operator<<(std::ostream &stream, object const &o) +-> std::ostream & +{ + emit(o, emitter::json, std::ostream_iterator<char>(stream)); + return stream; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/emit.ccm b/nihil.ucl/emit.ccm index c4aa79b..849c5a7 100644 --- a/nihil.ucl/emit.ccm +++ b/nihil.ucl/emit.ccm @@ -9,7 +9,7 @@ module; #include <cstdlib> #include <format> #include <iterator> -#include <iostream> +#include <iosfwd> #include <span> #include <string> #include <utility> @@ -34,8 +34,8 @@ export enum struct emitter { * * We can't throw exceptions here since we're called from C code. The emit * functions return an integer value, but it's not really clear what this is - * for (for example, returning errors?) and the C API seems to mostly ignore - * it. So, we just eat errors and keep going. + * for and the C API seems to mostly ignore it. So, we just eat errors and + * keep going. */ template<std::output_iterator<char> Iterator> struct emit_wrapper { @@ -130,7 +130,7 @@ private: export auto emit(object const &object, emitter format, std::output_iterator<char> auto &&it) - -> void +-> void { auto ucl_format = static_cast<ucl_emitter>(format); auto wrapper = emit_wrapper(it); @@ -144,12 +144,7 @@ export auto emit(object const &object, emitter format, * Basic ostream printer for UCL; default to JSON since it's probably what * most people expect. */ -export auto operator<<(std::ostream &stream, object const &o) - -> std::ostream & -{ - emit(o, emitter::json, std::ostream_iterator<char>(stream)); - return stream; -} +export auto operator<<(std::ostream &stream, object const &o) -> std::ostream &; } // namespace nihil::ucl diff --git a/nihil.ucl/error.cc b/nihil.ucl/error.cc new file mode 100644 index 0000000..2f19cb7 --- /dev/null +++ b/nihil.ucl/error.cc @@ -0,0 +1,19 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <string> +#include <utility> + +module nihil.ucl; + +namespace nihil::ucl { + +error::error(std::string what) + : generic_error(std::move(what)) +{ +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/error.ccm b/nihil.ucl/error.ccm index c6a0f2d..cdb5c2b 100644 --- a/nihil.ucl/error.ccm +++ b/nihil.ucl/error.ccm @@ -4,8 +4,7 @@ module; -#include <format> -#include <utility> +#include <string> export module nihil.ucl:error; @@ -17,7 +16,7 @@ namespace nihil::ucl { * Exception thrown when an issue occurs with UCL. */ export struct error : generic_error { - error(std::string what) : generic_error(std::move(what)) {} + error(std::string what); }; } // namespace nihil::ucl diff --git a/nihil.ucl/integer.cc b/nihil.ucl/integer.cc new file mode 100644 index 0000000..16328d4 --- /dev/null +++ b/nihil.ucl/integer.cc @@ -0,0 +1,76 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <compare> +#include <cstdlib> + +#include <ucl.h> + +module nihil.ucl; + +namespace nihil::ucl { + +integer::integer(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, uobj) +{ + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); +} + +integer::integer(noref_t, ::ucl_object_t *uobj) + : object(noref, uobj) +{ + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); +} + +integer::integer() + : integer(0) +{} + +integer::integer(contained_type value) + : object(noref, ::ucl_object_fromint(value)) +{ + if (_object == nullptr) + throw error("failed to create UCL object"); +} + +auto integer::value(this integer const &self) -> contained_type +{ + auto v = contained_type{}; + auto const *uobj = self.get_ucl_object(); + + if (::ucl_object_toint_safe(uobj, &v)) + return v; + + std::abort(); +} + +auto operator== (integer const &a, integer const &b) +-> bool +{ + return a.value() == b.value(); +} + +auto operator<=> (integer const &a, integer const &b) +-> std::strong_ordering +{ + return a.value() <=> b.value(); +} + +auto operator== (integer const &a, integer::contained_type b) +-> bool +{ + return a.value() == b; +} + +auto operator<=> (integer const &a, integer::contained_type b) +-> std::strong_ordering +{ + return a.value() <=> b; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/integer.ccm b/nihil.ucl/integer.ccm index 482a716..e43ae8b 100644 --- a/nihil.ucl/integer.ccm +++ b/nihil.ucl/integer.ccm @@ -4,10 +4,9 @@ module; -#include <cassert> +#include <compare> #include <cstdint> #include <cstdlib> -#include <string> #include <ucl.h> @@ -22,72 +21,28 @@ export struct integer final : object { inline static constexpr object_type ucl_type = object_type::integer; // Create a new integer from a UCL object. - integer(ref_t, ::ucl_object_t const *uobj) - : object(nihil::ucl::ref, uobj) - { - if (type() != ucl_type) - throw type_mismatch(ucl_type, type()); - } - - integer(noref_t, ::ucl_object_t *uobj) - : object(noref, uobj) - { - if (type() != ucl_type) - throw type_mismatch(ucl_type, type()); - } + integer(ref_t, ::ucl_object_t const *uobj); + integer(noref_t, ::ucl_object_t *uobj); // Create a new default-initialised integer. - integer() - : integer(0) - {} + integer(); // Create a new integer from a value. - explicit integer(contained_type value) - : object(noref, ::ucl_object_fromint(value)) - { - if (_object == nullptr) - throw error("failed to create UCL object"); - } + explicit integer(contained_type value); // Return the value of this object. - auto value(this integer const &self) -> contained_type - { - auto v = contained_type{}; - auto const *uobj = self.get_ucl_object(); - - if (::ucl_object_toint_safe(uobj, &v)) - return v; - - std::abort(); - } + auto value(this integer const &self) -> contained_type; }; /* * Comparison operators. */ -export auto operator== (integer const &a, integer const &b) - -> bool -{ - return a.value() == b.value(); -} - +export auto operator== (integer const &a, integer const &b) -> bool; +export auto operator== (integer const &a, integer::contained_type b) -> bool; export auto operator<=> (integer const &a, integer const &b) - -> std::strong_ordering -{ - return a.value() <=> b.value(); -} - -export auto operator== (integer const &a, integer::contained_type b) - -> bool -{ - return a.value() == b; -} - + -> std::strong_ordering; export auto operator<=> (integer const &a, integer::contained_type b) - -> std::strong_ordering -{ - return a.value() <=> b; -} + -> std::strong_ordering; } // namespace nihil::ucl diff --git a/nihil.ucl/object.cc b/nihil.ucl/object.cc new file mode 100644 index 0000000..f435b90 --- /dev/null +++ b/nihil.ucl/object.cc @@ -0,0 +1,123 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cstdlib> +#include <string> +#include <utility> + +#include <ucl.h> + +module nihil.ucl; + +namespace nihil::ucl { + +object::object(ref_t, ::ucl_object_t const *object) + : _object(::ucl_object_ref(object)) +{ +} + +object::object(noref_t, ::ucl_object_t *object) + : _object(object) +{ +} + +object::~object() { + if (_object != nullptr) + ::ucl_object_unref(_object); +} + +object::object(object &&other) noexcept + : _object(std::exchange(other._object, nullptr)) +{} + +object::object(object const &other) noexcept + : _object(nullptr) +{ + *this = other; +} + +auto object::operator=(this object &self, object &&other) noexcept + -> object & +{ + if (&self != &other) + self._object = std::exchange(other._object, nullptr); + return self; +} + +auto object::operator=(this object &self, object const &other) + -> object & +{ + if (&self != &other) { + auto *new_uobj = ::ucl_object_copy(other.get_ucl_object()); + if (new_uobj == nullptr) + throw error("failed to copy UCL object"); + + if (self._object != nullptr) + ::ucl_object_unref(self._object); + self._object = new_uobj; + } + + return self; +} + +auto object::ref(this object const &self) -> object +{ + return object(nihil::ucl::ref, self.get_ucl_object()); +} + +auto object::type(this object const &self) -> object_type +{ + auto utype = ::ucl_object_type(self.get_ucl_object()); + return static_cast<object_type>(utype); +} + +auto object::get_ucl_object(this object &self) -> ::ucl_object_t * +{ + if (self._object == nullptr) + throw error("attempt to access empty UCL object"); + return self._object; +} + +auto object::get_ucl_object(this object const &self) -> ::ucl_object_t const * +{ + if (self._object == nullptr) + throw error("attempt to access empty UCL object"); + return self._object; +} + +// Return the key of this object. +auto object::key(this object const &self) -> std::string_view +{ + auto dlen = std::size_t{}; + auto const *dptr = ::ucl_object_keyl(self.get_ucl_object(), + &dlen); + return {dptr, dlen}; +} + +auto swap(object &a, object &b) -> void +{ + std::swap(a._object, b._object); +} + +auto operator<=>(object const &lhs, object const &rhs) -> std::strong_ordering +{ + auto cmp = ::ucl_object_compare(lhs.get_ucl_object(), + rhs.get_ucl_object()); + + if (cmp < 0) + return std::strong_ordering::less; + else if (cmp > 0) + return std::strong_ordering::greater; + else + return std::strong_ordering::equal; +} + +auto operator==(object const &lhs, object const &rhs) -> bool +{ + return (lhs <=> rhs) == std::strong_ordering::equal; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/object.ccm b/nihil.ucl/object.ccm index b220335..5becfa8 100644 --- a/nihil.ucl/object.ccm +++ b/nihil.ucl/object.ccm @@ -10,12 +10,9 @@ module; * */ -#include <algorithm> -#include <cassert> +#include <compare> #include <cstddef> -#include <format> #include <string> -#include <utility> #include <ucl.h> @@ -44,103 +41,40 @@ export struct object { // Create an object from an existing ucl_object_t. The first argument // determines whether we ref the object or not. - object(ref_t, ::ucl_object_t const *object) - : _object(::ucl_object_ref(object)) - { - } - - object(noref_t, ::ucl_object_t *object) - : _object(object) - { - } + object(ref_t, ::ucl_object_t const *object); + object(noref_t, ::ucl_object_t *object); // Free our object on destruction. - virtual ~object() { - if (_object != nullptr) - ::ucl_object_unref(_object); - } + virtual ~object(); // Movable. - object(object &&other) noexcept - : _object(std::exchange(other._object, nullptr)) - {} - - auto operator=(this object &self, object &&other) noexcept - -> object & - { - if (&self != &other) - self._object = std::exchange(other._object, nullptr); - return self; - } + object(object &&other) noexcept; + auto operator=(this object &self, object &&other) noexcept -> object&; // Copyable. - object(object const &other) noexcept - : _object(nullptr) - { - *this = other; - } - - auto operator=(this object &self, object const &other) - -> object & - { - if (&self != &other) { - auto *new_uobj = ::ucl_object_copy(other.get_ucl_object()); - if (new_uobj == nullptr) - throw error("failed to copy UCL object"); - - if (self._object != nullptr) - ::ucl_object_unref(self._object); - self._object = new_uobj; - } - - return self; - } + // Note that this copies the entire UCL object. + object(object const &other) noexcept; + auto operator=(this object &self, object const &other) -> object &; // Increase the refcount of this object. - auto ref(this object const &self) -> object - { - return object(nihil::ucl::ref, self.get_ucl_object()); - } + auto ref(this object const &self) -> object; // Return the type of this object. - auto type(this object const &self) -> object_type - { - auto utype = ::ucl_object_type(self.get_ucl_object()); - return static_cast<object_type>(utype); - } + auto type(this object const &self) -> object_type; // Return the underlying object. - auto get_ucl_object(this object &self) -> ::ucl_object_t * - { - if (self._object == nullptr) - throw error("attempt to access empty UCL object"); - return self._object; - } - - auto get_ucl_object(this object const &self) -> ::ucl_object_t const * - { - if (self._object == nullptr) - throw error("attempt to access empty UCL object"); - return self._object; - } + auto get_ucl_object(this object &self) -> ::ucl_object_t *; + + auto get_ucl_object(this object const &self) -> ::ucl_object_t const *; // Return the key of this object. - auto key(this object const &self) -> std::string_view - { - auto dlen = std::size_t{}; - auto const *dptr = ::ucl_object_keyl(self.get_ucl_object(), - &dlen); - return {dptr, dlen}; - } + auto key(this object const &self) -> std::string_view; protected: // The object we're wrapping. ::ucl_object_t *_object = nullptr; - friend auto swap(object &a, object &b) -> void - { - std::swap(a._object, b._object); - } + friend auto swap(object &a, object &b) -> void; private: @@ -152,104 +86,8 @@ private: * Object comparison. */ +export auto operator==(object const &lhs, object const &rhs) -> bool; export auto operator<=>(object const &lhs, object const &rhs) - -> std::strong_ordering -{ - auto cmp = ::ucl_object_compare(lhs.get_ucl_object(), - rhs.get_ucl_object()); - - if (cmp < 0) - return std::strong_ordering::less; - else if (cmp > 0) - return std::strong_ordering::greater; - else - return std::strong_ordering::equal; -} - -export auto operator==(object const &lhs, object const &rhs) -> bool -{ - return (lhs <=> rhs) == std::strong_ordering::equal; -} - -/*********************************************************************** - * Object iteration. - */ - -export struct iterator { - using difference_type = std::ptrdiff_t; - using value_type = object; - using reference = value_type &; - using const_reference = value_type const &; - using pointer = value_type *; - using const_pointer = value_type const *; - - struct sentinel{}; - - explicit iterator(object const &obj) - { - _state = std::make_shared<state>(obj); - ++(*this); - } - - auto operator==(this iterator const &self, sentinel) -> bool - { - return (self._state->cur == nullptr); - } - - auto operator++(this iterator &self) -> iterator & - { - self._state->next(); - return self; - } - - auto operator++(this iterator &self, int) -> iterator & - { - self._state->next(); - return self; - } - - auto operator*(this iterator const &self) -> object { - return object(ref, self._state->cur); - } - -private: - struct state { - state(object const &obj) - { - auto const *uobj = obj.get_ucl_object(); - if ((iter = ::ucl_object_iterate_new(uobj)) == nullptr) - throw error("failed to create UCL iterator"); - } - - state(state const &) = delete; - auto operator=(this state &, state const &) -> state& = delete; - - ~state() { - if (iter != nullptr) - ::ucl_object_iterate_free(iter); - } - - auto next() -> void { - cur = ::ucl_object_iterate_safe(iter, true); - } - - ucl_object_iter_t iter = nullptr; - ucl_object_t const *cur = nullptr; - }; - - std::shared_ptr<state> _state; -}; - -static_assert(std::input_iterator<iterator>); - -export auto begin(object const &o) -> iterator -{ - return iterator(o); -} - -export auto end(object const &) -> iterator::sentinel -{ - return {}; -} + -> std::strong_ordering; } // namespace nihil::ucl diff --git a/nihil.ucl/parser.cc b/nihil.ucl/parser.cc new file mode 100644 index 0000000..816116d --- /dev/null +++ b/nihil.ucl/parser.cc @@ -0,0 +1,75 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <functional> +#include <string> + +#include <ucl.h> + +module nihil.ucl; + +namespace nihil::ucl { + +parse_error::parse_error(std::string what) + : error(std::move(what)) +{ +} + +auto macro_handler::handle(unsigned char const *data, std::size_t len, void *ud) +-> bool +{ + auto handler = static_cast<macro_handler *>(ud); + auto string = std::string_view( + reinterpret_cast<char const *>(data), + len); + return handler->callback(string); +} + +parser::parser(int flags) +{ + if ((_parser = ::ucl_parser_new(flags)) != nullptr) + return; + + throw error("failed to create UCL parser"); +} + +parser::parser() + : parser(0) +{ +} + +parser::~parser() +{ + if (_parser) + ::ucl_parser_free(_parser); +} + +auto parser::register_value( + this parser &self, + std::string_view variable, + std::string_view value) +-> void +{ + ::ucl_parser_register_variable( + self._parser, + std::string(variable).c_str(), + std::string(value).c_str()); +} + +auto parser::top(this parser &self) -> map<object> +{ + if (self._parser == nullptr) + throw error("attempt to call top() on an empty parser"); + + auto obj = ::ucl_parser_get_object(self._parser); + if (obj == nullptr) + throw error("attempt to call top() on an empty parser"); + + // ucl_parser_get_objects() refs the object for us. + return {noref, obj}; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/parser.ccm b/nihil.ucl/parser.ccm index 9b87773..f817b76 100644 --- a/nihil.ucl/parser.ccm +++ b/nihil.ucl/parser.ccm @@ -25,7 +25,7 @@ namespace nihil::ucl { * Exception thrown when an issue occurs parsing UCL. */ export struct parse_error : error { - parse_error(std::string what) : error(std::move(what)) {} + parse_error(std::string what); }; // UCL parser flags. @@ -33,8 +33,6 @@ export inline constexpr int parser_key_lower = UCL_PARSER_KEY_LOWERCASE; export inline constexpr int parser_zerocopy = UCL_PARSER_ZEROCOPY; export inline constexpr int parser_no_time = UCL_PARSER_NO_TIME; -export struct parser; - // A macro handler. This proxies the C API callback to the C++ API. using macro_callback_t = bool (std::string_view); @@ -42,39 +40,25 @@ struct macro_handler { std::function<macro_callback_t> callback; // Handle a callback from the C API. - static auto handle(unsigned char const *data, std::size_t len, void *ud) - -> bool - { - auto handler = static_cast<macro_handler *>(ud); - auto string = std::string_view( - reinterpret_cast<char const *>(data), - len); - return handler->callback(string); - } + static auto handle( + unsigned char const *data, + std::size_t len, void + *ud) + -> bool; }; /* * A UCL parser. This wraps the C ucl_parser API. */ export struct parser { - // Create a new parser with the given flags. - parser(int flags) { - if ((_parser = ::ucl_parser_new(flags)) != nullptr) - return; - - throw error("failed to create UCL parser"); - } + parser(int flags); // Create a new parser with the default flags. - parser() : parser(0) {} + parser(); // Destroy our parser when we're destroyed. - ~parser() - { - if (_parser) - ::ucl_parser_free(_parser); - } + ~parser(); // Add a parser macro. Unlike ucl_parser_register_macro, this doesn't // take a userdata parameter; it's assumed the user will use lambda @@ -99,17 +83,12 @@ export struct parser { // Add a parser variable. auto register_value(this parser &self, std::string_view variable, - std::string_view value) -> void - { - ::ucl_parser_register_variable(self._parser, - std::string(variable).c_str(), - std::string(value).c_str()); - } + std::string_view value) -> void; // Add data to the parser. auto add(this parser &self, std::ranges::contiguous_range auto &&data) - -> void + -> void // Only bytes (chars) are permitted. requires(sizeof(std::ranges::range_value_t<decltype(data)>) == 1) { @@ -125,7 +104,7 @@ export struct parser { } auto add(this parser &self, std::ranges::range auto &&data) - -> void + -> void requires (!std::ranges::contiguous_range<decltype(data)>) { auto cdata = std::vector<char>( @@ -135,18 +114,7 @@ export struct parser { } // Return the top object of this parser. - auto top(this parser &self) -> map<object> - { - if (self._parser == nullptr) - throw error("attempt to call top() on an empty parser"); - - auto obj = ::ucl_parser_get_object(self._parser); - if (obj == nullptr) - throw error("attempt to call top() on an empty parser"); - - // ucl_parser_get_objects() refs the object for us. - return {noref, obj}; - } + auto top(this parser &self) -> map<object>; private: // The parser object. Should never be null, unless we've been diff --git a/nihil.ucl/real.cc b/nihil.ucl/real.cc new file mode 100644 index 0000000..b371072 --- /dev/null +++ b/nihil.ucl/real.cc @@ -0,0 +1,79 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cassert> +#include <compare> +#include <cstdlib> +#include <string> + +#include <ucl.h> + +module nihil.ucl; + +namespace nihil::ucl { + +real::real(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, uobj) +{ + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); +} + +real::real(noref_t, ::ucl_object_t *uobj) + : object(noref, uobj) +{ + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); +} + +real::real() + : real(0) +{ +} + +real::real(contained_type value) + : object(noref, ::ucl_object_fromdouble(value)) +{ + if (_object == nullptr) + throw error("failed to create UCL object"); +} + +auto real::value(this real const &self) -> contained_type +{ + auto v = contained_type{}; + auto const *uobj = self.get_ucl_object(); + + if (::ucl_object_todouble_safe(uobj, &v)) + return v; + + std::abort(); +} + +auto operator== (real const &a, real const &b) +-> bool +{ + return a.value() == b.value(); +} + +auto operator<=> (real const &a, real const &b) +-> std::partial_ordering +{ + return a.value() <=> b.value(); +} + +auto operator== (real const &a, real::contained_type b) +-> bool +{ + return a.value() == b; +} + +auto operator<=> (real const &a, real::contained_type b) +-> std::partial_ordering +{ + return a.value() <=> b; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/real.ccm b/nihil.ucl/real.ccm index 260e993..e0ecbb5 100644 --- a/nihil.ucl/real.ccm +++ b/nihil.ucl/real.ccm @@ -4,9 +4,7 @@ module; -#include <cassert> -#include <cstdlib> -#include <string> +#include <compare> #include <ucl.h> @@ -22,72 +20,29 @@ export struct real final : object { inline static constexpr object_type ucl_type = object_type::real; // Create a new real from a UCL object. - real(ref_t, ::ucl_object_t const *uobj) - : object(nihil::ucl::ref, uobj) - { - if (type() != ucl_type) - throw type_mismatch(ucl_type, type()); - } - - real(noref_t, ::ucl_object_t *uobj) - : object(noref, uobj) - { - if (type() != ucl_type) - throw type_mismatch(ucl_type, type()); - } + real(ref_t, ::ucl_object_t const *uobj); + real(noref_t, ::ucl_object_t *uobj); // Create a default-initialised real. - real() - : real(0) - {} + real(); // Create a new real from a value. - explicit real(contained_type value) - : object(noref, ::ucl_object_fromdouble(value)) - { - if (_object == nullptr) - throw error("failed to create UCL object"); - } + explicit real(contained_type value); // Return the value of this real. - auto value(this real const &self) -> contained_type - { - auto v = contained_type{}; - auto const *uobj = self.get_ucl_object(); - - if (::ucl_object_todouble_safe(uobj, &v)) - return v; - - std::abort(); - } + auto value(this real const &self) -> contained_type; }; /* * Comparison operators. */ -export auto operator== (real const &a, real const &b) - -> bool -{ - return a.value() == b.value(); -} +export auto operator== (real const &a, real const &b) -> bool; +export auto operator== (real const &a, real::contained_type b) -> bool; export auto operator<=> (real const &a, real const &b) - -> std::partial_ordering -{ - return a.value() <=> b.value(); -} - -export auto operator== (real const &a, real::contained_type b) - -> bool -{ - return a.value() == b; -} - + -> std::partial_ordering; export auto operator<=> (real const &a, real::contained_type b) - -> std::partial_ordering -{ - return a.value() <=> b; -} + -> std::partial_ordering; } // namespace nihil::ucl diff --git a/nihil.ucl/string.cc b/nihil.ucl/string.cc new file mode 100644 index 0000000..d2f4618 --- /dev/null +++ b/nihil.ucl/string.cc @@ -0,0 +1,139 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <cstdlib> +#include <string> + +#include <ucl.h> + +module nihil.ucl; + +namespace nihil::ucl { + +string::string(ref_t, ::ucl_object_t const *uobj) + : object(nihil::ucl::ref, uobj) +{ + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); +} + +string::string(noref_t, ::ucl_object_t *uobj) + : object(noref, uobj) +{ + if (type() != ucl_type) + throw type_mismatch(ucl_type, type()); +} + +string::string() + : string(std::string_view("")) +{} + +string::string(std::string_view value) + : object(nihil::ucl::ref, + ::ucl_object_fromstring_common( + value.data(), value.size(), + UCL_STRING_RAW)) +{ + if (_object == nullptr) + throw error("failed to create UCL object"); +} + +auto string::value(this string const &self) -> contained_type +{ + char const *dptr{}; + std::size_t dlen; + + auto const *uobj = self.get_ucl_object(); + if (::ucl_object_tolstring_safe(uobj, &dptr, &dlen)) + return {dptr, dlen}; + + // This should never fail. + std::abort(); +} + +auto string::size(this string const &self) -> size_type +{ + return self.value().size(); +} + +auto string::empty(this string const &self) -> bool +{ + return self.size() == 0; +} + +auto string::data(this string const &self) -> pointer +{ + char const *dptr{}; + + auto const *uobj = self.get_ucl_object(); + if (::ucl_object_tostring_safe(uobj, &dptr)) + return dptr; + + // This should never fail. + std::abort(); +} + +auto string::begin(this string const &self) -> iterator +{ + return self.data(); +} + +auto string::end(this string const &self) -> iterator +{ + return self.data() + self.size(); +} + +auto operator== (string const &a, string const &b) + -> bool +{ + return a.value() == b.value(); +} + +auto operator<=> (string const &a, string const &b) + -> std::strong_ordering +{ + return a.value() <=> b.value(); +} + +/* + * For convenience, allow comparison with C++ strings without having to + * construct a temporary UCL object. + */ + +auto operator==(string const &lhs, std::string_view rhs) -> bool +{ + return lhs.value() == rhs; +} + +auto operator<=>(string const &lhs, std::string_view rhs) + -> std::strong_ordering +{ + return lhs.value() <=> rhs; +} + +auto operator==(string const &lhs, std::string const &rhs) -> bool +{ + return lhs == std::string_view(rhs); +} + +auto operator<=>(string const &lhs, std::string const &rhs) + -> std::strong_ordering +{ + return lhs <=> std::string_view(rhs); +} + +auto operator==(string const &lhs, char const *rhs) -> bool +{ + return lhs == std::string_view(rhs); +} + +auto operator<=>(string const &lhs, char const *rhs) + -> std::strong_ordering +{ + return lhs <=> std::string_view(rhs); +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/string.ccm b/nihil.ucl/string.ccm index f92c82c..f8dc1cd 100644 --- a/nihil.ucl/string.ccm +++ b/nihil.ucl/string.ccm @@ -4,7 +4,6 @@ module; -#include <cassert> #include <cstdlib> #include <string> @@ -28,35 +27,14 @@ export struct string final : object { using iterator = pointer; // Create a new string from a UCL object. - string(ref_t, ::ucl_object_t const *uobj) - : object(nihil::ucl::ref, uobj) - { - if (type() != ucl_type) - throw type_mismatch(ucl_type, type()); - } - - string(noref_t, ::ucl_object_t *uobj) - : object(noref, uobj) - { - if (type() != ucl_type) - throw type_mismatch(ucl_type, type()); - } + string(ref_t, ::ucl_object_t const *uobj); + string(noref_t, ::ucl_object_t *uobj); // Create a new empty string. - string() - : string(std::string_view("")) - {} + string(); // Create a new UCL string from a string. - explicit string(std::string_view value) - : object(nihil::ucl::ref, - ::ucl_object_fromstring_common( - value.data(), value.size(), - UCL_STRING_RAW)) - { - if (_object == nullptr) - throw error("failed to create UCL object"); - } + explicit string(std::string_view value); // Create a new UCL string from an iterator pair. template<std::contiguous_iterator Iterator> @@ -77,108 +55,44 @@ export struct string final : object { {} // Return the value of this string. - auto value(this string const &self) -> contained_type - { - char const *dptr{}; - std::size_t dlen; - - auto const *uobj = self.get_ucl_object(); - if (::ucl_object_tolstring_safe(uobj, &dptr, &dlen)) - return {dptr, dlen}; - - // This should never fail. - std::abort(); - } + auto value(this string const &self) -> contained_type; // Return the size of this string. - auto size(this string const &self) -> size_type - { - return self.value().size(); - } + auto size(this string const &self) -> size_type; // Test if this string is empty. - auto empty(this string const &self) -> bool - { - return self.size() == 0; - } + auto empty(this string const &self) -> bool; // Access this string's data - auto data(this string const &self) -> pointer - { - char const *dptr{}; - - auto const *uobj = self.get_ucl_object(); - if (::ucl_object_tostring_safe(uobj, &dptr)) - return dptr; - - // This should never fail. - std::abort(); - } + auto data(this string const &self) -> pointer; // Iterator access - auto begin(this string const &self) -> iterator - { - return self.data(); - } - - auto end(this string const &self) -> iterator - { - return self.data() + self.size(); - } + auto begin(this string const &self) -> iterator; + auto end(this string const &self) -> iterator; }; /* * Comparison operators. */ -export auto operator== (string const &a, string const &b) - -> bool -{ - return a.value() == b.value(); -} - +export auto operator== (string const &a, string const &b) -> bool; export auto operator<=> (string const &a, string const &b) - -> std::strong_ordering -{ - return a.value() <=> b.value(); -} + -> std::strong_ordering; /* * For convenience, allow comparison with C++ strings without having to * construct a temporary UCL object. */ -export auto operator==(string const &lhs, std::string_view rhs) -> bool -{ - return lhs.value() == rhs; -} +export auto operator==(string const &lhs, std::string_view rhs) -> bool; +export auto operator==(string const &lhs, std::string const &rhs) -> bool; +export auto operator==(string const &lhs, char const *rhs) -> bool; export auto operator<=>(string const &lhs, std::string_view rhs) - -> std::strong_ordering -{ - return lhs.value() <=> rhs; -} - -export auto operator==(string const &lhs, std::string const &rhs) -> bool -{ - return lhs == std::string_view(rhs); -} - + -> std::strong_ordering; export auto operator<=>(string const &lhs, std::string const &rhs) - -> std::strong_ordering -{ - return lhs <=> std::string_view(rhs); -} - -export auto operator==(string const &lhs, char const *rhs) -> bool -{ - return lhs == std::string_view(rhs); -} - + -> std::strong_ordering; export auto operator<=>(string const &lhs, char const *rhs) - -> std::strong_ordering -{ - return lhs <=> std::string_view(rhs); -} + -> std::strong_ordering; } // namespace nihil::ucl diff --git a/nihil.ucl/type.cc b/nihil.ucl/type.cc new file mode 100644 index 0000000..a008aa3 --- /dev/null +++ b/nihil.ucl/type.cc @@ -0,0 +1,62 @@ +/* + * This source code is released into the public domain. + */ + +module; + +#include <format> + +module nihil.ucl; + +namespace nihil::ucl { + +auto str(object_type type) -> std::string_view { + using namespace std::literals; + + switch (type) { + case object_type::object: + return "object"sv; + case object_type::array: + return "array"sv; + case object_type::integer: + return "integer"sv; + case object_type::real: + return "real"sv; + case object_type::string: + return "string"sv; + case object_type::boolean: + return "boolean"sv; + case object_type::time: + return "time"sv; + case object_type::userdata: + return "userdata"sv; + case object_type::null: + return "null"sv; + default: + // Don't fail here, since UCL might add more types that we + // don't know about. + return "unknown"sv; + } +} + +type_mismatch::type_mismatch( + object_type expected_type, object_type actual_type) + : error(std::format("UCL type mismatch: expected type '{}' " + "!= actual type '{}'", + str(expected_type), str(actual_type))) + , _expected_type(expected_type) + , _actual_type(actual_type) +{ +} + +auto type_mismatch::expected_type(this type_mismatch const &self) -> object_type +{ + return self._expected_type; +} + +auto type_mismatch::actual_type(this type_mismatch const &self) -> object_type +{ + return self._actual_type; +} + +} // namespace nihil::ucl diff --git a/nihil.ucl/type.ccm b/nihil.ucl/type.ccm index bf6c6bc..088d196 100644 --- a/nihil.ucl/type.ccm +++ b/nihil.ucl/type.ccm @@ -30,34 +30,7 @@ export enum struct object_type { }; // Get the name of a type. -export auto str(object_type type) -> std::string_view { - using namespace std::literals; - - switch (type) { - case object_type::object: - return "object"sv; - case object_type::array: - return "array"sv; - case object_type::integer: - return "integer"sv; - case object_type::real: - return "real"sv; - case object_type::string: - return "string"sv; - case object_type::boolean: - return "boolean"sv; - case object_type::time: - return "time"sv; - case object_type::userdata: - return "userdata"sv; - case object_type::null: - return "null"sv; - default: - // Don't fail here, since UCL might add more types that we - // don't know about. - return "unknown"sv; - } -} +export auto str(object_type type) -> std::string_view; // Concept of a UCL data type. export template<typename T> @@ -69,25 +42,12 @@ concept datatype = requires(T o) { // Exception thrown when a type assertion fails. export struct type_mismatch : error { - type_mismatch(object_type expected_type, object_type actual_type) - : error(std::format("UCL type mismatch: expected type '{}' " - "!= actual type '{}'", - str(expected_type), str(actual_type))) - , _expected_type(expected_type) - , _actual_type(actual_type) - {} + type_mismatch(object_type expected_type, object_type actual_type); // The type we expected. - auto expected_type(this type_mismatch const &self) -> object_type - { - return self._expected_type; - } - + auto expected_type(this type_mismatch const &self) -> object_type; // The type we got. - auto actual_type(this type_mismatch const &self) -> object_type - { - return self._actual_type; - } + auto actual_type(this type_mismatch const &self) -> object_type; private: object_type _expected_type; |
