Skip to content

Commit

Permalink
Add initial transient support
Browse files Browse the repository at this point in the history
This includes transient_hash_map and the core `transient` and
`persistent!` fns.
  • Loading branch information
jeaye committed Feb 17, 2024
1 parent 46ea760 commit 7cad956
Show file tree
Hide file tree
Showing 14 changed files with 269 additions and 4 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions include/cpp/jank/prelude.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,4 @@
#include <jank/runtime/seq.hpp>
#include <jank/runtime/behavior/numberable.hpp>
#include <jank/runtime/behavior/nameable.hpp>
#include <jank/runtime/behavior/transientable.hpp>
7 changes: 7 additions & 0 deletions include/cpp/jank/runtime/behavior/associatively_writable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,11 @@ namespace jank::runtime::behavior
t->assoc(object_ptr{}, object_ptr{})
} -> std::convertible_to<object_ptr>;
};

template <typename T>
concept associatively_writable_in_place = requires(T * const t) {
{
t->assoc_in_place(object_ptr{}, object_ptr{})
} -> std::convertible_to<object_ptr>;
};
}
7 changes: 7 additions & 0 deletions include/cpp/jank/runtime/behavior/consable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,11 @@ namespace jank::runtime::behavior
t->cons(object_ptr{})
}; // -> consable
};

template <typename T>
concept consable_in_place = requires(T * const t) {
{
t->cons_in_place(object_ptr{})
}; // -> consable_in_place
};
}
18 changes: 18 additions & 0 deletions include/cpp/jank/runtime/behavior/transientable.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once

namespace jank::runtime::behavior
{
template <typename T>
concept persistentable = requires(T * const t) {
{
t->to_persistent()
} -> std::convertible_to<object_ptr>;
};

template <typename T>
concept transientable = requires(T * const t) {
{
t->to_transient()
} -> std::convertible_to<object_ptr>;
};
}
6 changes: 6 additions & 0 deletions include/cpp/jank/runtime/erasure.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
#include <jank/runtime/obj/persistent_array_map_sequence.hpp>
#include <jank/runtime/obj/persistent_hash_map.hpp>
#include <jank/runtime/obj/persistent_hash_map_sequence.hpp>
#include <jank/runtime/obj/transient_hash_map.hpp>
#include <jank/runtime/obj/iterator.hpp>
#include <jank/runtime/obj/range.hpp>
#include <jank/runtime/obj/jit_function.hpp>
Expand Down Expand Up @@ -173,6 +174,11 @@ namespace jank::runtime
std::forward<Args>(args)...);
}
break;
case object_type::transient_hash_map:
{
return fn(expect_object<obj::transient_hash_map>(erased), std::forward<Args>(args)...);
}
break;
case object_type::persistent_set:
{
return fn(expect_object<obj::persistent_set>(erased), std::forward<Args>(args)...);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down
6 changes: 6 additions & 0 deletions include/cpp/jank/runtime/obj/persistent_hash_map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ namespace jank::runtime
{
using persistent_array_map = static_object<object_type::persistent_array_map>;
using persistent_array_map_ptr = native_box<persistent_array_map>;

using transient_hash_map = static_object<object_type::transient_hash_map>;
using transient_hash_map_ptr = native_box<transient_hash_map>;
}

template <>
Expand Down Expand Up @@ -68,6 +71,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{};
};

Expand Down
2 changes: 1 addition & 1 deletion include/cpp/jank/runtime/obj/persistent_string.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace jank::runtime
template <>
struct static_object<object_type::persistent_string> : gc
{
static constexpr bool pointer_free{ true };
static constexpr bool pointer_free{ false };

static_object() = default;
static_object(static_object &&) = default;
Expand Down
61 changes: 61 additions & 0 deletions include/cpp/jank/runtime/obj/transient_hash_map.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
#pragma once

#include <jank/runtime/object.hpp>

namespace jank::runtime
{
template <>
struct static_object<object_type::transient_hash_map> : gc
{
static constexpr bool pointer_free{ false };
using value_type = detail::native_transient_hash_map;

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<static_object> empty()
{
static auto const ret(make_box<static_object>());
return ret;
}

/* behavior::objectable */
native_bool equal(object const &) const;
native_persistent_string const &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<static_object> assoc_in_place(object_ptr key, object_ptr val);

/* behavior::consable_in_place */
native_box<static_object> cons_in_place(object_ptr head);

/* behavior::callable */
object_ptr call(object_ptr) const;
object_ptr call(object_ptr, object_ptr) const;

object base{ object_type::transient_hash_map };
value_type data;
mutable native_hash hash{};
};

namespace obj
{
using transient_hash_map = static_object<object_type::transient_hash_map>;
using transient_hash_map_ptr = native_box<transient_hash_map>;
}
}
1 change: 1 addition & 0 deletions include/cpp/jank/runtime/object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
9 changes: 6 additions & 3 deletions src/cpp/jank/runtime/obj/persistent_hash_map.cpp
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
#include <iostream>
#include <sstream>

#include <jank/runtime/util.hpp>
#include <jank/runtime/obj/native_function_wrapper.hpp>
#include <jank/runtime/obj/persistent_hash_map.hpp>
#include <jank/runtime/obj/persistent_vector.hpp>
#include <jank/runtime/obj/transient_hash_map.hpp>

namespace jank::runtime
{
Expand Down Expand Up @@ -147,4 +145,9 @@ namespace jank::runtime
}
return *found;
}

obj::transient_hash_map_ptr obj::persistent_hash_map::to_transient() const
{
return make_box<obj::transient_hash_map>(data);
}
}
117 changes: 117 additions & 0 deletions src/cpp/jank/runtime/obj/transient_hash_map.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
#include <jank/runtime/util.hpp>
#include <jank/runtime/obj/native_function_wrapper.hpp>
#include <jank/runtime/obj/transient_hash_map.hpp>

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_hash obj::transient_hash_map::to_hash() const
{
if(hash)
{
return hash;
}

return hash = hash::unordered(data.begin(), data.end());
}

size_t obj::transient_hash_map::count() const
{
return data.size();
}

object_ptr obj::transient_hash_map::get(object_ptr const key) const
{
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
{
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
{
auto const res(data.find(key));
if(res)
{
return make_box<obj::persistent_vector>(key, *res);
}
return obj::nil::nil_const();
}

native_bool obj::transient_hash_map::contains(object_ptr const key) const
{
return data.find(key);
}

obj::transient_hash_map_ptr
obj::transient_hash_map::assoc_in_place(object_ptr const key, object_ptr const val)
{
data.set(key, val);
return this;
}

obj::transient_hash_map_ptr obj::transient_hash_map::cons_in_place(object_ptr const head)
{
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<obj::persistent_vector>(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;
}

object_ptr obj::transient_hash_map::call(object_ptr const o) const
{
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
{
auto const found(data.find(o));
if(!found)
{
return fallback;
}
return *found;
}
}
36 changes: 36 additions & 0 deletions src/jank/clojure/core.jank
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>)
{ 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<T>)
{ return typed_o->to_persistent(); }
else
{ throw #{ (ex-info :not-persistentable {:o o}) }#; }
},
#{ o }#
);"))

; Functions.

(defn- spread [arglist]
Expand Down Expand Up @@ -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 }#);")))
Expand Down

0 comments on commit 7cad956

Please sign in to comment.