Skip to content

Commit

Permalink
Add atoms, not validators
Browse files Browse the repository at this point in the history
  • Loading branch information
jeaye committed May 10, 2024
1 parent e5a5b63 commit 0552799
Show file tree
Hide file tree
Showing 8 changed files with 311 additions and 14 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,7 @@ add_library(
src/cpp/jank/runtime/obj/chunked_cons.cpp
src/cpp/jank/runtime/obj/native_array_sequence.cpp
src/cpp/jank/runtime/obj/native_vector_sequence.cpp
src/cpp/jank/runtime/obj/atom.cpp
src/cpp/jank/runtime/obj/volatile.cpp
src/cpp/jank/runtime/obj/reduced.cpp
src/cpp/jank/runtime/behavior/callable.cpp
Expand Down
6 changes: 6 additions & 0 deletions include/cpp/jank/runtime/erasure.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include <jank/runtime/obj/persistent_set_sequence.hpp>
#include <jank/runtime/obj/native_array_sequence.hpp>
#include <jank/runtime/obj/native_vector_sequence.hpp>
#include <jank/runtime/obj/atom.hpp>
#include <jank/runtime/obj/volatile.hpp>
#include <jank/runtime/obj/reduced.hpp>
#include <jank/runtime/ns.hpp>
Expand Down Expand Up @@ -277,6 +278,11 @@ namespace jank::runtime
return fn(expect_object<obj::jit_function>(erased), std::forward<Args>(args)...);
}
break;
case object_type::atom:
{
return fn(expect_object<obj::atom>(erased), std::forward<Args>(args)...);
}
break;
case object_type::volatile_:
{
return fn(expect_object<obj::volatile_>(erased), std::forward<Args>(args)...);
Expand Down
47 changes: 47 additions & 0 deletions include/cpp/jank/runtime/obj/atom.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
#pragma once

namespace jank::runtime
{
template <>
struct static_object<object_type::atom> : gc
{
static constexpr native_bool pointer_free{ false };

static_object() = default;
static_object(object_ptr o);

/* 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::derefable */
object_ptr deref() const;

object_ptr reset(object_ptr o);
obj::persistent_vector_ptr reset_vals(object_ptr o);

object_ptr swap(object_ptr fn);
object_ptr swap(object_ptr fn, object_ptr a1);
object_ptr swap(object_ptr fn, object_ptr a1, object_ptr a2);
object_ptr swap(object_ptr fn, object_ptr a1, object_ptr a2, object_ptr rest);

obj::persistent_vector_ptr swap_vals(object_ptr fn);
obj::persistent_vector_ptr swap_vals(object_ptr fn, object_ptr a1);
obj::persistent_vector_ptr swap_vals(object_ptr fn, object_ptr a1, object_ptr a2);
obj::persistent_vector_ptr
swap_vals(object_ptr fn, object_ptr a1, object_ptr a2, object_ptr rest);

object_ptr compare_and_set(object_ptr old_val, object_ptr new_val);

object base{ object_type::atom };
std::atomic<object *> val{};
};

namespace obj
{
using atom = static_object<object_type::atom>;
using atom_ptr = native_box<atom>;
}
}
1 change: 1 addition & 0 deletions include/cpp/jank/runtime/object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ namespace jank::runtime
persistent_vector_sequence,
persistent_list_sequence,
persistent_set_sequence,
atom,
volatile_,
reduced,
ns,
Expand Down
1 change: 1 addition & 0 deletions include/cpp/jank/runtime/seq.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ namespace jank::runtime
native_bool is_nil(object_ptr o);
native_bool is_some(object_ptr o);
native_bool is_map(object_ptr o);
object_ptr cons(object_ptr head, object_ptr tail);
object_ptr conj(object_ptr s, object_ptr o);
object_ptr assoc(object_ptr m, object_ptr k, object_ptr v);
object_ptr dissoc(object_ptr m, object_ptr k);
Expand Down
174 changes: 174 additions & 0 deletions src/cpp/jank/runtime/obj/atom.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#include <jank/runtime/obj/atom.hpp>

namespace jank::runtime
{
obj::atom::static_object(object_ptr const o)
: val{ o }
{
assert(val);
}

native_bool obj::atom::equal(object const &o) const
{
return &o == &base;
}

native_persistent_string obj::atom::to_string() const
{
fmt::memory_buffer buff;
to_string(buff);
return native_persistent_string{ buff.data(), buff.size() };
}

void obj::atom::to_string(fmt::memory_buffer &buff) const
{
fmt::format_to(std::back_inserter(buff),
"{}@{}",
magic_enum::enum_name(base.type),
fmt::ptr(&base));
}

native_hash obj::atom::to_hash() const
{
return static_cast<native_hash>(reinterpret_cast<uintptr_t>(this));
}

object_ptr obj::atom::deref() const
{
return val.load();
}

object_ptr obj::atom::reset(object_ptr const o)
{
assert(o);
val = o;
return o;
}

obj::persistent_vector_ptr obj::atom::reset_vals(object_ptr const o)
{
while(true)
{
auto v(val.load());
if(val.compare_exchange_weak(v, o))
{
return make_box<obj::persistent_vector>(std::in_place, v, o);
}
}
}

/* NOLINTNEXTLINE(cppcoreguidelines-noexcept-swap) */
object_ptr obj::atom::swap(object_ptr const fn)
{
while(true)
{
auto v(val.load());
auto const next(dynamic_call(fn, v));
if(val.compare_exchange_weak(v, next))
{
return next;
}
}
}

/* NOLINTNEXTLINE(cppcoreguidelines-noexcept-swap) */
object_ptr obj::atom::swap(object_ptr fn, object_ptr a1)
{
while(true)
{
auto v(val.load());
auto const next(dynamic_call(fn, v, a1));
if(val.compare_exchange_weak(v, next))
{
return next;
}
}
}

/* NOLINTNEXTLINE(cppcoreguidelines-noexcept-swap) */
object_ptr obj::atom::swap(object_ptr fn, object_ptr a1, object_ptr a2)
{
while(true)
{
auto v(val.load());
auto const next(dynamic_call(fn, v, a1, a2));
if(val.compare_exchange_weak(v, next))
{
return next;
}
}
}

/* NOLINTNEXTLINE(cppcoreguidelines-noexcept-swap) */
object_ptr obj::atom::swap(object_ptr fn, object_ptr a1, object_ptr a2, object_ptr rest)
{
while(true)
{
auto v(val.load());
auto const next(apply_to(fn, conj(a1, conj(a2, rest))));
if(val.compare_exchange_weak(v, next))
{
return next;
}
}
}

obj::persistent_vector_ptr obj::atom::swap_vals(object_ptr const fn)
{
while(true)
{
auto v(val.load());
auto const next(dynamic_call(fn, v));
if(val.compare_exchange_weak(v, next))
{
return make_box<obj::persistent_vector>(std::in_place, v, next);
}
}
}

obj::persistent_vector_ptr obj::atom::swap_vals(object_ptr fn, object_ptr a1)
{
while(true)
{
auto v(val.load());
auto const next(dynamic_call(fn, v, a1));
if(val.compare_exchange_weak(v, next))
{
return make_box<obj::persistent_vector>(std::in_place, v, next);
}
}
}

obj::persistent_vector_ptr obj::atom::swap_vals(object_ptr fn, object_ptr a1, object_ptr a2)
{
while(true)
{
auto v(val.load());
auto const next(dynamic_call(fn, v, a1, a2));
if(val.compare_exchange_weak(v, next))
{
return make_box<obj::persistent_vector>(std::in_place, v, next);
}
}
}

obj::persistent_vector_ptr
obj::atom::swap_vals(object_ptr fn, object_ptr a1, object_ptr a2, object_ptr rest)
{
while(true)
{
auto v(val.load());
auto const next(apply_to(fn, conj(a1, conj(a2, rest))));
if(val.compare_exchange_weak(v, next))
{
return make_box<obj::persistent_vector>(std::in_place, v, next);
}
}
}

object_ptr obj::atom::compare_and_set(object_ptr old_val, object_ptr new_val)
{
object *old{ old_val };
return make_box(val.compare_exchange_weak(old, new_val));
}
}
13 changes: 13 additions & 0 deletions src/cpp/jank/runtime/seq.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,19 @@ namespace jank::runtime
s);
}

