aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLexi Winter <lexi@le-fay.org>2025-06-22 18:18:44 +0100
committerLexi Winter <lexi@le-fay.org>2025-06-22 18:18:44 +0100
commit639b270eed81f7c2627d810057d188e2e8ee67f9 (patch)
tree7b8f7628b799dba307f06cf813583d42bdc1838a
parent09b7a494bd5de24f380095003fb7da4939de43e5 (diff)
downloadnihil-639b270eed81f7c2627d810057d188e2e8ee67f9.tar.gz
nihil-639b270eed81f7c2627d810057d188e2e8ee67f9.tar.bz2
nihil.ucl: improve construction and comparison
-rw-r--r--nihil.ucl/boolean.ccm3
-rw-r--r--nihil.ucl/integer.ccm7
-rw-r--r--nihil.ucl/real.ccm8
-rw-r--r--nihil.ucl/string.ccm44
-rw-r--r--nihil.ucl/tests/array.cc28
-rw-r--r--nihil.ucl/tests/boolean.cc27
-rw-r--r--nihil.ucl/tests/integer.cc28
-rw-r--r--nihil.ucl/tests/real.cc28
-rw-r--r--nihil.ucl/tests/string.cc96
9 files changed, 227 insertions, 42 deletions
diff --git a/nihil.ucl/boolean.ccm b/nihil.ucl/boolean.ccm
index e9c161b..8510152 100644
--- a/nihil.ucl/boolean.ccm
+++ b/nihil.ucl/boolean.ccm
@@ -37,6 +37,9 @@ export struct boolean final : object {
throw type_mismatch(ucl_type, type());
}
+ // Create a new default-initialised boolean.
+ boolean() : boolean(false) {}
+
// Create a new boolean from a value.
explicit boolean(value_type value)
: object(noref, ::ucl_object_frombool(value))
diff --git a/nihil.ucl/integer.ccm b/nihil.ucl/integer.ccm
index 18f0049..b58c04d 100644
--- a/nihil.ucl/integer.ccm
+++ b/nihil.ucl/integer.ccm
@@ -37,8 +37,13 @@ export struct integer final : object {
throw type_mismatch(ucl_type, type());
}
+ // Create a new default-initialised integer.
+ integer()
+ : integer(0)
+ {}
+
// Create a new integer from a value.
- integer(value_type value)
+ explicit integer(value_type value)
: object(noref, ::ucl_object_fromint(value))
{
if (_object == nullptr)
diff --git a/nihil.ucl/real.ccm b/nihil.ucl/real.ccm
index d968667..51607f2 100644
--- a/nihil.ucl/real.ccm
+++ b/nihil.ucl/real.ccm
@@ -36,14 +36,20 @@ export struct real final : object {
throw type_mismatch(ucl_type, type());
}
+ // Create a default-initialised real.
+ real()
+ : real(0)
+ {}
+
// Create a new real from a value.
- real(value_type value)
+ explicit real(value_type value)
: object(noref, ::ucl_object_fromdouble(value))
{
if (_object == nullptr)
throw error("failed to create UCL object");
}
+ // Return the value of this real.
auto value(this real const &self) -> value_type
{
auto v = value_type{};
diff --git a/nihil.ucl/string.ccm b/nihil.ucl/string.ccm
index ffad847..64f88dc 100644
--- a/nihil.ucl/string.ccm
+++ b/nihil.ucl/string.ccm
@@ -36,8 +36,13 @@ export struct string final : object {
throw type_mismatch(ucl_type, type());
}
+ // Create a new empty string.
+ string()
+ : string(std::string_view(""))
+ {}
+
// Create a new UCL string from a string.
- string(std::string_view value)
+ explicit string(std::string_view value)
: object(nihil::ucl::ref,
::ucl_object_fromstring_common(
value.data(), value.size(),
@@ -75,6 +80,7 @@ export struct string final : object {
if (::ucl_object_tolstring_safe(uobj, &dptr, &dlen))
return {dptr, dlen};
+ // This should never fail.
std::abort();
}
};
@@ -95,16 +101,42 @@ export auto operator<=> (string const &a, string const &b)
return a.value() <=> b.value();
}
-export auto operator== (string const &a, string::value_type b)
- -> bool
+/*
+ * For convenience, allow comparison with C++ strings without having to
+ * construct a temporary UCL object.
+ */
+
+export auto operator==(string const &lhs, std::string_view rhs) -> bool
+{
+ return lhs.value() == rhs;
+}
+
+export auto operator<=>(string const &lhs, std::string_view rhs)
+ -> std::strong_ordering
+{
+ return lhs.value() <=> rhs;
+}
+
+export auto operator==(string const &lhs, std::string const &rhs) -> bool
+{
+ return lhs == std::string_view(rhs);
+}
+
+export auto operator<=>(string const &lhs, std::string const &rhs)
+ -> std::strong_ordering
+{
+ return lhs <=> std::string_view(rhs);
+}
+
+export auto operator==(string const &lhs, char const *rhs) -> bool
{
- return a.value() == b;
+ return lhs == std::string_view(rhs);
}
-export auto operator<=> (string const &a, string::value_type b)
+export auto operator<=>(string const &lhs, char const *rhs)
-> std::strong_ordering
{
- return a.value() <=> b;
+ return lhs <=> std::string_view(rhs);
}
} // namespace nihil::ucl
diff --git a/nihil.ucl/tests/array.cc b/nihil.ucl/tests/array.cc
index a4f26b6..ce86058 100644
--- a/nihil.ucl/tests/array.cc
+++ b/nihil.ucl/tests/array.cc
@@ -31,9 +31,9 @@ TEST_CASE("ucl: array: push_back", "[ucl]")
arr.push_back(integer(666));
REQUIRE(arr.size() == 3);
- REQUIRE(arr[0].value() == 1);
- REQUIRE(arr[1].value() == 42);
- REQUIRE(arr[2].value() == 666);
+ REQUIRE(arr[0] == 1);
+ REQUIRE(arr[1] == 42);
+ REQUIRE(arr[2] == 666);
REQUIRE_THROWS_AS(arr[3], std::out_of_range);
@@ -46,22 +46,22 @@ TEST_CASE("ucl: array: compare", "[ucl]")
using namespace nihil::ucl;
auto arr = array<integer>();
- arr.push_back(1);
- arr.push_back(42);
- arr.push_back(666);
+ arr.push_back(integer(1));
+ arr.push_back(integer(42));
+ arr.push_back(integer(666));
auto arr2 = array<integer>();
REQUIRE(arr != arr2);
- arr2.push_back(1);
- arr2.push_back(42);
- arr2.push_back(666);
+ arr2.push_back(integer(1));
+ arr2.push_back(integer(42));
+ arr2.push_back(integer(666));
REQUIRE(arr == arr2);
auto arr3 = array<integer>();
- arr3.push_back(1);
- arr3.push_back(1);
- arr3.push_back(1);
+ arr3.push_back(integer(1));
+ arr3.push_back(integer(1));
+ arr3.push_back(integer(1));
REQUIRE(arr != arr3);
}
@@ -69,7 +69,7 @@ TEST_CASE("ucl: array: iterator", "[ucl]")
{
using namespace nihil::ucl;
- auto arr = array<integer>{1, 42, 666};
+ auto arr = array<integer>{integer(1), integer(42), integer(666)};
auto it = arr.begin();
REQUIRE(*it == 1);
@@ -129,7 +129,7 @@ TEST_CASE("ucl: array is a sized_range", "[ucl]")
{
using namespace nihil::ucl;
- auto arr = array<integer>{1, 42, 666};
+ auto arr = array<integer>{integer(1), integer(42), integer(666)};
static_assert(std::ranges::sized_range<decltype(arr)>);
auto size = std::ranges::size(arr);
diff --git a/nihil.ucl/tests/boolean.cc b/nihil.ucl/tests/boolean.cc
index c023498..b0b3b58 100644
--- a/nihil.ucl/tests/boolean.cc
+++ b/nihil.ucl/tests/boolean.cc
@@ -10,19 +10,36 @@ import nihil.ucl;
TEST_CASE("ucl: boolean: construct", "[ucl]")
{
- auto obj = nihil::ucl::boolean(true);
- REQUIRE(object_cast<nihil::ucl::boolean>(obj).value() == true);
+ auto b = nihil::ucl::boolean(true);
+ REQUIRE(b == true);
}
-TEST_CASE("ucl: boolean: compare", "[ucl]")
+TEST_CASE("ucl: boolean: default construct", "[ucl]")
+{
+ auto b = nihil::ucl::boolean();
+ REQUIRE(b == false);
+}
+
+TEST_CASE("ucl: boolean: operator==", "[ucl]")
{
auto b = nihil::ucl::boolean(true);
- REQUIRE(b == nihil::ucl::boolean(true));
REQUIRE(b == true);
+ REQUIRE(b == nihil::ucl::boolean(true));
- REQUIRE(b != nihil::ucl::boolean(false));
REQUIRE(b != false);
+ REQUIRE(b != nihil::ucl::boolean(false));
+}
+
+TEST_CASE("ucl: boolean: operator<=>", "[ucl]")
+{
+ auto b = nihil::ucl::boolean(false);
+
+ REQUIRE(b < true);
+ REQUIRE(b < nihil::ucl::boolean(true));
+
+ REQUIRE(b >= false);
+ REQUIRE(b >= nihil::ucl::boolean(false));
}
TEST_CASE("ucl: boolean: parse", "[ucl]")
diff --git a/nihil.ucl/tests/integer.cc b/nihil.ucl/tests/integer.cc
index da119e6..c7db851 100644
--- a/nihil.ucl/tests/integer.cc
+++ b/nihil.ucl/tests/integer.cc
@@ -10,20 +10,36 @@ import nihil.ucl;
TEST_CASE("ucl: integer: construct", "[ucl]")
{
- auto obj = nihil::ucl::integer(42);
- REQUIRE(object_cast<nihil::ucl::integer>(obj).value() == 42);
+ auto i = nihil::ucl::integer(42);
+ REQUIRE(i == 42);
}
-TEST_CASE("ucl: integer: compare", "[ucl]")
+TEST_CASE("ucl: integer: default construct", "[ucl]")
+{
+ auto i = nihil::ucl::integer();
+ REQUIRE(i == 0);
+}
+
+TEST_CASE("ucl: integer: operator==", "[ucl]")
{
auto i = nihil::ucl::integer(42);
+ REQUIRE(i == 42);
REQUIRE(i == nihil::ucl::integer(42));
+
+ REQUIRE(i != 1);
REQUIRE(i != nihil::ucl::integer(1));
- REQUIRE((i == 42) == true);
+}
+
+TEST_CASE("ucl: integer: operator<=>", "[ucl]")
+{
+ auto i = nihil::ucl::integer(42);
+
+ REQUIRE(i < 43);
+ REQUIRE(i < nihil::ucl::integer(43));
- REQUIRE(i > nihil::ucl::integer(40));
- REQUIRE(i > 40);
+ REQUIRE(i > 1);
+ REQUIRE(i > nihil::ucl::integer(1));
}
TEST_CASE("ucl: parse: integer", "[ucl]")
diff --git a/nihil.ucl/tests/real.cc b/nihil.ucl/tests/real.cc
index 275684a..d97e767 100644
--- a/nihil.ucl/tests/real.cc
+++ b/nihil.ucl/tests/real.cc
@@ -16,16 +16,32 @@ TEST_CASE("ucl: real: construct", "[ucl]")
Catch::Matchers::WithinRel(42.1));
}
-TEST_CASE("ucl: real: compare", "[ucl]")
+TEST_CASE("ucl: real: default construct", "[ucl]")
{
- auto i = nihil::ucl::real(42);
+ auto i = nihil::ucl::real();
+ REQUIRE(i == 0);
+}
+
+TEST_CASE("ucl: real: operator==", "[ucl]")
+{
+ auto i = nihil::ucl::real(42.5);
+
+ REQUIRE(i == 42.5);
+ REQUIRE(i == nihil::ucl::real(42.5));
- REQUIRE(i == nihil::ucl::real(42));
+ REQUIRE(i != 1);
REQUIRE(i != nihil::ucl::real(1));
- REQUIRE((i == 42) == true);
+}
+
+TEST_CASE("ucl: real: operator<=>", "[ucl]")
+{
+ auto i = nihil::ucl::real(42.5);
+
+ REQUIRE(i < 43);
+ REQUIRE(i < nihil::ucl::real(43));
- REQUIRE(i > nihil::ucl::real(40));
- REQUIRE(i > 40);
+ REQUIRE(i > 1);
+ REQUIRE(i > nihil::ucl::real(1));
}
TEST_CASE("ucl: real: parse", "[ucl]")
diff --git a/nihil.ucl/tests/string.cc b/nihil.ucl/tests/string.cc
index 4385dbb..b702b51 100644
--- a/nihil.ucl/tests/string.cc
+++ b/nihil.ucl/tests/string.cc
@@ -2,16 +2,106 @@
* This source code is released into the public domain.
*/
+#include <list>
#include <string>
+#include <vector>
#include <catch2/catch_test_macros.hpp>
+#include <ucl.h>
import nihil.ucl;
-TEST_CASE("ucl: string: construct", "[ucl]")
+TEST_CASE("ucl: string: invariants", "[ucl]")
{
- auto obj = nihil::ucl::string("testing");
- REQUIRE(object_cast<nihil::ucl::string>(obj).value() == "testing");
+ using namespace nihil::ucl;
+
+ static_assert(std::same_as<std::string_view, string::value_type>);
+ REQUIRE(string::ucl_type == object_type::string);
+ REQUIRE(static_cast<::ucl_type>(string::ucl_type) == UCL_STRING);
+}
+
+TEST_CASE("ucl: string: default construct", "[ucl]")
+{
+ auto str = nihil::ucl::string();
+ REQUIRE(str == "");
+}
+
+TEST_CASE("ucl: string: construct from string literal", "[ucl]")
+{
+ auto str = nihil::ucl::string("testing");
+ REQUIRE(str == "testing");
+}
+
+TEST_CASE("ucl: string: construct from std::string", "[ucl]")
+{
+ auto str = nihil::ucl::string(std::string("testing"));
+ REQUIRE(str == "testing");
+}
+
+TEST_CASE("ucl: string: construct from std::string_view", "[ucl]")
+{
+ auto str = nihil::ucl::string(std::string_view("testing"));
+ REQUIRE(str == "testing");
+}
+
+TEST_CASE("ucl: string: construct from contiguous range", "[ucl]")
+{
+ auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = nihil::ucl::string(std::from_range, s);
+ REQUIRE(str == "testing");
+}
+
+TEST_CASE("ucl: string: construct from non-contiguous range", "[ucl]")
+{
+ auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = nihil::ucl::string(std::from_range, s);
+ REQUIRE(str == "testing");
+}
+
+TEST_CASE("ucl: string: construct from contiguous iterator", "[ucl]")
+{
+ auto s = std::vector{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = nihil::ucl::string(std::ranges::begin(s),
+ std::ranges::end(s));
+ REQUIRE(str == "testing");
+}
+
+TEST_CASE("ucl: string: construct from non-contiguous iterator", "[ucl]")
+{
+ auto s = std::list{'t', 'e', 's', 't', 'i', 'n', 'g'};
+ auto str = nihil::ucl::string(std::ranges::begin(s),
+ std::ranges::end(s));
+ REQUIRE(str == "testing");
+}
+
+TEST_CASE("ucl: string: operator==", "[ucl]")
+{
+ auto str = nihil::ucl::string("testing");
+
+ REQUIRE(str == nihil::ucl::string("testing"));
+ REQUIRE(str == std::string_view("testing"));
+ REQUIRE(str == std::string("testing"));
+ REQUIRE(str == "testing");
+
+ REQUIRE(str != nihil::ucl::string("test"));
+ REQUIRE(str != std::string_view("test"));
+ REQUIRE(str != std::string("test"));
+ REQUIRE(str != "test");
+}
+
+TEST_CASE("ucl: string: operator<=>", "[ucl]")
+{
+ auto str = nihil::ucl::string("testing");
+
+ REQUIRE(str < nihil::ucl::string("zzz"));
+ REQUIRE(str < std::string_view("zzz"));
+ REQUIRE(str < std::string("zzz"));
+ REQUIRE(str < "zzz");
+
+ REQUIRE(str > nihil::ucl::string("aaa"));
+ REQUIRE(str > std::string_view("aaa"));
+ REQUIRE(str > std::string("aaa"));
+ REQUIRE(str > "aaa");
}
TEST_CASE("ucl: string: parse", "[ucl]")