From 5c7f75239cf56d5303892123b6dcfe8e52aed292 Mon Sep 17 00:00:00 2001 From: jeaye Date: Sat, 9 Dec 2023 23:36:41 -0800 Subject: [PATCH] Add `list*` and all `apply` arities This also fixes an off-by-one in sequence length counting. --- include/cpp/jank/runtime/behavior/seqable.hpp | 2 +- .../runtime/obj/detail/iterator_sequence.hpp | 4 +- .../runtime/obj/native_array_sequence.hpp | 2 +- src/cpp/jank/runtime/behavior/callable.cpp | 2 +- src/cpp/jank/runtime/obj/cons.cpp | 2 +- .../runtime/obj/native_array_sequence.cpp | 16 ++- .../runtime/obj/native_vector_sequence.cpp | 6 +- src/cpp/jank/runtime/obj/vector.cpp | 10 +- src/cpp/jank/runtime/seq.cpp | 2 +- src/jank/clojure/core.jank | 114 +++++++++++++----- 10 files changed, 108 insertions(+), 52 deletions(-) diff --git a/include/cpp/jank/runtime/behavior/seqable.hpp b/include/cpp/jank/runtime/behavior/seqable.hpp index 4ee2ce86e..e465b8600 100644 --- a/include/cpp/jank/runtime/behavior/seqable.hpp +++ b/include/cpp/jank/runtime/behavior/seqable.hpp @@ -14,7 +14,7 @@ namespace jank::runtime::behavior { t->seq() } -> std::convertible_to; /* Returns a unique seq which can be updated in place. This is an optimization which allows * one allocation for a fresh seq which can then be mutated any number of times to traverse - * the data. */ + * the data. Also must return nullptr when the sequence is empty. */ { t->fresh_seq() } -> std::convertible_to; }; diff --git a/include/cpp/jank/runtime/obj/detail/iterator_sequence.hpp b/include/cpp/jank/runtime/obj/detail/iterator_sequence.hpp index 49afc27e0..03eb35d80 100644 --- a/include/cpp/jank/runtime/obj/detail/iterator_sequence.hpp +++ b/include/cpp/jank/runtime/obj/detail/iterator_sequence.hpp @@ -58,7 +58,7 @@ namespace jank::runtime::obj::detail native_box seq() { return static_cast(this); } native_box fresh_seq() const - { return jank::make_box(coll, begin, end, size); } + { return make_box(coll, begin, end, size); } /* behavior::countable */ size_t count() const @@ -75,7 +75,7 @@ namespace jank::runtime::obj::detail if(n == end) { return nullptr; } - return jank::make_box(coll, n, end, size); + return make_box(coll, n, end, size); } native_box next_in_place() { diff --git a/include/cpp/jank/runtime/obj/native_array_sequence.hpp b/include/cpp/jank/runtime/obj/native_array_sequence.hpp index ce2cd3da6..77bf0040c 100644 --- a/include/cpp/jank/runtime/obj/native_array_sequence.hpp +++ b/include/cpp/jank/runtime/obj/native_array_sequence.hpp @@ -7,7 +7,7 @@ namespace jank::runtime { static constexpr bool pointer_free{ false }; - static_object() = default; + static_object() = delete; static_object(static_object &&) = default; static_object(static_object const &) = default; static_object(object_ptr * const arr, size_t const size); diff --git a/src/cpp/jank/runtime/behavior/callable.cpp b/src/cpp/jank/runtime/behavior/callable.cpp index 47b445cea..b981ea4be 100644 --- a/src/cpp/jank/runtime/behavior/callable.cpp +++ b/src/cpp/jank/runtime/behavior/callable.cpp @@ -548,7 +548,7 @@ namespace jank::runtime if constexpr(seqable) { - auto const s(typed_args->seq()); + auto const s(typed_args->fresh_seq()); auto const length(runtime::detail::sequence_length(s, max_params + 1)); switch(length) { diff --git a/src/cpp/jank/runtime/obj/cons.cpp b/src/cpp/jank/runtime/obj/cons.cpp index 8df6e5b02..09aacd974 100644 --- a/src/cpp/jank/runtime/obj/cons.cpp +++ b/src/cpp/jank/runtime/obj/cons.cpp @@ -5,7 +5,7 @@ namespace jank::runtime { obj::cons::static_object(object_ptr const head, object_ptr const tail) : head{ head }, tail{ tail } - { } + { assert(head); } obj::cons_ptr obj::cons::seq() { return this; } diff --git a/src/cpp/jank/runtime/obj/native_array_sequence.cpp b/src/cpp/jank/runtime/obj/native_array_sequence.cpp index da403621c..2ca52aebe 100644 --- a/src/cpp/jank/runtime/obj/native_array_sequence.cpp +++ b/src/cpp/jank/runtime/obj/native_array_sequence.cpp @@ -4,10 +4,16 @@ namespace jank::runtime { obj::native_array_sequence::static_object(object_ptr * const arr, size_t const size) : arr{ arr }, size{ size } - { } + { + assert(arr); + assert(size > 0); + } obj::native_array_sequence::static_object(object_ptr * const arr, size_t const index, size_t const size) : arr{ arr }, index{ index }, size{ size } - { } + { + assert(arr); + assert(size > 0); + } /* behavior::objectable */ native_bool obj::native_array_sequence::equal(object const &o) const @@ -27,9 +33,9 @@ namespace jank::runtime /* behavior::seqable */ obj::native_array_sequence_ptr obj::native_array_sequence::seq() - { return size == 0 ? nullptr : this; } + { return this; } obj::native_array_sequence_ptr obj::native_array_sequence::fresh_seq() - { return size == 0 ? nullptr : make_box(arr, index, size); } + { return make_box(arr, index, size); } /* behavior::countable */ size_t obj::native_array_sequence::count() const @@ -71,5 +77,5 @@ namespace jank::runtime } obj::cons_ptr obj::native_array_sequence::cons(object_ptr const head) - { return make_box(head, size == 0 ? nullptr : this); } + { return make_box(head, this); } } diff --git a/src/cpp/jank/runtime/obj/native_vector_sequence.cpp b/src/cpp/jank/runtime/obj/native_vector_sequence.cpp index e5248bfff..69c8dd542 100644 --- a/src/cpp/jank/runtime/obj/native_vector_sequence.cpp +++ b/src/cpp/jank/runtime/obj/native_vector_sequence.cpp @@ -4,13 +4,13 @@ namespace jank::runtime { obj::native_vector_sequence::static_object(native_vector const &data, size_t index) : data{ data }, index{ index } - { } + { assert(!data.empty()); } obj::native_vector_sequence::static_object(native_vector &&data) : data{ std::move(data) } - { } + { assert(!data.empty()); } obj::native_vector_sequence::static_object(native_vector &&data, size_t index) : data{ std::move(data) }, index{ index } - { } + { assert(!data.empty()); } /* behavior::objectable */ native_bool obj::native_vector_sequence::equal(object const &o) const diff --git a/src/cpp/jank/runtime/obj/vector.cpp b/src/cpp/jank/runtime/obj/vector.cpp index 2cc91e8ba..f9aff3a14 100644 --- a/src/cpp/jank/runtime/obj/vector.cpp +++ b/src/cpp/jank/runtime/obj/vector.cpp @@ -19,7 +19,7 @@ namespace jank::runtime obj::vector_ptr obj::vector::create(object_ptr const s) { if(s == nullptr) - { return jank::make_box(); } + { return make_box(); } return visit_object ( @@ -32,7 +32,7 @@ namespace jank::runtime runtime::detail::native_transient_vector v; for(auto i(typed_s->fresh_seq()); i != nullptr; i = i->next_in_place()) { v.push_back(i->first()); } - return jank::make_box(v.persistent()); + return make_box(v.persistent()); } else { throw std::runtime_error{ fmt::format("invalid sequence: {}", typed_s->to_string()) }; } @@ -67,14 +67,14 @@ namespace jank::runtime { if(data.empty()) { return nullptr; } - return jank::make_box(const_cast(this)); + return make_box(const_cast(this)); } obj::persistent_vector_sequence_ptr obj::vector::fresh_seq() const { if(data.empty()) { return nullptr; } - return jank::make_box(const_cast(this)); + return make_box(const_cast(this)); } size_t obj::vector::count() const @@ -90,7 +90,7 @@ namespace jank::runtime object_ptr obj::vector::with_meta(object_ptr const m) const { auto const meta(behavior::detail::validate_meta(m)); - auto ret(jank::make_box(data)); + auto ret(make_box(data)); ret->meta = meta; return ret; } diff --git a/src/cpp/jank/runtime/seq.cpp b/src/cpp/jank/runtime/seq.cpp index cae27cdb2..8cb5259de 100644 --- a/src/cpp/jank/runtime/seq.cpp +++ b/src/cpp/jank/runtime/seq.cpp @@ -39,7 +39,7 @@ namespace jank::runtime { return typed_s->count(); } else if constexpr(behavior::seqable) { - size_t length{ 1 }; + size_t length{ 0 }; for(auto i(typed_s->fresh_seq()); i != nullptr && length < max; i = i->next_in_place()) { ++length; } return length; diff --git a/src/jank/clojure/core.jank b/src/jank/clojure/core.jank index 878ff8401..8522f927f 100644 --- a/src/jank/clojure/core.jank +++ b/src/jank/clojure/core.jank @@ -297,7 +297,7 @@ (cons 'let* (cons args body))) ; TODO: Higher arities. Needs clojure.core/spread, which needs clojure.core/cons. -(defn apply [f args] +(defn apply* [f args] (native/raw "__value = runtime::apply_to(#{ f }#, #{ args }#);")) (defn or* @@ -314,7 +314,7 @@ (defmacro or [& args] - (apply or* args)) + (apply* or* args)) (defn and* ([] @@ -330,7 +330,7 @@ (defmacro and [& args] - (apply and* args)) + (apply* and* args)) ; Takes a set of test/expr pairs. It evaluates each test one at a ; time. If a test returns logical true, cond evaluates and returns @@ -350,6 +350,64 @@ (defn macroexpand [form] (native/raw "__value = __rt_ctx.macroexpand(#{ form }#);")) +; Relations. +;; Miscellaneous. +(defn true? [o] + (native/raw "__value = runtime::detail::equal(#{ o }#, #{ true }#) ? #{ true }# : #{ false }#")) +(defn false? [o] + (native/raw "__value = runtime::detail::equal(#{ o }#, #{ false }#) ? #{ true }# : #{ false }#")) +(defn not [o] + (native/raw "__value = runtime::detail::equal(#{ o }#, #{ false }#) ? #{ true }# : #{ false }#")) +(defn some? [o] + (native/raw "__value = (o == obj::nil::nil_const()) ? #{ false }# : #{ true }#")) + +; Functions. + +; Takes a fn f and returns a fn that takes the same arguments as f, +; has the same effects, if any, and returns the opposite truth value. +(defn complement [f] + ; TODO: borked? + f + ;(fn + ; ([] (not (f))) + ; ([x] (not (f x))) + ; ([x y] (not (f x y))) + ; ([x y & zs] (not (apply f x y zs)))) + ) + +(defn- spread [arglist] + (cond + (nil? arglist) nil + (nil? (next arglist)) (seq (first arglist)) + :else (cons (first arglist) (spread (next arglist))))) + +; Creates a new seq containing the items prepended to the rest, the +; last of which will be treated as a sequence. +(defn list* + ([args] + (seq args)) + ([a args] + (cons a args)) + ([a b args] + (cons a (cons b args))) + ([a b c args] + (cons a (cons b (cons c args)))) + ([a b c d & more] + (cons a (cons b (cons c (cons d (spread more))))))) + +; Applies fn f to the argument list formed by prepending intervening arguments to args. +(defn apply + ([f args] + (native/raw "__value = runtime::apply_to(#{ f }#, #{ args }#);")) + ([f x args] + (native/raw "__value = runtime::apply_to(#{ f }#, #{ (list* x args) }#);")) + ([f x y args] + (native/raw "__value = runtime::apply_to(#{ f }#, #{ (list* x y args) }#);")) + ([f x y z args] + (native/raw "__value = runtime::apply_to(#{ f }#, #{ (list* x y z args) }#);")) + ([f a b c d & args] + (native/raw "__value = runtime::apply_to(#{ f }#, #{ (cons a (cons b (cons c (cons d (spread args))))) }#);"))) + ; Utils. (defn reduce* ([f coll] @@ -377,18 +435,6 @@ #{ coll }# );"))) - -; Relations. -;; Miscellaneous. -(defn true? [o] - (native/raw "__value = runtime::detail::equal(#{ o }#, #{ true }#) ? #{ true }# : #{ false }#")) -(defn false? [o] - (native/raw "__value = runtime::detail::equal(#{ o }#, #{ false }#) ? #{ true }# : #{ false }#")) -(defn not [o] - (native/raw "__value = runtime::detail::equal(#{ o }#, #{ false }#) ? #{ true }# : #{ false }#")) -(defn some? [o] - (native/raw "__value = (o == obj::nil::nil_const()) ? #{ false }# : #{ true }#")) - ; Strings. (defn string? [o] (native/raw "__value = make_box(#{ o }#->type == object_type::string);")) @@ -400,25 +446,29 @@ ([o & args] (native/raw "__value = visit_object ( - [=](auto const typed_args) -> object_ptr - { - using T = typename decltype(typed_args)::value_type; - - if constexpr(behavior::sequenceable) + [=](auto const typed_args) -> object_ptr { - fmt::memory_buffer buff; - buff.reserve(16); - runtime::detail::to_string(#{ o }#, buff); - runtime::detail::to_string(typed_args->first(), buff); - for(auto it(typed_args->next_in_place()); it != nullptr; it = it->next_in_place()) - { runtime::detail::to_string(typed_args->first(), buff); } - return make_box(native_string{ buff.data(), buff.size() }); - } - else - { throw #{ (ex-info :invalid-seq {:args args}) }#; } + using T = typename decltype(typed_args)::value_type; + + if constexpr(behavior::sequenceable) + { + fmt::memory_buffer buff; + buff.reserve(16); + runtime::detail::to_string(#{ o }#, buff); + if(0 < runtime::detail::sequence_length(typed_args)) + { + auto const fresh(typed_args->fresh_seq()); + runtime::detail::to_string(fresh->first(), buff); + for(auto it(fresh->next_in_place()); it != nullptr; it = it->next_in_place()) + { runtime::detail::to_string(it->first(), buff); } + } + return make_box(native_string{ buff.data(), buff.size() }); + } + else + { throw #{ (ex-info :invalid-seq {:args args}) }#; } }, - #{ args }# - );"))) + #{ args }# + );"))) (defn subs ([s start]