object_ptr cons(object_ptr const head, object_ptr const tail)
{
return visit_seqable(
[=](auto const typed_tail) -> object_ptr {
return make_box<jank::runtime::obj::cons>(head, typed_tail->seq());
},
[=]() -> object_ptr {
throw std::runtime_error{ fmt::format("not seqable: {}",
runtime::detail::to_string(tail)) };
},
tail);
}

object_ptr conj(object_ptr const s, object_ptr const o)
{
return visit_object(
Expand Down
82 changes: 68 additions & 14 deletions src/jank/clojure/core.jank
Original file line number Diff line number Diff line change
Expand Up @@ -134,19 +134,7 @@

(def cons
(fn* cons [head tail]
(native/raw "__value = visit_object
(
[=](auto const typed_tail) -> object_ptr
{
using T = typename decltype(typed_tail)::value_type;
if constexpr(behavior::seqable<T>)
{ return make_box<jank::runtime::obj::cons>(~{ head }, typed_tail->seq()); }
else
{ ~{ (throw (ex-info :invalid-cons-tail {:type (type tail)})) }; }
},
~{ tail }
);")))
(native/raw "__value = runtime::cons(~{ head }, ~{ tail });")))

(def seq?
(fn* seq? [o]
Expand Down Expand Up @@ -990,7 +978,73 @@
[coll]
(native/raw "__value = runtime::pop(~{ coll });"))

; Volatiles.
;; Atoms.
(defn atom
"Creates and returns an Atom with an initial value of x and zero or
more options (in any order):
:meta metadata-map
:validator validate-fn
If metadata-map is supplied, it will become the metadata on the
atom. validate-fn must be nil or a side-effect-free fn of one
argument, which will be passed the intended new state on any state
change. If the new state is unacceptable, the validate-fn should
return false or throw an exception."
([x]
(native/raw "__value = make_box<obj::atom>(~{ x });"))
; TODO: Validators.
#_([x & options] (setup-reference (atom x) options)))

(defn swap!
"Atomically swaps the value of atom to be:
(apply f current-value-of-atom args). Note that f may be called
multiple times, and thus should be free of side effects. Returns
the value that was swapped in."
([atom f]
(native/raw "__value = expect_object<obj::atom>(~{ atom })->swap(~{ f });"))
([atom f x]
(native/raw "__value = expect_object<obj::atom>(~{ atom })->swap(~{ f }, ~{ x });"))
([atom f x y]
(native/raw "__value = expect_object<obj::atom>(~{ atom })->swap(~{ f }, ~{ x }, ~{ y });"))
([atom f x y & args]
(native/raw "__value = expect_object<obj::atom>(~{ atom })->swap(~{ f }, ~{ x }, ~{ y }, ~{ args });")))

(defn swap-vals!
"Atomically swaps the value of atom to be:
(apply f current-value-of-atom args). Note that f may be called
multiple times, and thus should be free of side effects.
Returns [old new], the value of the atom before and after the swap."
([atom f]
(native/raw "__value = expect_object<obj::atom>(~{ atom })->swap_vals(~{ f });"))
([atom f x]
(native/raw "__value = expect_object<obj::atom>(~{ atom })->swap_vals(~{ f }, ~{ x });"))
([atom f x y]
(native/raw "__value = expect_object<obj::atom>(~{ atom })->swap_vals(~{ f }, ~{ x }, ~{ y });"))
([atom f x y & args]
(native/raw "__value = expect_object<obj::atom>(~{ atom })->swap_vals(~{ f }, ~{ x }, ~{ y }, ~{ args });")))

(defn compare-and-set!
"Atomically sets the value of atom to newval if and only if the
current value of the atom is identical to oldval. Returns true if
set happened, else false"
[atom oldval newval]
(native/raw "__value = expect_object<obj::atom>(~{ atom })->compare_and_set(~{ oldval }, ~{ newval });"))

(defn reset!
"Sets the value of atom to newval without regard for the
current value. Returns newval."
[atom newval]
(native/raw "__value = expect_object<obj::atom>(~{ atom })->reset(~{ newval });"))

(defn reset-vals!
"Sets the value of atom to newval. Returns [old new], the value of the
atom before and after the reset."
[atom newval]
(native/raw "__value = expect_object<obj::atom>(~{ atom })->reset_vals(~{ newval });"))

;; Volatiles.
(defn volatile!
"Creates and returns a Volatile with an initial value of val."
[val]
Expand Down

0 comments on commit 0552799

Please sign in to comment.