From f48c8be1977beb277bd0ca4f0bce51b571a301b6 Mon Sep 17 00:00:00 2001 From: jeaye Date: Fri, 16 Feb 2024 18:33:43 -0800 Subject: [PATCH 01/13] Add initial transient support This includes transient_hash_map and the core `transient` and `persistent!` fns. --- CMakeLists.txt | 1 + include/cpp/jank/prelude.hpp | 1 + .../behavior/associatively_writable.hpp | 7 + .../cpp/jank/runtime/behavior/consable.hpp | 7 + .../jank/runtime/behavior/transientable.hpp | 18 ++ include/cpp/jank/runtime/erasure.hpp | 6 + .../obj/detail/base_persistent_map.hpp | 1 + .../jank/runtime/obj/persistent_hash_map.hpp | 8 + .../jank/runtime/obj/persistent_string.hpp | 2 +- .../jank/runtime/obj/transient_hash_map.hpp | 69 ++++++++ include/cpp/jank/runtime/object.hpp | 1 + .../jank/runtime/obj/persistent_hash_map.cpp | 9 +- .../jank/runtime/obj/transient_hash_map.cpp | 156 ++++++++++++++++++ src/jank/clojure/core.jank | 36 ++++ 14 files changed, 318 insertions(+), 4 deletions(-) create mode 100644 include/cpp/jank/runtime/behavior/transientable.hpp create mode 100644 include/cpp/jank/runtime/obj/transient_hash_map.hpp create mode 100644 src/cpp/jank/runtime/obj/transient_hash_map.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 3878cd05..7d2c8bfd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -170,6 +170,7 @@ add_library( src/cpp/jank/runtime/obj/persistent_vector_sequence.cpp src/cpp/jank/runtime/obj/persistent_array_map.cpp src/cpp/jank/runtime/obj/persistent_hash_map.cpp + src/cpp/jank/runtime/obj/transient_hash_map.cpp src/cpp/jank/runtime/obj/persistent_set.cpp src/cpp/jank/runtime/obj/persistent_string.cpp src/cpp/jank/runtime/obj/cons.cpp diff --git a/include/cpp/jank/prelude.hpp b/include/cpp/jank/prelude.hpp index fe1d8506..40a6a22e 100644 --- a/include/cpp/jank/prelude.hpp +++ b/include/cpp/jank/prelude.hpp @@ -27,3 +27,4 @@ #include #include #include +#include diff --git a/include/cpp/jank/runtime/behavior/associatively_writable.hpp b/include/cpp/jank/runtime/behavior/associatively_writable.hpp index a7db9e7c..8bf45646 100644 --- a/include/cpp/jank/runtime/behavior/associatively_writable.hpp +++ b/include/cpp/jank/runtime/behavior/associatively_writable.hpp @@ -10,4 +10,11 @@ namespace jank::runtime::behavior t->assoc(object_ptr{}, object_ptr{}) } -> std::convertible_to; }; + + template + concept associatively_writable_in_place = requires(T * const t) { + { + t->assoc_in_place(object_ptr{}, object_ptr{}) + } -> std::convertible_to; + }; } diff --git a/include/cpp/jank/runtime/behavior/consable.hpp b/include/cpp/jank/runtime/behavior/consable.hpp index 467d389a..2135bd27 100644 --- a/include/cpp/jank/runtime/behavior/consable.hpp +++ b/include/cpp/jank/runtime/behavior/consable.hpp @@ -11,4 +11,11 @@ namespace jank::runtime::behavior t->cons(object_ptr{}) }; // -> consable }; + + template + concept consable_in_place = requires(T * const t) { + { + t->cons_in_place(object_ptr{}) + }; // -> consable_in_place + }; } diff --git a/include/cpp/jank/runtime/behavior/transientable.hpp b/include/cpp/jank/runtime/behavior/transientable.hpp new file mode 100644 index 00000000..a6b62505 --- /dev/null +++ b/include/cpp/jank/runtime/behavior/transientable.hpp @@ -0,0 +1,18 @@ +#pragma once + +namespace jank::runtime::behavior +{ + template + concept persistentable = requires(T * const t) { + { + t->to_persistent() + } -> std::convertible_to; + }; + + template + concept transientable = requires(T * const t) { + { + t->to_transient() + } -> std::convertible_to; + }; +} diff --git a/include/cpp/jank/runtime/erasure.hpp b/include/cpp/jank/runtime/erasure.hpp index 19ca66f7..d0c5e4d0 100644 --- a/include/cpp/jank/runtime/erasure.hpp +++ b/include/cpp/jank/runtime/erasure.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -173,6 +174,11 @@ namespace jank::runtime std::forward(args)...); } break; + case object_type::transient_hash_map: + { + return fn(expect_object(erased), std::forward(args)...); + } + break; case object_type::persistent_set: { return fn(expect_object(erased), std::forward(args)...); diff --git a/include/cpp/jank/runtime/obj/detail/base_persistent_map.hpp b/include/cpp/jank/runtime/obj/detail/base_persistent_map.hpp index 209abe16..3e9bd251 100644 --- a/include/cpp/jank/runtime/obj/detail/base_persistent_map.hpp +++ b/include/cpp/jank/runtime/obj/detail/base_persistent_map.hpp @@ -40,6 +40,7 @@ namespace jank::runtime::obj::detail return false; } + /* TODO: Is this enough? Value comparison may be needed. */ return to_hash() == hash::visit(p); } diff --git a/include/cpp/jank/runtime/obj/persistent_hash_map.hpp b/include/cpp/jank/runtime/obj/persistent_hash_map.hpp index c0a321e3..f7281ffe 100644 --- a/include/cpp/jank/runtime/obj/persistent_hash_map.hpp +++ b/include/cpp/jank/runtime/obj/persistent_hash_map.hpp @@ -11,6 +11,9 @@ namespace jank::runtime { using persistent_array_map = static_object; using persistent_array_map_ptr = native_box; + + using transient_hash_map = static_object; + using transient_hash_map_ptr = native_box; } template <> @@ -19,6 +22,8 @@ namespace jank::runtime object_type::persistent_hash_map_sequence, runtime::detail::native_persistent_hash_map> { + using transient_type = static_object; + static_object() = default; static_object(static_object &&) = default; static_object(static_object const &) = default; @@ -68,6 +73,9 @@ namespace jank::runtime object_ptr call(object_ptr) const; object_ptr call(object_ptr, object_ptr) const; + /* behavior::transientable */ + obj::transient_hash_map_ptr to_transient() const; + value_type data{}; }; diff --git a/include/cpp/jank/runtime/obj/persistent_string.hpp b/include/cpp/jank/runtime/obj/persistent_string.hpp index c6355095..3ef76567 100644 --- a/include/cpp/jank/runtime/obj/persistent_string.hpp +++ b/include/cpp/jank/runtime/obj/persistent_string.hpp @@ -8,7 +8,7 @@ namespace jank::runtime template <> struct static_object : gc { - static constexpr bool pointer_free{ true }; + static constexpr bool pointer_free{ false }; static_object() = default; static_object(static_object &&) = default; diff --git a/include/cpp/jank/runtime/obj/transient_hash_map.hpp b/include/cpp/jank/runtime/obj/transient_hash_map.hpp new file mode 100644 index 00000000..b0d6f12b --- /dev/null +++ b/include/cpp/jank/runtime/obj/transient_hash_map.hpp @@ -0,0 +1,69 @@ +#pragma once + +#include + +namespace jank::runtime +{ + template <> + struct static_object : gc + { + static constexpr bool pointer_free{ false }; + + using value_type = detail::native_transient_hash_map; + using persistent_type = static_object; + + static_object() = default; + static_object(static_object &&) = default; + static_object(static_object const &) = default; + static_object(detail::native_persistent_hash_map const &d); + static_object(detail::native_persistent_hash_map &&d); + static_object(value_type &&d); + + static native_box empty() + { + static auto const ret(make_box()); + return ret; + } + + /* behavior::objectable */ + native_bool equal(object const &) const; + native_persistent_string to_string() const; + void to_string(fmt::memory_buffer &buff) const; + native_hash to_hash() const; + + /* behavior::countable */ + size_t count() const; + + /* behavior::associatively_readable */ + object_ptr get(object_ptr const key) const; + object_ptr get(object_ptr const key, object_ptr const fallback) const; + object_ptr get_entry(object_ptr key) const; + native_bool contains(object_ptr key) const; + + /* behavior::associatively_writable_in_place */ + native_box assoc_in_place(object_ptr key, object_ptr val); + + /* behavior::consable_in_place */ + native_box cons_in_place(object_ptr head); + + /* behavior::persistentable */ + native_box to_persistent(); + + /* behavior::callable */ + object_ptr call(object_ptr) const; + object_ptr call(object_ptr, object_ptr) const; + + void assert_active() const; + + object base{ object_type::transient_hash_map }; + value_type data; + mutable native_hash hash{}; + native_bool active{ true }; + }; + + namespace obj + { + using transient_hash_map = static_object; + using transient_hash_map_ptr = native_box; + } +} diff --git a/include/cpp/jank/runtime/object.hpp b/include/cpp/jank/runtime/object.hpp index e6c1fbf2..85dae4fc 100644 --- a/include/cpp/jank/runtime/object.hpp +++ b/include/cpp/jank/runtime/object.hpp @@ -22,6 +22,7 @@ namespace jank::runtime persistent_array_map_sequence, persistent_hash_map, persistent_hash_map_sequence, + transient_hash_map, persistent_set, cons, range, diff --git a/src/cpp/jank/runtime/obj/persistent_hash_map.cpp b/src/cpp/jank/runtime/obj/persistent_hash_map.cpp index e245eeff..ccb4e450 100644 --- a/src/cpp/jank/runtime/obj/persistent_hash_map.cpp +++ b/src/cpp/jank/runtime/obj/persistent_hash_map.cpp @@ -1,10 +1,8 @@ -#include -#include - #include #include #include #include +#include namespace jank::runtime { @@ -147,4 +145,9 @@ namespace jank::runtime } return *found; } + + obj::transient_hash_map_ptr obj::persistent_hash_map::to_transient() const + { + return make_box(data); + } } diff --git a/src/cpp/jank/runtime/obj/transient_hash_map.cpp b/src/cpp/jank/runtime/obj/transient_hash_map.cpp new file mode 100644 index 00000000..58d00268 --- /dev/null +++ b/src/cpp/jank/runtime/obj/transient_hash_map.cpp @@ -0,0 +1,156 @@ +#include +#include +#include + +namespace jank::runtime +{ + obj::transient_hash_map::static_object(runtime::detail::native_persistent_hash_map &&d) + : data{ d.transient() } + { + } + + obj::transient_hash_map::static_object(runtime::detail::native_persistent_hash_map const &d) + : data{ d.transient() } + { + } + + obj::transient_hash_map::static_object(runtime::detail::native_transient_hash_map &&d) + : data{ d } + { + } + + native_bool obj::transient_hash_map::equal(object const &o) const + { + /* Transient equality, in Clojure, is based solely on identity. */ + return &base == &o; + } + + void obj::transient_hash_map::to_string(fmt::memory_buffer &buff) const + { + auto inserter(std::back_inserter(buff)); + fmt::format_to(inserter, "{}@{}", magic_enum::enum_name(base.type), fmt::ptr(&base)); + } + + native_persistent_string obj::transient_hash_map::to_string() const + { + fmt::memory_buffer buff; + to_string(buff); + return native_persistent_string{ buff.data(), buff.size() }; + } + + native_hash obj::transient_hash_map::to_hash() const + { + /* Hash is also based only on identity. Clojure uses default hashCode, which does the same. */ + return static_cast(reinterpret_cast(this)); + } + + size_t obj::transient_hash_map::count() const + { + assert_active(); + return data.size(); + } + + object_ptr obj::transient_hash_map::get(object_ptr const key) const + { + assert_active(); + auto const res(data.find(key)); + if(res) + { + return *res; + } + return obj::nil::nil_const(); + } + + object_ptr obj::transient_hash_map::get(object_ptr const key, object_ptr const fallback) const + { + assert_active(); + auto const res(data.find(key)); + if(res) + { + return *res; + } + return fallback; + } + + object_ptr obj::transient_hash_map::get_entry(object_ptr const key) const + { + assert_active(); + auto const res(data.find(key)); + if(res) + { + return make_box(key, *res); + } + return obj::nil::nil_const(); + } + + native_bool obj::transient_hash_map::contains(object_ptr const key) const + { + assert_active(); + return data.find(key); + } + + obj::transient_hash_map_ptr + obj::transient_hash_map::assoc_in_place(object_ptr const key, object_ptr const val) + { + assert_active(); + data.set(key, val); + return this; + } + + obj::transient_hash_map_ptr obj::transient_hash_map::cons_in_place(object_ptr const head) + { + assert_active(); + if(head->type != object_type::persistent_vector) + { + throw std::runtime_error{ fmt::format("invalid map entry: {}", + runtime::detail::to_string(head)) }; + } + + auto const vec(expect_object(head)); + if(vec->count() != 2) + { + throw std::runtime_error{ fmt::format("invalid map entry: {}", + runtime::detail::to_string(head)) }; + } + + data.set(vec->data[0], vec->data[1]); + return this; + } + + native_box obj::transient_hash_map::to_persistent() + { + assert_active(); + active = false; + return make_box(std::move(data)); + } + + object_ptr obj::transient_hash_map::call(object_ptr const o) const + { + assert_active(); + auto const found(data.find(o)); + if(!found) + { + return obj::nil::nil_const(); + } + return *found; + } + + object_ptr obj::transient_hash_map::call(object_ptr const o, object_ptr const fallback) const + { + assert_active(); + auto const found(data.find(o)); + if(!found) + { + return fallback; + } + return *found; + } + + void obj::transient_hash_map::assert_active() const + { + if(!active) + { + throw std::runtime_error{ "transient used after it's been made persistent" }; + } + } +} diff --git a/src/jank/clojure/core.jank b/src/jank/clojure/core.jank index 0fa10574..7c3a53c3 100644 --- a/src/jank/clojure/core.jank +++ b/src/jank/clojure/core.jank @@ -396,6 +396,41 @@ (defn some? [o] (native/raw "__value = (o == obj::nil::nil_const()) ? #{ false }# : #{ true }#")) +; Transients. +; Returns a new, transient version of the collection, in constant time. +(defn transient [o] + (native/raw "__value = visit_object + ( + [=](auto const typed_o) -> object_ptr + { + using T = typename decltype(typed_o)::value_type; + + if constexpr(behavior::transientable) + { return typed_o->to_transient(); } + else + { throw #{ (ex-info :not-transientable {:o o}) }#; } + }, + #{ o }# + );")) + +; Returns a new, persistent version of the transient collection, in +; constant time. The transient collection cannot be used after this +; call, any such use will throw an exception. +(defn persistent! [o] + (native/raw "__value = visit_object + ( + [=](auto const typed_o) -> object_ptr + { + using T = typename decltype(typed_o)::value_type; + + if constexpr(behavior::persistentable) + { return typed_o->to_persistent(); } + else + { throw #{ (ex-info :not-persistentable {:o o}) }#; } + }, + #{ o }# + );")) + ; Functions. (defn- spread [arglist] @@ -672,6 +707,7 @@ ;; Maps. (defn hash-map ([] + ; NOTE: Actually returns an array map. Clojure does the same. {}) ([& kvs] (native/raw "__value = obj::persistent_hash_map::create_from_seq(#{ kvs }#);"))) From e7bcbf37fba5663bfa94d35a751df20d7acb5c27 Mon Sep 17 00:00:00 2001 From: jeaye Date: Sat, 17 Feb 2024 23:22:44 +0000 Subject: [PATCH 02/13] Improve function string representation --- .../cpp/jank/runtime/obj/native_function_wrapper.hpp | 2 +- src/cpp/jank/runtime/obj/jit_function.cpp | 9 +++++++-- src/cpp/jank/runtime/obj/native_function_wrapper.cpp | 12 ++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/include/cpp/jank/runtime/obj/native_function_wrapper.hpp b/include/cpp/jank/runtime/obj/native_function_wrapper.hpp index 51d8def9..d8742dba 100644 --- a/include/cpp/jank/runtime/obj/native_function_wrapper.hpp +++ b/include/cpp/jank/runtime/obj/native_function_wrapper.hpp @@ -65,7 +65,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_persistent_string const &to_string() const; + native_persistent_string to_string() const; void to_string(fmt::memory_buffer &buff) const; native_hash to_hash() const; diff --git a/src/cpp/jank/runtime/obj/jit_function.cpp b/src/cpp/jank/runtime/obj/jit_function.cpp index 5b11382a..8767585e 100644 --- a/src/cpp/jank/runtime/obj/jit_function.cpp +++ b/src/cpp/jank/runtime/obj/jit_function.cpp @@ -9,12 +9,17 @@ namespace jank::runtime native_persistent_string obj::jit_function::to_string() { - return "jit function"; + fmt::memory_buffer buff; + to_string(buff); + return native_persistent_string{ buff.data(), buff.size() }; } void obj::jit_function::to_string(fmt::memory_buffer &buff) { - fmt::format_to(std::back_inserter(buff), "jit_function"); + fmt::format_to(std::back_inserter(buff), + "{}@{}", + magic_enum::enum_name(base.type), + fmt::ptr(&base)); } native_hash obj::jit_function::to_hash() const diff --git a/src/cpp/jank/runtime/obj/native_function_wrapper.cpp b/src/cpp/jank/runtime/obj/native_function_wrapper.cpp index 1128cfc5..a659abf3 100644 --- a/src/cpp/jank/runtime/obj/native_function_wrapper.cpp +++ b/src/cpp/jank/runtime/obj/native_function_wrapper.cpp @@ -21,13 +21,17 @@ namespace jank::runtime void obj::native_function_wrapper::to_string(fmt::memory_buffer &buff) const { - fmt::format_to(std::back_inserter(buff), "function"); + fmt::format_to(std::back_inserter(buff), + "{}@{}", + magic_enum::enum_name(base.type), + fmt::ptr(&base)); } - native_persistent_string const &obj::native_function_wrapper::to_string() const + native_persistent_string obj::native_function_wrapper::to_string() const { - static native_persistent_string const s{ "native_function_wrapper" }; - return s; + fmt::memory_buffer buff; + to_string(buff); + return native_persistent_string{ buff.data(), buff.size() }; } native_hash obj::native_function_wrapper::to_hash() const From 67d4771f10bb0be4af78d1fff5eed4d0d5307606 Mon Sep 17 00:00:00 2001 From: Saket Date: Fri, 8 Mar 2024 23:06:01 +0530 Subject: [PATCH 03/13] feat(transient-vector): adds initial support for transient vector --- include/cpp/jank/runtime/erasure.hpp | 6 + .../jank/runtime/obj/persistent_vector.hpp | 19 ++- .../cpp/jank/runtime/obj/transient_vector.hpp | 60 +++++++++ include/cpp/jank/runtime/object.hpp | 1 + .../jank/runtime/obj/persistent_vector.cpp | 6 + src/cpp/jank/runtime/obj/transient_vector.cpp | 118 ++++++++++++++++++ 6 files changed, 204 insertions(+), 6 deletions(-) create mode 100644 include/cpp/jank/runtime/obj/transient_vector.hpp create mode 100644 src/cpp/jank/runtime/obj/transient_vector.cpp diff --git a/include/cpp/jank/runtime/erasure.hpp b/include/cpp/jank/runtime/erasure.hpp index d0c5e4d0..0cdda692 100644 --- a/include/cpp/jank/runtime/erasure.hpp +++ b/include/cpp/jank/runtime/erasure.hpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include @@ -179,6 +180,11 @@ namespace jank::runtime return fn(expect_object(erased), std::forward(args)...); } break; + case object_type::transient_vector: + { + return fn(expect_object(erased), std::forward(args)...); + } + break; case object_type::persistent_set: { return fn(expect_object(erased), std::forward(args)...); diff --git a/include/cpp/jank/runtime/obj/persistent_vector.hpp b/include/cpp/jank/runtime/obj/persistent_vector.hpp index c53af002..7557c0a2 100644 --- a/include/cpp/jank/runtime/obj/persistent_vector.hpp +++ b/include/cpp/jank/runtime/obj/persistent_vector.hpp @@ -5,9 +5,19 @@ namespace jank::runtime { + namespace obj + { + using persistent_vector = static_object; + using persistent_vector_ptr = native_box; + + using transient_vector = static_object; + using transient_vector_ptr = native_box; + } + template <> struct static_object : gc { + using transient_type = static_object; using value_type = runtime::detail::native_persistent_vector; static constexpr bool pointer_free{ false }; @@ -51,15 +61,12 @@ namespace jank::runtime /* behavior::consable */ native_box cons(object_ptr head) const; + /* behavior::transientable */ + obj::transient_vector_ptr to_transient() const; + object base{ object_type::persistent_vector }; value_type data; option meta; mutable native_hash hash{}; }; - - namespace obj - { - using persistent_vector = static_object; - using persistent_vector_ptr = native_box; - } } diff --git a/include/cpp/jank/runtime/obj/transient_vector.hpp b/include/cpp/jank/runtime/obj/transient_vector.hpp new file mode 100644 index 00000000..33e31845 --- /dev/null +++ b/include/cpp/jank/runtime/obj/transient_vector.hpp @@ -0,0 +1,60 @@ +#pragma once + +#include + +namespace jank::runtime +{ + template <> + struct static_object : gc + { + static constexpr bool pointer_free{ false }; + + using value_type = detail::native_transient_vector; + using persistent_type = static_object; + + static_object() = default; + static_object(static_object &&) = default; + static_object(static_object const &) = default; + static_object(detail::native_persistent_vector const &d); + static_object(detail::native_persistent_vector &&d); + static_object(value_type &&d); + + static native_box empty() + { + static auto const ret(make_box()); + return ret; + } + + /* behavior::objectable */ + native_bool equal(object const &) const; + native_persistent_string to_string() const; + void to_string(fmt::memory_buffer &buff) const; + native_hash to_hash() const; + + /* behavior::countable */ + size_t count() const; + + /* behavior::consable_in_place */ + native_box cons_in_place(object_ptr head); + + /* behavior::persistentable */ + native_box to_persistent(); + + /* behavior::callable */ + object_ptr call(object_ptr) const; + object_ptr call(object_ptr, object_ptr) const; + + void assert_active() const; + + object base{ object_type::transient_vector }; + value_type data; + mutable native_hash hash{}; + native_bool active{ true }; + }; + + namespace obj + { + using transient_vector = static_object; + using transient_vector_ptr = native_box; + } +} diff --git a/include/cpp/jank/runtime/object.hpp b/include/cpp/jank/runtime/object.hpp index 85dae4fc..0a1e1953 100644 --- a/include/cpp/jank/runtime/object.hpp +++ b/include/cpp/jank/runtime/object.hpp @@ -23,6 +23,7 @@ namespace jank::runtime persistent_hash_map, persistent_hash_map_sequence, transient_hash_map, + transient_vector, persistent_set, cons, range, diff --git a/src/cpp/jank/runtime/obj/persistent_vector.cpp b/src/cpp/jank/runtime/obj/persistent_vector.cpp index 5b143e3a..0418d9df 100644 --- a/src/cpp/jank/runtime/obj/persistent_vector.cpp +++ b/src/cpp/jank/runtime/obj/persistent_vector.cpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace jank::runtime { @@ -102,6 +103,11 @@ namespace jank::runtime return ret; } + obj::transient_vector_ptr obj::persistent_vector::to_transient() const + { + return make_box(data); + } + object_ptr obj::persistent_vector::with_meta(object_ptr const m) const { auto const meta(behavior::detail::validate_meta(m)); diff --git a/src/cpp/jank/runtime/obj/transient_vector.cpp b/src/cpp/jank/runtime/obj/transient_vector.cpp new file mode 100644 index 00000000..dd2f7481 --- /dev/null +++ b/src/cpp/jank/runtime/obj/transient_vector.cpp @@ -0,0 +1,118 @@ +#include "jank/runtime/detail/object_util.hpp" +#include "jank/runtime/erasure.hpp" +#include "jank/type.hpp" +#include +#include +#include +#include + +namespace jank::runtime +{ + obj::transient_vector::static_object(runtime::detail::native_persistent_vector &&d) + : data{ d.transient() } + { + } + + obj::transient_vector::static_object(runtime::detail::native_persistent_vector const &d) + : data{ d.transient() } + { + } + + obj::transient_vector::static_object(runtime::detail::native_transient_vector &&d) + : data{ d } + { + } + + native_bool obj::transient_vector::equal(object const &o) const + { + /* Transient equality, in Clojure, is based solely on identity. */ + return &base == &o; + } + + native_persistent_string obj::transient_vector::to_string() const + { + fmt::memory_buffer buff; + to_string(buff); + return native_persistent_string{ buff.data(), buff.size() }; + } + + void obj::transient_vector::to_string(fmt::memory_buffer &buff) const + { + auto inserter(std::back_inserter(buff)); + fmt::format_to(inserter, "{}@{}", magic_enum::enum_name(base.type), fmt::ptr(&base)); + } + + native_hash obj::transient_vector::to_hash() const + { + /* Hash is also based only on identity. Clojure uses default hashCode, which does the same. */ + return static_cast(reinterpret_cast(this)); + } + + size_t obj::transient_vector::count() const + { + assert_active(); + return data.size(); + } + + obj::transient_vector_ptr obj::transient_vector::cons_in_place(object_ptr const head) + { + assert_active(); + data.push_back(head); + return this; + } + + native_box obj::transient_vector::to_persistent() + { + assert_active(); + active = false; + return make_box(std::move(data)); + } + + object_ptr obj::transient_vector::call(object_ptr const o) const + { + assert_active(); + if(o->type == object_type::integer) + { + auto const i(expect_object(o)->data); + if(i < 0 || data.size() <= static_cast(i)) + { + return obj::nil::nil_const(); + } + + return data.at(i); + } + else + { + throw std::runtime_error{ fmt::format("key must be an integer; found {}", + runtime::detail::to_string(o)) }; + } + } + + object_ptr obj::transient_vector::call(object_ptr const o, object_ptr const fallback) const + { + assert_active(); + if(o->type == object_type::integer) + { + auto const i(expect_object(o)->data); + if(i < 0 || data.size() <= static_cast(i)) + { + return fallback; + } + + return data.at(i); + } + else + { + throw std::runtime_error{ fmt::format("key must be an integer; found {}", + runtime::detail::to_string(o)) }; + } + } + + void obj::transient_vector::assert_active() const + { + if(!active) + { + throw std::runtime_error{ "transient used after it's been made persistent" }; + } + } +} From 7f750fc71c2c7ce0935dfb1dba4c956763ddd27f Mon Sep 17 00:00:00 2001 From: Saket Date: Sun, 10 Mar 2024 00:04:21 +0530 Subject: [PATCH 04/13] refactors and review comments --- CMakeLists.txt | 1 + .../jank/runtime/obj/persistent_vector.hpp | 9 +- .../cpp/jank/runtime/obj/transient_vector.hpp | 7 +- src/cpp/jank/evaluate.cpp | 3 +- src/cpp/jank/runtime/behavior/callable.cpp | 2 + src/cpp/jank/runtime/obj/transient_vector.cpp | 84 +++++++++++++++---- 6 files changed, 86 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7d2c8bfd..f9354c3c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -171,6 +171,7 @@ add_library( src/cpp/jank/runtime/obj/persistent_array_map.cpp src/cpp/jank/runtime/obj/persistent_hash_map.cpp src/cpp/jank/runtime/obj/transient_hash_map.cpp + src/cpp/jank/runtime/obj/transient_vector.cpp src/cpp/jank/runtime/obj/persistent_set.cpp src/cpp/jank/runtime/obj/persistent_string.cpp src/cpp/jank/runtime/obj/cons.cpp diff --git a/include/cpp/jank/runtime/obj/persistent_vector.hpp b/include/cpp/jank/runtime/obj/persistent_vector.hpp index 7557c0a2..d49d3126 100644 --- a/include/cpp/jank/runtime/obj/persistent_vector.hpp +++ b/include/cpp/jank/runtime/obj/persistent_vector.hpp @@ -7,9 +7,6 @@ namespace jank::runtime { namespace obj { - using persistent_vector = static_object; - using persistent_vector_ptr = native_box; - using transient_vector = static_object; using transient_vector_ptr = native_box; } @@ -69,4 +66,10 @@ namespace jank::runtime option meta; mutable native_hash hash{}; }; + + namespace obj + { + using persistent_vector = static_object; + using persistent_vector_ptr = native_box; + } } diff --git a/include/cpp/jank/runtime/obj/transient_vector.hpp b/include/cpp/jank/runtime/obj/transient_vector.hpp index 33e31845..0b0f32d5 100644 --- a/include/cpp/jank/runtime/obj/transient_vector.hpp +++ b/include/cpp/jank/runtime/obj/transient_vector.hpp @@ -42,7 +42,12 @@ namespace jank::runtime /* behavior::callable */ object_ptr call(object_ptr) const; - object_ptr call(object_ptr, object_ptr) const; + + /* behavior::associatively_readable */ + object_ptr get(object_ptr idx) const; + object_ptr get(object_ptr idx, object_ptr fallback) const; + object_ptr get_entry(object_ptr key) const; + native_bool contains(object_ptr key) const; void assert_active() const; diff --git a/src/cpp/jank/evaluate.cpp b/src/cpp/jank/evaluate.cpp index 7548436d..36f4a165 100644 --- a/src/cpp/jank/evaluate.cpp +++ b/src/cpp/jank/evaluate.cpp @@ -244,7 +244,8 @@ namespace jank::evaluate } } } - else if constexpr(std::same_as) + else if constexpr(std::same_as + || std::same_as) { auto const s(expr.arg_exprs.size()); if(s != 1) diff --git a/src/cpp/jank/runtime/behavior/callable.cpp b/src/cpp/jank/runtime/behavior/callable.cpp index 168d049c..6be50d3d 100644 --- a/src/cpp/jank/runtime/behavior/callable.cpp +++ b/src/cpp/jank/runtime/behavior/callable.cpp @@ -1,3 +1,4 @@ +#include "jank/runtime/obj/persistent_vector.hpp" #include #include @@ -69,6 +70,7 @@ namespace jank::runtime else if constexpr(std::same_as || std::same_as || std::same_as + || std::same_as || std::same_as) { return typed_source->call(a1); diff --git a/src/cpp/jank/runtime/obj/transient_vector.cpp b/src/cpp/jank/runtime/obj/transient_vector.cpp index dd2f7481..86650703 100644 --- a/src/cpp/jank/runtime/obj/transient_vector.cpp +++ b/src/cpp/jank/runtime/obj/transient_vector.cpp @@ -1,10 +1,10 @@ -#include "jank/runtime/detail/object_util.hpp" -#include "jank/runtime/erasure.hpp" -#include "jank/type.hpp" +#include +#include +#include #include #include #include -#include +#include namespace jank::runtime { @@ -65,46 +65,100 @@ namespace jank::runtime { assert_active(); active = false; - return make_box(std::move(data)); + return make_box(data.persistent()); } - object_ptr obj::transient_vector::call(object_ptr const o) const + object_ptr obj::transient_vector::call(object_ptr const idx) const { assert_active(); - if(o->type == object_type::integer) + if(idx->type == object_type::integer) { - auto const i(expect_object(o)->data); + auto const i(expect_object(idx)->data); + if(i < 0 || data.size() <= static_cast(i)) + { + throw std::runtime_error{ + fmt::format("Index out of bound; index = {}, count = {}", i, count()) + }; + } + + return data[i]; + } + else + { + throw std::runtime_error{ fmt::format("key must be an integer; found {}", + runtime::detail::to_string(idx)) }; + } + } + + object_ptr obj::transient_vector::get(object_ptr idx) const + { + assert_active(); + if(idx->type == object_type::integer) + { + auto const i(expect_object(idx)->data); if(i < 0 || data.size() <= static_cast(i)) { return obj::nil::nil_const(); } - return data.at(i); + return data[i]; } else { throw std::runtime_error{ fmt::format("key must be an integer; found {}", - runtime::detail::to_string(o)) }; + runtime::detail::to_string(idx)) }; } } - object_ptr obj::transient_vector::call(object_ptr const o, object_ptr const fallback) const + object_ptr obj::transient_vector::get(object_ptr idx, object_ptr fallback) const { assert_active(); - if(o->type == object_type::integer) + if(idx->type == object_type::integer) { - auto const i(expect_object(o)->data); + auto const i(expect_object(idx)->data); if(i < 0 || data.size() <= static_cast(i)) { return fallback; } - return data.at(i); + return data[i]; } else { throw std::runtime_error{ fmt::format("key must be an integer; found {}", - runtime::detail::to_string(o)) }; + runtime::detail::to_string(idx)) }; + } + } + + object_ptr obj::transient_vector::get_entry(object_ptr const key) const + { + if(key->type == object_type::integer) + { + auto const i(expect_object(key)->data); + if(i < 0 || data.size() <= static_cast(i)) + { + return obj::nil::nil_const(); + } + /* TODO: Map entry type? */ + return make_box(key, data[i]); + } + else + { + throw std::runtime_error{ fmt::format("get_entry on a vector must be an integer; found {}", + runtime::detail::to_string(key)) }; + } + } + + native_bool obj::transient_vector::contains(object_ptr const key) const + { + if(key->type == object_type::integer) + { + auto const i(expect_object(key)->data); + return i >= 0 && static_cast(i) < data.size(); + } + else + { + return false; } } From d7be321bfdfb686083a3cf77835b755ffea43d8b Mon Sep 17 00:00:00 2001 From: Saket Date: Sun, 10 Mar 2024 11:10:30 +0530 Subject: [PATCH 05/13] refactor(transient-vector): consts and removes unnecessary includes --- .../cpp/jank/runtime/obj/transient_vector.hpp | 14 ++++---- src/cpp/jank/runtime/behavior/callable.cpp | 3 -- .../jank/runtime/obj/transient_hash_map.cpp | 4 +-- src/cpp/jank/runtime/obj/transient_vector.cpp | 32 +++++++------------ 4 files changed, 20 insertions(+), 33 deletions(-) diff --git a/include/cpp/jank/runtime/obj/transient_vector.hpp b/include/cpp/jank/runtime/obj/transient_vector.hpp index 0b0f32d5..12031ad8 100644 --- a/include/cpp/jank/runtime/obj/transient_vector.hpp +++ b/include/cpp/jank/runtime/obj/transient_vector.hpp @@ -1,7 +1,5 @@ #pragma once -#include - namespace jank::runtime { template <> @@ -13,7 +11,7 @@ namespace jank::runtime using persistent_type = static_object; static_object() = default; - static_object(static_object &&) = default; + static_object(static_object &&) noexcept = default; static_object(static_object const &) = default; static_object(detail::native_persistent_vector const &d); static_object(detail::native_persistent_vector &&d); @@ -41,13 +39,13 @@ namespace jank::runtime native_box to_persistent(); /* behavior::callable */ - object_ptr call(object_ptr) const; + object_ptr call(object_ptr const) const; /* behavior::associatively_readable */ - object_ptr get(object_ptr idx) const; - object_ptr get(object_ptr idx, object_ptr fallback) const; - object_ptr get_entry(object_ptr key) const; - native_bool contains(object_ptr key) const; + object_ptr get(object_ptr const idx) const; + object_ptr get(object_ptr const idx, object_ptr const fallback) const; + object_ptr get_entry(object_ptr const idx) const; + native_bool contains(object_ptr const elem) const; void assert_active() const; diff --git a/src/cpp/jank/runtime/behavior/callable.cpp b/src/cpp/jank/runtime/behavior/callable.cpp index 6be50d3d..59e7e989 100644 --- a/src/cpp/jank/runtime/behavior/callable.cpp +++ b/src/cpp/jank/runtime/behavior/callable.cpp @@ -1,6 +1,3 @@ -#include "jank/runtime/obj/persistent_vector.hpp" -#include - #include #include diff --git a/src/cpp/jank/runtime/obj/transient_hash_map.cpp b/src/cpp/jank/runtime/obj/transient_hash_map.cpp index 58d00268..97af6be5 100644 --- a/src/cpp/jank/runtime/obj/transient_hash_map.cpp +++ b/src/cpp/jank/runtime/obj/transient_hash_map.cpp @@ -5,7 +5,7 @@ namespace jank::runtime { obj::transient_hash_map::static_object(runtime::detail::native_persistent_hash_map &&d) - : data{ d.transient() } + : data{ std::move(d).transient() } { } @@ -15,7 +15,7 @@ namespace jank::runtime } obj::transient_hash_map::static_object(runtime::detail::native_transient_hash_map &&d) - : data{ d } + : data{ std::move(d) } { } diff --git a/src/cpp/jank/runtime/obj/transient_vector.cpp b/src/cpp/jank/runtime/obj/transient_vector.cpp index 86650703..370ac4e6 100644 --- a/src/cpp/jank/runtime/obj/transient_vector.cpp +++ b/src/cpp/jank/runtime/obj/transient_vector.cpp @@ -1,15 +1,7 @@ -#include -#include -#include -#include -#include -#include -#include - namespace jank::runtime { obj::transient_vector::static_object(runtime::detail::native_persistent_vector &&d) - : data{ d.transient() } + : data{ std::move(d).transient() } { } @@ -19,7 +11,7 @@ namespace jank::runtime } obj::transient_vector::static_object(runtime::detail::native_transient_vector &&d) - : data{ d } + : data{ std::move(d) } { } @@ -90,7 +82,7 @@ namespace jank::runtime } } - object_ptr obj::transient_vector::get(object_ptr idx) const + object_ptr obj::transient_vector::get(object_ptr const idx) const { assert_active(); if(idx->type == object_type::integer) @@ -110,7 +102,7 @@ namespace jank::runtime } } - object_ptr obj::transient_vector::get(object_ptr idx, object_ptr fallback) const + object_ptr obj::transient_vector::get(object_ptr const idx, object_ptr const fallback) const { assert_active(); if(idx->type == object_type::integer) @@ -130,30 +122,30 @@ namespace jank::runtime } } - object_ptr obj::transient_vector::get_entry(object_ptr const key) const + object_ptr obj::transient_vector::get_entry(object_ptr const idx) const { - if(key->type == object_type::integer) + if(idx->type == object_type::integer) { - auto const i(expect_object(key)->data); + auto const i(expect_object(idx)->data); if(i < 0 || data.size() <= static_cast(i)) { return obj::nil::nil_const(); } /* TODO: Map entry type? */ - return make_box(key, data[i]); + return make_box(idx, data[i]); } else { throw std::runtime_error{ fmt::format("get_entry on a vector must be an integer; found {}", - runtime::detail::to_string(key)) }; + runtime::detail::to_string(idx)) }; } } - native_bool obj::transient_vector::contains(object_ptr const key) const + native_bool obj::transient_vector::contains(object_ptr const elem) const { - if(key->type == object_type::integer) + if(elem->type == object_type::integer) { - auto const i(expect_object(key)->data); + auto const i(expect_object(elem)->data); return i >= 0 && static_cast(i) < data.size(); } else From b5c4338df4ac4f4b281860e3fdfc9c1ec36314ec Mon Sep 17 00:00:00 2001 From: Saket Date: Sat, 16 Mar 2024 13:37:20 +0530 Subject: [PATCH 06/13] adds conj!, assoc! and dissoc! for transients --- include/cpp/jank/prelude.hpp | 1 + .../behavior/associatively_writable.hpp | 2 +- .../jank/runtime/obj/transient_hash_map.hpp | 3 +- .../jank/runtime/obj/transient_hash_map.cpp | 7 ++ src/jank/clojure/core.jank | 81 +++++++++++++++++++ 5 files changed, 92 insertions(+), 2 deletions(-) diff --git a/include/cpp/jank/prelude.hpp b/include/cpp/jank/prelude.hpp index 40a6a22e..cf24402e 100644 --- a/include/cpp/jank/prelude.hpp +++ b/include/cpp/jank/prelude.hpp @@ -28,3 +28,4 @@ #include #include #include +#include diff --git a/include/cpp/jank/runtime/behavior/associatively_writable.hpp b/include/cpp/jank/runtime/behavior/associatively_writable.hpp index 8bf45646..b0099eaa 100644 --- a/include/cpp/jank/runtime/behavior/associatively_writable.hpp +++ b/include/cpp/jank/runtime/behavior/associatively_writable.hpp @@ -14,7 +14,7 @@ namespace jank::runtime::behavior template concept associatively_writable_in_place = requires(T * const t) { { - t->assoc_in_place(object_ptr{}, object_ptr{}) + t->assoc_in_place(object_ptr{}, object_ptr{}), t->dissoc_in_place(object_ptr{}) } -> std::convertible_to; }; } diff --git a/include/cpp/jank/runtime/obj/transient_hash_map.hpp b/include/cpp/jank/runtime/obj/transient_hash_map.hpp index b0d6f12b..c992f5a8 100644 --- a/include/cpp/jank/runtime/obj/transient_hash_map.hpp +++ b/include/cpp/jank/runtime/obj/transient_hash_map.hpp @@ -41,7 +41,8 @@ namespace jank::runtime native_bool contains(object_ptr key) const; /* behavior::associatively_writable_in_place */ - native_box assoc_in_place(object_ptr key, object_ptr val); + native_box assoc_in_place(object_ptr const key, object_ptr const val); + native_box dissoc_in_place(object_ptr const key); /* behavior::consable_in_place */ native_box cons_in_place(object_ptr head); diff --git a/src/cpp/jank/runtime/obj/transient_hash_map.cpp b/src/cpp/jank/runtime/obj/transient_hash_map.cpp index 97af6be5..d0bf7aa6 100644 --- a/src/cpp/jank/runtime/obj/transient_hash_map.cpp +++ b/src/cpp/jank/runtime/obj/transient_hash_map.cpp @@ -97,6 +97,13 @@ namespace jank::runtime return this; } + obj::transient_hash_map_ptr obj::transient_hash_map::dissoc_in_place(object_ptr const key) + { + assert_active(); + data.erase(key); + return this; + } + obj::transient_hash_map_ptr obj::transient_hash_map::cons_in_place(object_ptr const head) { assert_active(); diff --git a/src/jank/clojure/core.jank b/src/jank/clojure/core.jank index 7c3a53c3..a746022c 100644 --- a/src/jank/clojure/core.jank +++ b/src/jank/clojure/core.jank @@ -431,6 +431,87 @@ #{ o }# );")) +(def conj!) +(def conj! + (fn* conj! + ([] + (transient [])) + ([coll] + coll) + ([coll x] + (native/raw "__value = visit_object + ( + [&](auto const typed_t, auto const head) -> object_ptr + { + using T = typename decltype(typed_t)::value_type; + + if constexpr(behavior::persistentable) + { + return typed_t->cons_in_place(head); + } + else + { throw #{ (ex-info :not-persistentable {:o coll}) }#; } + }, + #{ coll }#, + #{ x }# + ); + ")))) + +(def assoc!) +(def assoc! + (fn* assoc! + ([coll k v] + (native/raw "__value = visit_object + ( + [&](auto const typed_t, auto const key, auto const val) -> object_ptr + { + using T = typename decltype(typed_t)::value_type; + + if constexpr(behavior::associatively_writable_in_place) + { + return typed_t->assoc_in_place(key, val); + } + else + { throw #{ (ex-info :not-associatively_writable_in_place {:o coll}) }#; } + }, + #{ coll }#, + #{ k }#, + #{ v }# + ); + ")) + ([coll k v & kvs] + (let* [ret (assoc! coll k v)] + (if kvs + (recur ret (first kvs) (second kvs) (nnext kvs)) + ret))))) + +(def dissoc!) +(def dissoc! + (fn* dissoc! + ([coll k] + (native/raw "__value = visit_object + ( + [&](auto const typed_t, auto const key) -> object_ptr + { + using T = typename decltype(typed_t)::value_type; + + if constexpr(behavior::associatively_writable_in_place) + { + return typed_t->dissoc_in_place(key); + } + else + { throw #{ (ex-info :not-associatively_writable_in_place {:o coll}) }#; } + }, + #{ coll }#, + #{ k }# + ); + ")) + ([coll k & ks] + (let* [ret (dissoc! coll k)] + (if ks + (recur ret (first ks) (next ks)) + ret))))) + ; Functions. (defn- spread [arglist] From 93133df018c4b6dee7634ed2cb3685b148c24992 Mon Sep 17 00:00:00 2001 From: Saket Date: Sun, 17 Mar 2024 01:17:39 +0530 Subject: [PATCH 07/13] feat(transient-vector): adds pop! to core.jank --- .../cpp/jank/runtime/obj/transient_vector.hpp | 2 ++ src/cpp/jank/runtime/obj/transient_vector.cpp | 13 ++++++++++++ src/jank/clojure/core.jank | 21 +++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/include/cpp/jank/runtime/obj/transient_vector.hpp b/include/cpp/jank/runtime/obj/transient_vector.hpp index 12031ad8..6fced4c2 100644 --- a/include/cpp/jank/runtime/obj/transient_vector.hpp +++ b/include/cpp/jank/runtime/obj/transient_vector.hpp @@ -23,6 +23,8 @@ namespace jank::runtime return ret; } + native_box pop(); + /* behavior::objectable */ native_bool equal(object const &) const; native_persistent_string to_string() const; diff --git a/src/cpp/jank/runtime/obj/transient_vector.cpp b/src/cpp/jank/runtime/obj/transient_vector.cpp index 370ac4e6..87e85271 100644 --- a/src/cpp/jank/runtime/obj/transient_vector.cpp +++ b/src/cpp/jank/runtime/obj/transient_vector.cpp @@ -40,6 +40,19 @@ namespace jank::runtime return static_cast(reinterpret_cast(this)); } + obj::transient_vector_ptr obj::transient_vector::pop() + { + assert_active(); + if(data.empty()) + { + throw std::runtime_error{ "Can't pop empty vector" }; + } + + data.take(data.size() - 1); + + return this; + } + size_t obj::transient_vector::count() const { assert_active(); diff --git a/src/jank/clojure/core.jank b/src/jank/clojure/core.jank index a746022c..14a54a80 100644 --- a/src/jank/clojure/core.jank +++ b/src/jank/clojure/core.jank @@ -512,6 +512,27 @@ (recur ret (first ks) (next ks)) ret))))) +(def pop!) +(def pop! + (fn* pop! + ([coll] + (native/raw "__value = visit_object + ( + [&](auto const typed_t) -> object_ptr + { + using T = typename decltype(typed_t)::value_type; + + if constexpr(std::same_as) + { + return typed_t->pop(); + } + else + { throw #{ (ex-info :not-transient-vector {:o coll}) }#; } + }, + #{ coll }# + ); + ")))) + ; Functions. (defn- spread [arglist] From a3af729d79db1c62dd1851ac275428020d8202ae Mon Sep 17 00:00:00 2001 From: Saket Date: Mon, 18 Mar 2024 10:20:57 +0530 Subject: [PATCH 08/13] rename pop to pop_in_place and formatting fix --- .../cpp/jank/runtime/obj/transient_vector.hpp | 2 +- src/cpp/jank/runtime/obj/transient_vector.cpp | 7 +- src/jank/clojure/core.jank | 178 ++++++++---------- 3 files changed, 84 insertions(+), 103 deletions(-) diff --git a/include/cpp/jank/runtime/obj/transient_vector.hpp b/include/cpp/jank/runtime/obj/transient_vector.hpp index 6fced4c2..efe9e70c 100644 --- a/include/cpp/jank/runtime/obj/transient_vector.hpp +++ b/include/cpp/jank/runtime/obj/transient_vector.hpp @@ -23,7 +23,7 @@ namespace jank::runtime return ret; } - native_box pop(); + native_box pop_in_place(); /* behavior::objectable */ native_bool equal(object const &) const; diff --git a/src/cpp/jank/runtime/obj/transient_vector.cpp b/src/cpp/jank/runtime/obj/transient_vector.cpp index 87e85271..0d312acb 100644 --- a/src/cpp/jank/runtime/obj/transient_vector.cpp +++ b/src/cpp/jank/runtime/obj/transient_vector.cpp @@ -40,16 +40,13 @@ namespace jank::runtime return static_cast(reinterpret_cast(this)); } - obj::transient_vector_ptr obj::transient_vector::pop() + obj::transient_vector_ptr obj::transient_vector::pop_in_place() { assert_active(); if(data.empty()) - { - throw std::runtime_error{ "Can't pop empty vector" }; - } + { throw std::runtime_error{ "Can't pop empty vector" }; } data.take(data.size() - 1); - return this; } diff --git a/src/jank/clojure/core.jank b/src/jank/clojure/core.jank index 14a54a80..1b400eff 100644 --- a/src/jank/clojure/core.jank +++ b/src/jank/clojure/core.jank @@ -431,107 +431,91 @@ #{ o }# );")) -(def conj!) -(def conj! - (fn* conj! - ([] - (transient [])) - ([coll] - coll) - ([coll x] - (native/raw "__value = visit_object - ( - [&](auto const typed_t, auto const head) -> object_ptr - { - using T = typename decltype(typed_t)::value_type; +(defn conj! + ([] + (transient [])) + ([coll] + coll) + ([coll x] + (native/raw "__value = visit_object + ( + [=](auto const typed_t, auto const head) -> object_ptr + { + using T = typename decltype(typed_t)::value_type; - if constexpr(behavior::persistentable) - { - return typed_t->cons_in_place(head); - } - else - { throw #{ (ex-info :not-persistentable {:o coll}) }#; } - }, - #{ coll }#, - #{ x }# - ); - ")))) - -(def assoc!) -(def assoc! - (fn* assoc! - ([coll k v] - (native/raw "__value = visit_object - ( - [&](auto const typed_t, auto const key, auto const val) -> object_ptr - { - using T = typename decltype(typed_t)::value_type; + if constexpr(behavior::persistentable) + { return typed_t->cons_in_place(head); } + else + { throw #{ (ex-info :not-persistentable {:o coll}) }#; } + }, + #{ coll }#, + #{ x }# + ); + "))) - if constexpr(behavior::associatively_writable_in_place) - { - return typed_t->assoc_in_place(key, val); - } - else - { throw #{ (ex-info :not-associatively_writable_in_place {:o coll}) }#; } - }, - #{ coll }#, - #{ k }#, - #{ v }# - ); - ")) - ([coll k v & kvs] - (let* [ret (assoc! coll k v)] - (if kvs - (recur ret (first kvs) (second kvs) (nnext kvs)) - ret))))) - -(def dissoc!) -(def dissoc! - (fn* dissoc! - ([coll k] - (native/raw "__value = visit_object - ( - [&](auto const typed_t, auto const key) -> object_ptr - { - using T = typename decltype(typed_t)::value_type; +(defn assoc! + ([coll k v] + (native/raw "__value = visit_object + ( + [=](auto const typed_t, auto const key, auto const val) -> object_ptr + { + using T = typename decltype(typed_t)::value_type; - if constexpr(behavior::associatively_writable_in_place) - { - return typed_t->dissoc_in_place(key); - } - else - { throw #{ (ex-info :not-associatively_writable_in_place {:o coll}) }#; } - }, - #{ coll }#, - #{ k }# - ); - ")) - ([coll k & ks] - (let* [ret (dissoc! coll k)] - (if ks - (recur ret (first ks) (next ks)) - ret))))) - -(def pop!) -(def pop! - (fn* pop! - ([coll] - (native/raw "__value = visit_object - ( - [&](auto const typed_t) -> object_ptr - { - using T = typename decltype(typed_t)::value_type; + if constexpr(behavior::associatively_writable_in_place) + { return typed_t->assoc_in_place(key, val); } + else + { throw #{ (ex-info :not-associatively_writable_in_place {:o coll}) }#; } + }, + #{ coll }#, + #{ k }#, + #{ v }# + ); + ")) + ([coll k v & kvs] + (let [ret (assoc! coll k v)] + (if kvs + (recur ret (first kvs) (second kvs) (nnext kvs)) + ret)))) + +(defn dissoc! + ([coll k] + (native/raw "__value = visit_object + ( + [=](auto const typed_t, auto const key) -> object_ptr + { + using T = typename decltype(typed_t)::value_type; - if constexpr(std::same_as) - { - return typed_t->pop(); - } - else - { throw #{ (ex-info :not-transient-vector {:o coll}) }#; } - }, - #{ coll }# - ); - ")))) + if constexpr(behavior::associatively_writable_in_place) + { return typed_t->dissoc_in_place(key); } + else + { throw #{ (ex-info :not-associatively_writable_in_place {:o coll}) }#; } + }, + #{ coll }#, + #{ k }# + ); + ")) + ([coll k & ks] + (let [ret (dissoc! coll k)] + (if ks + (recur ret (first ks) (next ks)) + ret)))) + +(defn pop! + ([coll] + (native/raw "__value = visit_object + ( + [=](auto const typed_t) -> object_ptr + { + using T = typename decltype(typed_t)::value_type; + + if constexpr(std::same_as) + { return typed_t->pop_in_place(); } + else + { throw #{ (ex-info :not-transient-vector {:o coll}) }#; } + }, + #{ coll }# + ); + "))) ; Functions. From fcf48b2d97de95f6200140cad2d3eca6160fc523 Mon Sep 17 00:00:00 2001 From: Saket Date: Fri, 22 Mar 2024 22:07:31 +0530 Subject: [PATCH 09/13] addresses comments for refactoring and fixes --- include/cpp/jank/prelude.hpp | 1 + .../behavior/associatively_writable.hpp | 6 +- .../cpp/jank/runtime/obj/transient_vector.hpp | 4 +- src/cpp/jank/runtime/obj/transient_vector.cpp | 22 ++++---- src/jank/clojure/core.jank | 56 +++++++++---------- 5 files changed, 45 insertions(+), 44 deletions(-) diff --git a/include/cpp/jank/prelude.hpp b/include/cpp/jank/prelude.hpp index cf24402e..82688012 100644 --- a/include/cpp/jank/prelude.hpp +++ b/include/cpp/jank/prelude.hpp @@ -28,4 +28,5 @@ #include #include #include +#include #include diff --git a/include/cpp/jank/runtime/behavior/associatively_writable.hpp b/include/cpp/jank/runtime/behavior/associatively_writable.hpp index b0099eaa..4dd9a1b1 100644 --- a/include/cpp/jank/runtime/behavior/associatively_writable.hpp +++ b/include/cpp/jank/runtime/behavior/associatively_writable.hpp @@ -14,7 +14,11 @@ namespace jank::runtime::behavior template concept associatively_writable_in_place = requires(T * const t) { { - t->assoc_in_place(object_ptr{}, object_ptr{}), t->dissoc_in_place(object_ptr{}) + t->assoc_in_place(object_ptr{}, object_ptr{}) + } -> std::convertible_to; + + { + t->dissoc_in_place(object_ptr{}) } -> std::convertible_to; }; } diff --git a/include/cpp/jank/runtime/obj/transient_vector.hpp b/include/cpp/jank/runtime/obj/transient_vector.hpp index efe9e70c..11632bdb 100644 --- a/include/cpp/jank/runtime/obj/transient_vector.hpp +++ b/include/cpp/jank/runtime/obj/transient_vector.hpp @@ -23,8 +23,6 @@ namespace jank::runtime return ret; } - native_box pop_in_place(); - /* behavior::objectable */ native_bool equal(object const &) const; native_persistent_string to_string() const; @@ -49,6 +47,8 @@ namespace jank::runtime object_ptr get_entry(object_ptr const idx) const; native_bool contains(object_ptr const elem) const; + native_box pop_in_place(); + void assert_active() const; object base{ object_type::transient_vector }; diff --git a/src/cpp/jank/runtime/obj/transient_vector.cpp b/src/cpp/jank/runtime/obj/transient_vector.cpp index 0d312acb..86cb302e 100644 --- a/src/cpp/jank/runtime/obj/transient_vector.cpp +++ b/src/cpp/jank/runtime/obj/transient_vector.cpp @@ -40,16 +40,6 @@ namespace jank::runtime return static_cast(reinterpret_cast(this)); } - obj::transient_vector_ptr obj::transient_vector::pop_in_place() - { - assert_active(); - if(data.empty()) - { throw std::runtime_error{ "Can't pop empty vector" }; } - - data.take(data.size() - 1); - return this; - } - size_t obj::transient_vector::count() const { assert_active(); @@ -164,6 +154,18 @@ namespace jank::runtime } } + obj::transient_vector_ptr obj::transient_vector::pop_in_place() + { + assert_active(); + if(data.empty()) + { + throw std::runtime_error{ "Can't pop empty vector" }; + } + + data.take(data.size() - 1); + return this; + } + void obj::transient_vector::assert_active() const { if(!active) diff --git a/src/jank/clojure/core.jank b/src/jank/clojure/core.jank index 1e9035dd..f12c5216 100644 --- a/src/jank/clojure/core.jank +++ b/src/jank/clojure/core.jank @@ -408,9 +408,9 @@ if constexpr(behavior::transientable) { return typed_o->to_transient(); } else - { throw #{ (ex-info :not-transientable {:o o}) }#; } + { throw ~{ (ex-info :not-transientable {:o o}) }; } }, - #{ o }# + ~{ o } );")) ; Returns a new, persistent version of the transient collection, in @@ -426,9 +426,9 @@ if constexpr(behavior::persistentable) { return typed_o->to_persistent(); } else - { throw #{ (ex-info :not-persistentable {:o o}) }#; } + { throw ~{ (ex-info :not-persistentable {:o o}) }; } }, - #{ o }# + ~{ o } );")) (defn conj! @@ -439,17 +439,17 @@ ([coll x] (native/raw "__value = visit_object ( - [=](auto const typed_t, auto const head) -> object_ptr + [=](auto const typed_coll, auto const head) -> object_ptr { - using T = typename decltype(typed_t)::value_type; + using T = typename decltype(typed_coll)::value_type; - if constexpr(behavior::persistentable) - { return typed_t->cons_in_place(head); } + if constexpr(behavior::consable_in_place) + { return typed_coll->cons_in_place(head); } else - { throw #{ (ex-info :not-persistentable {:o coll}) }#; } + { throw ~{ (ex-info :not-persistentable {:o coll}) }; } }, - #{ coll }#, - #{ x }# + ~{ coll }, + ~{ x } ); "))) @@ -464,11 +464,11 @@ if constexpr(behavior::associatively_writable_in_place) { return typed_t->assoc_in_place(key, val); } else - { throw #{ (ex-info :not-associatively_writable_in_place {:o coll}) }#; } + { throw ~{ (ex-info :not-associatively_writable_in_place {:o coll}) }; } }, - #{ coll }#, - #{ k }#, - #{ v }# + ~{ coll }, + ~{ k }, + ~{ v } ); ")) ([coll k v & kvs] @@ -488,10 +488,10 @@ if constexpr(behavior::associatively_writable_in_place) { return typed_t->dissoc_in_place(key); } else - { throw #{ (ex-info :not-associatively_writable_in_place {:o coll}) }#; } + { throw ~{ (ex-info :not-associatively_writable_in_place {:o coll}) }; } }, - #{ coll }#, - #{ k }# + ~{ coll }, + ~{ k } ); ")) ([coll k & ks] @@ -502,19 +502,13 @@ (defn pop! ([coll] - (native/raw "__value = visit_object - ( - [=](auto const typed_t) -> object_ptr - { - using T = typename decltype(typed_t)::value_type; - - if constexpr(std::same_as) - { return typed_t->pop_in_place(); } - else - { throw #{ (ex-info :not-transient-vector {:o coll}) }#; } - }, - #{ coll }# - ); + (native/raw "if( ~{ coll }->type == object_type::transient_vector ) + { + auto typed_coll = expect_object(~{ coll }); + __value = typed_coll->pop_in_place(); + } + else + { throw ~{ (ex-info :not-transient-vector {:o coll}) }; } "))) ; Functions. From 8ab39cecb9808a4dfcf94e1869239fe52410f855 Mon Sep 17 00:00:00 2001 From: Saket Date: Fri, 22 Mar 2024 22:07:31 +0530 Subject: [PATCH 10/13] addresses comments for refactoring and fixes --- include/cpp/jank/prelude.hpp | 1 + .../behavior/associatively_writable.hpp | 6 +- .../cpp/jank/runtime/obj/transient_vector.hpp | 4 +- src/cpp/jank/runtime/obj/transient_vector.cpp | 22 +++--- src/jank/clojure/core.jank | 68 +++++++++---------- 5 files changed, 51 insertions(+), 50 deletions(-) diff --git a/include/cpp/jank/prelude.hpp b/include/cpp/jank/prelude.hpp index cf24402e..82688012 100644 --- a/include/cpp/jank/prelude.hpp +++ b/include/cpp/jank/prelude.hpp @@ -28,4 +28,5 @@ #include #include #include +#include #include diff --git a/include/cpp/jank/runtime/behavior/associatively_writable.hpp b/include/cpp/jank/runtime/behavior/associatively_writable.hpp index b0099eaa..4dd9a1b1 100644 --- a/include/cpp/jank/runtime/behavior/associatively_writable.hpp +++ b/include/cpp/jank/runtime/behavior/associatively_writable.hpp @@ -14,7 +14,11 @@ namespace jank::runtime::behavior template concept associatively_writable_in_place = requires(T * const t) { { - t->assoc_in_place(object_ptr{}, object_ptr{}), t->dissoc_in_place(object_ptr{}) + t->assoc_in_place(object_ptr{}, object_ptr{}) + } -> std::convertible_to; + + { + t->dissoc_in_place(object_ptr{}) } -> std::convertible_to; }; } diff --git a/include/cpp/jank/runtime/obj/transient_vector.hpp b/include/cpp/jank/runtime/obj/transient_vector.hpp index efe9e70c..11632bdb 100644 --- a/include/cpp/jank/runtime/obj/transient_vector.hpp +++ b/include/cpp/jank/runtime/obj/transient_vector.hpp @@ -23,8 +23,6 @@ namespace jank::runtime return ret; } - native_box pop_in_place(); - /* behavior::objectable */ native_bool equal(object const &) const; native_persistent_string to_string() const; @@ -49,6 +47,8 @@ namespace jank::runtime object_ptr get_entry(object_ptr const idx) const; native_bool contains(object_ptr const elem) const; + native_box pop_in_place(); + void assert_active() const; object base{ object_type::transient_vector }; diff --git a/src/cpp/jank/runtime/obj/transient_vector.cpp b/src/cpp/jank/runtime/obj/transient_vector.cpp index 0d312acb..86cb302e 100644 --- a/src/cpp/jank/runtime/obj/transient_vector.cpp +++ b/src/cpp/jank/runtime/obj/transient_vector.cpp @@ -40,16 +40,6 @@ namespace jank::runtime return static_cast(reinterpret_cast(this)); } - obj::transient_vector_ptr obj::transient_vector::pop_in_place() - { - assert_active(); - if(data.empty()) - { throw std::runtime_error{ "Can't pop empty vector" }; } - - data.take(data.size() - 1); - return this; - } - size_t obj::transient_vector::count() const { assert_active(); @@ -164,6 +154,18 @@ namespace jank::runtime } } + obj::transient_vector_ptr obj::transient_vector::pop_in_place() + { + assert_active(); + if(data.empty()) + { + throw std::runtime_error{ "Can't pop empty vector" }; + } + + data.take(data.size() - 1); + return this; + } + void obj::transient_vector::assert_active() const { if(!active) diff --git a/src/jank/clojure/core.jank b/src/jank/clojure/core.jank index 1e9035dd..c89a7101 100644 --- a/src/jank/clojure/core.jank +++ b/src/jank/clojure/core.jank @@ -408,9 +408,9 @@ if constexpr(behavior::transientable) { return typed_o->to_transient(); } else - { throw #{ (ex-info :not-transientable {:o o}) }#; } + { throw ~{ (ex-info :not-transientable {:o o}) }; } }, - #{ o }# + ~{ o } );")) ; Returns a new, persistent version of the transient collection, in @@ -426,9 +426,9 @@ if constexpr(behavior::persistentable) { return typed_o->to_persistent(); } else - { throw #{ (ex-info :not-persistentable {:o o}) }#; } + { throw ~{ (ex-info :not-persistentable {:o o}) }; } }, - #{ o }# + ~{ o } );")) (defn conj! @@ -439,17 +439,17 @@ ([coll x] (native/raw "__value = visit_object ( - [=](auto const typed_t, auto const head) -> object_ptr + [=](auto const typed_coll, auto const head) -> object_ptr { - using T = typename decltype(typed_t)::value_type; + using T = typename decltype(typed_coll)::value_type; - if constexpr(behavior::persistentable) - { return typed_t->cons_in_place(head); } + if constexpr(behavior::consable_in_place) + { return typed_coll->cons_in_place(head); } else - { throw #{ (ex-info :not-persistentable {:o coll}) }#; } + { throw ~{ (ex-info :not-persistentable {:o coll}) }; } }, - #{ coll }#, - #{ x }# + ~{ coll }, + ~{ x } ); "))) @@ -457,18 +457,18 @@ ([coll k v] (native/raw "__value = visit_object ( - [=](auto const typed_t, auto const key, auto const val) -> object_ptr + [=](auto const typed_coll, auto const key, auto const val) -> object_ptr { - using T = typename decltype(typed_t)::value_type; + using T = typename decltype(typed_coll)::value_type; if constexpr(behavior::associatively_writable_in_place) - { return typed_t->assoc_in_place(key, val); } + { return typed_coll->assoc_in_place(key, val); } else - { throw #{ (ex-info :not-associatively_writable_in_place {:o coll}) }#; } + { throw ~{ (ex-info :not-associatively_writable_in_place {:o coll}) }; } }, - #{ coll }#, - #{ k }#, - #{ v }# + ~{ coll }, + ~{ k }, + ~{ v } ); ")) ([coll k v & kvs] @@ -481,17 +481,17 @@ ([coll k] (native/raw "__value = visit_object ( - [=](auto const typed_t, auto const key) -> object_ptr + [=](auto const typed_coll, auto const key) -> object_ptr { - using T = typename decltype(typed_t)::value_type; + using T = typename decltype(typed_coll)::value_type; if constexpr(behavior::associatively_writable_in_place) - { return typed_t->dissoc_in_place(key); } + { return typed_coll->dissoc_in_place(key); } else - { throw #{ (ex-info :not-associatively_writable_in_place {:o coll}) }#; } + { throw ~{ (ex-info :not-associatively_writable_in_place {:o coll}) }; } }, - #{ coll }#, - #{ k }# + ~{ coll }, + ~{ k } ); ")) ([coll k & ks] @@ -502,19 +502,13 @@ (defn pop! ([coll] - (native/raw "__value = visit_object - ( - [=](auto const typed_t) -> object_ptr - { - using T = typename decltype(typed_t)::value_type; - - if constexpr(std::same_as) - { return typed_t->pop_in_place(); } - else - { throw #{ (ex-info :not-transient-vector {:o coll}) }#; } - }, - #{ coll }# - ); + (native/raw "if( ~{ coll }->type == object_type::transient_vector ) + { + auto transient_coll = expect_object(~{ coll }); + __value = transient_coll->pop_in_place(); + } + else + { throw ~{ (ex-info :not-transient-vector {:o coll}) }; } "))) ; Functions. From 0aa9e236376a0f1f1881244e364afec5072c8d63 Mon Sep 17 00:00:00 2001 From: Saket Date: Sat, 23 Mar 2024 00:26:38 +0530 Subject: [PATCH 11/13] feat(transient): adds transient_set and disj! fn --- CMakeLists.txt | 1 + include/cpp/jank/runtime/erasure.hpp | 6 + .../cpp/jank/runtime/obj/persistent_set.hpp | 9 ++ .../cpp/jank/runtime/obj/transient_set.hpp | 66 +++++++++ include/cpp/jank/runtime/object.hpp | 1 + src/cpp/jank/evaluate.cpp | 3 +- src/cpp/jank/runtime/behavior/callable.cpp | 2 + src/cpp/jank/runtime/obj/persistent_set.cpp | 8 +- src/cpp/jank/runtime/obj/transient_set.cpp | 127 ++++++++++++++++++ src/jank/clojure/core.jank | 17 +++ 10 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 include/cpp/jank/runtime/obj/transient_set.hpp create mode 100644 src/cpp/jank/runtime/obj/transient_set.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index c9fa0cf3..8f24e3be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -174,6 +174,7 @@ add_library( src/cpp/jank/runtime/obj/transient_hash_map.cpp src/cpp/jank/runtime/obj/transient_vector.cpp src/cpp/jank/runtime/obj/persistent_set.cpp + src/cpp/jank/runtime/obj/transient_set.cpp src/cpp/jank/runtime/obj/persistent_string.cpp src/cpp/jank/runtime/obj/cons.cpp src/cpp/jank/runtime/obj/range.cpp diff --git a/include/cpp/jank/runtime/erasure.hpp b/include/cpp/jank/runtime/erasure.hpp index 0cdda692..0fa5317c 100644 --- a/include/cpp/jank/runtime/erasure.hpp +++ b/include/cpp/jank/runtime/erasure.hpp @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -190,6 +191,11 @@ namespace jank::runtime return fn(expect_object(erased), std::forward(args)...); } break; + case object_type::transient_set: + { + return fn(expect_object(erased), std::forward(args)...); + } + break; case object_type::cons: { return fn(expect_object(erased), std::forward(args)...); diff --git a/include/cpp/jank/runtime/obj/persistent_set.hpp b/include/cpp/jank/runtime/obj/persistent_set.hpp index 19668916..1d06be23 100644 --- a/include/cpp/jank/runtime/obj/persistent_set.hpp +++ b/include/cpp/jank/runtime/obj/persistent_set.hpp @@ -5,6 +5,12 @@ namespace jank::runtime { + namespace obj + { + using transient_set = static_object; + using transient_set_ptr = native_box; + } + template <> struct static_object : gc { @@ -47,6 +53,9 @@ namespace jank::runtime /* behavior::callable */ object_ptr call(object_ptr) const; + /* behavior::transientable */ + obj::transient_set_ptr to_transient() const; + native_bool contains(object_ptr o) const; object base{ object_type::persistent_set }; diff --git a/include/cpp/jank/runtime/obj/transient_set.hpp b/include/cpp/jank/runtime/obj/transient_set.hpp new file mode 100644 index 00000000..d0f2f0d8 --- /dev/null +++ b/include/cpp/jank/runtime/obj/transient_set.hpp @@ -0,0 +1,66 @@ +#pragma once + +namespace jank::runtime +{ + template <> + struct static_object : gc + { + static constexpr bool pointer_free{ false }; + + using value_type = detail::native_transient_set; + using persistent_type = static_object; + + static_object() = default; + static_object(static_object &&) noexcept = default; + static_object(static_object const &) = default; + static_object(detail::native_persistent_set const &d); + static_object(detail::native_persistent_set &&d); + static_object(value_type &&d); + + static native_box empty() + { + static auto const ret(make_box()); + return ret; + } + + /* behavior::objectable */ + native_bool equal(object const &) const; + native_persistent_string to_string() const; + void to_string(fmt::memory_buffer &buff) const; + native_hash to_hash() const; + + /* behavior::countable */ + size_t count() const; + + /* behavior::consable_in_place */ + native_box cons_in_place(object_ptr head); + + /* behavior::persistentable */ + native_box to_persistent(); + + /* behavior::callable */ + object_ptr call(object_ptr const) const; + object_ptr call(object_ptr const, object_ptr const fallback) const; + + /* behavior::associatively_readable */ + object_ptr get(object_ptr const elem) const; + object_ptr get(object_ptr const elem, object_ptr const fallback) const; + object_ptr get_entry(object_ptr const elem) const; + native_bool contains(object_ptr const elem) const; + + native_box disjoin_in_place(object_ptr const elem); + + void assert_active() const; + + object base{ object_type::transient_set }; + value_type data; + mutable native_hash hash{}; + native_bool active{ true }; + }; + + namespace obj + { + using transient_set = static_object; + using transient_set_ptr = native_box; + } +} diff --git a/include/cpp/jank/runtime/object.hpp b/include/cpp/jank/runtime/object.hpp index 0a1e1953..b10afb76 100644 --- a/include/cpp/jank/runtime/object.hpp +++ b/include/cpp/jank/runtime/object.hpp @@ -23,6 +23,7 @@ namespace jank::runtime persistent_hash_map, persistent_hash_map_sequence, transient_hash_map, + transient_set, transient_vector, persistent_set, cons, diff --git a/src/cpp/jank/evaluate.cpp b/src/cpp/jank/evaluate.cpp index 36f4a165..ff9701a7 100644 --- a/src/cpp/jank/evaluate.cpp +++ b/src/cpp/jank/evaluate.cpp @@ -258,7 +258,8 @@ namespace jank::evaluate } else if constexpr(std::same_as || std::same_as - || std::same_as) + || std::same_as + || std::same_as) { auto const s(expr.arg_exprs.size()); switch(s) diff --git a/src/cpp/jank/runtime/behavior/callable.cpp b/src/cpp/jank/runtime/behavior/callable.cpp index 59e7e989..2ac7f650 100644 --- a/src/cpp/jank/runtime/behavior/callable.cpp +++ b/src/cpp/jank/runtime/behavior/callable.cpp @@ -68,6 +68,7 @@ namespace jank::runtime || std::same_as || std::same_as || std::same_as + || std::same_as || std::same_as) { return typed_source->call(a1); @@ -109,6 +110,7 @@ namespace jank::runtime } else if constexpr(std::same_as || std::same_as + || std::same_as || std::same_as) { return typed_source->call(a1, a2); diff --git a/src/cpp/jank/runtime/obj/persistent_set.cpp b/src/cpp/jank/runtime/obj/persistent_set.cpp index 7eca88bf..fd066f1f 100644 --- a/src/cpp/jank/runtime/obj/persistent_set.cpp +++ b/src/cpp/jank/runtime/obj/persistent_set.cpp @@ -1,6 +1,3 @@ -#include -#include - #include #include #include @@ -84,6 +81,11 @@ namespace jank::runtime return *found; } + obj::transient_set_ptr obj::persistent_set::to_transient() const + { + return make_box(data); + } + native_bool obj::persistent_set::contains(object_ptr const o) const { return data.find(o); diff --git a/src/cpp/jank/runtime/obj/transient_set.cpp b/src/cpp/jank/runtime/obj/transient_set.cpp new file mode 100644 index 00000000..0a7672e8 --- /dev/null +++ b/src/cpp/jank/runtime/obj/transient_set.cpp @@ -0,0 +1,127 @@ +namespace jank::runtime +{ + obj::transient_set::static_object(runtime::detail::native_persistent_set &&d) + : data{ std::move(d).transient() } + { + } + + obj::transient_set::static_object(runtime::detail::native_persistent_set const &d) + : data{ d.transient() } + { + } + + obj::transient_set::static_object(runtime::detail::native_transient_set &&d) + : data{ std::move(d) } + { + } + + native_bool obj::transient_set::equal(object const &o) const + { + /* Transient equality, in Clojure, is based solely on identity. */ + return &base == &o; + } + + native_persistent_string obj::transient_set::to_string() const + { + fmt::memory_buffer buff; + to_string(buff); + return native_persistent_string{ buff.data(), buff.size() }; + } + + void obj::transient_set::to_string(fmt::memory_buffer &buff) const + { + auto inserter(std::back_inserter(buff)); + fmt::format_to(inserter, "{}@{}", magic_enum::enum_name(base.type), fmt::ptr(&base)); + } + + native_hash obj::transient_set::to_hash() const + { + /* Hash is also based only on identity. Clojure uses default hashCode, which does the same. */ + return static_cast(reinterpret_cast(this)); + } + + size_t obj::transient_set::count() const + { + assert_active(); + return data.size(); + } + + obj::transient_set_ptr obj::transient_set::cons_in_place(object_ptr const elem) + { + assert_active(); + data.insert(elem); + return this; + } + + native_box obj::transient_set::to_persistent() + { + assert_active(); + active = false; + return make_box(data.persistent()); + } + + object_ptr obj::transient_set::call(object_ptr const elem) const + { + assert_active(); + auto const found(data.find(elem)); + if(!found) + { + return obj::nil::nil_const(); + } + return *found; + } + + object_ptr obj::transient_set::call(object_ptr const elem, object_ptr const fallback) const + { + assert_active(); + auto const found(data.find(elem)); + if(!found) + { + return fallback; + } + return *found; + } + + object_ptr obj::transient_set::get(object_ptr const elem) const + { + return call(elem); + } + + object_ptr obj::transient_set::get(object_ptr const elem, object_ptr const fallback) const + { + return call(elem, fallback); + } + + object_ptr obj::transient_set::get_entry(object_ptr const elem) const + { + auto const found = call(elem); + auto const nil(obj::nil::nil_const()); + if(found == nil) + { + return nil; + } + + return make_box(found, found); + } + + native_bool obj::transient_set::contains(object_ptr const elem) const + { + assert_active(); + return data.find(elem); + } + + obj::transient_set_ptr obj::transient_set::disjoin_in_place(object_ptr const elem) + { + assert_active(); + data.erase(elem); + return this; + } + + void obj::transient_set::assert_active() const + { + if(!active) + { + throw std::runtime_error{ "transient used after it's been made persistent" }; + } + } +} diff --git a/src/jank/clojure/core.jank b/src/jank/clojure/core.jank index f12c5216..4163148a 100644 --- a/src/jank/clojure/core.jank +++ b/src/jank/clojure/core.jank @@ -511,6 +511,23 @@ { throw ~{ (ex-info :not-transient-vector {:o coll}) }; } "))) +(defn disj! + ([set] set) + ([set elem] + (native/raw "if( ~{ set }->type == object_type::transient_set) + { + auto typed_set = expect_object(~{ set }); + __value = typed_set->disjoin_in_place(~{ elem }); + } + else + { throw ~{ (ex-info :not-transient-set {:o set}) }; } + ")) + ([set elem & elems] + (let [ret (disj! set elem)] + (if elems + (recur ret (first elems) (next elems)) + ret)))) + ; Functions. (defn- spread [arglist] From b25c90189d5561d8d76c0b36e1a57a527c39d09e Mon Sep 17 00:00:00 2001 From: Saket Date: Sat, 23 Mar 2024 00:47:25 +0530 Subject: [PATCH 12/13] copypasta typo fix --- src/jank/clojure/core.jank | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/jank/clojure/core.jank b/src/jank/clojure/core.jank index d3f67b34..db5ffe32 100644 --- a/src/jank/clojure/core.jank +++ b/src/jank/clojure/core.jank @@ -438,7 +438,7 @@ if constexpr(behavior::consable_in_place) { return typed_coll->cons_in_place(head); } else - { throw ~{ (ex-info :not-persistentable {:o coll}) }; } + { throw ~{ (ex-info :not-consable-in-place {:o coll}) }; } }, ~{ coll }, ~{ x } From b1e56e58afcd4a318866b62f3d21366e517426b7 Mon Sep 17 00:00:00 2001 From: Saket Date: Sat, 23 Mar 2024 00:55:51 +0530 Subject: [PATCH 13/13] fix lint --- src/cpp/jank/runtime/behavior/callable.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/cpp/jank/runtime/behavior/callable.cpp b/src/cpp/jank/runtime/behavior/callable.cpp index 2ac7f650..2ece01da 100644 --- a/src/cpp/jank/runtime/behavior/callable.cpp +++ b/src/cpp/jank/runtime/behavior/callable.cpp @@ -68,8 +68,7 @@ namespace jank::runtime || std::same_as || std::same_as || std::same_as - || std::same_as - || std::same_as) + || std::same_as || std::same_as) { return typed_source->call(a1); } @@ -110,8 +109,7 @@ namespace jank::runtime } else if constexpr(std::same_as || std::same_as - || std::same_as - || std::same_as) + || std::same_as || std::same_as) { return typed_source->call(a1, a2); }