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 f48c8be
Show file tree
Hide file tree
Showing 14 changed files with 318 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
8 changes: 8 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 All @@ -19,6 +22,8 @@ namespace jank::runtime
object_type::persistent_hash_map_sequence,
runtime::detail::native_persistent_hash_map>
{
using transient_type = static_object<object_type::transient_hash_map>;

static_object() = default;
static_object(static_object &&) = default;
static_object(static_object const &) = default;
Expand Down Expand Up @@ -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{};
};

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
69 changes: 69 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,69 @@
#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;
using persistent_type = static_object<object_type::persistent_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 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::persistentable */
native_box<persistent_type> 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<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);
}
}
156 changes: 156 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,156 @@
#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_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<native_hash>(reinterpret_cast<uintptr_t>(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<obj::persistent_vector>(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<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;
}

native_box<obj::transient_hash_map::persistent_type> obj::transient_hash_map::to_persistent()
{
assert_active();
active = false;
return make_box<obj::persistent_hash_map>(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" };
}
}
}
Loading

0 comments on commit f48c8be

Please sign in to comment.