From fcf48b2d97de95f6200140cad2d3eca6160fc523 Mon Sep 17 00:00:00 2001 From: Saket Date: Fri, 22 Mar 2024 22:07:31 +0530 Subject: [PATCH 1/3] 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 cf24402e2..826880123 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 b0099eaa6..4dd9a1b12 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 efe9e70c5..11632bdb1 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 0d312acbf..86cb302e1 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 1e9035dd7..f12c5216e 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 0aa9e236376a0f1f1881244e364afec5072c8d63 Mon Sep 17 00:00:00 2001 From: Saket Date: Sat, 23 Mar 2024 00:26:38 +0530 Subject: [PATCH 2/3] 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 c9fa0cf38..8f24e3be3 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 0cdda692c..0fa5317c7 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 196689161..1d06be237 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 000000000..d0f2f0d87 --- /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 0a1e19531..b10afb762 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 36f4a1652..ff9701a78 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 59e7e989b..2ac7f6505 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 7eca88bfe..fd066f1fe 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 000000000..0a7672e81 --- /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 f12c5216e..4163148aa 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 b1e56e58afcd4a318866b62f3d21366e517426b7 Mon Sep 17 00:00:00 2001 From: Saket Date: Sat, 23 Mar 2024 00:55:51 +0530 Subject: [PATCH 3/3] 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 2ac7f6505..2ece01da2 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); }