aboutsummaryrefslogtreecommitdiffstats
path: root/nihil.ucl
diff options
context:
space:
mode:
Diffstat (limited to 'nihil.ucl')
-rw-r--r--nihil.ucl/CMakeLists.txt34
-rw-r--r--nihil.ucl/array.ccm424
-rw-r--r--nihil.ucl/array.test.cc (renamed from nihil.ucl/tests/array.cc)141
-rw-r--r--nihil.ucl/boolean.cc106
-rw-r--r--nihil.ucl/boolean.ccm138
-rw-r--r--nihil.ucl/boolean.test.cc (renamed from nihil.ucl/tests/boolean.cc)99
-rw-r--r--nihil.ucl/emit.cc21
-rw-r--r--nihil.ucl/emit.ccm179
-rw-r--r--nihil.ucl/emit.test.cc (renamed from nihil.ucl/tests/emit.cc)11
-rw-r--r--nihil.ucl/errc.cc49
-rw-r--r--nihil.ucl/errc.ccm33
-rw-r--r--nihil.ucl/integer.cc102
-rw-r--r--nihil.ucl/integer.ccm165
-rw-r--r--nihil.ucl/integer.test.cc (renamed from nihil.ucl/tests/integer.cc)21
-rw-r--r--nihil.ucl/map.ccm263
-rw-r--r--nihil.ucl/map.test.cc (renamed from nihil.ucl/tests/map.cc)56
-rw-r--r--nihil.ucl/nihil.ucl.ccm8
-rw-r--r--nihil.ucl/object.cc114
-rw-r--r--nihil.ucl/object.ccm182
-rw-r--r--nihil.ucl/object.test.cc (renamed from nihil.ucl/tests/object.cc)5
-rw-r--r--nihil.ucl/object_cast.ccm19
-rw-r--r--nihil.ucl/parse.test.cc (renamed from nihil.ucl/tests/parse.cc)10
-rw-r--r--nihil.ucl/parser.cc102
-rw-r--r--nihil.ucl/parser.ccm158
-rw-r--r--nihil.ucl/real.cc104
-rw-r--r--nihil.ucl/real.ccm151
-rw-r--r--nihil.ucl/real.test.cc (renamed from nihil.ucl/tests/real.cc)67
-rw-r--r--nihil.ucl/string.cc187
-rw-r--r--nihil.ucl/string.ccm357
-rw-r--r--nihil.ucl/string.test.cc (renamed from nihil.ucl/tests/string.cc)74
-rw-r--r--nihil.ucl/tests/CMakeLists.txt20
-rw-r--r--nihil.ucl/type.cc62
-rw-r--r--nihil.ucl/type.ccm87
33 files changed, 1416 insertions, 2133 deletions
diff --git a/nihil.ucl/CMakeLists.txt b/nihil.ucl/CMakeLists.txt
index 9d8ab3a..5b8ed72 100644
--- a/nihil.ucl/CMakeLists.txt
+++ b/nihil.ucl/CMakeLists.txt
@@ -3,13 +3,12 @@
pkg_check_modules(LIBUCL REQUIRED libucl)
add_library(nihil.ucl STATIC)
-target_link_libraries(nihil.ucl PRIVATE nihil.error nihil.monad)
+target_link_libraries(nihil.ucl PRIVATE nihil.std nihil.core nihil.error nihil.monad)
target_sources(nihil.ucl
PUBLIC FILE_SET modules TYPE CXX_MODULES FILES
nihil.ucl.ccm
emit.ccm
- errc.ccm
object.ccm
object_cast.ccm
parser.ccm
@@ -21,18 +20,6 @@ target_sources(nihil.ucl
map.ccm
real.ccm
string.ccm
-
- PRIVATE
- emit.cc
- errc.cc
- parser.cc
- type.cc
-
- object.cc
- boolean.cc
- integer.cc
- real.cc
- string.cc
)
target_compile_options(nihil.ucl PUBLIC ${LIBUCL_CFLAGS_OTHER})
@@ -41,6 +28,23 @@ target_link_libraries(nihil.ucl PUBLIC ${LIBUCL_LIBRARIES})
target_link_directories(nihil.ucl PUBLIC ${LIBUCL_LIBRARY_DIRS})
if(NIHIL_TESTS)
- add_subdirectory(tests)
+ add_executable(nihil.ucl.test
+ array.test.cc
+ boolean.test.cc
+ emit.test.cc
+ integer.test.cc
+ map.test.cc
+ object.test.cc
+ parse.test.cc
+ real.test.cc
+ string.test.cc
+ )
+
+ target_link_libraries(nihil.ucl.test PRIVATE nihil.ucl Catch2::Catch2WithMain)
+
+ include(CTest)
+ include(Catch)
+ catch_discover_tests(nihil.ucl.test)
+
enable_testing()
endif()
diff --git a/nihil.ucl/array.ccm b/nihil.ucl/array.ccm
index e3730ab..3d211d5 100644
--- a/nihil.ucl/array.ccm
+++ b/nihil.ucl/array.ccm
@@ -1,165 +1,119 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
-#include <cassert>
-#include <cerrno>
-#include <cstdint>
-#include <cstdlib>
-#include <format>
-#include <iostream>
-#include <string>
-#include <system_error>
-#include <utility>
-
#include <ucl.h>
export module nihil.ucl:array;
+import nihil.std;
import :object;
namespace nihil::ucl {
-export template<datatype T>
+export template <datatype T>
struct array;
-export template<datatype T>
-struct array_iterator {
+//
+// The array iterator. This is hardened, i.e. it always checks bounds.
+//
+export template <datatype T>
+struct array_iterator
+{
using difference_type = std::ptrdiff_t;
using value_type = T;
- using reference = T&;
- using pointer = T*;
+ using reference = T &;
+ using pointer = T *;
array_iterator() = default;
- [[nodiscard]] auto operator* (this array_iterator const &self) -> T
+ // Return the value at this position. We don't do type checking here
+ // since we assume that was done when the array was created or cast.
+ [[nodiscard]] auto operator*(this array_iterator const &self) -> T
{
auto arr = self.get_array();
if (self.m_idx >= ::ucl_array_size(arr))
- throw std::logic_error(
- "nihil::ucl::array_iterator: "
- "access past end of array");
+ throw std::logic_error("nihil::ucl::array_iterator: "
+ "access past end of array");
auto uobj = ::ucl_array_find_index(arr, self.m_idx);
if (uobj == nullptr)
- throw std::runtime_error(
- "nihil::ucl::array_iterator: "
- "failed to fetch UCL array index");
+ throw std::runtime_error("nihil::ucl::array_iterator: "
+ "failed to fetch UCL array index");
- return T(nihil::ucl::ref, uobj);
+ return T(ref, uobj);
}
- [[nodiscard]] auto operator[] (this array_iterator const &self,
- difference_type idx)
- -> T
+ // Return the value at an offset.
+ [[nodiscard]] auto operator[](this array_iterator const &self, difference_type idx) -> T
{
return *(self + idx);
}
- auto operator++ (this array_iterator &self) -> array_iterator &
+ // Advance this iterator.
+ auto operator++(this array_iterator &self) -> array_iterator &
{
auto arr = self.get_array();
+ // If we're already at end, don't allow going any further.
if (self.m_idx == ::ucl_array_size(arr))
- throw std::logic_error(
- "nihil::ucl::array_iterator: "
- "iterating past end of array");
+ throw std::logic_error("nihil::ucl::array_iterator: "
+ "iterating past end of array");
++self.m_idx;
return self;
}
- auto operator++ (this array_iterator &self, int) -> array_iterator
+ auto operator++(this array_iterator &self, int) -> array_iterator
{
auto copy = self;
++self;
return copy;
}
- auto operator-- (this array_iterator &self) -> array_iterator&
+ auto operator--(this array_iterator &self) -> array_iterator &
{
if (self.m_idx == 0)
- throw std::logic_error(
- "nihil::ucl::array_iterator: "
- "iterating before start of array");
+ throw std::logic_error("nihil::ucl::array_iterator: "
+ "iterating before start of array");
--self.m_idx;
return self;
}
- auto operator-- (this array_iterator &self, int) -> array_iterator
+ auto operator--(this array_iterator &self, int) -> array_iterator
{
auto copy = self;
--self;
return copy;
}
- [[nodiscard]] auto operator== (this array_iterator const &lhs,
- array_iterator const &rhs)
- -> bool
- {
- // Empty iterators should compare equal.
- if (lhs.m_array == nullptr && rhs.m_array == nullptr)
- return true;
-
- if (lhs.get_array() != rhs.get_array())
- throw std::logic_error(
- "nihil::ucl::array_iterator: "
- "comparing iterators of different arrays");
-
- return lhs.m_idx == rhs.m_idx;
- }
-
- [[nodiscard]] auto operator<=> (this array_iterator const &lhs,
- array_iterator const &rhs)
- {
- // Empty iterators should compare equal.
- if (lhs.m_array == nullptr && rhs.m_array == nullptr)
- return std::strong_ordering::equal;
-
- if (lhs.get_array() != rhs.get_array())
- throw std::logic_error(
- "nihil::ucl::array_iterator: "
- "comparing iterators of different arrays");
-
- return lhs.m_idx <=> rhs.m_idx;
- }
-
- auto operator+= (this array_iterator &lhs, difference_type rhs)
- -> array_iterator &
+ auto operator+=(this array_iterator &lhs, difference_type rhs) -> array_iterator &
{
auto arr = lhs.get_array();
// m_idx cannot be greater than the array size
auto max_inc = ::ucl_array_size(arr) - lhs.m_idx;
if (std::cmp_greater(rhs, max_inc))
- throw std::logic_error(
- "nihil::ucl::array_iterator: "
- "iterating past end of array");
+ throw std::logic_error("nihil::ucl::array_iterator: "
+ "iterating past end of array");
lhs.m_idx += rhs;
return lhs;
}
- auto operator-= (this array_iterator &lhs, difference_type rhs)
- -> array_iterator &
+ auto operator-=(this array_iterator &lhs, difference_type rhs) -> array_iterator &
{
if (std::cmp_greater(rhs, lhs.m_idx))
- throw std::logic_error(
- "nihil::ucl::array_iterator: "
- "iterating before start of array");
+ throw std::logic_error("nihil::ucl::array_iterator: "
+ "iterating before start of array");
lhs.m_idx -= rhs;
return lhs;
}
- [[nodiscard]] auto operator- (this array_iterator const &lhs,
- array_iterator const &rhs)
- -> difference_type
+ [[nodiscard]] auto
+ operator-(this array_iterator const &lhs, array_iterator const &rhs) -> difference_type
{
if (lhs.get_array() != rhs.get_array())
- throw std::logic_error(
- "nihil::ucl::array_iterator: "
- "comparing iterators of different arrays");
+ throw std::logic_error("nihil::ucl::array_iterator: "
+ "comparing iterators of different arrays");
return lhs.m_idx - rhs.m_idx;
}
@@ -167,146 +121,148 @@ struct array_iterator {
private:
friend struct array<T>;
- ::ucl_object_t const * m_array{};
- std::size_t m_idx{};
+ ::ucl_object_t const *m_array{};
+ std::size_t m_idx{};
- [[nodiscard]] auto get_array(this array_iterator const &self)
- -> ::ucl_object_t const *
+ [[nodiscard]] auto get_array(this array_iterator const &self) -> ::ucl_object_t const *
{
if (self.m_array == nullptr)
- throw std::logic_error(
- "nihil::ucl::array_iterator: "
- "attempt to access an empty iterator");
+ throw std::logic_error("nihil::ucl::array_iterator: "
+ "attempt to access an empty iterator");
return self.m_array;
}
-
- array_iterator(::ucl_object_t const *array, std::size_t idx)
+
+ array_iterator(::ucl_object_t const *array, std::size_t const idx)
: m_array(array)
, m_idx(idx)
- {}
-};
+ {
+ }
-export template<datatype T> [[nodiscard]]
-auto operator+(array_iterator<T> const &lhs,
- typename array_iterator<T>::difference_type rhs)
--> array_iterator<T>
-{
- auto copy = lhs;
- copy += rhs;
- return copy;
-}
-
-export template<datatype T> [[nodiscard]]
-auto operator+(typename array_iterator<T>::difference_type lhs,
- array_iterator<T> const &rhs)
- -> array_iterator<T>
-{
- return rhs - lhs;
-}
+ [[nodiscard]] friend auto
+ operator==(array_iterator const &lhs, array_iterator const &rhs) -> bool
+ {
+ // Empty iterators should compare equal.
+ if (lhs.m_array == nullptr && rhs.m_array == nullptr)
+ return true;
-export template<datatype T> [[nodiscard]]
-auto operator-(array_iterator<T> const &lhs,
- typename array_iterator<T>::difference_type rhs)
- -> array_iterator<T>
-{
- auto copy = lhs;
- copy -= rhs;
- return copy;
-}
+ if (lhs.get_array() != rhs.get_array())
+ throw std::logic_error("nihil::ucl::array_iterator: "
+ "comparing iterators of different arrays");
-export template<datatype T = object>
-struct array final : object {
- inline static constexpr object_type ucl_type = object_type::array;
+ return lhs.m_idx == rhs.m_idx;
+ }
+
+ [[nodiscard]] friend auto operator<=>(array_iterator const &lhs, array_iterator const &rhs)
+ {
+ // Empty iterators should compare equal.
+ if (lhs.m_array == nullptr && rhs.m_array == nullptr)
+ return std::strong_ordering::equal;
+
+ if (lhs.get_array() != rhs.get_array())
+ throw std::logic_error("nihil::ucl::array_iterator: "
+ "comparing iterators of different arrays");
+
+ return lhs.m_idx <=> rhs.m_idx;
+ }
+
+ [[nodiscard]] friend auto
+ operator+(array_iterator const &lhs, difference_type rhs) -> array_iterator
+ {
+ auto copy = lhs;
+ copy += rhs;
+ return copy;
+ }
+
+ [[nodiscard]] friend auto
+ operator+(difference_type lhs, array_iterator const &rhs) -> array_iterator
+ {
+ return rhs - lhs;
+ }
+
+ [[nodiscard]] friend auto
+ operator-(array_iterator const &lhs, difference_type rhs) -> array_iterator
+ {
+ auto copy = lhs;
+ copy -= rhs;
+ return copy;
+ }
+};
+
+export template <datatype T = object>
+struct array final : object
+{
+ static constexpr object_type ucl_type = object_type::array;
using value_type = T;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator = array_iterator<T>;
- /*
- * Create an empty array. Throws std::system_error on failure.
- */
- array() : object(noref, [] {
+ // Movable.
+ array(array &&) noexcept = default;
+ auto operator=(array &&) noexcept -> array & = default;
+
+ // Copyable. Note that this copies the entire UCL object.
+ array(array const &) = default;
+ auto operator=(array const &) -> array & = default;
+
+ ~array() override = default;
+
+ // Create an empty array. Throws std::system_error on failure.
+ array()
+ : object(noref, [] {
auto *uobj = ::ucl_object_typed_new(UCL_ARRAY);
if (uobj == nullptr)
- throw std::system_error(
- std::make_error_code(std::errc(errno)));
+ throw std::system_error(std::make_error_code(sys_error()));
return uobj;
}())
{
}
- /*
- * Create an array from a UCL object. Throws type_mismatch
- * on failure.
- *
- * Unlike object_cast<>, this does not check the type of the contained
- * elements, which means object access can throw type_mismatch.
- */
+ // Create an array from a UCL object. Throws type_mismatch on failure.
+ //
+ // Unlike object_cast<>, this does not check the type of the contained
+ // elements, which means object access can throw type_mismatch.
array(ref_t, ::ucl_object_t const *uobj)
- : object(nihil::ucl::ref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != array::ucl_type)
- throw type_mismatch(array::ucl_type,
- actual_type);
- return uobj;
- }())
+ : object(nihil::ucl::ref, ensure_ucl_type(uobj, array::ucl_type))
{
}
array(noref_t, ::ucl_object_t *uobj)
- : object(nihil::ucl::noref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != array::ucl_type)
- throw type_mismatch(array::ucl_type,
- actual_type);
- return uobj;
- }())
+ : object(nihil::ucl::noref, ensure_ucl_type(uobj, array::ucl_type))
{
}
- /*
- * Create an array from an iterator pair.
- */
- template<std::input_iterator Iterator>
- requires(std::convertible_to<std::iter_value_t<Iterator>, T>)
- array(Iterator first, Iterator last)
- : array()
+ // Create an array from a range.
+ template <std::ranges::range Range>
+ requires(std::convertible_to<std::ranges::range_value_t<Range>, T>)
+ explicit array(Range const &range)
+ : array()
{
// This is exception safe, because if we throw here the
// base class destructor will free the array.
- while (first != last) {
- push_back(*first);
- ++first;
- }
+ for (auto &&elm : range)
+ push_back(elm);
}
- /*
- * Create an array from a range.
- */
- template<std::ranges::range Range>
- requires(std::convertible_to<std::ranges::range_value_t<Range>, T>)
- array(std::from_range_t, Range &&range)
- : array(std::ranges::begin(range),
- std::ranges::end(range))
+ // Create an array from an iterator pair.
+ template <std::input_iterator Iterator>
+ requires(std::convertible_to<std::iter_value_t<Iterator>, T>)
+ array(Iterator first, Iterator last)
+ : array(std::ranges::subrange(first, last))
{
}
- /*
- * Create an array from an initializer_list.
- */
+ // Create an array from an initializer_list.
array(std::initializer_list<T> const &list)
- : array(std::ranges::begin(list),
- std::ranges::end(list))
+ : array(std::ranges::subrange(list))
{
}
- /*
- * Array iterator access.
- */
+ //
+ // Array iterator access.
+ //
[[nodiscard]] auto begin(this array const &self) -> iterator
{
@@ -318,133 +274,111 @@ struct array final : object {
return {self.get_ucl_object(), self.size()};
}
- /*
- * Return the size of this array.
- */
+ // Return the size of this array.
[[nodiscard]] auto size(this array const &self) -> size_type
{
return ::ucl_array_size(self.get_ucl_object());
}
- /*
- * Test if this array is empty.
- */
+ // Test if this array is empty.
[[nodiscard]] auto empty(this array const &self) -> bool
{
return self.size() == 0;
}
- /*
- * Reserve space for future insertions.
- */
- auto reserve(this array &self, size_type nelems) -> void
+ // Reserve space for future insertions.
+ auto reserve(this array &self, size_type const nelems) -> void
{
::ucl_object_reserve(self.get_ucl_object(), nelems);
}
- /*
- * Append an element to the array.
- */
+ // Append an element to the array.
auto push_back(this array &self, value_type const &v) -> void
{
auto uobj = ::ucl_object_ref(v.get_ucl_object());
::ucl_array_append(self.get_ucl_object(), uobj);
}
- /*
- * Prepend an element to the array.
- */
+ // Prepend an element to the array.
auto push_front(this array &self, value_type const &v) -> void
{
auto uobj = ::ucl_object_ref(v.get_ucl_object());
::ucl_array_prepend(self.get_ucl_object(), uobj);
}
- /*
- * Access an array element by index.
- */
- [[nodiscard]] auto at(this array const &self, size_type idx) -> T
+ // Access an array element by index.
+ [[nodiscard]] auto at(this array const &self, size_type const idx) -> T
{
if (idx >= self.size())
throw std::out_of_range("UCL array index out of range");
auto uobj = ::ucl_array_find_index(self.get_ucl_object(), idx);
if (uobj == nullptr)
- throw std::runtime_error(
- "failed to fetch UCL array index");
+ throw std::runtime_error("failed to fetch UCL array index");
return T(nihil::ucl::ref, uobj);
}
- [[nodiscard]] auto operator[] (this array const &self, size_type idx) -> T
+ [[nodiscard]] auto operator[](this array const &self, size_type const idx) -> T
{
return self.at(idx);
}
- /*
- * Return the first element.
- */
+ // Return the first element.
[[nodiscard]] auto front(this array const &self) -> T
{
return self.at(0);
}
- /*
- * Return the last element.
- */
+ // Return the last element.
[[nodiscard]] auto back(this array const &self) -> T
{
if (self.empty())
throw std::out_of_range("attempt to access back() on "
- "empty UCL array");
+ "empty UCL array");
return self.at(self.size() - 1);
}
-};
-/*
- * Comparison operators.
- */
-
-export template<datatype T> [[nodiscard]]
-auto operator==(array<T> const &a, array<T> const &b) -> bool
-{
- if (a.size() != b.size())
- return false;
+private:
+ //
+ // Comparison operators.
+ //
- for (typename array<T>::size_type i = 0; i < a.size(); ++i)
- if (a.at(i) != b.at(i))
+ [[nodiscard]] friend auto operator==(array const &a, array const &b) -> bool
+ {
+ if (a.size() != b.size())
return false;
- return true;
-}
+ for (size_type i = 0; i < a.size(); ++i)
+ if (a.at(i) != b.at(i))
+ return false;
-/*
- * Print an array to an ostream; uses the same format as std::format().
- */
-export template<datatype T>
-auto operator<<(std::ostream &strm, array<T> const &a) -> std::ostream &
-{
- return strm << std::format("{}", a);
-}
+ return true;
+ }
+
+ // Print an array to an ostream; uses the same format as std::format().
+ friend auto operator<<(std::ostream &strm, array const &a) -> std::ostream &
+ {
+ return strm << std::format("{}", a);
+ }
+};
} // namespace nihil::ucl
-/*
- * std::formatter for an array. The output format is a list of values
- * on a single line: [1, 2, 3].
- */
-export template<typename T>
+// std::formatter for an array. The output format is a list of values
+// on a single line: [1, 2, 3].
+export template <typename T>
struct std::formatter<nihil::ucl::array<T>, char>
{
- template<class ParseContext>
- constexpr ParseContext::iterator parse(ParseContext& ctx)
+ template <class ParseContext>
+ constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
{
+ (void)this;
return ctx.begin();
}
- template<class FmtContext>
- FmtContext::iterator format(nihil::ucl::array<T> const &o,
- FmtContext& ctx) const
+ template <class FmtContext>
+ auto format(nihil::ucl::array<T> const &o, FmtContext &ctx) const -> FmtContext::iterator
{
auto it = ctx.out();
bool first = true;
diff --git a/nihil.ucl/tests/array.cc b/nihil.ucl/array.test.cc
index 866fa45..89394a0 100644
--- a/nihil.ucl/tests/array.cc
+++ b/nihil.ucl/array.test.cc
@@ -1,18 +1,12 @@
-/*
- * This source code is released into the public domain.
- */
-
-#include <algorithm>
-#include <concepts>
-#include <expected>
-#include <ranges>
-#include <string>
+// This source code is released into the public domain.
#include <catch2/catch_test_macros.hpp>
#include <ucl.h>
+import nihil.std;
import nihil.ucl;
+namespace {
TEST_CASE("ucl: array: invariants", "[ucl]")
{
using namespace nihil::ucl;
@@ -27,42 +21,44 @@ TEST_CASE("ucl: array: invariants", "[ucl]")
static_assert(std::equality_comparable<array<>>);
static_assert(std::totally_ordered<array<>>);
static_assert(std::swappable<array<>>);
-
+
static_assert(std::ranges::sized_range<array<integer>>);
- static_assert(std::same_as<std::ranges::range_value_t<array<integer>>,
- integer>);
+ static_assert(std::same_as<std::ranges::range_value_t<array<integer>>, integer>);
}
TEST_CASE("ucl: array: constructor", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("default") {
+ SECTION("default")
+ {
auto arr = array<integer>();
REQUIRE(arr.size() == 0);
REQUIRE(str(arr.type()) == "array");
}
- SECTION("from range") {
+ SECTION("from range")
+ {
auto vec = std::vector{integer(1), integer(42)};
- auto arr = array<integer>(std::from_range, vec);
+ auto arr = array<integer>(vec);
REQUIRE(arr.size() == 2);
REQUIRE(arr[0] == 1);
REQUIRE(arr[1] == 42);
}
- SECTION("from iterator pair") {
+ SECTION("from iterator pair")
+ {
auto vec = std::vector{integer(1), integer(42)};
- auto arr = array<integer>(std::ranges::begin(vec),
- std::ranges::end(vec));
+ auto arr = array<integer>(std::ranges::begin(vec), std::ranges::end(vec));
REQUIRE(arr.size() == 2);
REQUIRE(arr[0] == 1);
REQUIRE(arr[1] == 42);
}
- SECTION("from initializer_list") {
+ SECTION("from initializer_list")
+ {
auto arr = array<integer>{integer(1), integer(42)};
REQUIRE(arr.size() == 2);
@@ -75,9 +71,10 @@ TEST_CASE("ucl: array: construct from UCL object", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("ref, correct type") {
- auto uarr = ::ucl_object_typed_new(UCL_ARRAY);
- auto uint = ::ucl_object_fromint(42);
+ SECTION("ref, correct type")
+ {
+ auto *uarr = ::ucl_object_typed_new(UCL_ARRAY);
+ auto *uint = ::ucl_object_fromint(42);
::ucl_array_append(uarr, uint);
auto arr = array<integer>(ref, uarr);
@@ -86,34 +83,38 @@ TEST_CASE("ucl: array: construct from UCL object", "[ucl]")
::ucl_object_unref(uarr);
}
- SECTION("noref, correct type") {
- auto uarr = ::ucl_object_typed_new(UCL_ARRAY);
- auto uint = ::ucl_object_fromint(42);
+ SECTION("noref, correct type")
+ {
+ auto *uarr = ::ucl_object_typed_new(UCL_ARRAY);
+ auto *uint = ::ucl_object_fromint(42);
::ucl_array_append(uarr, uint);
auto arr = array<integer>(noref, uarr);
REQUIRE(arr[0] == 42);
}
- SECTION("ref, wrong element type") {
- auto uarr = ::ucl_object_typed_new(UCL_ARRAY);
- auto uint = ::ucl_object_frombool(true);
+ SECTION("ref, wrong element type")
+ {
+ auto *uarr = ::ucl_object_typed_new(UCL_ARRAY);
+ auto *uint = ::ucl_object_frombool(true);
::ucl_array_append(uarr, uint);
auto arr = array<integer>(noref, uarr);
REQUIRE_THROWS_AS(arr[0], type_mismatch);
}
- SECTION("ref, wrong type") {
- auto uobj = ::ucl_object_frombool(true);
+ SECTION("ref, wrong type")
+ {
+ auto *uobj = ::ucl_object_frombool(true);
REQUIRE_THROWS_AS(array(ref, uobj), type_mismatch);
::ucl_object_unref(uobj);
}
- SECTION("noref, wrong type") {
- auto uobj = ::ucl_object_frombool(true);
+ SECTION("noref, wrong type")
+ {
+ auto *uobj = ::ucl_object_frombool(true);
REQUIRE_THROWS_AS(array(noref, uobj), type_mismatch);
@@ -125,10 +126,8 @@ TEST_CASE("ucl: array: swap", "[ucl]")
{
// do not add using namespace nihil::ucl
- auto arr1 = nihil::ucl::array<nihil::ucl::integer>{
- nihil::ucl::integer(1),
- nihil::ucl::integer(2)
- };
+ auto arr1 = nihil::ucl::array<nihil::ucl::integer>{nihil::ucl::integer(1),
+ nihil::ucl::integer(2)};
auto arr2 = nihil::ucl::array<nihil::ucl::integer>{
nihil::ucl::integer(3),
@@ -169,9 +168,7 @@ TEST_CASE("ucl: array: compare", "[ucl]")
{
using namespace nihil::ucl;
- auto arr = array<integer>{
- integer(1), integer(42), integer(666)
- };
+ auto arr = array<integer>{integer(1), integer(42), integer(666)};
auto arr2 = array<integer>();
REQUIRE(arr != arr2);
@@ -181,9 +178,7 @@ TEST_CASE("ucl: array: compare", "[ucl]")
arr2.push_back(integer(666));
REQUIRE(arr == arr2);
- auto arr3 = array<integer>{
- integer(1), integer(1), integer(1)
- };
+ auto arr3 = array<integer>{integer(1), integer(1), integer(1)};
REQUIRE(arr != arr3);
}
@@ -237,33 +232,33 @@ TEST_CASE("ucl: array: emit", "[ucl]")
auto ucl = parse("array = [1, 42, 666];").value();
auto output = std::format("{:c}", ucl);
- REQUIRE(output ==
-"array [\n"
-" 1,\n"
-" 42,\n"
-" 666,\n"
-"]\n");
+ REQUIRE(output == "array [\n"
+ " 1,\n"
+ " 42,\n"
+ " 666,\n"
+ "]\n");
}
TEST_CASE("ucl: array: format", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("empty array") {
+ SECTION("empty array")
+ {
auto arr = array<integer>();
REQUIRE(std::format("{}", arr) == "[]");
}
- SECTION("bare array") {
- auto arr = array<integer>{
- integer(1), integer(42), integer(666)
- };
+ SECTION("bare array")
+ {
+ auto arr = array<integer>{integer(1), integer(42), integer(666)};
auto output = std::format("{}", arr);
REQUIRE(output == "[1, 42, 666]");
}
- SECTION("parsed array") {
+ SECTION("parsed array")
+ {
auto ucl = parse("array = [1, 42, 666];").value();
auto arr = object_cast<array<integer>>(ucl["array"]).value();
@@ -276,7 +271,8 @@ TEST_CASE("ucl: array: print to ostream", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("empty array") {
+ SECTION("empty array")
+ {
auto arr = array<integer>();
auto strm = std::ostringstream();
strm << arr;
@@ -284,17 +280,17 @@ TEST_CASE("ucl: array: print to ostream", "[ucl]")
REQUIRE(strm.str() == "[]");
}
- SECTION("bare array") {
- auto arr = array<integer>{
- integer(1), integer(42), integer(666)
- };
+ SECTION("bare array")
+ {
+ auto arr = array<integer>{integer(1), integer(42), integer(666)};
auto strm = std::ostringstream();
strm << arr;
REQUIRE(strm.str() == "[1, 42, 666]");
}
- SECTION("parsed array") {
+ SECTION("parsed array")
+ {
auto ucl = parse("array = [1, 42, 666];").value();
auto arr = object_cast<array<integer>>(ucl["array"]).value();
auto strm = std::ostringstream();
@@ -325,12 +321,10 @@ TEST_CASE("ucl: array is a sized_range", "[ucl]")
std::ranges::copy(arr, std::back_inserter(vec));
REQUIRE(std::ranges::equal(arr, vec));
- auto arr_as_ints =
- arr | std::views::transform(&integer::value);
+ auto arr_as_ints = arr | std::views::transform(&integer::value);
auto int_vec = std::vector<integer::contained_type>();
std::ranges::copy(arr_as_ints, std::back_inserter(int_vec));
REQUIRE(int_vec == std::vector<std::int64_t>{1, 42, 666});
-
}
TEST_CASE("ucl: array: bad object_cast", "[ucl]")
@@ -435,16 +429,18 @@ TEST_CASE("array iterator: invalid operations", "[ucl]")
{
using namespace nihil::ucl;
- auto arr = array<integer>{ integer(42) };
+ auto arr = array<integer>{integer(42)};
auto it = arr.begin();
- SECTION("decrement before start") {
+ SECTION("decrement before start")
+ {
REQUIRE_THROWS_AS(--it, std::logic_error);
REQUIRE_THROWS_AS(it--, std::logic_error);
REQUIRE_THROWS_AS(it - 1, std::logic_error);
}
- SECTION("increment past end") {
+ SECTION("increment past end")
+ {
++it;
REQUIRE(it == arr.end());
@@ -453,7 +449,8 @@ TEST_CASE("array iterator: invalid operations", "[ucl]")
REQUIRE_THROWS_AS(it + 1, std::logic_error);
}
- SECTION("dereference iterator at end") {
+ SECTION("dereference iterator at end")
+ {
REQUIRE_THROWS_AS(it[1], std::logic_error);
++it;
@@ -462,17 +459,21 @@ TEST_CASE("array iterator: invalid operations", "[ucl]")
REQUIRE_THROWS_AS(*it, std::logic_error);
}
- SECTION("compare with different array") {
- auto arr2 = array<integer>{ integer(42) };
+ SECTION("compare with different array")
+ {
+ auto arr2 = array<integer>{integer(42)};
REQUIRE_THROWS_AS(it == arr2.begin(), std::logic_error);
REQUIRE_THROWS_AS(it > arr2.begin(), std::logic_error);
REQUIRE_THROWS_AS(it - arr2.begin(), std::logic_error);
}
- SECTION("compare with empty iterator") {
+ SECTION("compare with empty iterator")
+ {
auto it2 = array_iterator<integer>();
REQUIRE_THROWS_AS(it == it2, std::logic_error);
REQUIRE_THROWS_AS(it > it2, std::logic_error);
REQUIRE_THROWS_AS(it - it2, std::logic_error);
}
}
+
+} // anonymous namespace
diff --git a/nihil.ucl/boolean.cc b/nihil.ucl/boolean.cc
deleted file mode 100644
index 91f2b17..0000000
--- a/nihil.ucl/boolean.cc
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <compare>
-#include <cstdlib>
-#include <expected>
-#include <system_error>
-
-#include <ucl.h>
-
-module nihil.ucl;
-
-import nihil.error;
-
-namespace nihil::ucl {
-
-auto make_boolean(boolean::contained_type value)
- -> std::expected<boolean, error>
-{
- auto *uobj = ::ucl_object_frombool(value);
- if (uobj == nullptr)
- return std::unexpected(error(
- errc::failed_to_create_object,
- error(std::errc(errno))));
-
- return boolean(noref, uobj);
-}
-
-boolean::boolean()
- : boolean(false)
-{
-}
-
-boolean::boolean(contained_type value)
- : object(noref, [&] {
- auto *uobj = ::ucl_object_frombool(value);
- if (uobj == nullptr)
- throw std::system_error(
- std::make_error_code(std::errc(errno)));
- return uobj;
- }())
-{
-}
-
-boolean::boolean(ref_t, ::ucl_object_t const *uobj)
- : object(nihil::ucl::ref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != boolean::ucl_type)
- throw type_mismatch(boolean::ucl_type, actual_type);
- return uobj;
- }())
-{
-}
-
-boolean::boolean(noref_t, ::ucl_object_t *uobj)
- : object(nihil::ucl::noref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != boolean::ucl_type)
- throw type_mismatch(boolean::ucl_type, actual_type);
- return uobj;
- }())
-{
-}
-
-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 068dfdd..4cacdc4 100644
--- a/nihil.ucl/boolean.ccm
+++ b/nihil.ucl/boolean.ccm
@@ -1,90 +1,118 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
-#include <cassert>
-#include <cstdint>
-#include <cstdlib>
-#include <expected>
-#include <format>
-#include <string>
-
#include <ucl.h>
export module nihil.ucl:boolean;
+import nihil.std;
+import nihil.core;
import :object;
namespace nihil::ucl {
-export struct boolean final : object {
+export struct boolean final : object
+{
using contained_type = bool;
- inline static constexpr object_type ucl_type = object_type::boolean;
+ static constexpr object_type ucl_type = object_type::boolean;
+
+ // Create a boolean holding the value false. Throws std::system_error
+ // on failure.
+ boolean()
+ : boolean(false)
+ {
+ }
+
+ // Create a boolean holding a specific value. Throws std::system_error
+ // on failure.
+ explicit boolean(bool const value)
+ : object(noref, [&] {
+ auto *uobj = ::ucl_object_frombool(value);
+ if (uobj == nullptr)
+ throw std::system_error(std::make_error_code(sys_error()));
+ return uobj;
+ }())
+ {
+ }
- /*
- * Create a boolean holding the value false. Throws std::system_error
- * on failure.
- */
- boolean();
+ // Create a new boolean from a UCL object. Throws type_mismatch
+ // on failure.
- /*
- * Create a boolean holding a specific value. Throws std::system_error
- * on failure.
- */
- explicit boolean(bool);
+ boolean(ref_t, ::ucl_object_t const *uobj)
+ : object(nihil::ucl::ref, ensure_ucl_type(uobj, boolean::ucl_type))
+ {
+ }
- /*
- * Create a new boolean from a UCL object. Throws type_mismatch
- * on failure.
- */
- boolean(ref_t, ::ucl_object_t const *uobj);
- boolean(noref_t, ::ucl_object_t *uobj);
+ boolean(noref_t, ::ucl_object_t *uobj)
+ : object(nihil::ucl::noref, ensure_ucl_type(uobj, boolean::ucl_type))
+ {
+ }
// Return this object's value.
- auto value(this boolean const &self) -> contained_type;
-};
+ auto value(this boolean const &self) -> contained_type
+ {
+ auto v = contained_type{};
+ auto const *uobj = self.get_ucl_object();
-/*
- * Boolean constructors. These return an error instead of throwing.
- */
+ if (::ucl_object_toboolean_safe(uobj, &v))
+ return v;
-export [[nodiscard]] auto
-make_boolean(boolean::contained_type = false) -> std::expected<boolean, error>;
+ throw std::runtime_error("ucl_object_toboolean_safe failed");
+ }
+
+private:
+ // Comparison operators.
+ [[nodiscard]] friend auto operator==(boolean const &a, boolean const &b) -> bool
+ {
+ return a.value() == b.value();
+ }
-/*
- * Comparison operators.
- */
+ [[nodiscard]] friend auto
+ operator<=>(boolean const &a, boolean const &b) -> std::strong_ordering
+ {
+ return static_cast<int>(a.value()) <=> static_cast<int>(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;
-export auto operator<=> (boolean const &a, boolean::contained_type b)
- -> std::strong_ordering;
+ [[nodiscard]] friend auto operator==(boolean const &a, contained_type const b) -> bool
+ {
+ return a.value() == b;
+ }
+
+ [[nodiscard]] friend auto
+ operator<=>(boolean const &a, contained_type const b) -> std::strong_ordering
+ {
+ return static_cast<int>(a.value()) <=> static_cast<int>(b);
+ }
+};
+
+// Boolean constructors. This returns an error instead of throwing.
+export [[nodiscard]] auto
+make_boolean(boolean::contained_type const value = false) -> std::expected<boolean, error>
+{
+ if (auto *uobj = ::ucl_object_frombool(value); uobj == nullptr)
+ return error(errc::failed_to_create_object, error(sys_error()));
+ else
+ return boolean(noref, uobj);
+}
} // namespace nihil::ucl
-/*
- * std::formatter for a boolean. This provides the same format operations
- * as std::formatter<bool>.
- */
-export template<>
+// std::formatter for a boolean. This provides the same format operations
+// as std::formatter<bool>.
+export template <>
struct std::formatter<nihil::ucl::boolean, char>
{
std::formatter<bool> base_formatter;
- template<class ParseContext>
- constexpr ParseContext::iterator parse(ParseContext& ctx)
+ template <class ParseContext>
+ constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
{
return base_formatter.parse(ctx);
}
- template<class FmtContext>
- FmtContext::iterator format(nihil::ucl::boolean const &o,
- FmtContext& ctx) const
+ template <class FmtContext>
+ auto format(nihil::ucl::boolean const &o, FmtContext &ctx) const -> FmtContext::iterator
{
return base_formatter.format(o.value(), ctx);
}
diff --git a/nihil.ucl/tests/boolean.cc b/nihil.ucl/boolean.test.cc
index f7ef95e..9fb0148 100644
--- a/nihil.ucl/tests/boolean.cc
+++ b/nihil.ucl/boolean.test.cc
@@ -1,16 +1,15 @@
-/*
- * This source code is released into the public domain.
- */
-
-#include <concepts>
-#include <string>
+// This source code is released into the public domain.
#include <catch2/catch_test_macros.hpp>
#include <ucl.h>
+import nihil.std;
import nihil.ucl;
-TEST_CASE("ucl: boolean: invariants", "[ucl]")
+namespace {
+inline auto constexpr *test_tags = "[nihil][nihil.ucl][nihil.ucl.boolean]";
+
+TEST_CASE("ucl: boolean: invariants", test_tags)
{
using namespace nihil::ucl;
@@ -27,18 +26,24 @@ TEST_CASE("ucl: boolean: invariants", "[ucl]")
static_assert(std::swappable<boolean>);
}
-TEST_CASE("ucl: boolean: constructor", "[ucl]")
+SCENARIO("Constructing a ucl::boolean", test_tags)
{
using namespace nihil::ucl;
- SECTION("default") {
+ GIVEN ("A default-constructed boolean") {
auto b = boolean();
- REQUIRE(b == false);
+
+ THEN ("The value is false") {
+ REQUIRE(b == false);
+ }
}
- SECTION("with value") {
+ GIVEN ("A boolean constructed from a true value") {
auto b = boolean(true);
- REQUIRE(b == true);
+
+ THEN ("The value is true") {
+ REQUIRE(b == true);
+ }
}
}
@@ -46,8 +51,9 @@ TEST_CASE("ucl: boolean: construct from UCL object", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("ref, correct type") {
- auto uobj = ::ucl_object_frombool(true);
+ SECTION("ref, correct type")
+ {
+ auto *uobj = ::ucl_object_frombool(true);
auto i = boolean(ref, uobj);
REQUIRE(i == true);
@@ -55,23 +61,26 @@ TEST_CASE("ucl: boolean: construct from UCL object", "[ucl]")
::ucl_object_unref(uobj);
}
- SECTION("noref, correct type") {
- auto uobj = ::ucl_object_frombool(true);
+ SECTION("noref, correct type")
+ {
+ auto *uobj = ::ucl_object_frombool(true);
auto i = boolean(noref, uobj);
REQUIRE(i == true);
}
- SECTION("ref, wrong type") {
- auto uobj = ::ucl_object_fromint(1);
+ SECTION("ref, wrong type")
+ {
+ auto *uobj = ::ucl_object_fromint(1);
REQUIRE_THROWS_AS(boolean(ref, uobj), type_mismatch);
::ucl_object_unref(uobj);
}
- SECTION("noref, wrong type") {
- auto uobj = ::ucl_object_fromint(1);
+ SECTION("noref, wrong type")
+ {
+ auto *uobj = ::ucl_object_fromint(1);
REQUIRE_THROWS_AS(boolean(noref, uobj), type_mismatch);
@@ -83,12 +92,14 @@ TEST_CASE("ucl: boolean: make_boolean", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("default value") {
+ SECTION("default value")
+ {
auto b = make_boolean().value();
REQUIRE(b == false);
}
- SECTION("explicit value") {
+ SECTION("explicit value")
+ {
auto b = make_boolean(true).value();
REQUIRE(b == true);
}
@@ -97,7 +108,7 @@ TEST_CASE("ucl: boolean: make_boolean", "[ucl]")
TEST_CASE("ucl: boolean: swap", "[ucl]")
{
// do not add using namespace nihil::ucl
-
+
auto b1 = nihil::ucl::boolean(true);
auto b2 = nihil::ucl::boolean(false);
@@ -117,14 +128,11 @@ TEST_CASE("ucl: boolean: key()", "[ucl]")
{
using namespace nihil::ucl;
- auto err = parse("a_bool = true");
- REQUIRE(err);
-
- auto obj = *err;
- REQUIRE(object_cast<boolean>(obj["a_bool"])->key() == "a_bool");
+ auto obj = parse("a_bool = true").value();
+ REQUIRE(object_cast<boolean>(obj["a_bool"]).value().key() == "a_bool");
- auto b = nihil::ucl::boolean(true);
- REQUIRE(b.key() == "");
+ auto b = boolean(true);
+ REQUIRE(b.key().empty() == true);
}
TEST_CASE("ucl: boolean: comparison", "[ucl]")
@@ -133,22 +141,26 @@ TEST_CASE("ucl: boolean: comparison", "[ucl]")
auto b = boolean(true);
- SECTION("operator==") {
+ SECTION("operator==")
+ {
REQUIRE(b == true);
REQUIRE(b == boolean(true));
}
- SECTION("operator!=") {
+ SECTION("operator!=")
+ {
REQUIRE(b != false);
REQUIRE(b != boolean(false));
}
- SECTION("operator<") {
+ SECTION("operator<")
+ {
REQUIRE(b <= true);
REQUIRE(b <= nihil::ucl::boolean(true));
}
- SECTION("operator>") {
+ SECTION("operator>")
+ {
REQUIRE(b > false);
REQUIRE(b > nihil::ucl::boolean(false));
}
@@ -172,8 +184,7 @@ TEST_CASE("ucl: boolean: parse and emit", "[ucl]")
auto ucl = parse("bool = true;").value();
auto output = std::string();
- emit(ucl, nihil::ucl::emitter::configuration,
- std::back_inserter(output));
+ emit(ucl, nihil::ucl::emitter::configuration, std::back_inserter(output));
REQUIRE(output == "bool = true;\n");
}
@@ -182,12 +193,14 @@ TEST_CASE("ucl: boolean: format", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("bare boolean") {
+ SECTION("bare boolean")
+ {
auto str = std::format("{}", boolean(true));
REQUIRE(str == "true");
}
- SECTION("parsed boolean") {
+ SECTION("parsed boolean")
+ {
auto obj = parse("bool = true;").value();
auto b = object_cast<boolean>(obj["bool"]).value();
@@ -195,7 +208,8 @@ TEST_CASE("ucl: boolean: format", "[ucl]")
REQUIRE(str == "true");
}
- SECTION("with format string") {
+ SECTION("with format string")
+ {
auto str = std::format("{: >5}", boolean(true));
REQUIRE(str == " true");
}
@@ -205,14 +219,16 @@ TEST_CASE("ucl: boolean: print to ostream", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("bare boolean") {
+ SECTION("bare boolean")
+ {
auto strm = std::ostringstream();
strm << boolean(true);
REQUIRE(strm.str() == "true");
}
- SECTION("parsed boolean") {
+ SECTION("parsed boolean")
+ {
auto obj = parse("bool = true;").value();
auto i = object_cast<boolean>(obj["bool"]).value();
@@ -222,3 +238,4 @@ TEST_CASE("ucl: boolean: print to ostream", "[ucl]")
REQUIRE(strm.str() == "true");
}
}
+} // anonymous namespace
diff --git a/nihil.ucl/emit.cc b/nihil.ucl/emit.cc
deleted file mode 100644
index 480ddd8..0000000
--- a/nihil.ucl/emit.cc
+++ /dev/null
@@ -1,21 +0,0 @@
-/*
- * 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 b88f8e7..64b8f4f 100644
--- a/nihil.ucl/emit.ccm
+++ b/nihil.ucl/emit.ccm
@@ -1,110 +1,83 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
-#include <array>
-#include <charconv>
-#include <cstdlib>
-#include <format>
-#include <iterator>
-#include <iosfwd>
-#include <span>
-#include <string>
-#include <utility>
-
#include <ucl.h>
export module nihil.ucl:emit;
+import nihil.std;
import :object;
namespace nihil::ucl {
-export enum struct emitter {
+export enum struct emitter : std::uint8_t {
configuration = UCL_EMIT_CONFIG,
compact_json = UCL_EMIT_JSON_COMPACT,
json = UCL_EMIT_JSON,
yaml = UCL_EMIT_YAML,
};
-/*
- * Wrap ucl_emitter_functions for a particular output iterator type.
- *
- * 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 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 {
- emit_wrapper(Iterator iterator_)
- : iterator(std::move(iterator_))
- {}
-
- static auto append_character(unsigned char c, std::size_t nchars,
- void *ud)
- noexcept -> int
- try {
- auto *self = static_cast<emit_wrapper *>(ud);
-
- while (nchars--)
- *self->iterator++ = static_cast<char>(c);
+// Wrap ucl_emitter_functions for a particular output iterator type.
+//
+// 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 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
+{
+ explicit emit_wrapper(Iterator iterator)
+ : m_iterator(std::move(iterator))
+ {
+ }
+ static auto
+ append_character(unsigned char const c, std::size_t nchars, void *const ud) noexcept -> int
+ try {
+ auto &self = check_magic(ud);
+ self.m_iterator =
+ std::ranges::fill_n(self.m_iterator, nchars, static_cast<char>(c));
return 0;
} catch (...) {
return 0;
}
- static auto append_len(unsigned char const *str, std::size_t len,
- void *ud)
- noexcept -> int
+ static auto append_len(unsigned char const *const str, std::size_t const len,
+ void *const ud) noexcept -> int
try {
- auto *self = static_cast<emit_wrapper *>(ud);
-
- for (auto c : std::span(str, len))
- *self->iterator++ = static_cast<char>(c);
-
+ auto &self = check_magic(ud);
+ self.m_iterator = std::ranges::copy(std::span(str, len), self.m_iterator).out;
return 0;
} catch (...) {
return 0;
}
- static auto append_int(std::int64_t value, void *ud)
- noexcept -> int
+ static auto append_int(std::int64_t const value, void *const ud) noexcept -> int
try {
- auto constexpr bufsize =
- std::numeric_limits<std::int64_t>::digits10;
- auto buf = std::array<char, bufsize>();
+ auto &self = check_magic(ud);
- auto *self = static_cast<emit_wrapper *>(ud);
- auto result = std::to_chars(buf.data(), buf.data() + buf.size(),
- value, 10);
-
- if (result.ec == std::errc())
- for (auto c : std::span(buf.data(), result.ptr))
- *self->iterator++ = c;
+ auto buf = std::array<char, std::numeric_limits<std::int64_t>::digits10>();
+ auto result = std::to_chars(begin(buf), end(buf), value, 10);
+ if (result.ec == std::errc()) {
+ auto chars = std::span(buf.data(), result.ptr);
+ self.m_iterator = std::ranges::copy(chars, self.m_iterator).out;
+ }
return 0;
} catch (...) {
return 0;
}
- static auto append_double(double value, void *ud)
- noexcept -> int
+ static auto append_double(double const value, void *const ud) noexcept -> int
try {
- auto constexpr bufsize =
- std::numeric_limits<double>::digits10;
- auto buf = std::array<char, bufsize>();
-
- auto *self = static_cast<emit_wrapper *>(ud);
- auto result = std::to_chars(buf.data(), buf.data() + buf.size(),
- value);
+ auto &self = check_magic(ud);
- if (result.ec == std::errc())
- for (auto c : std::span(buf.data(), result.ptr))
- *self->iterator++ = c;
+ auto buf = std::array<char, std::numeric_limits<double>::digits10>();
+ auto result = std::to_chars(begin(buf), end(buf), value);
+ if (result.ec == std::errc()) {
+ auto chars = std::span(buf.data(), result.ptr);
+ self.m_iterator = std::ranges::copy(chars, self.m_iterator).out;
+ }
return 0;
} catch (...) {
@@ -124,40 +97,61 @@ struct emit_wrapper {
return ret;
}
+ [[nodiscard]] auto iterator(this emit_wrapper &self) -> Iterator &
+ {
+ return self.m_iterator;
+ }
+
+ [[nodiscard]] auto iterator(this emit_wrapper const &self) -> Iterator const &
+ {
+ return self.m_iterator;
+ }
+
private:
- Iterator iterator{};
+ Iterator m_iterator{};
+ std::uint64_t m_magic = wrapper_magic;
+
+ // Harden against memory errors.
+ static constexpr auto wrapper_magic = std::uint32_t{0x57524150};
+
+ static auto check_magic(void *p) -> emit_wrapper &
+ {
+ auto *ret = static_cast<emit_wrapper *>(p);
+ if (ret->m_magic != wrapper_magic)
+ throw std::runtime_error("Invalid emit_wrapper pointer");
+ return *ret;
+ }
};
-export auto emit(object const &object, emitter format,
- std::output_iterator<char> auto &&it)
- -> void
+export auto
+emit(object const &object, emitter const format, std::output_iterator<char> auto &&it) -> void
{
auto ucl_format = static_cast<ucl_emitter>(format);
auto wrapper = emit_wrapper(it);
auto functions = wrapper.get_functions();
- ::ucl_object_emit_full(object.get_ucl_object(), ucl_format,
- &functions, nullptr);
+ ::ucl_object_emit_full(object.get_ucl_object(), ucl_format, &functions, nullptr);
}
-/*
- * Basic ostream printer for UCL; default to JSON since it's probably what
- * most people expect.
- */
-export auto operator<<(std::ostream &, object const &) -> std::ostream &;
+// Basic ostream printer for UCL; default to JSON since it's probably what
+// most people expect. Note that most derived UCL types override this.
+export auto operator<<(std::ostream &stream, object const &o) -> std::ostream &
+{
+ emit(o, emitter::json, std::ostream_iterator<char>(stream));
+ return stream;
+}
} // namespace nihil::ucl
-/*
- * Specialisation of std::formatter<> for object.
- */
-template<std::derived_from<nihil::ucl::object> T>
+// Specialisation of std::formatter<> for object. Note that most derived
+// UCL types override this.
+template <std::derived_from<nihil::ucl::object> T>
struct std::formatter<T, char>
{
nihil::ucl::emitter emitter = nihil::ucl::emitter::json;
- template<class ParseContext>
- constexpr ParseContext::iterator parse(ParseContext& ctx)
+ template <class ParseContext>
+ constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
{
auto it = ctx.begin();
auto end = ctx.end();
@@ -179,8 +173,7 @@ struct std::formatter<T, char>
case '}':
return it;
default:
- throw std::format_error("Invalid format string "
- "for UCL object");
+ throw std::format_error("Invalid format string for UCL object");
}
++it;
@@ -189,9 +182,8 @@ struct std::formatter<T, char>
return it;
}
- template<class FmtContext>
- FmtContext::iterator format(nihil::ucl::object const &o,
- FmtContext& ctx) const
+ template <class FmtContext>
+ auto format(nihil::ucl::object const &o, FmtContext &ctx) const -> FmtContext::iterator
{
// We can't use emit() here since the context iterator is not
// an std::output_iterator.
@@ -202,8 +194,7 @@ struct std::formatter<T, char>
auto wrapper = nihil::ucl::emit_wrapper(out);
auto functions = wrapper.get_functions();
- ::ucl_object_emit_full(o.get_ucl_object(), ucl_format,
- &functions, nullptr);
- return out;
+ ::ucl_object_emit_full(o.get_ucl_object(), ucl_format, &functions, nullptr);
+ return wrapper.iterator();
}
};
diff --git a/nihil.ucl/tests/emit.cc b/nihil.ucl/emit.test.cc
index a7dcd71..51c4e0e 100644
--- a/nihil.ucl/tests/emit.cc
+++ b/nihil.ucl/emit.test.cc
@@ -1,14 +1,11 @@
-/*
- * This source code is released into the public domain.
- */
-
-#include <format>
-#include <sstream>
+// This source code is released into the public domain.
#include <catch2/catch_test_macros.hpp>
+import nihil.std;
import nihil.ucl;
+namespace {
TEST_CASE("ucl: emit to std::ostream", "[ucl]")
{
using namespace std::literals;
@@ -91,3 +88,5 @@ TEST_CASE("ucl: emit YAML with std::format", "[ucl]")
" 666\n"
"]");
}
+
+} // anonymous namespace
diff --git a/nihil.ucl/errc.cc b/nihil.ucl/errc.cc
deleted file mode 100644
index 0b65b86..0000000
--- a/nihil.ucl/errc.cc
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <string>
-#include <system_error>
-
-module nihil.ucl;
-
-namespace nihil::ucl {
-
-struct ucl_error_category final : std::error_category {
- auto name() const noexcept -> char const * override;
- auto message(int err) const -> std::string override;
-};
-
-auto ucl_category() noexcept -> std::error_category &
-{
- static auto category = ucl_error_category();
- return category;
-}
-
-auto make_error_condition(errc ec) -> std::error_condition
-{
- return {static_cast<int>(ec), ucl_category()};
-}
-
-auto ucl_error_category::name() const noexcept -> char const *
-{
- return "nihil.ucl";
-}
-
-auto ucl_error_category::message(int err) const -> std::string
-{
- switch (static_cast<errc>(err)) {
- case errc::no_error:
- return "No error";
- case errc::failed_to_create_object:
- return "Failed to create UCL object";
- case errc::type_mismatch:
- return "UCL type does not match expected type";
- default:
- return "Undefined error";
- }
-}
-
-} // namespace nihil::ucl
diff --git a/nihil.ucl/errc.ccm b/nihil.ucl/errc.ccm
deleted file mode 100644
index 8f0444d..0000000
--- a/nihil.ucl/errc.ccm
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <string>
-#include <system_error>
-
-export module nihil.ucl:errc;
-
-namespace nihil::ucl {
-
-export enum struct errc {
- no_error = 0,
-
- // ucl_object_new() or similar failed, e.g. out of memory
- failed_to_create_object,
- // Trying to create an object from a UCL object of the wrong type
- type_mismatch,
-};
-
-export auto ucl_category() noexcept -> std::error_category &;
-export auto make_error_condition(errc ec) -> std::error_condition;
-
-} // namespace nihil::ucl
-
-namespace std {
-
-export template<>
-struct is_error_condition_enum<nihil::ucl::errc> : true_type {};
-
-} // namespace std
diff --git a/nihil.ucl/integer.cc b/nihil.ucl/integer.cc
deleted file mode 100644
index 825d8f6..0000000
--- a/nihil.ucl/integer.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <compare>
-#include <cstdlib>
-#include <expected>
-#include <system_error>
-
-#include <ucl.h>
-
-module nihil.ucl;
-
-import nihil.error;
-
-namespace nihil::ucl {
-
-integer::integer()
- : integer(0)
-{
-}
-
-integer::integer(contained_type value)
- : integer(noref, [&] {
- auto *uobj = ::ucl_object_fromint(value);
- if (uobj == nullptr)
- throw std::system_error(
- std::make_error_code(std::errc(errno)));
- return uobj;
- }())
-{
-}
-
-integer::integer(ref_t, ::ucl_object_t const *uobj)
- : object(nihil::ucl::ref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != integer::ucl_type)
- throw type_mismatch(integer::ucl_type, actual_type);
- return uobj;
- }())
-{
-}
-
-integer::integer(noref_t, ::ucl_object_t *uobj)
- : object(nihil::ucl::noref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != integer::ucl_type)
- throw type_mismatch(integer::ucl_type, actual_type);
- return uobj;
- }())
-{
-}
-
-auto make_integer(integer::contained_type value)
- -> std::expected<integer, error>
-{
- auto *uobj = ::ucl_object_fromint(value);
- if (uobj == nullptr)
- return std::unexpected(error(
- errc::failed_to_create_object,
- error(std::errc(errno))));
-
- return integer(noref, uobj);
-}
-
-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 e35a471..eb7fa6b 100644
--- a/nihil.ucl/integer.ccm
+++ b/nihil.ucl/integer.ccm
@@ -1,114 +1,139 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
-#include <compare>
-#include <cstdint>
-#include <cstdlib>
-#include <expected>
-#include <format>
-#include <utility>
-
#include <ucl.h>
export module nihil.ucl:integer;
+import nihil.std;
+import nihil.core;
+import nihil.error;
import :object;
import :type;
namespace nihil::ucl {
-export struct integer final : object {
+export struct integer final : object
+{
using contained_type = std::int64_t;
- inline static constexpr object_type ucl_type = object_type::integer;
-
- /*
- * Create an integer holding the value 0. Throws std::system_error
- * on failure.
- */
- integer();
-
- /*
- * Create an integer holding a specific value. Throws std::system_error
- * on failure.
- */
- explicit integer(contained_type value);
-
- /*
- * Create a new integer from a UCL object. Throws type_mismatch
- * on failure.
- */
- integer(ref_t, ::ucl_object_t const *uobj);
- integer(noref_t, ::ucl_object_t *uobj);
+ static constexpr object_type ucl_type = object_type::integer;
+
+ // Create an integer holding the value 0. Throws std::system_error
+ // on failure.
+ integer()
+ : integer(0)
+ {
+ }
+
+ // Create an integer holding a specific value. Throws std::system_error
+ // on failure.
+ explicit integer(contained_type value)
+ : integer(noref, [&] {
+ auto *uobj = ::ucl_object_fromint(value);
+ if (uobj == nullptr)
+ throw std::system_error(std::make_error_code(sys_error()));
+ return uobj;
+ }())
+ {
+ }
+
+ // Create a new integer from a UCL object. Throws type_mismatch
+ // on failure.
+ integer(ref_t, ::ucl_object_t const *uobj)
+ : object(nihil::ucl::ref, ensure_ucl_type(uobj, integer::ucl_type))
+ {
+ }
+
+ integer(noref_t, ::ucl_object_t *uobj)
+ : object(nihil::ucl::noref, ensure_ucl_type(uobj, integer::ucl_type))
+ {
+ }
// Return the value of this object.
- [[nodiscard]] auto value(this integer const &self) -> contained_type;
-};
+ [[nodiscard]] auto value(this integer const &self) -> contained_type
+ {
+ auto v = contained_type{};
+ auto const *uobj = self.get_ucl_object();
-/*
- * Integer constructors. These return an error instead of throwing.
- */
+ if (::ucl_object_toint_safe(uobj, &v))
+ return v;
-export [[nodiscard]] auto
-make_integer(integer::contained_type = 0) -> std::expected<integer, error>;
+ throw std::runtime_error("ucl_object_toint_safe failed");
+ }
-/*
- * Comparison operators.
- */
+private:
+ //
+ // Comparison operators.
+ //
-export [[nodiscard]] auto operator== (integer const &a,
- integer const &b) -> bool;
+ [[nodiscard]] friend auto operator==(integer const &a, integer const &b) -> bool
+ {
+ return a.value() == b.value();
+ }
-export [[nodiscard]] auto operator== (integer const &a,
- integer::contained_type b) -> bool;
+ [[nodiscard]] friend auto operator==(integer const &a, integer::contained_type b) -> bool
+ {
+ return a.value() == b;
+ }
-export [[nodiscard]] auto operator<=> (integer const &a,
- integer const &b)
- -> std::strong_ordering;
+ [[nodiscard]] friend auto
+ operator<=>(integer const &a, integer const &b) -> std::strong_ordering
+ {
+ return a.value() <=> b.value();
+ }
-export [[nodiscard]] auto operator<=> (integer const &a,
- integer::contained_type b)
- -> std::strong_ordering;
+ [[nodiscard]] friend auto
+ operator<=>(integer const &a, integer::contained_type b) -> std::strong_ordering
+ {
+ return a.value() <=> b;
+ }
+};
+
+// Integer constructors. This returns an error instead of throwing.
+export [[nodiscard]] auto
+make_integer(integer::contained_type value = 0) -> std::expected<integer, error>
+{
+ auto *uobj = ::ucl_object_fromint(value);
+ if (uobj == nullptr)
+ return error(errc::failed_to_create_object, error(sys_error()));
+
+ return integer(noref, uobj);
+}
-/*
- * Literal operator.
- */
+// Literal operator for integers.
inline namespace literals {
-export constexpr auto operator""_ucl (unsigned long long i) -> integer
+export constexpr auto operator""_ucl(unsigned long long i) -> integer
{
if (std::cmp_greater(i, std::numeric_limits<std::int64_t>::max()))
throw std::out_of_range("literal out of range");
return integer(static_cast<std::int64_t>(i));
}
-} // namespace nihil::ucl::literals
+} // namespace literals
} // namespace nihil::ucl
-namespace nihil { inline namespace literals {
- export using namespace ::nihil::ucl::literals;
-}} // namespace nihil::literals
+namespace nihil {
+inline namespace literals {
+export using namespace ::nihil::ucl::literals;
+} // namespace literals
+} // namespace nihil
-/*
- * std::formatter for an integer. This provides the same format operations
- * as std::formatter<std::int64_t>.
- */
-export template<>
+// std::formatter for an integer. This provides the same format operations
+// as std::formatter<std::int64_t>.
+export template <>
struct std::formatter<nihil::ucl::integer, char>
{
std::formatter<std::int64_t> base_formatter;
- template<class ParseContext>
- constexpr ParseContext::iterator parse(ParseContext& ctx)
+ template <class ParseContext>
+ constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
{
return base_formatter.parse(ctx);
}
- template<class FmtContext>
- FmtContext::iterator format(nihil::ucl::integer const &o,
- FmtContext& ctx) const
+ template <class FmtContext>
+ auto format(nihil::ucl::integer const &o, FmtContext &ctx) const -> FmtContext::iterator
{
return base_formatter.format(o.value(), ctx);
}
diff --git a/nihil.ucl/tests/integer.cc b/nihil.ucl/integer.test.cc
index 6584764..68567e9 100644
--- a/nihil.ucl/tests/integer.cc
+++ b/nihil.ucl/integer.test.cc
@@ -1,16 +1,12 @@
-/*
- * This source code is released into the public domain.
- */
-
-#include <concepts>
-#include <cstdint>
-#include <string>
+// This source code is released into the public domain.
#include <catch2/catch_test_macros.hpp>
#include <ucl.h>
+import nihil.std;
import nihil.ucl;
+namespace {
TEST_CASE("ucl: integer: invariants", "[ucl]")
{
using namespace nihil::ucl;
@@ -67,7 +63,7 @@ TEST_CASE("ucl: integer: construct from UCL object", "[ucl]")
using namespace nihil::ucl;
SECTION("ref, correct type") {
- auto uobj = ::ucl_object_fromint(42);
+ auto *uobj = ::ucl_object_fromint(42);
auto i = integer(ref, uobj);
REQUIRE(i == 42);
@@ -76,14 +72,14 @@ TEST_CASE("ucl: integer: construct from UCL object", "[ucl]")
}
SECTION("noref, correct type") {
- auto uobj = ::ucl_object_fromint(42);
+ auto *uobj = ::ucl_object_fromint(42);
auto i = integer(noref, uobj);
REQUIRE(i == 42);
}
SECTION("ref, wrong type") {
- auto uobj = ::ucl_object_frombool(true);
+ auto *uobj = ::ucl_object_frombool(true);
REQUIRE_THROWS_AS(integer(ref, uobj), type_mismatch);
@@ -91,7 +87,7 @@ TEST_CASE("ucl: integer: construct from UCL object", "[ucl]")
}
SECTION("noref, wrong type") {
- auto uobj = ::ucl_object_frombool(true);
+ auto *uobj = ::ucl_object_frombool(true);
REQUIRE_THROWS_AS(integer(noref, uobj), type_mismatch);
@@ -147,7 +143,7 @@ TEST_CASE("ucl: integer: key()", "[ucl]")
SECTION("bare integer, no key") {
auto i = 42_ucl;
- REQUIRE(i.key() == "");
+ REQUIRE(i.key().empty() == true);
}
}
@@ -245,3 +241,4 @@ TEST_CASE("ucl: integer: print to ostream", "[ucl]")
REQUIRE(strm.str() == "42");
}
}
+} // anonymous namespace
diff --git a/nihil.ucl/map.ccm b/nihil.ucl/map.ccm
index fa77601..73ef583 100644
--- a/nihil.ucl/map.ccm
+++ b/nihil.ucl/map.ccm
@@ -1,47 +1,41 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
-#include <cassert>
-#include <cstdint>
-#include <cstdlib>
-#include <format>
-#include <memory>
-#include <optional>
-#include <string>
-#include <system_error>
-
#include <ucl.h>
export module nihil.ucl:map;
+import nihil.std;
import :object;
namespace nihil::ucl {
// Exception thrown when map::operator[] does not find the key.
-export struct key_not_found : error {
- key_not_found(std::string_view key)
+export struct key_not_found : error
+{
+ explicit key_not_found(std::string_view key)
: error(std::format("key '{}' not found in map", key))
, m_key(key)
- {}
+ {
+ }
auto key(this key_not_found const &self) -> std::string_view
{
return self.m_key;
}
-
+
private:
std::string m_key;
};
-export template<datatype T>
+export template <datatype T>
struct map;
-template<datatype T>
-struct map_iterator {
+// The map iterator. UCL doesn't provide a way to copy an iterator, so this is an
+// input iterator: it can only go forwards.
+template <datatype T>
+struct map_iterator
+{
using difference_type = std::ptrdiff_t;
using value_type = std::pair<std::string_view, T>;
using reference = value_type &;
@@ -49,242 +43,219 @@ struct map_iterator {
using pointer = value_type *;
using const_pointer = value_type const *;
- struct sentinel{};
+ struct sentinel
+ {
+ };
- [[nodiscard]] auto operator==(this map_iterator const &self, sentinel)
- -> bool
+ [[nodiscard]] auto operator==(this map_iterator const &self, sentinel) -> bool
{
- return (self.m_state->cur == nullptr);
+ return self.m_state->current_object() == nullptr;
}
auto operator++(this map_iterator &self) -> map_iterator &
{
- self.m_state->next();
+ self.m_state->advance();
return self;
}
auto operator++(this map_iterator &self, int) -> map_iterator &
{
- self.m_state->next();
+ self.m_state->advance();
return self;
}
- [[nodiscard]] auto operator*(this map_iterator const &self)
- -> value_type
+ [[nodiscard]] auto operator*(this map_iterator const &self) -> value_type
{
- auto obj = T(ref, self.m_state->cur);
+ auto *cur = self.m_state->current_object();
+ if (cur == nullptr)
+ throw std::logic_error("map_iterator::operator* called on end()");
+
+ auto obj = T(ref, cur);
return {obj.key(), std::move(obj)};
}
private:
friend struct map<T>;
- map_iterator(::ucl_object_t const *obj)
+ explicit map_iterator(::ucl_object_t const *obj)
: m_state(std::make_shared<state>(obj))
{
++(*this);
}
- struct state {
- state(::ucl_object_t const *obj)
+ struct state
+ {
+ explicit state(::ucl_object_t const *obj)
+ : m_ucl_iterator([obj] {
+ if (auto *iter = ::ucl_object_iterate_new(obj); iter != nullptr)
+ return iter;
+ throw std::system_error(make_error_code(sys_error()));
+ }())
{
- iter = ::ucl_object_iterate_new(obj);
- if (iter == nullptr)
- throw std::system_error(make_error_code(
- std::errc(errno)));
}
state(state const &) = delete;
- auto operator=(this state &, state const &) -> state& = delete;
+ auto operator=(state const &) -> state & = delete;
+
+ state(state &&) = delete;
+ auto operator=(state &&) -> state & = delete;
~state()
{
- if (iter != nullptr)
- ::ucl_object_iterate_free(iter);
+ if (m_ucl_iterator != nullptr)
+ ::ucl_object_iterate_free(m_ucl_iterator);
+ }
+
+ auto advance(this state &self) -> void
+ {
+ self.m_current_object = ::ucl_object_iterate_safe(self.m_ucl_iterator, true);
}
- auto next() -> void
+ auto current_object(this state const &self) -> ::ucl_object_t const *
{
- cur = ::ucl_object_iterate_safe(iter, true);
+ return self.m_current_object;
}
- ucl_object_iter_t iter = nullptr;
- ucl_object_t const *cur = nullptr;
+ private:
+ ucl_object_iter_t m_ucl_iterator = nullptr;
+ ucl_object_t const *m_current_object = nullptr;
};
std::shared_ptr<state> m_state;
};
-export template<datatype T = object>
-struct map final : object {
- inline static constexpr object_type ucl_type = object_type::object;
+export template <datatype T = object>
+struct map final : object
+{
+ static constexpr auto ucl_type = object_type::object;
using value_type = std::pair<std::string_view, T>;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
using iterator = map_iterator<T>;
+ using sentinel = typename iterator::sentinel;
- /*
- * Create an empty map. Throws std::system_error on failure.
- */
- map() : object(noref, [] {
+ ~map() override = default;
+
+ // Create an empty map. Throws std::system_error on failure.
+ map()
+ : object(noref, [] {
auto *uobj = ::ucl_object_typed_new(UCL_OBJECT);
if (uobj == nullptr)
- throw std::system_error(
- std::make_error_code(std::errc(errno)));
+ throw std::system_error(std::make_error_code(sys_error()));
return uobj;
}())
{
}
- /*
- * Create a map from a UCL object. Throws type_mismatch on failure.
- *
- * Unlike object_cast<>, this does not check the type of the contained
- * elements, which means object access can throw type_mismatch.
- */
- map(ref_t, ::ucl_object_t const *uobj)
- : object(nihil::ucl::ref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != map::ucl_type)
- throw type_mismatch(map::ucl_type,
- actual_type);
- return uobj;
- }())
+ // Create a map from a UCL object. Throws type_mismatch on failure.
+ //
+ // Unlike object_cast<>, this does not check the type of the contained
+ // elements, which means object access can throw type_mismatch.
+ map(ref_t, ::ucl_object_t const * const uobj)
+ : object(nihil::ucl::ref, ensure_ucl_type(uobj, map::ucl_type))
{
- if (type() != ucl_type)
- throw type_mismatch(ucl_type, type());
}
- map(noref_t, ::ucl_object_t *uobj)
- : object(nihil::ucl::noref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != map::ucl_type)
- throw type_mismatch(map::ucl_type,
- actual_type);
- return uobj;
- }())
+ map(noref_t, ::ucl_object_t * const uobj)
+ : object(nihil::ucl::noref, ensure_ucl_type(uobj, map::ucl_type))
{
}
- /*
- * Create a map from an iterator pair.
- */
- template<std::input_iterator Iterator>
- requires(std::convertible_to<std::iter_value_t<Iterator>, value_type>)
- map(Iterator first, Iterator last)
- : map()
+ // Create a map from a range of value types.
+ template <std::ranges::range Range>
+ requires(std::convertible_to<std::ranges::range_value_t<Range>, value_type>)
+ explicit map(Range const &range)
+ : map()
{
// This is exception safe, because if we throw here the
// base class destructor will free the map.
- while (first != last) {
- insert(*first);
- ++first;
- }
+ for (auto &&v: range)
+ insert(v);
}
- /*
- * Create a map from a range.
- */
- template<std::ranges::range Range>
- requires(std::convertible_to<std::ranges::range_value_t<Range>,
- value_type>)
- map(std::from_range_t, Range &&range)
- : map(std::ranges::begin(range),
- std::ranges::end(range))
+ // Create a map from an iterator pair.
+ template <std::input_iterator Iterator>
+ requires(std::convertible_to<std::iter_value_t<Iterator>, value_type>)
+ map(Iterator first, Iterator last)
+ : map(std::ranges::subrange(first, last))
{
}
- /*
- * Create a map from an initializer_list.
- */
+ // Create a map from an initializer_list.
map(std::initializer_list<value_type> const &list)
- : map(std::ranges::begin(list), std::ranges::end(list))
+ : map(std::ranges::subrange(std::ranges::begin(list), std::ranges::end(list)))
{
}
- /*
- * Map iterator access.
- */
+ // Copyable. Note that this copies the entire UCL object.
+ map(map const &) = default;
+ auto operator=(map const &) -> map & = default;
+
+ // Movable.
+ map(map &&) = default;
+ auto operator=(map &&other) -> map & = default;
+
+ //
+ // Map iterator access.
+ //
[[nodiscard]] auto begin(this map const &self) -> iterator
{
- return {self.get_ucl_object()};
+ return iterator(self.get_ucl_object());
}
- [[nodiscard]] auto end(this map const &) -> iterator::sentinel
+ [[nodiscard]] auto end(this map const &) -> sentinel
{
return {};
}
- /*
- * Reserve space for future insertions.
- */
- auto reserve(this map &self, size_type nelems) -> void
+ // Reserve space for future insertions.
+ auto reserve(this map &self, size_type const nelems) -> void
{
::ucl_object_reserve(self.get_ucl_object(), nelems);
}
- /*
- * Add an element to the map.
- */
+ // Add an element to the map.
auto insert(this map &self, value_type const &v) -> void
{
auto uobj = ::ucl_object_ref(v.second.get_ucl_object());
- ::ucl_object_insert_key(self.get_ucl_object(), uobj,
- v.first.data(), v.first.size(), true);
+ ::ucl_object_insert_key(self.get_ucl_object(), uobj, v.first.data(), v.first.size(),
+ true);
}
- /*
- * Access a map element by key.
- */
- [[nodiscard]] auto find(this map const &self, std::string_view key)
- -> std::optional<T>
+ // Access a map element by key.
+ [[nodiscard]] auto find(this map const &self, std::string_view const key) -> std::optional<T>
{
- auto const *obj = ::ucl_object_lookup_len(
- self.get_ucl_object(),
- key.data(), key.size());
+ auto const *obj =
+ ::ucl_object_lookup_len(self.get_ucl_object(), key.data(), key.size());
if (obj == nullptr)
return {};
return {T(nihil::ucl::ref, obj)};
}
- /*
- * Remove an object from the map.
- */
- auto remove(this map &self, std::string_view key) -> bool
+ // Remove an object from the map.
+ auto remove(this map &self, std::string_view const key) -> bool
{
- return ::ucl_object_delete_keyl(self.get_ucl_object(),
- key.data(), key.size());
+ return ::ucl_object_delete_keyl(self.get_ucl_object(), key.data(), key.size());
}
- /*
- * Remove an object from the map and return it.
- */
- auto pop(this map &self, std::string_view key)
- -> std::optional<T>
+ // Remove an object from the map and return it. If the map is empty, returns nullopt.
+ auto pop(this map &self, std::string_view const key) -> std::optional<T>
{
- auto *uobj = ::ucl_object_pop_keyl(self.get_ucl_object(),
- key.data(), key.size());
+ auto *uobj = ::ucl_object_pop_keyl(self.get_ucl_object(), key.data(), key.size());
if (uobj)
return T(noref, uobj);
return {};
}
- /*
- * Equivalent to find(), except it throws key_not_found if the key
- * doesn't exist in the map.
- */
- [[nodiscard]] auto operator[] (this map const &self,
- std::string_view key)
- -> T
+ // Equivalent to find(), except it throws key_not_found if the key
+ // doesn't exist in the map.
+ [[nodiscard]] auto operator[](this map const &self, std::string_view const key) -> T
{
- auto obj = self.find(key);
- if (obj)
+ if (auto obj = self.find(key); obj )
return *obj;
throw key_not_found(key);
}
diff --git a/nihil.ucl/tests/map.cc b/nihil.ucl/map.test.cc
index 7240cb3..6d31af2 100644
--- a/nihil.ucl/tests/map.cc
+++ b/nihil.ucl/map.test.cc
@@ -1,16 +1,14 @@
-/*
- * This source code is released into the public domain.
- */
-
-#include <concepts>
+// This source code is released into the public domain.
#include <catch2/catch_test_macros.hpp>
#include <ucl.h>
+import nihil.std;
import nihil.ucl;
//NOLINTBEGIN(bugprone-unchecked-optional-access)
+namespace {
TEST_CASE("ucl: map: invariants", "[ucl]")
{
using namespace nihil::ucl;
@@ -43,9 +41,9 @@ TEST_CASE("ucl: map: construct from initializer_list", "[ucl]")
using namespace std::literals;
auto map = nihil::ucl::map<integer>{
- {"1"sv, integer(1)},
- {"42"sv, integer(42)},
- };
+ {"1"sv, integer(1)},
+ {"42"sv, integer(42)},
+ };
REQUIRE(str(map.type()) == "object");
REQUIRE(map["1"] == 1);
@@ -58,11 +56,11 @@ TEST_CASE("ucl: map: construct from range", "[ucl]")
using namespace std::literals;
auto vec = std::vector<std::pair<std::string_view, integer>>{
- {"1"sv, integer(1)},
- {"42"sv, integer(42)},
- };
+ {"1"sv, integer(1)},
+ {"42"sv, integer(42)},
+ };
- auto map = nihil::ucl::map<integer>(std::from_range, vec);
+ auto map = nihil::ucl::map<integer>(vec);
REQUIRE(str(map.type()) == "object");
REQUIRE(map["1"] == 1);
@@ -75,9 +73,9 @@ TEST_CASE("ucl: map: construct from iterator pair", "[ucl]")
using namespace std::literals;
auto vec = std::vector<std::pair<std::string_view, integer>>{
- {"1"sv, integer(1)},
- {"42"sv, integer(42)},
- };
+ {"1"sv, integer(1)},
+ {"42"sv, integer(42)},
+ };
auto map = nihil::ucl::map<integer>(std::ranges::begin(vec),
std::ranges::end(vec));
@@ -107,9 +105,9 @@ TEST_CASE("ucl: map: find", "[ucl]")
using namespace std::literals;
auto map = nihil::ucl::map<integer>{
- {"1"sv, integer(1)},
- {"42"sv, integer(42)},
- };
+ {"1"sv, integer(1)},
+ {"42"sv, integer(42)},
+ };
auto obj = map.find("42");
REQUIRE(obj.value() == 42);
@@ -124,11 +122,11 @@ TEST_CASE("ucl: map: iterate", "[ucl]")
using namespace std::literals;
auto map = nihil::ucl::map<integer>{
- {"1"sv, integer(1)},
- {"42"sv, integer(42)},
- };
+ {"1"sv, integer(1)},
+ {"42"sv, integer(42)},
+ };
- auto i = 0u;
+ auto i = 0U;
for (auto [key, value] : map) {
if (key == "1")
@@ -155,9 +153,9 @@ TEST_CASE("ucl: map: remove", "[uc]")
using namespace nihil::ucl;
auto map = nihil::ucl::map<integer>{
- {"1"sv, integer(1)},
- {"42"sv, integer(42)},
- };
+ {"1"sv, integer(1)},
+ {"42"sv, integer(42)},
+ };
REQUIRE(map.find("42") != std::nullopt);
REQUIRE(map.remove("42") == true);
@@ -173,9 +171,9 @@ TEST_CASE("ucl: map: pop", "[uc]")
using namespace nihil::ucl;
auto map = nihil::ucl::map<integer>{
- {"1"sv, integer(1)},
- {"42"sv, integer(42)},
- };
+ {"1"sv, integer(1)},
+ {"42"sv, integer(42)},
+ };
REQUIRE(map.find("42") != std::nullopt);
@@ -189,4 +187,6 @@ TEST_CASE("ucl: map: pop", "[uc]")
REQUIRE(!obj);
}
+} // anonymous namespace
+
//NOLINTEND(bugprone-unchecked-optional-access)
diff --git a/nihil.ucl/nihil.ucl.ccm b/nihil.ucl/nihil.ucl.ccm
index b16eb3d..daa751b 100644
--- a/nihil.ucl/nihil.ucl.ccm
+++ b/nihil.ucl/nihil.ucl.ccm
@@ -1,13 +1,7 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
+// This source code is released into the public domain.
export module nihil.ucl;
export import :emit;
-export import :errc;
export import :object;
export import :object_cast;
export import :parser;
diff --git a/nihil.ucl/object.cc b/nihil.ucl/object.cc
deleted file mode 100644
index 53fc4c7..0000000
--- a/nihil.ucl/object.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * 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)
- : m_object(::ucl_object_ref(object))
-{
-}
-
-object::object(noref_t, ::ucl_object_t *object)
- : m_object(object)
-{
-}
-
-object::~object() {
- if (m_object != nullptr)
- ::ucl_object_unref(m_object);
-}
-
-object::object(object &&other) noexcept
- : m_object(std::exchange(other.m_object, nullptr))
-{}
-
-object::object(object const &other)
- : m_object(nullptr)
-{
- m_object = ::ucl_object_copy(other.get_ucl_object());
- if (m_object == nullptr)
- throw std::runtime_error("failed to copy UCL object");
-}
-
-auto object::operator=(this object &self, object &&other) noexcept
- -> object &
-{
- if (&self != &other)
- self.m_object = std::exchange(other.m_object, nullptr);
- return self;
-}
-
-auto object::operator=(this object &self, object const &other) -> object &
-{
- return self = object(other);
-}
-
-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.m_object == nullptr)
- throw std::logic_error("attempt to access empty UCL object");
- return self.m_object;
-}
-
-auto object::get_ucl_object(this object const &self) -> ::ucl_object_t const *
-{
- if (self.m_object == nullptr)
- throw std::logic_error("attempt to access empty UCL object");
- return self.m_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.m_object, b.m_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 9a7eaf7..93dc4db 100644
--- a/nihil.ucl/object.ccm
+++ b/nihil.ucl/object.ccm
@@ -1,88 +1,172 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
-/*
- * A UCL object. The object is immutable and internally refcounted, so it
- * may be copied as needed.
- *
- */
-
-#include <compare>
-#include <cstddef>
-#include <string>
+// A UCL object. The object is immutable and internally refcounted, so it
+// may be copied as needed.
#include <ucl.h>
export module nihil.ucl:object;
+import nihil.std;
import :type;
namespace nihil::ucl {
-/***********************************************************************
- * The basic object type.
- */
+//***********************************************************************
+// The basic object type.
// Ref the UCL object when creating an object.
-export inline constexpr struct ref_t {} ref;
+export inline constexpr struct ref_t
+{
+} ref;
+
// Don't ref the UCL object.
-export inline constexpr struct noref_t {} noref;
+export inline constexpr struct noref_t
+{
+} noref;
-export struct object {
- inline static constexpr object_type ucl_type = object_type::object;
+export struct object
+{
+ static constexpr object_type ucl_type = object_type::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(noref_t, ::ucl_object_t *object);
+ object(ref_t, ::ucl_object_t const *object)
+ : m_object(::ucl_object_ref(object))
+ {
+ }
+
+ object(noref_t, ::ucl_object_t *object)
+ : m_object(object)
+ {
+ }
// Free our object on destruction.
- virtual ~object();
+ virtual ~object()
+ {
+ if (m_object != nullptr)
+ ::ucl_object_unref(m_object);
+ }
// Movable.
- object(object &&other) noexcept;
- auto operator=(this object &self, object &&other) noexcept -> object&;
-
- // Copyable.
- // Note that this copies the entire UCL object.
- object(object const &other);
- auto operator=(this object &self, object const &other) -> object &;
+ object(object &&other) noexcept
+ : m_object(std::exchange(other.m_object, nullptr))
+ {
+ }
+
+ auto operator=(this object &self, object &&other) noexcept -> object &
+ {
+ if (&self != &other)
+ self.m_object = std::exchange(other.m_object, nullptr);
+ return self; // NOLINT
+ }
+
+ // Copyable. Note that this copies the entire UCL object.
+
+ object(object const &other)
+ : m_object([&] {
+ auto *uobj = ::ucl_object_copy(other.get_ucl_object());
+ if (uobj == nullptr)
+ throw std::runtime_error("failed to copy UCL object");
+ return uobj;
+ }())
+ {
+ }
+
+ auto operator=(this object &self, object const &other) -> object &
+ {
+ if (&self != &other)
+ self = object(other);
+ return self; // NOLINT
+ }
// Increase the refcount of this object.
- [[nodiscard]] auto ref(this object const &self) -> object;
+ [[nodiscard]] auto ref(this object const &self) -> object
+ {
+ return {nihil::ucl::ref, self.get_ucl_object()};
+ }
// Return the type of this object.
- [[nodiscard]] auto type(this object const &self) -> object_type;
+ [[nodiscard]] auto type(this object const &self) -> object_type
+ {
+ auto utype = ::ucl_object_type(self.get_ucl_object());
+ return static_cast<object_type>(utype);
+ }
// Return the underlying object.
- [[nodiscard]] auto get_ucl_object(this object &self)
- -> ::ucl_object_t *;
-
- [[nodiscard]] auto get_ucl_object(this object const &self)
- -> ::ucl_object_t const *;
+ [[nodiscard]] auto get_ucl_object(this object &self) -> ::ucl_object_t *
+ {
+ if (self.m_object == nullptr)
+ throw std::logic_error("attempt to access empty UCL object");
+ return self.m_object;
+ }
+
+ [[nodiscard]] auto get_ucl_object(this object const &self) -> ::ucl_object_t const *
+ {
+ if (self.m_object == nullptr)
+ throw std::logic_error("attempt to access empty UCL object");
+ return self.m_object;
+ }
// Return the key of this object.
- [[nodiscard]] auto key(this object const &self) -> std::string_view;
+ [[nodiscard]] 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};
+ }
protected:
+ friend auto swap(object &a, object &b) noexcept -> void
+ {
+ std::swap(a.m_object, b.m_object);
+ }
+
+ // Helper to validate the type of a UCL object. Throws type_mismatch if the
+ // type doesn't match, or else returns the pointer.
+ [[nodiscard]] static auto ensure_ucl_type(::ucl_object_t const *uobj, object_type type)
+ -> ::ucl_object_t const *
+ {
+ if (static_cast<object_type>(::ucl_object_type(uobj)) != type)
+ throw type_mismatch(type, static_cast<object_type>(::ucl_object_type(uobj)));
+ return uobj;
+ }
+
+ [[nodiscard]] static auto ensure_ucl_type(::ucl_object_t *uobj, object_type type)
+ -> ::ucl_object_t *
+ {
+ if (static_cast<object_type>(::ucl_object_type(uobj)) != type)
+ throw type_mismatch(type, static_cast<object_type>(::ucl_object_type(uobj)));
+ return uobj;
+ }
+
+private:
// The object we're wrapping.
::ucl_object_t *m_object = nullptr;
- friend auto swap(object &a, object &b) -> void;
+ //
+ // Object comparison.
+ //
+
+ [[nodiscard]] friend 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;
+ }
+
+ [[nodiscard]] friend auto operator==(object const &lhs, object const &rhs) -> bool
+ {
+ return (lhs <=> rhs) == std::strong_ordering::equal;
+ }
};
-/***********************************************************************
- * Object comparison.
- */
-
-export [[nodiscard]] auto operator==(object const &lhs, object const &rhs)
- -> bool;
-
-export [[nodiscard]] auto operator<=>(object const &lhs, object const &rhs)
- -> std::strong_ordering;
-
} // namespace nihil::ucl
diff --git a/nihil.ucl/tests/object.cc b/nihil.ucl/object.test.cc
index 3ad180e..557653c 100644
--- a/nihil.ucl/tests/object.cc
+++ b/nihil.ucl/object.test.cc
@@ -1,11 +1,10 @@
-/*
- * This source code is released into the public domain.
- */
+// This source code is released into the public domain.
#include <catch2/catch_test_macros.hpp>
#include <ucl.h>
+import nihil.std;
import nihil.ucl;
TEST_CASE("ucl object: get_ucl_object", "[ucl]")
diff --git a/nihil.ucl/object_cast.ccm b/nihil.ucl/object_cast.ccm
index 3fa9eba..5a09085 100644
--- a/nihil.ucl/object_cast.ccm
+++ b/nihil.ucl/object_cast.ccm
@@ -1,17 +1,11 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
-#include <coroutine>
-#include <cstdlib>
-#include <expected>
-
#include <ucl.h>
export module nihil.ucl:object_cast;
+import nihil.std;
import nihil.monad;
import :type;
import :object;
@@ -19,10 +13,9 @@ import :array;
namespace nihil::ucl {
-/*
- * Ensure a UCL object is convertible to another type. Throws type_mismatch
- * if not.
- */
+//
+// Ensure a UCL object is convertible to another type.
+//
// Implementation for basic types.
template<datatype To>
@@ -80,7 +73,7 @@ struct convert_check<array<T>>
export template<datatype To>
auto object_cast(object const &from) -> std::expected<To, type_mismatch>
{
- auto uobj = from.get_ucl_object();
+ auto const *uobj = from.get_ucl_object();
co_await convert_check<To>{}.check(uobj);
co_return To(nihil::ucl::ref, uobj);
diff --git a/nihil.ucl/tests/parse.cc b/nihil.ucl/parse.test.cc
index 43ce219..79a722d 100644
--- a/nihil.ucl/tests/parse.cc
+++ b/nihil.ucl/parse.test.cc
@@ -1,14 +1,12 @@
-/*
- * This source code is released into the public domain.
- */
-
-#include <string>
+// This source code is released into the public domain.
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
+import nihil.std;
import nihil.ucl;
+namespace {
TEST_CASE("ucl parse: iterate array", "[ucl]")
{
using namespace std::literals;
@@ -53,3 +51,5 @@ TEST_CASE("ucl parse: iterate hash", "[ucl]")
REQUIRE(object_cast<string>(value) == "test");
}
}
+
+} // anonymous namespace
diff --git a/nihil.ucl/parser.cc b/nihil.ucl/parser.cc
deleted file mode 100644
index 0a08670..0000000
--- a/nihil.ucl/parser.cc
+++ /dev/null
@@ -1,102 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <expected>
-#include <functional>
-#include <string>
-
-#include <ucl.h>
-
-module nihil.ucl;
-
-import nihil.error;
-
-namespace nihil::ucl {
-
-auto make_parser(int flags) -> std::expected<parser, error>
-{
- auto *p = ::ucl_parser_new(flags);
- if (p != nullptr)
- return p;
-
- // TODO: Is there a way to get the actual error here?
- return std::unexpected(error("failed to create parser"));
-}
-
-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(::ucl_parser *uclp)
- : m_parser(uclp)
-{
-}
-
-parser::~parser()
-{
- if (m_parser)
- ::ucl_parser_free(m_parser);
-}
-
-parser::parser(parser &&other) noexcept
- : m_parser(std::exchange(other.m_parser, nullptr))
- , m_macros(std::move(other.m_macros))
-{
-}
-
-auto parser::operator=(this parser &self, parser &&other) noexcept
- -> parser &
-{
- if (&self != &other) {
- if (self.m_parser)
- ::ucl_parser_free(self.m_parser);
-
- self.m_parser = std::exchange(other.m_parser, nullptr);
- self.m_macros = std::move(other.m_macros);
- }
-
- return self;
-}
-
-auto parser::register_value(
- this parser &self,
- std::string_view variable,
- std::string_view value)
- -> void
-{
- ::ucl_parser_register_variable(
- self.get_parser(),
- std::string(variable).c_str(),
- std::string(value).c_str());
-}
-
-auto parser::top(this parser &self) -> map<object>
-{
- auto obj = ::ucl_parser_get_object(self.get_parser());
- if (obj != nullptr)
- // ucl_parser_get_object() refs the object for us.
- return {noref, obj};
-
- throw std::logic_error(
- "attempt to call top() on an invalid ucl::parser");
-}
-
-auto parser::get_parser(this parser &self) -> ::ucl_parser *
-{
- if (self.m_parser == nullptr)
- throw std::logic_error("attempt to fetch a null ucl::parser");
-
- return self.m_parser;
-}
-
-} // namespace nihil::ucl
diff --git a/nihil.ucl/parser.ccm b/nihil.ucl/parser.ccm
index 5fa3495..0100fda 100644
--- a/nihil.ucl/parser.ccm
+++ b/nihil.ucl/parser.ccm
@@ -1,21 +1,11 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
-#include <coroutine>
-#include <expected>
-#include <format>
-#include <functional>
-#include <memory>
-#include <string>
-#include <vector>
-
#include <ucl.h>
export module nihil.ucl:parser;
+import nihil.std;
import nihil.monad;
import :object;
import :map;
@@ -28,79 +18,98 @@ export inline constexpr int parser_zerocopy = UCL_PARSER_ZEROCOPY;
export inline constexpr int parser_no_time = UCL_PARSER_NO_TIME;
// A macro handler. This proxies the C API callback to the C++ API.
-using macro_callback_t = bool (std::string_view);
+using macro_callback_t = bool(std::string_view);
-struct macro_handler {
+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;
+ 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);
+ }
};
-/*
- * A UCL parser. This wraps the C ucl_parser API.
- *
- * parser itself is not exported; use make_parser() to create one.
- */
-struct parser {
+// A UCL parser. This wraps the C ucl_parser API.
+//
+// parser itself is not exported; use make_parser() to create one.
+struct parser
+{
// Create a parser from a UCL parser.
- parser(::ucl_parser *);
+ explicit parser(::ucl_parser *uclp)
+ : m_parser(uclp)
+ {
+ }
// Destroy our parser when we're destroyed.
- ~parser();
+ ~parser()
+ {
+ if (m_parser != nullptr)
+ ::ucl_parser_free(m_parser);
+ }
// Not copyable.
parser(parser const &) = delete;
auto operator=(this parser &, parser const &) -> parser & = delete;
// Movable.
- parser(parser &&) noexcept;
- auto operator=(this parser &, parser &&) noexcept -> parser &;
+ parser(parser &&other) noexcept
+ : m_parser(std::exchange(other.m_parser, nullptr))
+ , m_macros(std::move(other.m_macros))
+ {
+ }
+
+ auto operator=(this parser &self, parser &&other) noexcept -> parser &
+ {
+ if (&self != &other) {
+ if (self.m_parser != nullptr)
+ ::ucl_parser_free(self.m_parser);
+
+ self.m_parser = std::exchange(other.m_parser, nullptr);
+ self.m_macros = std::move(other.m_macros);
+ }
+
+ return self; // NOLINT
+ }
// Add a parser macro. Unlike ucl_parser_register_macro, this doesn't
// take a userdata parameter; it's assumed the user will use lambda
// capture or similar if needed.
- template<std::invocable<std::string_view> F>
- auto register_macro(this parser &self,
- std::string_view name,
- F &&func)
- -> void
- requires (std::same_as<bool, std::invoke_result<F>>)
+
+ template <std::invocable<std::string_view> F>
+ auto register_macro(this parser &self, std::string_view name, F &&func) -> void
+ requires(std::same_as<bool, std::invoke_result<F>>)
{
- auto handler = std::make_unique<macro_handler>(
- std::forward<F>(func));
+ auto handler = std::make_unique<macro_handler>(std::forward<F>(func));
auto cname = std::string(name);
- ::ucl_parser_register_macro(
- self.get_parser(), cname.c_str(),
- &macro_handler::handle, handler.get());
+ ::ucl_parser_register_macro(self.get_parser(), cname.c_str(),
+ &macro_handler::handle, handler.get());
self.m_macros.emplace_back(std::move(handler));
}
// Add a parser variable.
- auto register_value(this parser &self,
- std::string_view variable,
- std::string_view value)
- -> void;
+ auto
+ register_value(this parser &self, std::string_view variable, std::string_view value) -> void
+ {
+ ::ucl_parser_register_variable(self.get_parser(), std::string(variable).c_str(),
+ std::string(value).c_str());
+ }
// Add data to the parser.
- [[nodiscard]] auto add(this parser &self,
- std::ranges::contiguous_range auto &&data)
+ [[nodiscard]] auto add(this parser &self, std::ranges::contiguous_range auto &&data)
-> std::expected<void, error>
- // Only bytes (chars) are permitted.
+ // Only bytes (chars) are permitted.
requires(sizeof(std::ranges::range_value_t<decltype(data)>) == 1)
{
auto *p = self.get_parser();
- auto dptr = reinterpret_cast<unsigned char const *>(
- std::ranges::data(data));
+ auto dptr = reinterpret_cast<unsigned char const *>(std::ranges::data(data));
- auto ret = ::ucl_parser_add_chunk(
- p, dptr, std::ranges::size(data));
+ auto ret = ::ucl_parser_add_chunk(p, dptr, std::ranges::size(data));
if (ret == true)
return {};
@@ -108,23 +117,34 @@ struct parser {
return std::unexpected(error(::ucl_parser_get_error(p)));
}
- [[nodiscard]] auto add(this parser &self,
- std::ranges::range auto &&data)
- -> std::expected<void, error>
- requires (!std::ranges::contiguous_range<decltype(data)>)
+ [[nodiscard]] auto
+ add(this parser &self, std::ranges::range auto &&data) -> std::expected<void, error>
+ requires(!std::ranges::contiguous_range<decltype(data)>)
{
- auto cdata = std::vector<char>(
- std::from_range,
- std::forward<decltype(data)>(data));
+ auto cdata = std::vector<char>(std::from_range, std::forward<decltype(data)>(data));
co_await self.add(std::move(cdata));
co_return {};
}
// Return the top object of this parser.
- [[nodiscard]] auto top(this parser &self) -> map<object>;
+ [[nodiscard]] auto top(this parser &self) -> map<object>
+ {
+ auto *obj = ::ucl_parser_get_object(self.get_parser());
+ if (obj != nullptr)
+ // ucl_parser_get_object() refs the object for us.
+ return {noref, obj};
+
+ throw std::logic_error("attempt to call top() on an invalid ucl::parser");
+ }
// Return the stored parser object.
- [[nodiscard]] auto get_parser(this parser &self) -> ::ucl_parser *;
+ [[nodiscard]] auto get_parser(this parser &self) -> ::ucl_parser *
+ {
+ if (self.m_parser == nullptr)
+ throw std::logic_error("attempt to fetch a null ucl::parser");
+
+ return self.m_parser;
+ }
private:
// The parser object. Should never be null, unless we've been
@@ -137,22 +157,26 @@ private:
};
// Create a parser with the given flags.
-export [[nodiscard]] auto
-make_parser(int flags = 0) -> std::expected<parser, error>;
+export [[nodiscard]] auto make_parser(int flags = 0) -> std::expected<parser, error>
+{
+ auto *p = ::ucl_parser_new(flags);
+ if (p != nullptr)
+ return {parser(p)};
+
+ // TODO: Is there a way to get the actual error here?
+ return std::unexpected(error("failed to create parser"));
+}
// Utility function to parse something and return the top-level object.
export [[nodiscard]] auto
-parse(int flags, std::ranges::range auto &&data)
- -> std::expected<map<object>, error>
+parse(int flags, std::ranges::range auto &&data) -> std::expected<map<object>, error>
{
auto p = co_await make_parser(flags);
co_await p.add(std::forward<decltype(data)>(data));
co_return p.top();
}
-export [[nodiscard]] auto
-parse(std::ranges::range auto &&data)
- -> std::expected<map<object>, error>
+export [[nodiscard]] auto parse(std::ranges::range auto &&data) -> std::expected<map<object>, error>
{
co_return co_await parse(0, std::forward<decltype(data)>(data));
}
diff --git a/nihil.ucl/real.cc b/nihil.ucl/real.cc
deleted file mode 100644
index 6d9e082..0000000
--- a/nihil.ucl/real.cc
+++ /dev/null
@@ -1,104 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <cassert>
-#include <compare>
-#include <cstdlib>
-#include <expected>
-#include <string>
-#include <system_error>
-
-#include <ucl.h>
-
-module nihil.ucl;
-
-import nihil.error;
-
-namespace nihil::ucl {
-
-auto make_real(real::contained_type value)
- -> std::expected<real, error>
-{
- auto *uobj = ::ucl_object_fromdouble(value);
- if (uobj == nullptr)
- return std::unexpected(error(
- errc::failed_to_create_object,
- error(std::errc(errno))));
-
- return real(noref, uobj);
-}
-
-real::real()
- : real(0)
-{
-}
-
-real::real(contained_type value)
- : real(noref, [&] {
- auto *uobj = ::ucl_object_fromdouble(value);
- if (uobj == nullptr)
- throw std::system_error(
- std::make_error_code(std::errc(errno)));
- return uobj;
- }())
-{
-}
-
-real::real(ref_t, ::ucl_object_t const *uobj)
- : object(nihil::ucl::ref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != real::ucl_type)
- throw type_mismatch(real::ucl_type, actual_type);
- return uobj;
- }())
-{
-}
-
-real::real(noref_t, ::ucl_object_t *uobj)
- : object(nihil::ucl::noref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != real::ucl_type)
- throw type_mismatch(real::ucl_type, actual_type);
- return uobj;
- }())
-{
-}
-
-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 f425a9a..b432617 100644
--- a/nihil.ucl/real.ccm
+++ b/nihil.ucl/real.ccm
@@ -1,78 +1,106 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
-#include <compare>
-#include <expected>
-#include <format>
-#include <utility>
-
#include <ucl.h>
export module nihil.ucl:real;
+import nihil.std;
+import nihil.core;
import :object;
import :type;
namespace nihil::ucl {
-export struct real final : object {
+export struct real final : object
+{
using contained_type = double;
- inline static constexpr object_type ucl_type = object_type::real;
+ static constexpr object_type ucl_type = object_type::real;
- /*
- * Create a real holding the value 0. Throws std::system_error
- * on failure.
- */
- real();
+ // Create a real holding the value 0. Throws std::system_error
+ // on failure.
+ real()
+ : real(0)
+ {
+ }
- /*
- * Create a real holding a specific value. Throws std::system_error
- * on failure.
- */
- explicit real(contained_type value);
+ // Create a real holding a specific value. Throws std::system_error
+ // on failure.
+ explicit real(contained_type value)
+ : real(noref, [&] {
+ auto *uobj = ::ucl_object_fromdouble(value);
+ if (uobj == nullptr)
+ throw std::system_error(std::make_error_code(sys_error()));
+ return uobj;
+ }())
+ {
+ }
- /*
- * Create a new real from a UCL object. Throws type_mismatch
- * on failure.
- */
- real(ref_t, ::ucl_object_t const *uobj);
- real(noref_t, ::ucl_object_t *uobj);
+ // Create a new real from a UCL object. Throws type_mismatch
+ // on failure.
+ real(ref_t, ::ucl_object_t const *uobj)
+ : object(nihil::ucl::ref, ensure_ucl_type(uobj, real::ucl_type))
+ {
+ }
+
+ real(noref_t, ::ucl_object_t *uobj)
+ : object(nihil::ucl::noref, ensure_ucl_type(uobj, real::ucl_type))
+ {
+ }
// Return the value of this real.
- [[nodiscard]] auto value(this real const &self) -> contained_type;
-};
+ [[nodiscard]] 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;
-/*
- * Real constructors. These return an error instead of throwing.
- */
+ throw std::runtime_error("ucl_object_todouble_safe failed");
+ }
-export [[nodiscard]] auto
-make_real(real::contained_type = 0) -> std::expected<real, error>;
+private:
+ //
+ // Comparison operators.
+ //
-/*
- * Comparison operators.
- */
+ [[nodiscard]] friend auto operator==(real const &a, real const &b) -> bool
+ {
+ return a.value() == b.value();
+ }
-export [[nodiscard]] auto operator== (real const &a, real const &b) -> bool;
+ [[nodiscard]] friend auto operator<=>(real const &a, real const &b) -> std::partial_ordering
+ {
+ return a.value() <=> b.value();
+ }
-export [[nodiscard]] auto operator== (real const &a,
- real::contained_type b) -> bool;
+ [[nodiscard]] friend auto operator==(real const &a, real::contained_type b) -> bool
+ {
+ return a.value() == b;
+ }
-export [[nodiscard]] auto operator<=> (real const &a, real const &b)
- -> std::partial_ordering;
+ [[nodiscard]] friend auto
+ operator<=>(real const &a, real::contained_type b) -> std::partial_ordering
+ {
+ return a.value() <=> b;
+ }
+};
-export [[nodiscard]] auto operator<=> (real const &a, real::contained_type b)
- -> std::partial_ordering;
+// Real constructor. This returns an error instead of throwing.
+export [[nodiscard]] auto make_real(real::contained_type value = 0) -> std::expected<real, error>
+{
+ auto *uobj = ::ucl_object_fromdouble(value);
+ if (uobj == nullptr)
+ return std::unexpected(error(errc::failed_to_create_object, error(sys_error())));
-/*
- * Literal operator.
- */
+ return real(noref, uobj);
+}
+
+// Literal operator.
inline namespace literals {
-export constexpr auto operator""_ucl (long double d) -> real
+export constexpr auto operator""_ucl(long double d) -> real
{
if (d > static_cast<long double>(std::numeric_limits<double>::max()) ||
d < static_cast<long double>(std::numeric_limits<double>::min()))
@@ -80,32 +108,31 @@ export constexpr auto operator""_ucl (long double d) -> real
return real(static_cast<double>(d));
}
-} // namespace nihil::ucl::literals
+} // namespace literals
} // namespace nihil::ucl
-namespace nihil { inline namespace literals {
- export using namespace ::nihil::ucl::literals;
-}} // namespace nihil::literals
+namespace nihil {
+inline namespace literals {
+export using namespace ::nihil::ucl::literals;
+}
+} // namespace nihil
-/*
- * std::formatter for a real. This provides the same format operations
- * as std::formatter<double>;
- */
-export template<>
+// std::formatter for a real. This provides the same format operations
+// as std::formatter<double>;
+export template <>
struct std::formatter<nihil::ucl::real, char>
{
std::formatter<double> base_formatter;
- template<class ParseContext>
- constexpr ParseContext::iterator parse(ParseContext& ctx)
+ template <class ParseContext>
+ constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
{
return base_formatter.parse(ctx);
}
- template<class FmtContext>
- FmtContext::iterator format(nihil::ucl::real const &o,
- FmtContext& ctx) const
+ template <class FmtContext>
+ auto format(nihil::ucl::real const &o, FmtContext &ctx) const -> FmtContext::iterator
{
return base_formatter.format(o.value(), ctx);
}
diff --git a/nihil.ucl/tests/real.cc b/nihil.ucl/real.test.cc
index 421917e..e880d9a 100644
--- a/nihil.ucl/tests/real.cc
+++ b/nihil.ucl/real.test.cc
@@ -1,16 +1,13 @@
-/*
- * This source code is released into the public domain.
- */
-
-#include <concepts>
-#include <string>
+// This source code is released into the public domain.
#include <catch2/catch_test_macros.hpp>
#include <catch2/matchers/catch_matchers_floating_point.hpp>
#include <ucl.h>
+import nihil.std;
import nihil.ucl;
+namespace {
TEST_CASE("ucl: real: invariants", "[ucl]")
{
using namespace nihil::ucl;
@@ -32,12 +29,12 @@ TEST_CASE("ucl: real: constructor", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("default") {
+ SECTION ("default") {
auto r = real();
REQUIRE(r == 0);
}
- SECTION("with value") {
+ SECTION ("with value") {
auto r = real(42.1);
REQUIRE_THAT(r.value(), Catch::Matchers::WithinRel(42.1));
}
@@ -45,7 +42,7 @@ TEST_CASE("ucl: real: constructor", "[ucl]")
TEST_CASE("ucl: real: literal", "[ucl]")
{
- SECTION("with namespace nihil::ucl::literals") {
+ SECTION ("with namespace nihil::ucl::literals") {
using namespace nihil::ucl::literals;
auto r = 42.5_ucl;
@@ -53,7 +50,7 @@ TEST_CASE("ucl: real: literal", "[ucl]")
REQUIRE_THAT(r.value(), Catch::Matchers::WithinRel(42.5));
}
- SECTION("with namespace nihil::literals") {
+ SECTION ("with namespace nihil::literals") {
using namespace nihil::literals;
auto r = 42.5_ucl;
@@ -66,8 +63,8 @@ TEST_CASE("ucl: real: construct from UCL object", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("ref, correct type") {
- auto uobj = ::ucl_object_fromdouble(42);
+ SECTION ("ref, correct type") {
+ auto *uobj = ::ucl_object_fromdouble(42);
auto r = real(ref, uobj);
REQUIRE(r == 42);
@@ -75,23 +72,23 @@ TEST_CASE("ucl: real: construct from UCL object", "[ucl]")
::ucl_object_unref(uobj);
}
- SECTION("noref, correct type") {
- auto uobj = ::ucl_object_fromdouble(42);
+ SECTION ("noref, correct type") {
+ auto *uobj = ::ucl_object_fromdouble(42);
auto r = real(noref, uobj);
REQUIRE(r == 42);
}
- SECTION("ref, wrong type") {
- auto uobj = ::ucl_object_fromint(42);
+ SECTION ("ref, wrong type") {
+ auto *uobj = ::ucl_object_fromint(42);
REQUIRE_THROWS_AS(real(ref, uobj), type_mismatch);
::ucl_object_unref(uobj);
}
- SECTION("noref, wrong type") {
- auto uobj = ::ucl_object_fromint(42);
+ SECTION ("noref, wrong type") {
+ auto *uobj = ::ucl_object_fromint(42);
REQUIRE_THROWS_AS(real(noref, uobj), type_mismatch);
@@ -103,12 +100,12 @@ TEST_CASE("ucl: real: make_real", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("default value") {
+ SECTION ("default value") {
auto i = make_real().value();
REQUIRE(i == 0);
}
- SECTION("explicit value") {
+ SECTION ("explicit value") {
auto i = make_real(42).value();
REQUIRE(i == 42);
}
@@ -117,7 +114,7 @@ TEST_CASE("ucl: real: make_real", "[ucl]")
TEST_CASE("ucl: real: swap", "[ucl]")
{
// do not add using namespace nihil::ucl
-
+
auto r1 = nihil::ucl::real(1);
auto r2 = nihil::ucl::real(2);
@@ -139,15 +136,15 @@ TEST_CASE("ucl: real: key()", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("parsed with key") {
+ SECTION ("parsed with key") {
auto obj = parse("a_real = 42.5").value();
auto r = object_cast<real>(obj["a_real"]).value();
REQUIRE(r.key() == "a_real");
}
- SECTION("bare real, no key") {
+ SECTION ("bare real, no key") {
auto i = 42.5_ucl;
- REQUIRE(i.key() == "");
+ REQUIRE(i.key().empty() == true);
}
}
@@ -157,22 +154,22 @@ TEST_CASE("ucl: real: comparison", "[ucl]")
auto i = nihil::ucl::real(42.5);
- SECTION("operator==") {
+ SECTION ("operator==") {
REQUIRE(i == 42.5);
REQUIRE(i == 42.5_ucl);
}
- SECTION("operator!=") {
+ SECTION ("operator!=") {
REQUIRE(i != 1);
REQUIRE(i != 1._ucl);
}
- SECTION("operator<") {
+ SECTION ("operator<") {
REQUIRE(i < 43);
REQUIRE(i < 43._ucl);
}
- SECTION("operator>") {
+ SECTION ("operator>") {
REQUIRE(i > 1);
REQUIRE(i > 1._ucl);
}
@@ -186,8 +183,7 @@ TEST_CASE("ucl: real: parse", "[ucl]")
auto v = obj["value"];
REQUIRE(v.key() == "value");
- REQUIRE_THAT(object_cast<real>(v).value().value(),
- Catch::Matchers::WithinRel(42.1));
+ REQUIRE_THAT(object_cast<real>(v).value().value(), Catch::Matchers::WithinRel(42.1));
}
TEST_CASE("ucl: real: parse and emit", "[ucl]")
@@ -206,12 +202,12 @@ TEST_CASE("ucl: real: format", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("bare real") {
+ SECTION ("bare real") {
auto str = std::format("{}", 42.5_ucl);
REQUIRE(str == "42.5");
}
- SECTION("parsed real") {
+ SECTION ("parsed real") {
auto obj = parse("real = 42.5;").value();
auto r = object_cast<real>(obj["real"]).value();
@@ -219,7 +215,7 @@ TEST_CASE("ucl: real: format", "[ucl]")
REQUIRE(str == "42.5");
}
- SECTION("with format string") {
+ SECTION ("with format string") {
auto str = std::format("{:10.5f}", 42.5_ucl);
REQUIRE(str == " 42.50000");
}
@@ -229,14 +225,14 @@ TEST_CASE("ucl: real: print to ostream", "[ucl]")
{
using namespace nihil::ucl;
- SECTION("bare real") {
+ SECTION ("bare real") {
auto strm = std::ostringstream();
strm << 42.5_ucl;
REQUIRE(strm.str() == "42.5");
}
- SECTION("parsed real") {
+ SECTION ("parsed real") {
auto obj = parse("real = 42.5;").value();
auto i = object_cast<real>(obj["real"]).value();
@@ -246,3 +242,4 @@ TEST_CASE("ucl: real: print to ostream", "[ucl]")
REQUIRE(strm.str() == "42.5");
}
}
+} // anonymous namespace
diff --git a/nihil.ucl/string.cc b/nihil.ucl/string.cc
deleted file mode 100644
index 67e97f4..0000000
--- a/nihil.ucl/string.cc
+++ /dev/null
@@ -1,187 +0,0 @@
-/*
- * This source code is released into the public domain.
- */
-
-module;
-
-#include <cstdlib>
-#include <expected>
-#include <iosfwd>
-#include <string>
-#include <system_error>
-
-#include <ucl.h>
-
-module nihil.ucl;
-
-import nihil.error;
-
-namespace nihil::ucl {
-
-auto make_string() -> std::expected<string, error>
-{
- return make_string(std::string_view(""));
-}
-
-auto make_string(char const *s) -> std::expected<string, error>
-{
- return make_string(std::string_view(s));
-}
-
-auto make_string(std::string_view s) -> std::expected<string, error>
-{
- auto *uobj = ::ucl_object_fromstring_common(
- s.data(), s.size(), UCL_STRING_RAW);
-
- if (uobj == nullptr)
- return std::unexpected(error(
- errc::failed_to_create_object,
- error(std::errc(errno))));
-
- return string(noref, uobj);
-}
-
-string::string(ref_t, ::ucl_object_t const *uobj)
- : object(nihil::ucl::ref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != string::ucl_type)
- throw type_mismatch(string::ucl_type, actual_type);
- return uobj;
- }())
-{
-}
-
-string::string(noref_t, ::ucl_object_t *uobj)
- : object(nihil::ucl::noref, [&] {
- auto actual_type = static_cast<object_type>(
- ::ucl_object_type(uobj));
- if (actual_type != string::ucl_type)
- throw type_mismatch(string::ucl_type, actual_type);
- return uobj;
- }())
-{
-}
-
-string::string()
- : string(std::string_view(""))
-{}
-
-string::string(std::string_view value)
- : string(noref, [&] {
- auto *uobj = ::ucl_object_fromstring_common(
- value.data(), value.size(), UCL_STRING_RAW);
- if (uobj == nullptr)
- throw std::system_error(
- std::make_error_code(std::errc(errno)));
- return uobj;
- }())
-{
-}
-
-string::string(char const *value)
- : string(std::string_view(value))
-{
-}
-
-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);
-}
-
-auto operator<<(std::ostream &strm, string const &s) -> std::ostream &
-{
- return strm << s.value();
-}
-
-} // namespace nihil::ucl
diff --git a/nihil.ucl/string.ccm b/nihil.ucl/string.ccm
index c757bf1..4b46e39 100644
--- a/nihil.ucl/string.ccm
+++ b/nihil.ucl/string.ccm
@@ -1,27 +1,21 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
-#include <cstdlib>
-#include <expected>
-#include <format>
-#include <iosfwd>
-#include <string>
-
#include <ucl.h>
export module nihil.ucl:string;
+import nihil.std;
+import nihil.core;
import :object;
import :type;
namespace nihil::ucl {
-export struct string final : object {
+export struct string final : object
+{
using contained_type = std::string_view;
- inline static constexpr object_type ucl_type = object_type::string;
+ static constexpr object_type ucl_type = object_type::string;
// string is a container of char
using value_type = char const;
@@ -31,159 +25,236 @@ export struct string final : object {
using pointer = value_type *;
using iterator = pointer;
- /*
- * Create a new empty string. Throws std::system_error on failure.
- */
- string();
-
- /*
- * Create a string from a value. Throws std::system_error on failure.
- */
- explicit string(std::string_view);
-
- /*
- * Create a string from a C literal. Throws std::system_error
- * on failure.
- */
- explicit string(char const *);
-
- /*
- * Create a string from a contiguous range. The range's value type
- * must be char. Throws std::system_error on failure.
- */
- template<std::ranges::contiguous_range Range>
- requires (!std::same_as<std::string_view, Range> &&
- std::same_as<char, std::ranges::range_value_t<Range>>)
- explicit string(Range &&range)
- : string(std::string_view(std::ranges::begin(range),
- std::ranges::end(range)))
- {}
-
- /*
- * Create a string from a non-contiguous range. This requires a
- * temporary value due to limitations of the UCL C API.
- */
- template<std::ranges::range Range>
- requires (!std::ranges::contiguous_range<Range> &&
- std::same_as<char, std::ranges::range_value_t<Range>>)
- explicit string(Range &&range)
- : string(std::string(std::from_range, range))
- {}
-
- /*
- * Create a string from an iterator pair. The iterator's value type
- * must be char. If the iterator pair is not contiguous, the value
- * will be copied to a temporary first.
- *
- * Throws std::system_error on failure.
- */
- template<std::input_iterator Iterator>
- requires (std::same_as<char, std::iter_value_t<Iterator>>)
+ // Create a new empty string. Throws std::system_error on failure.
+ string()
+ : string(std::string_view(""))
+ {
+ }
+
+ // Create a string from a value. Throws std::system_error on failure.
+ explicit string(std::string_view value)
+ : string(noref, [&] {
+ auto *uobj = ::ucl_object_fromstring_common(value.data(), value.size(),
+ UCL_STRING_RAW);
+ if (uobj == nullptr)
+ throw std::system_error(std::make_error_code(sys_error()));
+ return uobj;
+ }())
+ {
+ }
+
+ // Create a string from a C literal. Throws std::system_error
+ // on failure.
+ explicit string(char const *value)
+ : string(std::string_view(value))
+ {
+ }
+
+ // Create a string from a contiguous range. The range's value type
+ // must be char. Throws std::system_error on failure.
+ template <std::ranges::contiguous_range Range>
+ requires(!std::same_as<std::string_view, Range> &&
+ std::same_as<char, std::ranges::range_value_t<Range>>)
+ explicit string(Range const &range)
+ : string(std::string_view(std::ranges::begin(range), std::ranges::end(range)))
+ {
+ }
+
+ // Create a string from a non-contiguous range. This requires a
+ // temporary value due to limitations of the UCL C API.
+ template <std::ranges::range Range>
+ requires(!std::ranges::contiguous_range<Range> &&
+ std::same_as<char, std::ranges::range_value_t<Range>>)
+ explicit string(Range range)
+ : string(std::string(std::from_range, std::forward<Range>(range)))
+ {
+ }
+
+ // Create a string from an iterator pair. The iterator's value type
+ // must be char. If the iterator pair is not contiguous, the value
+ // will be copied to a temporary first.
+ //
+ // Throws std::system_error on failure.
+ template <std::input_iterator Iterator>
+ requires(std::same_as<char, std::iter_value_t<Iterator>>)
string(Iterator first, Iterator last)
: string(std::ranges::subrange(first, last))
- {}
+ {
+ }
+
+ // Create a new string from a UCL object. Throws type_mismatch
+ // on failure.
+ string(ref_t, ::ucl_object_t const *uobj)
+ : object(nihil::ucl::ref, ensure_ucl_type(uobj, string::ucl_type))
+ {
+ }
- /*
- * Create a new string from a UCL object. Throws type_mismatch
- * on failure.
- */
- string(ref_t, ::ucl_object_t const *uobj);
- string(noref_t, ::ucl_object_t *uobj);
+ string(noref_t, ::ucl_object_t *uobj)
+ : object(nihil::ucl::noref, ensure_ucl_type(uobj, string::ucl_type))
+ {
+ }
// Return the value of this string.
- [[nodiscard]] auto value(this string const &self) -> contained_type;
+ [[nodiscard]] 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};
+
+ throw std::runtime_error("ucl_object_tolstring_safe() failed");
+ }
// Return the size of this string.
- [[nodiscard]] auto size(this string const &self) -> size_type;
+ [[nodiscard]] auto size(this string const &self) -> size_type
+ {
+ return self.value().size();
+ }
// Test if this string is empty.
- [[nodiscard]] auto empty(this string const &self) -> bool;
+ [[nodiscard]] auto empty(this string const &self) -> bool
+ {
+ return self.size() == 0;
+ }
// Access this string's data
- [[nodiscard]] auto data(this string const &self) -> pointer;
+ [[nodiscard]] 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;
+
+ throw std::runtime_error("ucl_object_tostring_safe() failed");
+ }
// Iterator access
- [[nodiscard]] auto begin(this string const &self) -> iterator;
- [[nodiscard]] auto end(this string const &self) -> iterator;
-};
+ [[nodiscard]] auto begin(this string const &self) -> iterator
+ {
+ return self.data();
+ }
-/*
- * String constructors. These return an error instead of throwing.
- */
+ [[nodiscard]] auto end(this string const &self) -> iterator
+ {
+ return self.data() + self.size();
+ }
-// Empty string
-export [[nodiscard]] auto
-make_string() -> std::expected<string, error>;
+private:
+ //
+ // Comparison operators.
+ //
+
+ [[nodiscard]] friend auto operator==(string const &a, string const &b) -> bool
+ {
+ return a.value() == b.value();
+ }
+
+ [[nodiscard]] friend 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.
+
+ [[nodiscard]] friend auto operator==(string const &lhs, std::string_view rhs) -> bool
+ {
+ return lhs.value() == rhs;
+ }
+
+ [[nodiscard]] friend auto
+ operator<=>(string const &lhs, std::string_view rhs) -> std::strong_ordering
+ {
+ return lhs.value() <=> rhs;
+ }
+
+ [[nodiscard]] friend auto operator==(string const &lhs, std::string const &rhs) -> bool
+ {
+ return lhs == std::string_view(rhs);
+ }
+
+ [[nodiscard]] friend auto
+ operator<=>(string const &lhs, std::string const &rhs) -> std::strong_ordering
+ {
+ return lhs <=> std::string_view(rhs);
+ }
+
+ [[nodiscard]] friend auto operator==(string const &lhs, char const *rhs) -> bool
+ {
+ return lhs == std::string_view(rhs);
+ }
+
+ [[nodiscard]] friend auto
+ operator<=>(string const &lhs, char const *rhs) -> std::strong_ordering
+ {
+ return lhs <=> std::string_view(rhs);
+ }
+
+ // Stream output.
+ friend auto operator<<(std::ostream &strm, string const &s) -> std::ostream &
+ {
+ return strm << s.value();
+ }
+};
+
+//
+// String constructors. These return an error instead of throwing.
+//
// From string_view
-export [[nodiscard]] auto
-make_string(std::string_view) -> std::expected<string, error>;
+export [[nodiscard]] auto make_string(std::string_view s) -> std::expected<string, error>
+{
+ auto *uobj = ::ucl_object_fromstring_common(s.data(), s.size(), UCL_STRING_RAW);
+
+ if (uobj == nullptr)
+ return std::unexpected(error(errc::failed_to_create_object, error(sys_error())));
+
+ return string(noref, uobj);
+}
+
+// Empty string
+export [[nodiscard]] auto make_string() -> std::expected<string, error>
+{
+ return make_string(std::string_view(""));
+}
// From C literal
-export [[nodiscard]] auto
-make_string(char const *) -> std::expected<string, error>;
+export [[nodiscard]] auto make_string(char const *s) -> std::expected<string, error>
+{
+ return make_string(std::string_view(s));
+}
// From contiguous range
-export template<std::ranges::contiguous_range Range>
-requires (!std::same_as<std::string_view, Range> &&
- std::same_as<char, std::ranges::range_value_t<Range>>)
+export template <std::ranges::contiguous_range Range>
+requires(!std::same_as<std::string_view, Range> &&
+ std::same_as<char, std::ranges::range_value_t<Range>>)
[[nodiscard]] auto make_string(Range &&range)
{
- return make_string(std::string_view(range));
+ return make_string(std::string_view(std::forward<Range>(range)));
}
// From non-contiguous range
-export template<std::ranges::range Range>
-requires (!std::ranges::contiguous_range<Range> &&
- std::same_as<char, std::ranges::range_value_t<Range>>)
+export template <std::ranges::range Range>
+requires(!std::ranges::contiguous_range<Range> &&
+ std::same_as<char, std::ranges::range_value_t<Range>>)
[[nodiscard]] auto make_string(Range &&range)
{
- return make_string(std::string(std::from_range, range));
+ return make_string(std::string(std::from_range, std::forward<Range>(range)));
}
// From iterator pair
-export template<std::input_iterator Iterator>
-requires (std::same_as<char, std::iter_value_t<Iterator>>)
+export template <std::input_iterator Iterator>
+requires(std::same_as<char, std::iter_value_t<Iterator>>)
[[nodiscard]] auto make_string(Iterator first, Iterator last)
{
return make_string(std::ranges::subrange(first, last));
}
/*
- * Comparison operators.
- */
-
-export [[nodiscard]] auto operator== (string const &a, string const &b) -> bool;
-export [[nodiscard]] auto operator<=> (string const &a, string const &b)
- -> std::strong_ordering;
-
-/*
- * For convenience, allow comparison with C++ strings without having to
- * construct a temporary UCL object.
- */
-
-export [[nodiscard]] auto operator==(string const &lhs,
- std::string_view rhs) -> bool;
-
-export [[nodiscard]] auto operator==(string const &lhs,
- std::string const &rhs) -> bool;
-
-export [[nodiscard]] auto operator==(string const &lhs,
- char const *rhs) -> bool;
-
-export [[nodiscard]] auto operator<=>(string const &lhs,
- std::string_view rhs)
- -> std::strong_ordering;
-
-export [[nodiscard]] auto operator<=>(string const &lhs,
- std::string const &rhs)
- -> std::strong_ordering;
-
-export [[nodiscard]] auto operator<=>(string const &lhs,
- char const *rhs)
- -> std::strong_ordering;
-
-/*
* Print a string to a stream.
*/
export auto operator<<(std::ostream &, string const &) -> std::ostream &;
@@ -192,37 +263,37 @@ export auto operator<<(std::ostream &, string const &) -> std::ostream &;
* Literal operator.
*/
inline namespace literals {
- export constexpr auto operator""_ucl (char const *s, std::size_t n)
- -> string
- {
- return string(std::string_view(s, n));
- }
-} // namespace nihil::ucl::literals
+export constexpr auto operator""_ucl(char const *s, std::size_t n) -> string
+{
+ return string(std::string_view(s, n));
+}
+} // namespace literals
} // namespace nihil::ucl
-namespace nihil { inline namespace literals {
- export using namespace ::nihil::ucl::literals;
-}} // namespace nihil::literals
+namespace nihil {
+inline namespace literals {
+export using namespace ::nihil::ucl::literals;
+}
+} // namespace nihil
/*
* std::formatter for a string. This provides the same format operations
* as std::formatter<std::string_view>.
*/
-export template<>
+export template <>
struct std::formatter<nihil::ucl::string, char>
{
std::formatter<std::string_view> base_formatter;
- template<class ParseContext>
- constexpr ParseContext::iterator parse(ParseContext& ctx)
+ template <class ParseContext>
+ constexpr auto parse(ParseContext &ctx) -> ParseContext::iterator
{
return base_formatter.parse(ctx);
}
- template<class FmtContext>
- FmtContext::iterator format(nihil::ucl::string const &o,
- FmtContext& ctx) const
+ template <class FmtContext>
+ auto format(nihil::ucl::string const &o, FmtContext &ctx) const -> FmtContext::iterator
{
return base_formatter.format(o.value(), ctx);
}
diff --git a/nihil.ucl/tests/string.cc b/nihil.ucl/string.test.cc
index 6409b8d..68c57e8 100644
--- a/nihil.ucl/tests/string.cc
+++ b/nihil.ucl/string.test.cc
@@ -1,18 +1,12 @@
-/*
- * This source code is released into the public domain.
- */
-
-#include <concepts>
-#include <list>
-#include <sstream>
-#include <string>
-#include <vector>
+// This source code is released into the public domain.
#include <catch2/catch_test_macros.hpp>
#include <ucl.h>
+import nihil.std;
import nihil.ucl;
+namespace {
TEST_CASE("ucl: string: invariants", "[ucl]")
{
using namespace nihil::ucl;
@@ -127,23 +121,23 @@ TEST_CASE("ucl: string: construct from UCL object", "[ucl]")
using namespace nihil::ucl;
SECTION("ref, correct type") {
- auto uobj = ::ucl_object_fromstring("testing");
+ auto *uobj = ::ucl_object_fromstring("testing");
- auto s = string(ref, uobj);
+ auto const s = string(ref, uobj);
REQUIRE(s == "testing");
::ucl_object_unref(uobj);
}
SECTION("noref, correct type") {
- auto uobj = ::ucl_object_fromstring("testing");
+ auto *uobj = ::ucl_object_fromstring("testing");
- auto s = string(noref, uobj);
+ auto const s = string(noref, uobj);
REQUIRE(s == "testing");
}
SECTION("ref, wrong type") {
- auto uobj = ::ucl_object_frombool(true);
+ auto *uobj = ::ucl_object_frombool(true);
REQUIRE_THROWS_AS(string(ref, uobj), type_mismatch);
@@ -151,7 +145,7 @@ TEST_CASE("ucl: string: construct from UCL object", "[ucl]")
}
SECTION("noref, wrong type") {
- auto uobj = ::ucl_object_frombool(true);
+ auto *uobj = ::ucl_object_frombool(true);
REQUIRE_THROWS_AS(string(noref, uobj), type_mismatch);
@@ -165,53 +159,53 @@ TEST_CASE("ucl: string: make_string", "[ucl]")
using namespace std::literals;
SECTION("empty string") {
- auto str = make_string().value();
+ auto const str = make_string().value();
REQUIRE(str.type() == object_type::string);
REQUIRE(str == "");
}
SECTION("from string literal") {
- auto str = make_string("testing").value();
+ auto const str = make_string("testing").value();
REQUIRE(str.type() == object_type::string);
REQUIRE(str == "testing");
}
SECTION("from std::string") {
- auto str = make_string("testing"s).value();
+ auto const str = make_string("testing"s).value();
REQUIRE(str.type() == object_type::string);
REQUIRE(str == "testing");
}
SECTION("from std::string_view") {
- auto str = make_string("testing"sv).value();
+ auto const str = make_string("testing"sv).value();
REQUIRE(str.type() == object_type::string);
REQUIRE(str == "testing");
}
SECTION("from contiguous range") {
- auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
- auto str = make_string(s).value();
+ auto const s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto const str = make_string(s).value();
REQUIRE(str.type() == object_type::string);
REQUIRE(str == "testing");
}
SECTION("from non-contiguous range") {
- auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
- auto str = make_string(s).value();
+ auto const s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto const str = make_string(s).value();
REQUIRE(str.type() == object_type::string);
REQUIRE(str == "testing");
}
SECTION("from contiguous iterator pair") {
- auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
- auto str = make_string(s.begin(), s.end()).value();
+ auto const s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto const str = make_string(s.begin(), s.end()).value();
REQUIRE(str.type() == object_type::string);
REQUIRE(str == "testing");
}
SECTION("from non-contiguous iterator pair") {
- auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
- auto str = make_string(s.begin(), s.end()).value();
+ auto const s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto const str = make_string(s.begin(), s.end()).value();
REQUIRE(str.type() == object_type::string);
REQUIRE(str == "testing");
}
@@ -234,22 +228,19 @@ TEST_CASE("ucl: string: value()", "[ucl]")
{
using namespace nihil::ucl;
- auto s = string("te\"st");
- REQUIRE(s.value() == "te\"st");
+ auto const s = string(R"(te"st)");
+ REQUIRE(s.value() == R"(te"st)");
}
TEST_CASE("ucl: string: key()", "[ucl]")
{
using namespace nihil::ucl;
- auto err = parse("a_string = \"test\"");
- REQUIRE(err);
-
- auto obj = *err;
- REQUIRE(object_cast<string>(obj["a_string"])->key() == "a_string");
+ auto obj = parse(R"(a_string = "test")").value();
+ REQUIRE(object_cast<string>(obj["a_string"]).value().key() == "a_string");
- auto s = string("test");
- REQUIRE(s.key() == "");
+ auto const s = string("test");
+ REQUIRE(s.key().empty() == true);
}
TEST_CASE("ucl: string: size", "[ucl]")
@@ -280,7 +271,7 @@ TEST_CASE("ucl: string: iterate", "[ucl]")
auto end = str.end();
static_assert(std::sentinel_for<decltype(end),
- decltype(begin)>);
+ decltype(begin)>);
REQUIRE(*begin == 't');
++begin;
@@ -339,7 +330,7 @@ TEST_CASE("ucl: string: parse", "[ucl]")
{
using namespace nihil::ucl;
- auto obj = parse("value = \"te\\\"st\"").value();
+ auto obj = parse(R"(value = "te\"st")").value();
auto v = obj["value"];
REQUIRE(v.key() == "value");
@@ -350,7 +341,7 @@ TEST_CASE("ucl: string: emit", "[ucl]")
{
using namespace nihil::ucl;
- auto ucl = parse("str = \"te\\\"st\";").value();
+ auto ucl = parse(R"(str = "te\"st";)").value();
auto output = std::string();
emit(ucl, emitter::configuration, std::back_inserter(output));
@@ -371,7 +362,7 @@ TEST_CASE("ucl: string: format", "[ucl]")
}
SECTION("parsed string") {
- auto obj = parse("string = \"te\\\"st\";").value();
+ auto obj = parse(R"(string = "te\"st";)").value();
auto s = object_cast<string>(obj["string"]).value();
auto str = std::format("{}", s);
@@ -399,7 +390,7 @@ TEST_CASE("ucl: string: print to ostream", "[ucl]")
}
SECTION("parsed string") {
- auto obj = parse("string = \"te\\\"st\";").value();
+ auto obj = parse(R"(string = "te\"st";)").value();
auto s = object_cast<string>(obj["string"]).value();
auto strm = std::ostringstream();
@@ -413,3 +404,4 @@ TEST_CASE("ucl: string: print to ostream", "[ucl]")
REQUIRE(str == " te\"st");
}
}
+} // anonymous namespace
diff --git a/nihil.ucl/tests/CMakeLists.txt b/nihil.ucl/tests/CMakeLists.txt
deleted file mode 100644
index 13f30fa..0000000
--- a/nihil.ucl/tests/CMakeLists.txt
+++ /dev/null
@@ -1,20 +0,0 @@
-# This source code is released into the public domain.
-
-add_executable(nihil.ucl.test
- emit.cc
- parse.cc
-
- object.cc
- array.cc
- boolean.cc
- integer.cc
- map.cc
- real.cc
- string.cc
-)
-
-target_link_libraries(nihil.ucl.test PRIVATE nihil.ucl Catch2::Catch2WithMain)
-
-include(CTest)
-include(Catch)
-catch_discover_tests(nihil.ucl.test)
diff --git a/nihil.ucl/type.cc b/nihil.ucl/type.cc
deleted file mode 100644
index 7d9cad7..0000000
--- a/nihil.ucl/type.cc
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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(
- "expected type '{}' != actual type '{}'",
- ucl::str(expected_type), ucl::str(actual_type)))
- , m_expected_type(expected_type)
- , m_actual_type(actual_type)
-{
-}
-
-auto type_mismatch::expected_type(this type_mismatch const &self) -> object_type
-{
- return self.m_expected_type;
-}
-
-auto type_mismatch::actual_type(this type_mismatch const &self) -> object_type
-{
- return self.m_actual_type;
-}
-
-} // namespace nihil::ucl
diff --git a/nihil.ucl/type.ccm b/nihil.ucl/type.ccm
index f3b3aef..476546a 100644
--- a/nihil.ucl/type.ccm
+++ b/nihil.ucl/type.ccm
@@ -1,40 +1,61 @@
-/*
- * This source code is released into the public domain.
- */
-
+// This source code is released into the public domain.
module;
-#include <concepts>
-#include <format>
-#include <stdexcept>
-#include <string>
-
#include <ucl.h>
export module nihil.ucl:type;
+import nihil.std;
import nihil.error;
namespace nihil::ucl {
// Our strongly-typed version of ::ucl_type.
-export enum struct object_type {
- object = UCL_OBJECT,
- array = UCL_ARRAY,
- integer = UCL_INT,
- real = UCL_FLOAT,
- string = UCL_STRING,
- boolean = UCL_BOOLEAN,
- time = UCL_TIME,
- userdata = UCL_USERDATA,
- null = UCL_NULL,
+export enum struct object_type : std::uint8_t {
+ object = UCL_OBJECT,
+ array = UCL_ARRAY,
+ integer = UCL_INT,
+ real = UCL_FLOAT,
+ string = UCL_STRING,
+ boolean = UCL_BOOLEAN,
+ time = UCL_TIME,
+ userdata = UCL_USERDATA,
+ null = UCL_NULL,
};
// Get the name of a type.
-export auto str(object_type type) -> std::string_view;
+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;
+ }
+}
// Concept of a UCL data type.
-export template<typename T>
+export template <typename T>
concept datatype = requires(T o) {
{ o.get_ucl_object() } -> std::convertible_to<::ucl_object_t const *>;
{ o.type() } -> std::same_as<object_type>;
@@ -42,14 +63,28 @@ 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);
+export struct type_mismatch : error
+{
+ type_mismatch(object_type expected_type, object_type actual_type)
+ : error(std::format("expected type '{}' != actual type '{}'",
+ ucl::str(expected_type), ucl::str(actual_type)))
+ , m_expected_type(expected_type)
+ , m_actual_type(actual_type)
+ {
+ }
// The type we expected.
- auto expected_type(this type_mismatch const &self) -> object_type;
+ auto expected_type(this type_mismatch const &self) -> object_type
+ {
+ return self.m_expected_type;
+ }
+
// The type we got.
- auto actual_type(this type_mismatch const &self) -> object_type;
-
+ auto actual_type(this type_mismatch const &self) -> object_type
+ {
+ return self.m_actual_type;
+ }
+
private:
object_type m_expected_type;
object_type m_actual_type;