diff --git a/CMakeLists.txt b/CMakeLists.txt index 0101bd38d..169a88296 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -297,6 +297,7 @@ if(jank_tests) add_executable( jank_test_exe test/cpp/main.cpp + test/cpp/jank/native_persistent_string.cpp test/cpp/jank/read/lex.cpp test/cpp/jank/read/parse.cpp test/cpp/jank/analyze/box.cpp diff --git a/include/cpp/jank/analyze/expr/function.hpp b/include/cpp/jank/analyze/expr/function.hpp index c8483e099..704db71fd 100644 --- a/include/cpp/jank/analyze/expr/function.hpp +++ b/include/cpp/jank/analyze/expr/function.hpp @@ -60,7 +60,7 @@ namespace jank::analyze::expr template struct function : expression_base { - native_string name; + native_persistent_string name; native_vector> arities; runtime::object_ptr to_runtime_data() const diff --git a/include/cpp/jank/analyze/expr/native_raw.hpp b/include/cpp/jank/analyze/expr/native_raw.hpp index f82137c18..b2ecd9d23 100644 --- a/include/cpp/jank/analyze/expr/native_raw.hpp +++ b/include/cpp/jank/analyze/expr/native_raw.hpp @@ -16,7 +16,7 @@ namespace jank::analyze::expr template struct native_raw : expression_base { - using chunk_t = boost::variant>; + using chunk_t = boost::variant>; native_vector chunks; @@ -34,7 +34,7 @@ namespace jank::analyze::expr { using T = std::decay_t; - if constexpr(std::same_as) + if constexpr(std::same_as) { return make_box(d); } else { return d->to_runtime_data(); } diff --git a/include/cpp/jank/codegen/escape.hpp b/include/cpp/jank/codegen/escape.hpp index dd4d9a7b0..7c60d0e8c 100644 --- a/include/cpp/jank/codegen/escape.hpp +++ b/include/cpp/jank/codegen/escape.hpp @@ -5,7 +5,7 @@ * https://github.com/fmtlib/fmt/issues/825#issuecomment-1227501168 */ namespace jank::codegen { - template + template struct escape_view { template @@ -32,9 +32,9 @@ namespace jank::codegen typename S::value_type esc{ '\\' }; }; - constexpr escape_view escaped - (native_string_view const &sv, char const q = '"', char const e = '\\') - { return escape_view{ sv, q, e }; } + constexpr escape_view escaped + (native_persistent_string_view const &sv, char const q = '"', char const e = '\\') + { return escape_view{ sv, q, e }; } } template diff --git a/include/cpp/jank/codegen/processor.hpp b/include/cpp/jank/codegen/processor.hpp index 89c0c0aae..ebed045b6 100644 --- a/include/cpp/jank/codegen/processor.hpp +++ b/include/cpp/jank/codegen/processor.hpp @@ -29,18 +29,18 @@ namespace jank::codegen handle() = default; handle(handle const &) = default; handle(handle &&) = default; - handle(native_string const &name, bool boxed); - handle(native_string const &boxed_name); - handle(native_string const &boxed_name, native_string const &unboxed_name); + handle(native_persistent_string const &name, bool boxed); + handle(native_persistent_string const &boxed_name); + handle(native_persistent_string const &boxed_name, native_persistent_string const &unboxed_name); handle(analyze::local_binding const &binding); handle& operator =(handle const &) = default; handle& operator =(handle &&) = default; - native_string str(bool needs_box) const; + native_persistent_string str(bool needs_box) const; - native_string boxed_name; - native_string unboxed_name; + native_persistent_string boxed_name; + native_persistent_string unboxed_name; }; enum class compilation_target @@ -60,14 +60,14 @@ namespace jank::codegen ( runtime::context &rt_ctx, analyze::expression_ptr const &expr, - native_string_view const &module, + native_persistent_string_view const &module, compilation_target target ); processor ( runtime::context &rt_ctx, analyze::expr::function const &expr, - native_string_view const &module, + native_persistent_string_view const &module, compilation_target target ); processor(processor const &) = delete; @@ -164,19 +164,19 @@ namespace jank::codegen bool box_needed ); - native_string declaration_str(); + native_persistent_string declaration_str(); void build_header(); void build_body(); void build_footer(); - native_string expression_str(bool box_needed); + native_persistent_string expression_str(bool box_needed); - native_string module_init_str(native_string_view const &module); + native_persistent_string module_init_str(native_persistent_string_view const &module); void format_elided_var ( - native_string_view const &start, - native_string_view const &end, - native_string_view const &ret_tmp, + native_persistent_string_view const &start, + native_persistent_string_view const &end, + native_persistent_string_view const &ret_tmp, native_vector> const &arg_exprs, analyze::expr::function_arity const &fn_arity, bool arg_box_needed, @@ -184,16 +184,16 @@ namespace jank::codegen ); void format_direct_call ( - native_string const &source_tmp, - native_string_view const &ret_tmp, + native_persistent_string const &source_tmp, + native_persistent_string_view const &ret_tmp, native_vector> const &arg_exprs, analyze::expr::function_arity const &fn_arity, bool arg_box_needed ); void format_dynamic_call ( - native_string const &source_tmp, - native_string_view const &ret_tmp, + native_persistent_string const &source_tmp, + native_persistent_string_view const &ret_tmp, native_vector> const &arg_exprs, analyze::expr::function_arity const &fn_arity, bool arg_box_needed @@ -203,7 +203,7 @@ namespace jank::codegen /* This is stored just to keep the expression alive. */ analyze::expression_ptr root_expr{}; analyze::expr::function const &root_fn; - native_string module; + native_persistent_string module; compilation_target target{}; runtime::obj::symbol struct_name; diff --git a/include/cpp/jank/detail/to_runtime_data.hpp b/include/cpp/jank/detail/to_runtime_data.hpp index 01d4af99b..c6e652e65 100644 --- a/include/cpp/jank/detail/to_runtime_data.hpp +++ b/include/cpp/jank/detail/to_runtime_data.hpp @@ -23,7 +23,7 @@ namespace jank::detail runtime::object_ptr to_runtime_data(native_box const &d) { return make_box(fmt::format("box({})", reinterpret_cast(d.data))); } - inline runtime::object_ptr to_runtime_data(native_string const &d) + inline runtime::object_ptr to_runtime_data(native_persistent_string const &d) { return make_box(d); } inline runtime::object_ptr to_runtime_data(runtime::obj::symbol const &d) { return make_box(d); } diff --git a/include/cpp/jank/jit/processor.hpp b/include/cpp/jank/jit/processor.hpp index 431798975..2417e9100 100644 --- a/include/cpp/jank/jit/processor.hpp +++ b/include/cpp/jank/jit/processor.hpp @@ -16,9 +16,9 @@ namespace jank::jit { processor(runtime::context &rt_ctx, native_integer optimization_level); - result, native_string> eval(codegen::processor &cg_prc) const; - void eval_string(native_string const &s) const; - void load_object(native_string_view const &path) const; + result, native_persistent_string> eval(codegen::processor &cg_prc) const; + void eval_string(native_persistent_string const &s) const; + void load_object(native_persistent_string_view const &path) const; std::unique_ptr interpreter; native_integer optimization_level{}; diff --git a/include/cpp/jank/native_box_util.hpp b/include/cpp/jank/native_box_util.hpp index 195901141..369a14990 100644 --- a/include/cpp/jank/native_box_util.hpp +++ b/include/cpp/jank/native_box_util.hpp @@ -20,5 +20,5 @@ namespace jank native_box make_box(native_integer const i); native_box make_box(size_t const i); native_box make_box(native_real const r); - native_box make_box(native_string_view const &s); + native_box make_box(native_persistent_string_view const &s); } diff --git a/include/cpp/jank/native_persistent_string.hpp b/include/cpp/jank/native_persistent_string.hpp new file mode 100644 index 000000000..f5b646ac6 --- /dev/null +++ b/include/cpp/jank/native_persistent_string.hpp @@ -0,0 +1,626 @@ +#pragma once + +#include +#include + +namespace jank +{ + /* This is a not-completely-standard replacement for std::string, with a few goals in mind: + * + * 1. Be as fast, or faster, than `std::string` and `folly::fbstring` + * 2. Support hashing, with cached value + * 3. Be immutable (i.e. no copy on substrings, writes only done in constructors, no mutators) + * 4. Not a goal: Complete standard compliance (which allows us to cheat) + * + * To accomplish this, we follow folly's three-word design, with an overlayed union spanning + * all three words. We use the right-most byte of the string to categorize it into one of + * three possible states (assuming a 64bit machine): + * + * 1. Small (23 characters or less, not including the null-terminator) + * 2. Large owned (24 or more characters, with unique ownership over the memory) + * 3. Large shared (24 or more characters, with shared ownership over the memory) + * + * Shared ownership just relies on jank's garbage collector. No additional bookkeping, such + * as reference counting, is done. + * + * Within that right-most byte, these three categories are determined by two dedicated bits. + * If the most-significant-bit (MSB) is set, the string is large and shared. If the next MSB + * is also set, the string is large and owned. The remaining 6 bits on that byte are used + * to store the size of the string, in the case of a small string. + * + * Rather than just storing the size, we store the remaining capacity, which is the + * (max_small_size - size). The benefit of this is that, when the small string is as large + * as possible, i.e. 23 bytes on a 64bit machine, the remaining capacity will be 0, and the + * two flag bits will be 0, and thus the byte will be 0 and can act as the null-terminator. + * + * In the large case, only the two flag bits of the third word are used. Sharing is done by + * updating the flag bits on both strings to be shared. We share on both copy construction + * as well as substring operations. Since share substrings, shared strings may not be + * null-terminated. We'll lazily own the string if c_str() is called on a shared string, but + * data() is not expected to return a null-terminated string. + */ + struct native_persistent_string + { + using value_type = char; + using allocator_type = native_allocator; + using allocator_traits = std::allocator_traits; + using size_type = allocator_traits::size_type; + using traits_type = std::char_traits; + using pointer_type = value_type*; + using const_pointer_type = value_type const*; + using iterator = pointer_type; + using const_iterator = const_pointer_type; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + + static constexpr size_type npos{ std::numeric_limits::max() }; + + constexpr native_persistent_string() noexcept + { set_small_size(0); } + + constexpr native_persistent_string(native_persistent_string const &s) noexcept + : store{ s.store } + { + /* NOTE: An if saves us a couple of instructions, versus a switch here. */ + if(s.get_category() != category::small) + { + const_cast(s).store.large.set_category(category::large_shared); + store.large.set_category(category::large_shared); + } + } + + constexpr native_persistent_string(native_persistent_string &&s) noexcept + : store{ std::move(s.store) } + { s.set_small_size(0); } + + template + constexpr native_persistent_string(value_type const (&new_data)[N]) noexcept + { + if constexpr(N <= max_small_size) + { init_small(new_data, N); } + else + { init_large_owned(new_data, N); } + } + + [[gnu::nonnull (2)]] + constexpr native_persistent_string(const_pointer_type const s) noexcept + : native_persistent_string{ s, traits_type::length(s) } + { } + + [[gnu::nonnull (2)]] + constexpr native_persistent_string(const_pointer_type const s, size_type const size) noexcept + { + if(size <= max_small_size) + { init_small(s, size); } + else + { init_large_owned(s, size); } + } + + [[gnu::nonnull (2, 4)]] + constexpr native_persistent_string + ( + const_pointer_type const lhs, size_type const lhs_size, + const_pointer_type const rhs, size_type const rhs_size + ) noexcept + { + auto const combined_size(lhs_size + rhs_size); + if(combined_size <= max_small_size) + { init_small(lhs, lhs_size, rhs, rhs_size); } + else + { init_large_owned(lhs, lhs_size, rhs, rhs_size); } + } + + constexpr native_persistent_string(native_persistent_string_view const &s) + : native_persistent_string{ s.data(), s.size() } + { } + + constexpr native_persistent_string(native_transient_string const &s) + : native_persistent_string{ s.data(), s.size() } + { } + + constexpr native_persistent_string(native_persistent_string const &s, size_type const pos, size_type count) + { + auto const s_length(s.size()); + if(s_length < pos) [[unlikely]] + { throw std::runtime_error{ "position outside of string" }; } + else if(count == npos || s_length < pos + count) + { count = s_length - pos; } + + if(count <= max_small_size) + { init_small(s.data() + pos, count); } + else + { + /* NOTE: Not necessarily null-terminated! */ + const_cast(s).store.large.set_category(category::large_shared); + init_large_shared(s.data() + pos, count); + } + } + + constexpr ~native_persistent_string() noexcept + { destroy(); } + + /*** Data accessors. ***/ + [[gnu::always_inline, gnu::flatten, gnu::hot, gnu::const]] + constexpr native_bool empty() const noexcept + { return size() == 0; } + + [[gnu::always_inline, gnu::flatten, gnu::hot, gnu::const]] + constexpr size_type size() const noexcept + { return (get_category() == category::small) ? get_small_size() : store.large.size; } + + /* XXX: The contents returned, for large shared strings, may not be null-terminated. If + * you require that, use c_str(). Whenever possible, use data() and size(). */ + [[gnu::always_inline, gnu::flatten, gnu::hot, gnu::returns_nonnull, gnu::const]] + constexpr const_pointer_type data() const noexcept + { return (get_category() == category::small) ? store.small : store.large.data; } + + /* Always returns a null-terminated string. For shared large strings, we'll allocate + * and copy the contents upon calling c_str(). If you can use data() and size(), do that. */ + [[gnu::returns_nonnull]] + constexpr const_pointer_type c_str() const noexcept + { + switch(get_category()) + { + case category::small: + return store.small; + case category::large_owned: + return store.large.data; + /* For shared strings, we lazily convert to an owned string, since a shared string + * is not guaranteed to be null-terminated. */ + case category::large_shared: + { + /* Shared strings are always large. */ + const_cast(this)->init_large_owned(store.large.data, store.large.size); + return store.large.data; + } + } + } + + /*** Searches. ***/ + [[gnu::const]] + constexpr size_type find(native_persistent_string const &pattern, size_type const pos = 0) const noexcept + { return find(pattern.data(), pos, pattern.size()); } + [[gnu::const, gnu::nonnull (2)]] + constexpr size_type find(const_pointer_type const pattern, size_type const pos = 0) const noexcept + { return find(pattern, pos, traits_type::length(pattern)); } + [[gnu::const, gnu::nonnull (2)]] + constexpr size_type find + ( + const_pointer_type const pattern, + size_type const pos, + size_type const pattern_length + ) const noexcept + { + auto const corpus_length(size()); + if(pattern_length == 0) [[unlikely]] + { return pos <= corpus_length ? pos : npos; } + else if(corpus_length <= pos) [[unlikely]] + { return npos; } + + auto const pattern_start(pattern[0]); + auto const corpus_start(data()); + auto const corpus_last(corpus_start + corpus_length); + auto corpus_pos(corpus_start + pos); + auto remaining_len(corpus_length - pos); + + while(remaining_len >= pattern_length) + { + corpus_pos = traits_type::find(corpus_pos, remaining_len - pattern_length + 1, pattern_start); + if(!corpus_pos) + { return npos; } + + /* We compare the full string here, including the first character which we've + * already matched, since the pattern is likely aligned and comparing from the start + * will be faster for memcmp. */ + if(traits_type::compare(corpus_pos, pattern, pattern_length) == 0) + { return corpus_pos - corpus_start; } + remaining_len = corpus_last - ++corpus_pos; + } + return npos; + } + + [[gnu::const]] + constexpr size_type find(value_type c, size_type pos = 0) const noexcept + { + size_type ret{ npos }; + auto const length(size()); + if(pos < length) [[likely]] + { + auto d(data()); + auto const n(length - pos); + auto const p(traits_type::find(d + pos, n, c)); + /* TODO: cmov instead of branch here? */ + if(p) + { ret = p - d; } + } + return ret; + } + + [[gnu::const]] + constexpr size_type rfind(native_persistent_string const &s, size_type const pos = npos) const noexcept + { return rfind(s.data(), pos, s.size()); } + [[gnu::const, gnu::nonnull (2)]] + constexpr size_type rfind(const_pointer_type const s, size_type const pos = npos) const noexcept + { return rfind(s, pos, traits_type::length(s)); } + [[gnu::const, gnu::nonnull (2)]] + constexpr size_type rfind(const_pointer_type const s, size_type pos, size_type const n) const noexcept + { + auto const length(size()); + if(n > length) + { return npos; } + + pos = std::min(pos, length - n); + if(n == 0) [[unlikely]] + { return pos; } + + auto const beg(data()); + auto it(beg + pos); + for(;; --it) + { + if(*it == *s && traits_type::compare(it, s, n) == 0) + { return it - beg; } + else if(it == beg) + { break; } + } + return npos; + } + + [[gnu::const]] + constexpr size_t rfind(value_type const c, size_type const pos = npos) const noexcept + { + auto length(size()); + if(length) + { + auto d(data()); + if(--length > pos) + { length = pos; } + for(++length; length-- > 0; ) + { + if(d[length] == c) + { return length; } + } + } + return npos; + } + + /*** Immutable modifications. ***/ + constexpr native_persistent_string substr(size_type const pos = 0, size_type const count = npos) const + { return { *this, pos, count }; } + + /*** Mutations. ***/ + constexpr native_persistent_string& operator =(native_persistent_string const &rhs) + { + if(this == &rhs) + { return *this; } + + destroy(); + + store = rhs.store; + if(rhs.get_category() == category::large_owned) + { + const_cast(rhs).store.large.set_category(category::large_shared); + store.large.set_category(category::large_shared); + } + + return *this; + } + + constexpr native_persistent_string& operator =(const_pointer_type const rhs) + { + destroy(); + + auto const length(traits_type::length(rhs)); + if(length <= max_small_size) + { init_small(rhs, length); } + else + { init_large_owned(rhs, length); } + + return *this; + } + + constexpr native_persistent_string& operator =(native_transient_string const &rhs) + { + destroy(); + + auto const length(rhs.size()); + if(length <= max_small_size) + { init_small(rhs.data(), length); } + else + { init_large_owned(rhs.data(), length); } + + return *this; + } + + /*** Comparisons. ***/ + [[gnu::const]] + constexpr native_bool operator !=(native_persistent_string const &s) const noexcept + { + auto const length(size()); + return length != s.size() || traits_type::compare(data(), s.data(), length); + } + [[gnu::const]] + constexpr native_bool operator ==(native_persistent_string const &s) const noexcept + { return !(*this != s); } + + [[gnu::const, gnu::nonnull (2)]] + constexpr native_bool operator !=(const_pointer_type const s) const noexcept + { + auto const length(traits_type::length(s)); + return size() != length || traits_type::compare(data(), s, length); + } + [[gnu::const, gnu::nonnull (2)]] + constexpr native_bool operator ==(const_pointer_type const s) const noexcept + { return !(*this != s); } + + [[gnu::const]] + constexpr int compare(native_persistent_string const &s) const + { + auto const length(size()); + auto const s_length(s.size()); + auto const common_length(std::min(length, s_length)); + + int r(traits_type::compare(data(), s.data(), common_length)); + r = !r ? static_cast(length - s_length) : r; + return r; + } + + /*** Iterators. ***/ + [[gnu::const]] + constexpr const_iterator begin() const noexcept + { return data(); } + + [[gnu::const]] + constexpr const_iterator cbegin() const noexcept + { return begin(); } + + [[gnu::const]] + constexpr const_iterator end() const noexcept + { return data() + size(); } + + [[gnu::const]] + constexpr const_iterator cend() const + { return end(); } + + [[gnu::const]] + constexpr const_reverse_iterator rbegin() const noexcept + { return const_reverse_iterator(end()); } + + [[gnu::const]] + constexpr const_reverse_iterator crbegin() const noexcept + { return rbegin(); } + + [[gnu::const]] + constexpr const_reverse_iterator rend() const noexcept + { return const_reverse_iterator(begin()); } + + [[gnu::const]] + constexpr const_reverse_iterator crend() const noexcept + { return rend(); } + + /*** Conversions. ***/ + constexpr operator native_persistent_string_view() const + { return { data(), size() }; } + + /*** Hashing. ***/ + constexpr native_integer to_hash() const noexcept + { + if(store.hash != 0) + { return store.hash; } + + /* https://github.com/openjdk/jdk/blob/7e30130e354ebfed14617effd2a517ab2f4140a5/src/java.base/share/classes/java/lang/StringLatin1.java#L194 */ + auto const ptr(data()); + for(size_t i{}; i != size(); ++i) + { store.hash = 31 * store.hash + (ptr[i] & 0xff); } + return store.hash; + } + + private: + static constexpr native_bool is_little_endian{ std::endian::native == std::endian::little }; + + enum class category : uint8_t + { + small = 0, + large_shared = is_little_endian ? 0b10000000 : 0b00000001, + large_owned = is_little_endian ? 0b11000000 : 0b00000011 + }; + + struct large_storage + { + pointer_type data; + size_type size; + size_type extra; + + constexpr void set_category(category const new_category) noexcept + { extra = static_cast(new_category) << category_shift; } + }; + + static constexpr uint8_t last_char_index{ sizeof(large_storage) - 1 }; + static constexpr uint8_t max_small_size{ last_char_index / sizeof(value_type) }; + /* The size is shifted to/from storage, to account for the 2 extra data bits. */ + static constexpr uint8_t small_shift{ is_little_endian ? 0 : 2 }; + static constexpr uint8_t category_extraction_mask{ is_little_endian ? 0b11000000 : 0b00000011 }; + static constexpr uint8_t category_shift{ (sizeof(size_type) - 1) * 8 }; + + /* Our storage provides three ways of accessing the same data: + * 1. Direct bytes (used to access the right-most flag byte) + * 2. In-place char buffer (used for small categories) + * 3. As a large_storage instance, containing a pointer, size, and capacity + */ + struct storage : allocator_type + { + /* TODO: What if we store a max of 22 chars and dedicate a byte for flags with no masking? */ + union + { + uint8_t bytes[sizeof(large_storage)]; + value_type small[sizeof(large_storage) / sizeof(value_type)]; + large_storage large; + }; + /* TODO: Benchmark benefit of storing this hash vs calculating it each time. */ + mutable native_integer hash{}; + }; + + constexpr void destroy() + { + /* NOTE: No performance difference between if/switch here. */ + if(get_category() == category::large_owned) + { allocator_traits::deallocate(store, store.large.data, store.large.size + 1); } + } + + [[gnu::always_inline, gnu::flatten, gnu::hot, gnu::const]] + constexpr category get_category() const noexcept + { return static_cast(store.bytes[last_char_index] & category_extraction_mask); } + + [[gnu::always_inline, gnu::flatten, gnu::hot, gnu::const]] + constexpr size_type get_small_size() const noexcept + { + assert(get_category() == category::small); + auto const small_shifted(static_cast(store.small[max_small_size]) >> small_shift); + assert(max_small_size >= small_shifted); + return max_small_size - small_shifted; + } + + [[gnu::always_inline, gnu::flatten, gnu::hot]] + constexpr void set_small_size(size_type const s) noexcept + { + assert(s <= max_small_size); + store.small[s] = 0; + store.small[max_small_size] = value_type((max_small_size - s) << small_shift); + assert(get_category() == category::small && size() == s); + } + + [[gnu::always_inline, gnu::flatten, gnu::hot]] + constexpr void init_small(const_pointer_type const data, uint8_t const size) noexcept + { + /* If `data` is word-aligned, we can do three quick word copies. */ + if((std::bit_cast(data) & (sizeof(size_type) - 1)) == 0) + { + auto const aligned_data(std::assume_aligned(data)); + uint8_t const byte_size(size * sizeof(value_type)); + constexpr uint8_t word_width{ sizeof(size_type) }; + /* NOTE: We're writing in reverse order here, but it uses one less instruction and + * is marginally faster than duplicating the code each each case to write in order. */ + switch((byte_size + word_width - 1) / word_width) + { + case 3: + store.large.extra = std::bit_cast(aligned_data)[2]; + case 2: + store.large.size = std::bit_cast(aligned_data)[1]; + case 1: + store.large.data = std::bit_cast(aligned_data)[0]; + case 0: + break; + } + } + else + { traits_type::copy(store.small, data, size); } + + set_small_size(size); + } + + [[gnu::always_inline, gnu::flatten, gnu::hot]] + constexpr void init_small + ( + const_pointer_type const lhs, uint8_t const lhs_size, + const_pointer_type const rhs, uint8_t const rhs_size + ) noexcept + { + assert(lhs_size + rhs_size <= max_small_size); + traits_type::copy(store.small, lhs, lhs_size); + traits_type::copy(store.small + lhs_size, rhs, rhs_size); + set_small_size(lhs_size + rhs_size); + } + + [[gnu::always_inline, gnu::flatten, gnu::hot]] + constexpr void init_large_shared(const_pointer_type const data, size_type const size) noexcept + { + /* NOTE: This is likely NOT null-terminated. We need to look out for this in c_str(). */ + assert(max_small_size < size); + store.large.data = const_cast(data); + store.large.size = size; + store.large.set_category(category::large_shared); + } + + [[gnu::always_inline, gnu::flatten, gnu::hot]] + constexpr void init_large_owned(const_pointer_type const data, size_type const size) noexcept + { + assert(max_small_size < size); + /* TODO: Apply gnu::malloc to this fn. */ + store.large.data = std::assume_aligned(store.allocate(size + 1)); + traits_type::copy(store.large.data, data, size); + store.large.data[size] = 0; + store.large.size = size; + store.large.set_category(category::large_owned); + } + + [[gnu::always_inline, gnu::flatten, gnu::hot]] + constexpr void init_large_owned + ( + const_pointer_type const lhs, size_type const lhs_size, + const_pointer_type const rhs, size_type const rhs_size + ) noexcept + { + auto const size(lhs_size + rhs_size); + assert(max_small_size < size); + store.large.data = std::assume_aligned(store.allocate(size + 1)); + traits_type::copy(store.large.data, lhs, lhs_size); + traits_type::copy(store.large.data + lhs_size, rhs, rhs_size); + store.large.data[size] = 0; + store.large.size = size; + store.large.set_category(category::large_owned); + } + + storage store; + }; + + [[gnu::const]] + constexpr native_bool operator < + (native_persistent_string const &lhs, native_persistent_string const &rhs) noexcept + { return lhs.compare(rhs) < 0; } + + constexpr native_persistent_string operator + + (native_persistent_string const &lhs, native_persistent_string const &rhs) noexcept + { return { lhs.data(), lhs.size(), rhs.data(), rhs.size() }; } + + constexpr native_persistent_string operator + + (native_persistent_string::const_pointer_type const lhs, native_persistent_string const &rhs) noexcept + { return { lhs, native_persistent_string::traits_type::length(lhs), rhs.data(), rhs.size() }; } + + constexpr native_persistent_string operator + + (native_persistent_string const &lhs, native_persistent_string::const_pointer_type const rhs) noexcept + { return { lhs.data(), lhs.size(), rhs, native_persistent_string::traits_type::length(rhs) }; } + + constexpr native_persistent_string operator + + (native_persistent_string const &lhs, native_persistent_string::value_type const rhs) noexcept + { return { lhs.data(), lhs.size(), &rhs, 1 }; } + + constexpr std::ostream& operator <<(std::ostream &os, native_persistent_string const &s) + { return os << static_cast(s); } +} + +template <> +struct fmt::formatter : private formatter +{ + using formatter::parse; + + template + auto format(jank::native_persistent_string const &s, Context &ctx) const + { return formatter::format({ s.data(), s.size() }, ctx); } +}; + +namespace std +{ + template <> + struct hash + { + size_t operator()(jank::native_persistent_string const &s) const + { return s.to_hash(); } + }; + + template <> + struct formatter : formatter + { + template + auto format(jank::native_persistent_string const &s, Context &ctx) const + { return formatter::format({ s.data(), s.size() }, ctx); } + }; +} diff --git a/include/cpp/jank/profile/time.hpp b/include/cpp/jank/profile/time.hpp index f0321df3a..12e8a81ce 100644 --- a/include/cpp/jank/profile/time.hpp +++ b/include/cpp/jank/profile/time.hpp @@ -5,18 +5,18 @@ namespace jank::profile { void configure(util::cli::options const &opts); - void enter(native_string_view const ®ion); - void exit(native_string_view const ®ion); - void report(native_string_view const &boundary); + void enter(native_persistent_string_view const ®ion); + void exit(native_persistent_string_view const ®ion); + void report(native_persistent_string_view const &boundary); struct timer { timer() = delete; - timer(native_string_view const ®ion); + timer(native_persistent_string_view const ®ion); ~timer(); - void report(native_string_view const &boundary) const; + void report(native_persistent_string_view const &boundary) const; - native_string region; + native_persistent_string region; }; } diff --git a/include/cpp/jank/read/lex.hpp b/include/cpp/jank/read/lex.hpp index 08defe9c1..d10b328f2 100644 --- a/include/cpp/jank/read/lex.hpp +++ b/include/cpp/jank/read/lex.hpp @@ -42,13 +42,13 @@ namespace jank::read::lex token(size_t const p, token_kind const k); token(size_t const p, token_kind const k, native_integer const); token(size_t const p, token_kind const k, native_real const); - token(size_t const p, token_kind const k, native_string_view const); + token(size_t const p, token_kind const k, native_persistent_string_view const); token(size_t const p, token_kind const k, bool const); token(size_t const p, size_t const s, token_kind const k); token(size_t const p, size_t const s, token_kind const k, native_integer const); token(size_t const p, size_t const s, token_kind const k, native_real const); - token(size_t const p, size_t const s, token_kind const k, native_string_view const); + token(size_t const p, size_t const s, token_kind const k, native_persistent_string_view const); token(size_t const p, size_t const s, token_kind const k, bool const); bool operator ==(token const &rhs) const; @@ -71,7 +71,7 @@ namespace jank::read::lex no_data, native_integer, native_real, - native_string_view, + native_persistent_string_view, native_bool > data; }; @@ -83,16 +83,16 @@ namespace jank::read { struct error { - error(size_t const s, native_string const &m); - error(size_t const s, size_t const e, native_string const &m); - error(native_string const &m); + error(size_t const s, native_persistent_string const &m); + error(size_t const s, size_t const e, native_persistent_string const &m); + error(native_persistent_string const &m); bool operator ==(error const &rhs) const; bool operator !=(error const &rhs) const; size_t start{}; size_t end{}; - native_string message; + native_persistent_string message; }; std::ostream& operator <<(std::ostream &os, error const &e); } @@ -119,7 +119,7 @@ namespace jank::read::lex processor &p; }; - processor(native_string_view const &f); + processor(native_persistent_string_view const &f); result next(); option peek() const; @@ -131,6 +131,6 @@ namespace jank::read::lex size_t pos{}; /* Whether or not the previous token requires a space after it. */ bool require_space{}; - native_string_view file; + native_persistent_string_view file; }; } diff --git a/include/cpp/jank/runtime/behavior/nameable.hpp b/include/cpp/jank/runtime/behavior/nameable.hpp new file mode 100644 index 000000000..0c5616d06 --- /dev/null +++ b/include/cpp/jank/runtime/behavior/nameable.hpp @@ -0,0 +1,11 @@ +#pragma once + +namespace jank::runtime::behavior +{ + template + concept nameable = requires(T * const t) + { + { t->get_name() } -> std::convertible_to; + { t->get_namespace() } -> std::convertible_to; + }; +} diff --git a/include/cpp/jank/runtime/behavior/seqable.hpp b/include/cpp/jank/runtime/behavior/seqable.hpp index e465b8600..7e1ae6b23 100644 --- a/include/cpp/jank/runtime/behavior/seqable.hpp +++ b/include/cpp/jank/runtime/behavior/seqable.hpp @@ -38,7 +38,7 @@ namespace jank::runtime::behavior namespace detail { template - void to_string(It const &begin, It const &end, native_string_view const open, char const close, fmt::memory_buffer &buff) + void to_string(It const &begin, It const &end, native_persistent_string_view const open, char const close, fmt::memory_buffer &buff) { auto inserter(std::back_inserter(buff)); for(auto const c : open) diff --git a/include/cpp/jank/runtime/context.hpp b/include/cpp/jank/runtime/context.hpp index 1181d06c9..8cdb246f7 100644 --- a/include/cpp/jank/runtime/context.hpp +++ b/include/cpp/jank/runtime/context.hpp @@ -42,14 +42,14 @@ namespace jank::runtime obj::symbol_ptr qualify_symbol(obj::symbol_ptr const &); option find_local(obj::symbol_ptr const &); - result intern_var(obj::symbol_ptr const &); - result intern_var(native_string const &ns, native_string const &name); + result intern_var(obj::symbol_ptr const &); + result intern_var(native_persistent_string const &ns, native_persistent_string const &name); option find_var(obj::symbol_ptr const &); - option find_var(native_string const &ns, native_string const &name); + option find_var(native_persistent_string const &ns, native_persistent_string const &name); - result intern_keyword(obj::symbol const &sym, bool const resolved); - result intern_keyword - (native_string_view const &ns, native_string_view const &name, bool resolved); + result intern_keyword(obj::symbol const &sym, bool const resolved); + result intern_keyword + (native_persistent_string_view const &ns, native_persistent_string_view const &name, bool resolved); object_ptr macroexpand1(object_ptr o); object_ptr macroexpand(object_ptr o); @@ -59,9 +59,9 @@ namespace jank::runtime static object_ptr println(object_ptr more); void eval_prelude(); - object_ptr eval_file(native_string_view const &path); - object_ptr eval_string(native_string_view const &code); - native_vector analyze_string(native_string_view const &code, native_bool const eval = true); + object_ptr eval_file(native_persistent_string_view const &path); + object_ptr eval_string(native_persistent_string_view const &code); + native_vector analyze_string(native_persistent_string_view const &code, native_bool const eval = true); /* Finds the specified module on the class path and loads it. If * the module is already loaded, nothing is done. @@ -74,19 +74,19 @@ namespace jank::runtime * Module /meow.cat refers to module meow.cat * Module meow.cat refers to foo.bar$meow.cat */ - result load_module(native_string_view const &module); + result load_module(native_persistent_string_view const &module); /* Does all the same work as load_module, but also writes compiled files to the file system. */ - result compile_module(native_string_view const &module); + result compile_module(native_persistent_string_view const &module); - void write_module(native_string_view const &module, native_string_view const &contents) const; + void write_module(native_persistent_string_view const &module, native_persistent_string_view const &contents) const; /* Generates a unique name for use with anything from codgen structs, * lifted vars, to shadowed locals. */ - static native_string unique_string(); - static native_string unique_string(native_string_view const &prefix); + static native_persistent_string unique_string(); + static native_persistent_string unique_string(native_persistent_string_view const &prefix); static obj::symbol unique_symbol(); - static obj::symbol unique_symbol(native_string_view const &prefix); + static obj::symbol unique_symbol(native_persistent_string_view const &prefix); folly::Synchronized> namespaces; folly::Synchronized> keywords; @@ -114,9 +114,9 @@ namespace jank::runtime /* TODO: This needs to be a dynamic var. */ bool compiling{}; /* TODO: This needs to be a dynamic var. */ - native_string_view current_module; - native_unordered_map> module_dependencies; - native_string output_dir; + native_persistent_string_view current_module; + native_unordered_map> module_dependencies; + native_persistent_string output_dir; module::loader module_loader; }; } diff --git a/include/cpp/jank/runtime/detail/object_util.hpp b/include/cpp/jank/runtime/detail/object_util.hpp index 56eaf374a..aa823edda 100644 --- a/include/cpp/jank/runtime/detail/object_util.hpp +++ b/include/cpp/jank/runtime/detail/object_util.hpp @@ -5,7 +5,7 @@ namespace jank::runtime::detail { - native_string to_string(object_ptr const o); + native_persistent_string to_string(object_ptr const o); native_integer to_hash(object_ptr const o); native_real to_real(object_ptr const o); void to_string(object_ptr const o, fmt::memory_buffer &buff); @@ -50,7 +50,7 @@ namespace jank { return make_box(r); } [[gnu::always_inline, gnu::flatten, gnu::hot]] - inline auto make_box(native_string_view const &s) + inline auto make_box(native_persistent_string_view const &s) { return make_box(s); } [[gnu::always_inline, gnu::flatten, gnu::hot]] diff --git a/include/cpp/jank/runtime/module/loader.hpp b/include/cpp/jank/runtime/module/loader.hpp index e7236fb3e..9b2b8fa55 100644 --- a/include/cpp/jank/runtime/module/loader.hpp +++ b/include/cpp/jank/runtime/module/loader.hpp @@ -15,18 +15,18 @@ namespace jank::runtime::module object_ptr to_runtime_data() const; /* If the file is within a JAR, this will be the path to the JAR. */ - option archive_path; + option archive_path; /* If there's an archive path, this path is within the archive. Otherwise, it's the * filesystem path. */ - native_string path; + native_persistent_string path; }; - native_string path_to_module(boost::filesystem::path const &path); - native_string module_to_path(native_string_view const &module); - native_string module_to_native_ns(native_string_view const &module); - native_string nest_module(native_string const &module, native_string const &sub); - native_string nest_native_ns(native_string const &native_ns, native_string const &end); - native_bool is_nested_module(native_string const &module); + native_persistent_string path_to_module(boost::filesystem::path const &path); + native_persistent_string module_to_path(native_persistent_string_view const &module); + native_persistent_string module_to_native_ns(native_persistent_string_view const &module); + native_persistent_string nest_module(native_persistent_string const &module, native_persistent_string const &sub); + native_persistent_string nest_native_ns(native_persistent_string const &native_ns, native_persistent_string const &end); + native_bool is_nested_module(native_persistent_string const &module); struct loader { @@ -55,25 +55,25 @@ namespace jank::runtime::module static constexpr char module_separator{ ':' }; #endif - loader(context &rt_ctx, native_string_view const &ps); + loader(context &rt_ctx, native_persistent_string_view const &ps); - native_bool is_loaded(native_string_view const &) const; - void set_loaded(native_string_view const &); - result load_ns(native_string_view const &module); - result load(native_string_view const &module); - result load_pcm(file_entry const &entry) const; - result load_cpp(file_entry const &entry) const; - result load_jank(file_entry const &entry) const; - result load_cljc(file_entry const &entry) const; + native_bool is_loaded(native_persistent_string_view const &) const; + void set_loaded(native_persistent_string_view const &); + result load_ns(native_persistent_string_view const &module); + result load(native_persistent_string_view const &module); + result load_pcm(file_entry const &entry) const; + result load_cpp(file_entry const &entry) const; + result load_jank(file_entry const &entry) const; + result load_cljc(file_entry const &entry) const; object_ptr to_runtime_data() const; context &rt_ctx; - native_string paths; + native_persistent_string paths; /* TODO: These will need synchonization. */ /* This maps module strings to entries. Module strings are like fully qualified Java * class names. */ - native_unordered_map entries; - native_set loaded; + native_unordered_map entries; + native_set loaded; }; } diff --git a/include/cpp/jank/runtime/ns.hpp b/include/cpp/jank/runtime/ns.hpp index fc5e2e013..869364929 100644 --- a/include/cpp/jank/runtime/ns.hpp +++ b/include/cpp/jank/runtime/ns.hpp @@ -24,16 +24,16 @@ namespace jank::runtime var_ptr intern_var(obj::symbol_ptr const &); option find_var(obj::symbol_ptr const &); - result add_alias(obj::symbol_ptr const &sym, native_box const &ns); + result add_alias(obj::symbol_ptr const &sym, native_box const &ns); option find_alias(obj::symbol_ptr const &sym) const; - result refer(obj::symbol_ptr const sym, var_ptr const var); + result refer(obj::symbol_ptr const sym, var_ptr const var); obj::persistent_hash_map_ptr get_mappings() const; /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string() const; + native_persistent_string to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; diff --git a/include/cpp/jank/runtime/obj/cons.hpp b/include/cpp/jank/runtime/obj/cons.hpp index c6069e678..5bf5fa47b 100644 --- a/include/cpp/jank/runtime/obj/cons.hpp +++ b/include/cpp/jank/runtime/obj/cons.hpp @@ -17,7 +17,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string(); + native_persistent_string to_string(); void to_string(fmt::memory_buffer &buff); native_integer to_hash() const; diff --git a/include/cpp/jank/runtime/obj/detail/base_persistent_map.hpp b/include/cpp/jank/runtime/obj/detail/base_persistent_map.hpp index 3ce0af586..2285b0f2a 100644 --- a/include/cpp/jank/runtime/obj/detail/base_persistent_map.hpp +++ b/include/cpp/jank/runtime/obj/detail/base_persistent_map.hpp @@ -63,11 +63,11 @@ namespace jank::runtime::obj::detail } void to_string(fmt::memory_buffer &buff) const { to_string_impl(static_cast(this)->data.begin(), static_cast(this)->data.end(), buff); } - native_string to_string() const + native_persistent_string to_string() const { fmt::memory_buffer buff; to_string_impl(static_cast(this)->data.begin(), static_cast(this)->data.end(), buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } /* TODO: Cache this. */ native_integer to_hash() const diff --git a/include/cpp/jank/runtime/obj/detail/base_persistent_map_sequence.hpp b/include/cpp/jank/runtime/obj/detail/base_persistent_map_sequence.hpp index ac70a8dea..b474118dc 100644 --- a/include/cpp/jank/runtime/obj/detail/base_persistent_map_sequence.hpp +++ b/include/cpp/jank/runtime/obj/detail/base_persistent_map_sequence.hpp @@ -66,11 +66,11 @@ namespace jank::runtime::obj::detail void to_string(fmt::memory_buffer &buff) const { return to_string_impl(buff); } - native_string to_string() const + native_persistent_string to_string() const { fmt::memory_buffer buff; to_string_impl(buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } native_integer to_hash() const diff --git a/include/cpp/jank/runtime/obj/detail/iterator_sequence.hpp b/include/cpp/jank/runtime/obj/detail/iterator_sequence.hpp index 03eb35d80..783d6d992 100644 --- a/include/cpp/jank/runtime/obj/detail/iterator_sequence.hpp +++ b/include/cpp/jank/runtime/obj/detail/iterator_sequence.hpp @@ -45,11 +45,11 @@ namespace jank::runtime::obj::detail void to_string(fmt::memory_buffer &buff) const { return behavior::detail::to_string(begin, end, "(", ')', buff); } - native_string to_string() const + native_persistent_string to_string() const { fmt::memory_buffer buff; behavior::detail::to_string(begin, end, "(", ')', buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } native_integer to_hash() const { return reinterpret_cast(this); } diff --git a/include/cpp/jank/runtime/obj/iterator.hpp b/include/cpp/jank/runtime/obj/iterator.hpp index 6f362ef46..14de6c0e9 100644 --- a/include/cpp/jank/runtime/obj/iterator.hpp +++ b/include/cpp/jank/runtime/obj/iterator.hpp @@ -23,7 +23,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string(); + native_persistent_string to_string(); void to_string(fmt::memory_buffer &buff); native_integer to_hash() const; diff --git a/include/cpp/jank/runtime/obj/jit_function.hpp b/include/cpp/jank/runtime/obj/jit_function.hpp index 495850a04..f6749f844 100644 --- a/include/cpp/jank/runtime/obj/jit_function.hpp +++ b/include/cpp/jank/runtime/obj/jit_function.hpp @@ -23,7 +23,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string(); + native_persistent_string to_string(); void to_string(fmt::memory_buffer &buff); native_integer to_hash() const; diff --git a/include/cpp/jank/runtime/obj/keyword.hpp b/include/cpp/jank/runtime/obj/keyword.hpp index e71aa4136..e79778e49 100644 --- a/include/cpp/jank/runtime/obj/keyword.hpp +++ b/include/cpp/jank/runtime/obj/keyword.hpp @@ -28,7 +28,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string() const; + native_persistent_string to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; @@ -36,8 +36,8 @@ namespace jank::runtime object_ptr with_meta(object_ptr m) const; /* behavior::nameable */ - native_string const& get_name() const; - native_string const& get_namespace() const; + native_persistent_string const& get_name() const; + native_persistent_string const& get_namespace() const; /* behavior::callable */ object_ptr call(object_ptr) const; diff --git a/include/cpp/jank/runtime/obj/list.hpp b/include/cpp/jank/runtime/obj/list.hpp index 1b9bcb081..5c662d14f 100644 --- a/include/cpp/jank/runtime/obj/list.hpp +++ b/include/cpp/jank/runtime/obj/list.hpp @@ -35,7 +35,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string() const; + native_persistent_string to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; diff --git a/include/cpp/jank/runtime/obj/native_array_sequence.hpp b/include/cpp/jank/runtime/obj/native_array_sequence.hpp index 77bf0040c..ffd992936 100644 --- a/include/cpp/jank/runtime/obj/native_array_sequence.hpp +++ b/include/cpp/jank/runtime/obj/native_array_sequence.hpp @@ -20,7 +20,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &o) const; void to_string(fmt::memory_buffer &buff) const; - native_string to_string() const; + native_persistent_string to_string() const; native_integer to_hash(); /* behavior::seqable */ diff --git a/include/cpp/jank/runtime/obj/native_function_wrapper.hpp b/include/cpp/jank/runtime/obj/native_function_wrapper.hpp index 133534406..dba895fde 100644 --- a/include/cpp/jank/runtime/obj/native_function_wrapper.hpp +++ b/include/cpp/jank/runtime/obj/native_function_wrapper.hpp @@ -17,13 +17,16 @@ namespace jank::runtime function_type() = default; template - function_type(R (* const f)(Args...)) : function_type(value_type{ f }) + function_type(R (* const f)(Args...)) + : function_type{ value_type{ f } } { } template - function_type(value_type &&f) : value{ std::move(f) } + function_type(value_type &&f) + : value{ std::move(f) } { } template - function_type(value_type const &f) : value{ f } + function_type(value_type const &f) + : value{ f } { } template @@ -52,7 +55,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string const& to_string() const; + native_persistent_string const& to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; diff --git a/include/cpp/jank/runtime/obj/native_vector_sequence.hpp b/include/cpp/jank/runtime/obj/native_vector_sequence.hpp index 23840323d..723ef3b47 100644 --- a/include/cpp/jank/runtime/obj/native_vector_sequence.hpp +++ b/include/cpp/jank/runtime/obj/native_vector_sequence.hpp @@ -17,7 +17,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &o) const; void to_string(fmt::memory_buffer &buff) const; - native_string to_string() const; + native_persistent_string to_string() const; native_integer to_hash(); /* behavior::seqable */ diff --git a/include/cpp/jank/runtime/obj/nil.hpp b/include/cpp/jank/runtime/obj/nil.hpp index c6142902f..d39a5a956 100644 --- a/include/cpp/jank/runtime/obj/nil.hpp +++ b/include/cpp/jank/runtime/obj/nil.hpp @@ -16,7 +16,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string const& to_string() const; + native_persistent_string const& to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; diff --git a/include/cpp/jank/runtime/obj/number.hpp b/include/cpp/jank/runtime/obj/number.hpp index b523c905c..1c3616145 100644 --- a/include/cpp/jank/runtime/obj/number.hpp +++ b/include/cpp/jank/runtime/obj/number.hpp @@ -17,7 +17,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string() const; + native_persistent_string to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; @@ -37,7 +37,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string() const; + native_persistent_string to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; @@ -61,7 +61,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string() const; + native_persistent_string to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; diff --git a/include/cpp/jank/runtime/obj/persistent_vector_sequence.hpp b/include/cpp/jank/runtime/obj/persistent_vector_sequence.hpp index c465de30e..7da6f4b45 100644 --- a/include/cpp/jank/runtime/obj/persistent_vector_sequence.hpp +++ b/include/cpp/jank/runtime/obj/persistent_vector_sequence.hpp @@ -25,7 +25,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; void to_string(fmt::memory_buffer &buff) const; - native_string to_string() const; + native_persistent_string to_string() const; native_integer to_hash() const; /* behavior::countable */ diff --git a/include/cpp/jank/runtime/obj/range.hpp b/include/cpp/jank/runtime/obj/range.hpp index 368073d63..ba9612425 100644 --- a/include/cpp/jank/runtime/obj/range.hpp +++ b/include/cpp/jank/runtime/obj/range.hpp @@ -18,7 +18,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string(); + native_persistent_string to_string(); void to_string(fmt::memory_buffer &buff); native_integer to_hash() const; diff --git a/include/cpp/jank/runtime/obj/set.hpp b/include/cpp/jank/runtime/obj/set.hpp index 6498a7b27..a99cc0134 100644 --- a/include/cpp/jank/runtime/obj/set.hpp +++ b/include/cpp/jank/runtime/obj/set.hpp @@ -25,7 +25,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string() const; + native_persistent_string to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; diff --git a/include/cpp/jank/runtime/obj/string.hpp b/include/cpp/jank/runtime/obj/string.hpp index eb9cf2472..f188c7d17 100644 --- a/include/cpp/jank/runtime/obj/string.hpp +++ b/include/cpp/jank/runtime/obj/string.hpp @@ -13,17 +13,17 @@ namespace jank::runtime static_object() = default; static_object(static_object &&) = default; static_object(static_object const &) = default; - static_object(native_string const &d); - static_object(native_string &&d); + static_object(native_persistent_string const &d); + static_object(native_persistent_string &&d); /* behavior::objectable */ native_bool equal(object const &) const; - native_string const& to_string() const; + native_persistent_string const& to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; - result, native_string> substring(native_integer start) const; - result, native_string> substring(native_integer start, native_integer end) const; + result, native_persistent_string> substring(native_integer start) const; + result, native_persistent_string> substring(native_integer start, native_integer end) const; /* Returns -1 when not found. Turns the arg into a string, so it accepts anything. * Searches for the whole string, not just a char. */ @@ -34,7 +34,7 @@ namespace jank::runtime size_t count() const; object base{ object_type::string }; - native_string data; + native_persistent_string data; }; namespace obj diff --git a/include/cpp/jank/runtime/obj/symbol.hpp b/include/cpp/jank/runtime/obj/symbol.hpp index 805df8128..44231b680 100644 --- a/include/cpp/jank/runtime/obj/symbol.hpp +++ b/include/cpp/jank/runtime/obj/symbol.hpp @@ -20,17 +20,17 @@ namespace jank::runtime static_object(static_object &&) = default; static_object(static_object const &) = default; static_object(object &&base); - static_object(native_string const &d); - static_object(native_string &&d); - static_object(native_string const &ns, native_string const &n); - static_object(native_string &&ns, native_string &&n); + static_object(native_persistent_string const &d); + static_object(native_persistent_string &&d); + static_object(native_persistent_string const &ns, native_persistent_string const &n); + static_object(native_persistent_string &&ns, native_persistent_string &&n); static_object& operator=(static_object const&) = default; static_object& operator=(static_object &&) = default; /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string() const; + native_persistent_string to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; @@ -41,15 +41,15 @@ namespace jank::runtime object_ptr with_meta(object_ptr m) const; /* behavior::nameable */ - native_string const& get_name() const; - native_string const& get_namespace() const; + native_persistent_string const& get_name() const; + native_persistent_string const& get_namespace() const; bool operator ==(static_object const &rhs) const; bool operator <(static_object const &rhs) const; object base{ object_type::symbol }; - native_string ns; - native_string name; + native_persistent_string ns; + native_persistent_string name; option meta; }; diff --git a/include/cpp/jank/runtime/obj/vector.hpp b/include/cpp/jank/runtime/obj/vector.hpp index f32470356..53aea33f7 100644 --- a/include/cpp/jank/runtime/obj/vector.hpp +++ b/include/cpp/jank/runtime/obj/vector.hpp @@ -27,7 +27,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string() const; + native_persistent_string to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; diff --git a/include/cpp/jank/runtime/object.hpp b/include/cpp/jank/runtime/object.hpp index b3e761bfa..a333f9bd8 100644 --- a/include/cpp/jank/runtime/object.hpp +++ b/include/cpp/jank/runtime/object.hpp @@ -56,7 +56,7 @@ namespace jank::runtime concept objectable = requires(T * const t) { { t->equal(std::declval()) } -> std::convertible_to; - { t->to_string() } -> std::convertible_to; + { t->to_string() } -> std::convertible_to; { t->to_string(std::declval()) } -> std::same_as; { t->to_hash() } -> std::convertible_to; { t->base } -> std::same_as; diff --git a/include/cpp/jank/runtime/seq.hpp b/include/cpp/jank/runtime/seq.hpp index 62429888a..70293fd35 100644 --- a/include/cpp/jank/runtime/seq.hpp +++ b/include/cpp/jank/runtime/seq.hpp @@ -32,11 +32,11 @@ namespace jank::runtime template requires behavior::sequenceable - native_string to_string(native_box const s) + native_persistent_string to_string(native_box const s) { fmt::memory_buffer buff; detail::to_string(s, buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } template diff --git a/include/cpp/jank/runtime/util.hpp b/include/cpp/jank/runtime/util.hpp index bd523a156..4c42deb49 100644 --- a/include/cpp/jank/runtime/util.hpp +++ b/include/cpp/jank/runtime/util.hpp @@ -31,6 +31,6 @@ namespace jank::runtime inline auto truthy(native_box const &d) { return truthy(d.data); } } - native_string munge(native_string const &o); + native_persistent_string munge(native_persistent_string const &o); object_ptr munge(object_ptr o); } diff --git a/include/cpp/jank/runtime/var.hpp b/include/cpp/jank/runtime/var.hpp index 60745fe96..c9fc233cc 100644 --- a/include/cpp/jank/runtime/var.hpp +++ b/include/cpp/jank/runtime/var.hpp @@ -26,7 +26,7 @@ namespace jank::runtime /* behavior::objectable */ native_bool equal(object const &) const; - native_string to_string() const; + native_persistent_string to_string() const; void to_string(fmt::memory_buffer &buff) const; native_integer to_hash() const; diff --git a/include/cpp/jank/type.hpp b/include/cpp/jank/type.hpp index abef9f62e..7e6cf9eff 100644 --- a/include/cpp/jank/type.hpp +++ b/include/cpp/jank/type.hpp @@ -22,7 +22,7 @@ namespace jank using native_integer = long long; using native_real = long double; using native_bool = bool; - using native_string_view = std::string_view; + using native_persistent_string_view = std::string_view; template using native_vector = folly::fbvector>; @@ -41,12 +41,11 @@ namespace jank > using native_unordered_map = boost::unordered_map >>; -} -/* TODO: Folly strings leak memory, since they're not using the GC and the GC isn't - * running destructors. */ + using native_transient_string = std::string; +} -/* XXX: native_string.hpp includes this file to learn about integer types, but we also include it - * to forward our string type. Pragma once allows this to work, but we need to make sure the order - * is right. */ -#include +/* NOTE: native_persistent_string.hpp includes this file to learn about integer + * types, but we also include it to forward our string type. Pragma once allows + * this to work, but we need to make sure the order is right. */ +#include diff --git a/include/cpp/jank/util/cli.hpp b/include/cpp/jank/util/cli.hpp index 73f55b12f..732fcbbe7 100644 --- a/include/cpp/jank/util/cli.hpp +++ b/include/cpp/jank/util/cli.hpp @@ -14,13 +14,13 @@ namespace jank::util::cli struct options { /* Runtime. */ - native_string class_path; + native_transient_string class_path; native_bool profiler_enabled{}; - native_string profiler_file{ "jank.profile" }; + native_transient_string profiler_file{ "jank.profile" }; native_bool gc_incremental{}; /* Compilation. */ - native_string compilation_path{ "classes" }; + native_transient_string compilation_path{ "classes" }; #ifdef JANK_RELEASE native_integer optimization_level{ 3 }; #else @@ -28,11 +28,11 @@ namespace jank::util::cli #endif /* Run command. */ - native_string target_file; + native_transient_string target_file; /* Compile command. */ - native_string target_ns; - native_string target_runtime{ "dynamic" }; + native_transient_string target_ns; + native_transient_string target_runtime{ "dynamic" }; /* REPL command. */ native_bool repl_server{}; diff --git a/include/cpp/jank/util/mapped_file.hpp b/include/cpp/jank/util/mapped_file.hpp index 985eb1a13..91f6ac823 100644 --- a/include/cpp/jank/util/mapped_file.hpp +++ b/include/cpp/jank/util/mapped_file.hpp @@ -20,5 +20,5 @@ namespace jank::util size_t size{}; }; - result map_file(native_string_view const &path); + result map_file(native_persistent_string_view const &path); } diff --git a/include/cpp/jank/util/process_location.hpp b/include/cpp/jank/util/process_location.hpp index a2d188c5b..c4e8dc765 100644 --- a/include/cpp/jank/util/process_location.hpp +++ b/include/cpp/jank/util/process_location.hpp @@ -20,8 +20,8 @@ namespace jank::util if(_NSGetExecutablePath(nullptr, &path_length) != -1 || path_length <= 1) { return none; } - /* XXX: This needs to be a std::string, not a native_string. */ - std::string path(path_length, native_string::value_type{}); + /* XXX: This needs to be a std::string, not a native_persistent_string. */ + std::string path(path_length, native_persistent_string::value_type{}); if(_NSGetExecutablePath(path.data(), &path_length) != 0) { return none; } return boost::filesystem::canonical(path); diff --git a/ray.jank b/ray.jank index 99435f18b..4091a75c6 100644 --- a/ray.jank +++ b/ray.jank @@ -1,3 +1,5 @@ +(ns ray) + ; TODO: jank can't have `or` yet, due to no ; syntax quoting in macros. This program doesn't ; require anything other than logical or on two bools, though. diff --git a/src/cpp/jank/analyze/processor.cpp b/src/cpp/jank/analyze/processor.cpp index 2350757ae..f5772a9bb 100644 --- a/src/cpp/jank/analyze/processor.cpp +++ b/src/cpp/jank/analyze/processor.cpp @@ -144,18 +144,19 @@ namespace jank::analyze auto found_local(current_frame->find_local_or_capture(sym)); if(found_local.is_some()) { - local_frame::register_captures(found_local.unwrap()); + auto &unwrapped_local(found_local.unwrap()); + local_frame::register_captures(unwrapped_local); /* Since we're referring to a local, we're boxed if it is boxed. */ - needs_box |= found_local.unwrap().binding.needs_box; + needs_box |= unwrapped_local.binding.needs_box; /* Captured locals are always boxed, even if the originating local is not. */ - if(!found_local.unwrap().crossed_fns.empty()) + if(!unwrapped_local.crossed_fns.empty()) { needs_box = true; /* Capturing counts as a boxed usage for the originating local. */ - found_local.unwrap().binding.has_boxed_usage = true; + unwrapped_local.binding.has_boxed_usage = true; /* The first time we reference a captured local from within a function, we get here. * We determine that we had to cross one or more function scopes to find the relevant @@ -168,9 +169,9 @@ namespace jank::analyze } if(needs_box) - { found_local.unwrap().binding.has_boxed_usage = true; } + { unwrapped_local.binding.has_boxed_usage = true; } else - { found_local.unwrap().binding.has_unboxed_usage = true; } + { unwrapped_local.binding.has_unboxed_usage = true; } return make_box ( @@ -178,7 +179,7 @@ namespace jank::analyze { expression_base{ {}, expr_type, current_frame, needs_box }, sym, - found_local.unwrap().binding + unwrapped_local.binding } ); } @@ -345,7 +346,7 @@ namespace jank::analyze { return err(error{ "fn missing forms" }); } auto list(full_list); - native_string name; + native_persistent_string name; auto first_elem(list->data.rest().first().unwrap()); if(first_elem->type == runtime::object_type::symbol) { @@ -768,18 +769,18 @@ namespace jank::analyze decltype(expr::native_raw::chunks) chunks; /* TODO: Just use } for end and rely on token parsing info for when that is. * This requires storing line/col start/end meta in each object. */ - constexpr native_string_view interp_start{ "#{" }, interp_end{ "}#" }; - for(size_t it{}; it != native_string::npos; ) + constexpr native_persistent_string_view interp_start{ "#{" }, interp_end{ "}#" }; + for(size_t it{}; it != native_persistent_string::npos; ) { auto const next_start(code_str->data.find(interp_start.data(), it)); - if(next_start == native_string::npos) + if(next_start == native_persistent_string::npos) { /* This is the final chunk. */ - chunks.emplace_back(native_string_view{ code_str->data.data() + it }); + chunks.emplace_back(native_persistent_string_view{ code_str->data.data() + it }); break; } auto const next_end(code_str->data.find(interp_end.data(), next_start)); - if(next_end == native_string::npos) + if(next_end == native_persistent_string::npos) { return err(error{ fmt::format("no matching {} found for native/raw interpolation", interp_end) }); } read::lex::processor l_prc @@ -798,7 +799,7 @@ namespace jank::analyze { return result.expect_err_move(); } if(next_start - it > 0) - { chunks.emplace_back(native_string_view{ code_str->data.data() + it, next_start - it }); } + { chunks.emplace_back(native_persistent_string_view{ code_str->data.data() + it, next_start - it }); } chunks.emplace_back(result.expect_ok()); it = next_end + interp_end.size(); diff --git a/src/cpp/jank/codegen/processor.cpp b/src/cpp/jank/codegen/processor.cpp index d6fce71fc..4f1785342 100644 --- a/src/cpp/jank/codegen/processor.cpp +++ b/src/cpp/jank/codegen/processor.cpp @@ -53,10 +53,10 @@ namespace jank::codegen * But our runtime requires params to be const&, so we can't mutate them; we need to shadow * them. So, for tail recursive fns, we name the params with this suffix and then define * the actual param names as mutable locals outside of the while loop. */ - constexpr native_string_view const recur_suffix{ "__recur" }; + constexpr native_persistent_string_view const recur_suffix{ "__recur" }; /* TODO: Consider making this a on the typed object: the C++ name. */ - native_string_view gen_constant_type(runtime::object_ptr const o, bool const boxed) + native_persistent_string_view gen_constant_type(runtime::object_ptr const o, bool const boxed) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wswitch-enum" @@ -189,11 +189,11 @@ namespace jank::codegen ); } - native_string boxed_local_name(native_string const &local_name) + native_persistent_string boxed_local_name(native_persistent_string const &local_name) { return local_name + "__boxed"; } } - handle::handle(native_string const &name, bool const boxed) + handle::handle(native_persistent_string const &name, bool const boxed) { if(boxed) { @@ -206,10 +206,10 @@ namespace jank::codegen boxed_name = fmt::format("jank::make_box({})", unboxed_name); } } - handle::handle(native_string const &boxed_name) + handle::handle(native_persistent_string const &boxed_name) : boxed_name{ boxed_name }, unboxed_name{ boxed_name } { } - handle::handle(native_string const &boxed_name, native_string const &unboxed_name) + handle::handle(native_persistent_string const &boxed_name, native_persistent_string const &unboxed_name) : boxed_name{ boxed_name }, unboxed_name{ unboxed_name } { if(this->boxed_name.empty()) @@ -231,7 +231,7 @@ namespace jank::codegen { unboxed_name = runtime::munge(binding.name->name); } } - native_string handle::str(bool const needs_box) const + native_persistent_string handle::str(bool const needs_box) const { if(needs_box) { @@ -247,7 +247,7 @@ namespace jank::codegen ( runtime::context &rt_ctx, analyze::expression_ptr const &expr, - native_string_view const &module, + native_persistent_string_view const &module, compilation_target const target ) : rt_ctx{ rt_ctx }, @@ -262,7 +262,7 @@ namespace jank::codegen ( runtime::context &rt_ctx, analyze::expr::function const &expr, - native_string_view const &module, + native_persistent_string_view const &module, compilation_target const target ) : rt_ctx{ rt_ctx }, @@ -379,9 +379,9 @@ namespace jank::codegen void processor::format_elided_var ( - native_string_view const &start, - native_string_view const &end, - native_string_view const &ret_tmp, + native_persistent_string_view const &start, + native_persistent_string_view const &end, + native_persistent_string_view const &ret_tmp, native_vector> const &arg_exprs, analyze::expr::function_arity const &fn_arity, bool const arg_box_needed, @@ -395,7 +395,7 @@ namespace jank::codegen { arg_tmps.emplace_back(gen(arg_expr, fn_arity, arg_box_needed).unwrap()); } auto inserter(std::back_inserter(body_buffer)); - native_string_view ret_box; + native_persistent_string_view ret_box; if(ret_box_needed) { ret_box = "jank::make_box("; } fmt::format_to(inserter, "auto const {}({}{}", ret_tmp, ret_box, start); @@ -412,8 +412,8 @@ namespace jank::codegen void processor::format_direct_call ( - native_string const &source_tmp, - native_string_view const &ret_tmp, + native_persistent_string const &source_tmp, + native_persistent_string_view const &ret_tmp, native_vector> const &arg_exprs, analyze::expr::function_arity const &fn_arity, bool const arg_box_needed @@ -441,8 +441,8 @@ namespace jank::codegen void processor::format_dynamic_call ( - native_string const &source_tmp, - native_string_view const &ret_tmp, + native_persistent_string const &source_tmp, + native_persistent_string_view const &ret_tmp, native_vector> const &arg_exprs, analyze::expr::function_arity const &fn_arity, bool const arg_box_needed @@ -1044,7 +1044,7 @@ namespace jank::codegen size_t interpolated_chunk_it{}; for(auto const &chunk : expr.chunks) { - auto const * const code(boost::get(&chunk)); + auto const * const code(boost::get(&chunk)); if(code != nullptr) { fmt::format_to(inserter, "{}", *code); } else @@ -1061,7 +1061,7 @@ namespace jank::codegen return ret_tmp; } - native_string processor::declaration_str() + native_persistent_string processor::declaration_str() { if(!generated_declaration) { @@ -1071,11 +1071,11 @@ namespace jank::codegen generated_declaration = true; } - native_string ret; + native_transient_string ret; ret.reserve(header_buffer.size() + body_buffer.size() + footer_buffer.size()); - ret += native_string_view{ header_buffer.data(), header_buffer.size() }; - ret += native_string_view{ body_buffer.data(), body_buffer.size() }; - ret += native_string_view{ footer_buffer.data(), footer_buffer.size() }; + ret += native_persistent_string_view{ header_buffer.data(), header_buffer.size() }; + ret += native_persistent_string_view{ body_buffer.data(), body_buffer.size() }; + ret += native_persistent_string_view{ footer_buffer.data(), footer_buffer.size() }; //std::cout << ret << std::endl; return ret; } @@ -1267,7 +1267,7 @@ namespace jank::codegen else if(!highest_fixed_arity || highest_fixed_arity->fn_ctx->param_count < arity.fn_ctx->param_count) { highest_fixed_arity = &arity; } - native_string_view recur_suffix; + native_persistent_string_view recur_suffix; if(arity.fn_ctx->is_tail_recursive) { recur_suffix = detail::recur_suffix; } @@ -1368,7 +1368,7 @@ namespace jank::codegen { fmt::format_to(inserter, "}}"); } } - native_string processor::expression_str(bool const box_needed) + native_persistent_string processor::expression_str(bool const box_needed) { auto const module_ns(runtime::module::module_to_native_ns(module)); @@ -1376,7 +1376,7 @@ namespace jank::codegen { auto inserter(std::back_inserter(expression_buffer)); - native_string_view close = ").data"; + native_persistent_string_view close = ").data"; if(box_needed) { fmt::format_to @@ -1422,7 +1422,7 @@ namespace jank::codegen return { expression_buffer.data(), expression_buffer.size() }; } - native_string processor::module_init_str(native_string_view const &module) + native_persistent_string processor::module_init_str(native_persistent_string_view const &module) { fmt::memory_buffer module_buffer; auto inserter(std::back_inserter(module_buffer)); @@ -1445,7 +1445,7 @@ namespace jank::codegen fmt::format_to(inserter, "static void __init(){{"); fmt::format_to(inserter, "jank::profile::timer __timer{{ \"ns __init\" }};"); - fmt::format_to(inserter, "constexpr auto const deps(jank::util::make_array("); + fmt::format_to(inserter, "constexpr auto const deps(jank::util::make_array("); bool needs_comma{}; for(auto const &dep : rt_ctx.module_dependencies[module]) { @@ -1469,9 +1469,9 @@ namespace jank::codegen /* Namespace */ fmt::format_to(inserter, "}}"); - native_string ret; + native_transient_string ret; ret.reserve(module_buffer.size()); - ret += native_string_view{ module_buffer.data(), module_buffer.size() }; + ret += native_persistent_string_view{ module_buffer.data(), module_buffer.size() }; return ret; } } diff --git a/src/cpp/jank/jit/processor.cpp b/src/cpp/jank/jit/processor.cpp index 64546b8a7..26dbc0932 100644 --- a/src/cpp/jank/jit/processor.cpp +++ b/src/cpp/jank/jit/processor.cpp @@ -82,7 +82,7 @@ namespace jank::jit auto const include_path(jank_path / "../include"); - native_string_view O{ "0" }; + native_persistent_string_view O{ "0" }; switch(optimization_level) { case 0: @@ -124,7 +124,7 @@ namespace jank::jit ); } - result, native_string> processor::eval(codegen::processor &cg_prc) const + result, native_persistent_string> processor::eval(codegen::processor &cg_prc) const { profile::timer timer{ "jit eval" }; /* TODO: Improve Cling to accept string_views instead. */ @@ -148,14 +148,14 @@ namespace jank::jit return ok(ret_val); } - void processor::eval_string(native_string const &s) const + void processor::eval_string(native_persistent_string const &s) const { jank::profile::timer timer{ "jit eval_string" }; //fmt::println("JIT eval string {}", s); interpreter->process(static_cast(s)); } - //void processor::load_object(native_string_view const &path) const + //void processor::load_object(native_persistent_string_view const &path) const //{ // auto buf(std::move(llvm::MemoryBuffer::getFile(path.data()).get())); // llvm::cantFail(interpreter->m_Executor->m_JIT->Jit->addObjectFile(std::move(buf))); diff --git a/src/cpp/jank/profile/time.cpp b/src/cpp/jank/profile/time.cpp index 201c60bd0..25730dd8c 100644 --- a/src/cpp/jank/profile/time.cpp +++ b/src/cpp/jank/profile/time.cpp @@ -2,7 +2,7 @@ namespace jank::profile { - constexpr native_string_view tag{ "jank::profile" }; + constexpr native_persistent_string_view tag{ "jank::profile" }; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) static native_bool enabled{}; // NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) @@ -34,31 +34,31 @@ namespace jank::profile } } - void enter(native_string_view const ®ion) + void enter(native_persistent_string_view const ®ion) { if(enabled) { fmt::println(output, "{} {} enter {}", tag, now(), region); } } - void exit(native_string_view const ®ion) + void exit(native_persistent_string_view const ®ion) { if(enabled) { fmt::println(output, "{} {} exit {}", tag, now(), region); } } - void report(native_string_view const &boundary) + void report(native_persistent_string_view const &boundary) { if(enabled) { fmt::println(output, "{} {} report {}", tag, now(), boundary); } } - timer::timer(native_string_view const ®ion) + timer::timer(native_persistent_string_view const ®ion) : region{ region } { enter(region); } timer::~timer() { exit(region); } - void timer::report(native_string_view const &boundary) const + void timer::report(native_persistent_string_view const &boundary) const { jank::profile::report(boundary); } } diff --git a/src/cpp/jank/read/lex.cpp b/src/cpp/jank/read/lex.cpp index bf81f98a6..eebc1f87a 100644 --- a/src/cpp/jank/read/lex.cpp +++ b/src/cpp/jank/read/lex.cpp @@ -9,13 +9,13 @@ using namespace std::string_view_literals; namespace jank::read { - error::error(size_t const s, native_string const &m) + error::error(size_t const s, native_persistent_string const &m) : start{ s }, end{ s }, message{ m } { } - error::error(size_t const s, size_t const e, native_string const &m) + error::error(size_t const s, size_t const e, native_persistent_string const &m) : start{ s }, end{ e }, message{ m } { } - error::error(native_string const &m) + error::error(native_persistent_string const &m) : message{ m } { } @@ -37,7 +37,7 @@ namespace jank::read [&](auto &&arg) { using T = std::decay_t; - if constexpr(std::is_same_v || std::is_same_v) + if constexpr(std::is_same_v || std::is_same_v) { os << std::quoted(arg); } else { os << arg; } @@ -57,7 +57,7 @@ namespace jank::read token::token(size_t const p, token_kind const k, native_real const d) : pos{ p }, kind{ k }, data{ d } { } - token::token(size_t const p, token_kind const k, native_string_view const d) + token::token(size_t const p, token_kind const k, native_persistent_string_view const d) : pos{ p }, kind{ k }, data{ d } { } token::token(size_t const p, token_kind const k, bool const d) @@ -73,7 +73,7 @@ namespace jank::read token::token(size_t const p, size_t const s, token_kind const k, native_real const d) : pos{ p }, size{ s }, kind{ k }, data{ d } { } - token::token(size_t const p, size_t const s, token_kind const k, native_string_view const d) + token::token(size_t const p, size_t const s, token_kind const k, native_persistent_string_view const d) : pos{ p }, size{ s }, kind{ k }, data{ d } { } token::token(size_t const p, size_t const s, token_kind const k, bool const d) @@ -93,7 +93,7 @@ namespace jank::read std::ostream& operator <<(std::ostream &os, token::no_data const &) { return os << ""; } - processor::processor(native_string_view const &f) : file{ f } + processor::processor(native_persistent_string_view const &f) : file{ f } { } processor::iterator::value_type const& processor::iterator::operator *() const @@ -198,7 +198,7 @@ namespace jank::read else { ++pos; - native_string_view const comment + native_persistent_string_view const comment { file.data() + token_start + leading_semis, pos - token_start - leading_semis @@ -280,7 +280,7 @@ namespace jank::read ++pos; } require_space = true; - native_string_view const name{ file.data() + token_start, ++pos - token_start }; + native_persistent_string_view const name{ file.data() + token_start, ++pos - token_start }; if(name[0] == '/' && name.size() > 1) { return err(error{ token_start, "invalid symbol" }); } else if(name == "nil") @@ -315,7 +315,7 @@ namespace jank::read ++pos; } require_space = true; - native_string_view const name{ file.data() + token_start + 1, ++pos - token_start - 1 }; + native_persistent_string_view const name{ file.data() + token_start + 1, ++pos - token_start - 1 }; if(name[0] == '/' && name.size() > 1) { return err(error{ token_start, "invalid keyword: starts with /" }); } else if(name[0] == ':' && name.size() == 1) @@ -364,11 +364,11 @@ namespace jank::read } require_space = true; pos++; - return ok(token{ token_start, pos - token_start, token_kind::string, native_string_view(file.data() + token_start + 1, pos - token_start - 2) }); + return ok(token{ token_start, pos - token_start, token_kind::string, native_persistent_string_view(file.data() + token_start + 1, pos - token_start - 2) }); } default: ++pos; - return err(error{ token_start, native_string{ "unexpected character: " } + file[token_start] }); + return err(error{ token_start, native_persistent_string{ "unexpected character: " } + file[token_start] }); } } diff --git a/src/cpp/jank/read/parse.cpp b/src/cpp/jank/read/parse.cpp index 81fc67cd1..5abf53d73 100644 --- a/src/cpp/jank/read/parse.cpp +++ b/src/cpp/jank/read/parse.cpp @@ -72,7 +72,7 @@ namespace jank::read::parse case lex::token_kind::close_paren: case lex::token_kind::close_curly_bracket: if(expected_closer != token.kind) - { return err(error{ token.pos, native_string{ "unexpected closing character" } }); } + { return err(error{ token.pos, native_persistent_string{ "unexpected closing character" } }); } ++token_current; expected_closer = none; return ok(nullptr); @@ -96,9 +96,8 @@ namespace jank::read::parse return ok(nullptr); default: { - native_string msg{ "unexpected token kind: " }; - msg += magic_enum::enum_name(token.kind); - return err(error{ token.pos, msg }); + native_persistent_string msg{ fmt::format("unexpected token kind: {}", magic_enum::enum_name(token.kind)) }; + return err(error{ token.pos, std::move(msg) }); } } } @@ -188,7 +187,7 @@ namespace jank::read::parse if(val_result.is_err()) { return val_result; } else if(val_result.expect_ok() == nullptr) - { return err(error{ start_token.pos, native_string{ "invalid value after quote" } }); } + { return err(error{ start_token.pos, native_persistent_string{ "invalid value after quote" } }); } return runtime::erase ( @@ -218,10 +217,10 @@ namespace jank::read::parse { auto const token((*token_current).expect_ok()); ++token_current; - auto const sv(boost::get(token.data)); + auto const sv(boost::get(token.data)); auto const slash(sv.find('/')); - native_string ns, name; - if(slash != native_string::npos) + native_persistent_string ns, name; + if(slash != native_persistent_string::npos) { /* If it's only a slash, it's a name. Otherwise, it's a ns/name separator. */ if(sv.size() == 1) @@ -252,14 +251,14 @@ namespace jank::read::parse { auto const token((*token_current).expect_ok()); ++token_current; - auto const sv(boost::get(token.data)); + auto const sv(boost::get(token.data)); /* A :: keyword either resolves to the current ns or an alias, depending on * whether or not it's qualified. */ bool const resolved{ sv[0] != ':' }; auto const slash(sv.find('/')); - native_string ns, name; - if(slash != native_string::npos) + native_persistent_string ns, name; + if(slash != native_persistent_string::npos) { if(resolved) { ns = sv.substr(0, slash); } @@ -294,8 +293,8 @@ namespace jank::read::parse { auto const token(token_current->expect_ok()); ++token_current; - auto const sv(boost::get(token.data)); - return ok(make_box(native_string{ sv.data(), sv.size() })); + auto const sv(boost::get(token.data)); + return ok(make_box(native_persistent_string{ sv.data(), sv.size() })); } processor::iterator processor::begin() diff --git a/src/cpp/jank/runtime/context.cpp b/src/cpp/jank/runtime/context.cpp index f06c5134b..80a24eb50 100644 --- a/src/cpp/jank/runtime/context.cpp +++ b/src/cpp/jank/runtime/context.cpp @@ -138,7 +138,7 @@ namespace jank::runtime } } - option context::find_var(native_string const &ns, native_string const &name) + option context::find_var(native_persistent_string const &ns, native_persistent_string const &name) { return find_var(make_box(ns, name)); } option context::find_local(obj::symbol_ptr const &) @@ -152,7 +152,7 @@ namespace jank::runtime eval_file(src_path.string()); } - object_ptr context::eval_file(native_string_view const &path) + object_ptr context::eval_file(native_persistent_string_view const &path) { auto const file(util::map_file(path)); if(file.is_err()) @@ -160,7 +160,7 @@ namespace jank::runtime return eval_string({ file.expect_ok().head, file.expect_ok().size }); } - object_ptr context::eval_string(native_string_view const &code) + object_ptr context::eval_string(native_persistent_string_view const &code) { profile::timer timer{ "rt eval_string" }; read::lex::processor l_prc{ code }; @@ -201,7 +201,7 @@ namespace jank::runtime return ret; } - native_vector context::analyze_string(native_string_view const &code, native_bool const eval) + native_vector context::analyze_string(native_persistent_string_view const &code, native_bool const eval) { profile::timer timer{ "rt analyze_string" }; read::lex::processor l_prc{ code }; @@ -219,11 +219,11 @@ namespace jank::runtime return ret; } - result context::load_module(native_string_view const &module) + result context::load_module(native_persistent_string_view const &module) { auto const ns(current_ns()); - native_string absolute_module; + native_persistent_string absolute_module; if(module.starts_with('/')) { absolute_module = module.substr(1); } else @@ -238,8 +238,8 @@ namespace jank::runtime try { - result res{ ok() }; - if(absolute_module.find('$') == native_string::npos) + result res{ ok() }; + if(absolute_module.find('$') == native_persistent_string::npos) { res = module_loader.load_ns(absolute_module); } else { res = module_loader.load(absolute_module); } @@ -251,7 +251,7 @@ namespace jank::runtime { return err(detail::to_string(e)); } } - result context::compile_module(native_string_view const &module) + result context::compile_module(native_persistent_string_view const &module) { module_dependencies.clear(); @@ -268,7 +268,7 @@ namespace jank::runtime return res; } - void context::write_module(native_string_view const &module, native_string_view const &contents) const + void context::write_module(native_persistent_string_view const &module, native_persistent_string_view const &contents) const { profile::timer timer{ "write_module" }; boost::filesystem::path const dir{ output_dir }; @@ -284,16 +284,16 @@ namespace jank::runtime } } - native_string context::unique_string() + native_persistent_string context::unique_string() { return unique_string("G_"); } - native_string context::unique_string(native_string_view const &prefix) + native_persistent_string context::unique_string(native_persistent_string_view const &prefix) { static std::atomic_size_t index{ 1 }; return fmt::format(FMT_COMPILE("{}_{}"), prefix.data(), index++); } obj::symbol context::unique_symbol() { return unique_symbol("G_"); } - obj::symbol context::unique_symbol(native_string_view const &prefix) + obj::symbol context::unique_symbol(native_persistent_string_view const &prefix) { return { "", unique_string(prefix) }; } void context::dump() const @@ -362,11 +362,11 @@ namespace jank::runtime ns_ptr context::current_ns() { return expect_object(find_var("clojure.core", "*ns*").unwrap()->get_root()); } - result context::intern_var - (native_string const &ns, native_string const &name) + result context::intern_var + (native_persistent_string const &ns, native_persistent_string const &name) { return intern_var(make_box(ns, name)); } - result context::intern_var(obj::symbol_ptr const &qualified_sym) + result context::intern_var(obj::symbol_ptr const &qualified_sym) { profile::timer timer{ "intern_var" }; if(qualified_sym->ns.empty()) @@ -381,11 +381,11 @@ namespace jank::runtime } /* TODO: Swap these. The other one makes a symbol anyway. */ - result context::intern_keyword(obj::symbol const &sym, bool const resolved) + result context::intern_keyword(obj::symbol const &sym, bool const resolved) { return intern_keyword(sym.ns, sym.name, resolved); } - result context::intern_keyword - (native_string_view const &ns, native_string_view const &name, bool const resolved) + result context::intern_keyword + (native_persistent_string_view const &ns, native_persistent_string_view const &name, bool const resolved) { profile::timer timer{ "rt intern_keyword" }; obj::symbol sym{ ns, name }; diff --git a/src/cpp/jank/runtime/detail/object_util.cpp b/src/cpp/jank/runtime/detail/object_util.cpp index 44c2aa05a..7bc59e028 100644 --- a/src/cpp/jank/runtime/detail/object_util.cpp +++ b/src/cpp/jank/runtime/detail/object_util.cpp @@ -2,7 +2,7 @@ namespace jank::runtime::detail { - native_string to_string(object_ptr const o) + native_persistent_string to_string(object_ptr const o) { return visit_object ( diff --git a/src/cpp/jank/runtime/module/loader.cpp b/src/cpp/jank/runtime/module/loader.cpp index 383d83735..71e9c3c4d 100644 --- a/src/cpp/jank/runtime/module/loader.cpp +++ b/src/cpp/jank/runtime/module/loader.cpp @@ -11,7 +11,7 @@ namespace jank::runtime::module { /* This turns `foo_bar/spam/meow.cljc` into `foo-bar.spam.meow`. */ - native_string path_to_module(boost::filesystem::path const &path) + native_persistent_string path_to_module(boost::filesystem::path const &path) { //static std::regex const underscore{ "_" }; static std::regex const slash{ "/" }; @@ -24,7 +24,7 @@ namespace jank::runtime::module return ret; } - native_string module_to_path(native_string_view const &module) + native_persistent_string module_to_path(native_persistent_string_view const &module) { static std::regex const dash{ "-" }; static std::regex const dot{ "\\." }; @@ -36,7 +36,7 @@ namespace jank::runtime::module return ret; } - native_string module_to_native_ns(native_string_view const &module) + native_persistent_string module_to_native_ns(native_persistent_string_view const &module) { static std::regex const dash{ "-" }; static std::regex const dot{ "\\." }; @@ -52,14 +52,14 @@ namespace jank::runtime::module return ret; } - native_string nest_module(native_string const &module, native_string const &sub) + native_persistent_string nest_module(native_persistent_string const &module, native_persistent_string const &sub) { assert(!module.empty()); assert(!sub.empty()); return module + "$" + sub; } - native_string nest_native_ns(native_string const &native_ns, native_string const &end) + native_persistent_string nest_native_ns(native_persistent_string const &native_ns, native_persistent_string const &end) { assert(!native_ns.empty()); assert(!end.empty()); @@ -67,8 +67,8 @@ namespace jank::runtime::module } /* If it has two or more occurences of $, it's nested. */ - native_bool is_nested_module(native_string const &module) - { return module.find_first_of('$') != module.find_last_of('$'); } + native_bool is_nested_module(native_persistent_string const &module) + { return module.find('$') != module.rfind('$'); } template void visit_jar_entry(file_entry const &entry, F const &fn) @@ -85,7 +85,7 @@ namespace jank::runtime::module void register_entry ( - native_unordered_map &entries, + native_unordered_map &entries, boost::filesystem::path const &resource_path, file_entry const &entry ) @@ -142,7 +142,7 @@ namespace jank::runtime::module } } - void register_directory(native_unordered_map &entries, boost::filesystem::path const &path) + void register_directory(native_unordered_map &entries, boost::filesystem::path const &path) { for(auto const &f : boost::filesystem::recursive_directory_iterator{ path }) { @@ -151,7 +151,7 @@ namespace jank::runtime::module } } - void register_jar(native_unordered_map &entries, native_string_view const &path) + void register_jar(native_unordered_map &entries, native_persistent_string_view const &path) { libzippp::ZipArchive zf{ std::string{ path } }; auto success(zf.open(libzippp::ZipArchive::ReadOnly)); @@ -170,7 +170,7 @@ namespace jank::runtime::module } } - void register_path(native_unordered_map &entries, native_string_view const &path) + void register_path(native_unordered_map &entries, native_persistent_string_view const &path) { /* It's entirely possible to have empty entries in the classpath, mainly due to lazy string * concatenation. We just ignore them. This means something like "::::" is valid. */ @@ -188,22 +188,24 @@ namespace jank::runtime::module { register_entry(entries, "", { none, p.string() }); } } - loader::loader(context &rt_ctx, native_string_view const &ps) - : rt_ctx{ rt_ctx }, paths{ ps } + loader::loader(context &rt_ctx, native_persistent_string_view const &ps) + : rt_ctx{ rt_ctx } { auto const jank_path(jank::util::process_location().unwrap().parent_path()); + native_transient_string paths{ ps }; paths += fmt::format(":{}", (jank_path / "../src/jank").string()); paths += fmt::format(":{}", rt_ctx.output_dir); + this->paths = paths; size_t start{}; size_t i{ paths.find(module_separator, start) }; /* Looks like it's either an empty path list or there's only entry. */ - if(i == native_string_view::npos) + if(i == native_persistent_string_view::npos) { register_path(entries, paths); } else { - while(i != native_string_view::npos) + while(i != native_persistent_string_view::npos) { register_path(entries, paths.substr(start, i - start)); @@ -225,13 +227,13 @@ namespace jank::runtime::module ); } - native_bool loader::is_loaded(native_string_view const &module) const + native_bool loader::is_loaded(native_persistent_string_view const &module) const { return loaded.contains(module); } - void loader::set_loaded(native_string_view const &module) + void loader::set_loaded(native_persistent_string_view const &module) { loaded.emplace(module); } - result loader::load_ns(native_string_view const &module) + result loader::load_ns(native_persistent_string_view const &module) { profile::timer timer{ "load_ns" }; bool const compiling{ rt_ctx.compiling }; @@ -275,13 +277,13 @@ namespace jank::runtime::module return ok(); } - result loader::load(native_string_view const &module) + result loader::load(native_persistent_string_view const &module) { auto const &entry(entries.find(module)); if(entry == entries.end()) { return err(fmt::format("unable to find module: {}", module)); } - result res + result res { err(fmt::format("no sources for registered module: {}", module)) }; bool const compiling{ rt_ctx.compiling }; @@ -312,10 +314,10 @@ namespace jank::runtime::module return ok(); } - result loader::load_pcm(file_entry const &) const + result loader::load_pcm(file_entry const &) const { return err("Not yet implemented: PCM loading"); } - result loader::load_cpp(file_entry const &entry) const + result loader::load_cpp(file_entry const &entry) const { if(entry.archive_path.is_some()) { @@ -337,7 +339,7 @@ namespace jank::runtime::module return ok(); } - result loader::load_jank(file_entry const &entry) const + result loader::load_jank(file_entry const &entry) const { if(entry.archive_path.is_some()) { @@ -354,7 +356,7 @@ namespace jank::runtime::module return ok(); } - result loader::load_cljc(file_entry const &) const + result loader::load_cljc(file_entry const &) const { return err("Not yet implemented: CLJC loading"); } object_ptr loader::to_runtime_data() const diff --git a/src/cpp/jank/runtime/ns.cpp b/src/cpp/jank/runtime/ns.cpp index 35c0f276d..0279c1e53 100644 --- a/src/cpp/jank/runtime/ns.cpp +++ b/src/cpp/jank/runtime/ns.cpp @@ -41,7 +41,7 @@ namespace jank::runtime return { expect_object(*found) }; } - result ns::add_alias(obj::symbol_ptr const &sym, native_box const &ns) + result ns::add_alias(obj::symbol_ptr const &sym, native_box const &ns) { auto locked_aliases(aliases.wlock()); auto const found((*locked_aliases)->data.find(sym)); @@ -61,7 +61,7 @@ namespace jank::runtime return none; } - result ns::refer(obj::symbol_ptr const sym, var_ptr const var) + result ns::refer(obj::symbol_ptr const sym, var_ptr const var) { auto locked_vars(vars.wlock()); if(auto const found = (*locked_vars)->data.find(sym)) @@ -103,7 +103,7 @@ namespace jank::runtime auto const v(expect_object(&o)); return name == v->name; } - native_string ns::to_string() const + native_persistent_string ns::to_string() const /* TODO: Maybe cache this. */ { return name->to_string(); } void ns::to_string(fmt::memory_buffer &buff) const diff --git a/src/cpp/jank/runtime/obj/cons.cpp b/src/cpp/jank/runtime/obj/cons.cpp index 09aacd974..481cbc330 100644 --- a/src/cpp/jank/runtime/obj/cons.cpp +++ b/src/cpp/jank/runtime/obj/cons.cpp @@ -98,7 +98,7 @@ namespace jank::runtime void obj::cons::to_string(fmt::memory_buffer &buff) { runtime::detail::to_string(seq(), buff); } - native_string obj::cons::to_string() + native_persistent_string obj::cons::to_string() { return runtime::detail::to_string(seq()); } native_integer obj::cons::to_hash() const diff --git a/src/cpp/jank/runtime/obj/iterator.cpp b/src/cpp/jank/runtime/obj/iterator.cpp index 6c4451e76..de5651146 100644 --- a/src/cpp/jank/runtime/obj/iterator.cpp +++ b/src/cpp/jank/runtime/obj/iterator.cpp @@ -88,7 +88,7 @@ namespace jank::runtime void obj::iterator::to_string(fmt::memory_buffer &buff) { runtime::detail::to_string(seq(), buff); } - native_string obj::iterator::to_string() + native_persistent_string obj::iterator::to_string() { return runtime::detail::to_string(seq()); } native_integer obj::iterator::to_hash() const diff --git a/src/cpp/jank/runtime/obj/jit_function.cpp b/src/cpp/jank/runtime/obj/jit_function.cpp index d819e19f3..2b9b6f618 100644 --- a/src/cpp/jank/runtime/obj/jit_function.cpp +++ b/src/cpp/jank/runtime/obj/jit_function.cpp @@ -5,7 +5,7 @@ namespace jank::runtime native_bool obj::jit_function::equal(object const &rhs) const { return &base == &rhs; } - native_string obj::jit_function::to_string() + native_persistent_string obj::jit_function::to_string() { return "jit function"; } void obj::jit_function::to_string(fmt::memory_buffer &buff) diff --git a/src/cpp/jank/runtime/obj/keyword.cpp b/src/cpp/jank/runtime/obj/keyword.cpp index e8d786056..23c2cd296 100644 --- a/src/cpp/jank/runtime/obj/keyword.cpp +++ b/src/cpp/jank/runtime/obj/keyword.cpp @@ -23,11 +23,11 @@ namespace jank::runtime } void obj::keyword::to_string(fmt::memory_buffer &buff) const { return to_string_impl(sym, buff); } - native_string obj::keyword::to_string() const + native_persistent_string obj::keyword::to_string() const { fmt::memory_buffer buff; to_string_impl(sym, buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } native_integer obj::keyword::to_hash() const { return reinterpret_cast(this); } @@ -40,10 +40,10 @@ namespace jank::runtime return ret; } - native_string const& obj::keyword::get_name() const + native_persistent_string const& obj::keyword::get_name() const { return sym.name; } - native_string const& obj::keyword::get_namespace() const + native_persistent_string const& obj::keyword::get_namespace() const { return sym.ns; } object_ptr obj::keyword::call(object_ptr const m) const diff --git a/src/cpp/jank/runtime/obj/list.cpp b/src/cpp/jank/runtime/obj/list.cpp index 18e6b4ab9..ab82a8a6a 100644 --- a/src/cpp/jank/runtime/obj/list.cpp +++ b/src/cpp/jank/runtime/obj/list.cpp @@ -46,11 +46,11 @@ namespace jank::runtime void obj::list::to_string(fmt::memory_buffer &buff) const { return behavior::detail::to_string(data.begin(), data.end(), "(", ')', buff); } - native_string obj::list::to_string() const + native_persistent_string obj::list::to_string() const { fmt::memory_buffer buff; behavior::detail::to_string(data.begin(), data.end(), "(", ')', buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } /* TODO: Cache this. */ native_integer obj::list::to_hash() const diff --git a/src/cpp/jank/runtime/obj/native_array_sequence.cpp b/src/cpp/jank/runtime/obj/native_array_sequence.cpp index 2ca52aebe..a9d5baec8 100644 --- a/src/cpp/jank/runtime/obj/native_array_sequence.cpp +++ b/src/cpp/jank/runtime/obj/native_array_sequence.cpp @@ -22,11 +22,11 @@ namespace jank::runtime void obj::native_array_sequence::to_string(fmt::memory_buffer &buff) const { return behavior::detail::to_string(arr + index, arr + size, "(", ')', buff); } - native_string obj::native_array_sequence::to_string() const + native_persistent_string obj::native_array_sequence::to_string() const { fmt::memory_buffer buff; behavior::detail::to_string(arr + index, arr + size, "(", ')', buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } native_integer obj::native_array_sequence::to_hash() { return reinterpret_cast(this); } diff --git a/src/cpp/jank/runtime/obj/native_function_wrapper.cpp b/src/cpp/jank/runtime/obj/native_function_wrapper.cpp index 58e1c715f..4353dbe48 100644 --- a/src/cpp/jank/runtime/obj/native_function_wrapper.cpp +++ b/src/cpp/jank/runtime/obj/native_function_wrapper.cpp @@ -18,9 +18,9 @@ namespace jank::runtime void obj::native_function_wrapper::to_string(fmt::memory_buffer &buff) const { fmt::format_to(std::back_inserter(buff), "function"); } - native_string const& obj::native_function_wrapper::to_string() const + native_persistent_string const& obj::native_function_wrapper::to_string() const { - static native_string const s{ "native_function_wrapper" }; + static native_persistent_string const s{ "native_function_wrapper" }; return s; } diff --git a/src/cpp/jank/runtime/obj/native_vector_sequence.cpp b/src/cpp/jank/runtime/obj/native_vector_sequence.cpp index 04b365375..9226e0911 100644 --- a/src/cpp/jank/runtime/obj/native_vector_sequence.cpp +++ b/src/cpp/jank/runtime/obj/native_vector_sequence.cpp @@ -19,11 +19,11 @@ namespace jank::runtime void obj::native_vector_sequence::to_string(fmt::memory_buffer &buff) const { return behavior::detail::to_string(data.begin(), data.end(), "(", ')', buff); } - native_string obj::native_vector_sequence::to_string() const + native_persistent_string obj::native_vector_sequence::to_string() const { fmt::memory_buffer buff; behavior::detail::to_string(data.begin(), data.end(), "(", ')', buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } native_integer obj::native_vector_sequence::to_hash() diff --git a/src/cpp/jank/runtime/obj/nil.cpp b/src/cpp/jank/runtime/obj/nil.cpp index ef5e3fc09..d557bcea0 100644 --- a/src/cpp/jank/runtime/obj/nil.cpp +++ b/src/cpp/jank/runtime/obj/nil.cpp @@ -15,9 +15,9 @@ namespace jank::runtime native_bool obj::nil::equal(object const &o) const { return &o == &base; } - native_string const& obj::nil::to_string() const + native_persistent_string const& obj::nil::to_string() const { - static native_string s{ "nil" }; + static native_persistent_string s{ "nil" }; return s; } void obj::nil::to_string(fmt::memory_buffer &buff) const diff --git a/src/cpp/jank/runtime/obj/number.cpp b/src/cpp/jank/runtime/obj/number.cpp index da301c621..d4a2c4a76 100644 --- a/src/cpp/jank/runtime/obj/number.cpp +++ b/src/cpp/jank/runtime/obj/number.cpp @@ -39,11 +39,11 @@ namespace jank::runtime void obj::boolean::to_string(fmt::memory_buffer &buff) const { return to_string_impl(data, buff); } - native_string obj::boolean::to_string() const + native_persistent_string obj::boolean::to_string() const { fmt::memory_buffer buff; to_string_impl(data, buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } native_integer obj::boolean::to_hash() const @@ -64,7 +64,7 @@ namespace jank::runtime return data == i->data; } - native_string obj::integer::to_string() const + native_persistent_string obj::integer::to_string() const { return fmt::format(FMT_COMPILE("{}"), data); } void obj::integer::to_string(fmt::memory_buffer &buff) const @@ -94,7 +94,7 @@ namespace jank::runtime return hasher(data) == hasher(r->data); } - native_string obj::real::to_string() const + native_persistent_string obj::real::to_string() const { return fmt::format(FMT_COMPILE("{}"), data); } void obj::real::to_string(fmt::memory_buffer &buff) const diff --git a/src/cpp/jank/runtime/obj/persistent_vector_sequence.cpp b/src/cpp/jank/runtime/obj/persistent_vector_sequence.cpp index b09e8a35c..3a6e59ef3 100644 --- a/src/cpp/jank/runtime/obj/persistent_vector_sequence.cpp +++ b/src/cpp/jank/runtime/obj/persistent_vector_sequence.cpp @@ -23,7 +23,7 @@ namespace jank::runtime buff ); } - native_string obj::persistent_vector_sequence::to_string() const + native_persistent_string obj::persistent_vector_sequence::to_string() const { fmt::memory_buffer buff; behavior::detail::to_string diff --git a/src/cpp/jank/runtime/obj/range.cpp b/src/cpp/jank/runtime/obj/range.cpp index 1a937841f..d8df15be1 100644 --- a/src/cpp/jank/runtime/obj/range.cpp +++ b/src/cpp/jank/runtime/obj/range.cpp @@ -91,7 +91,7 @@ namespace jank::runtime void obj::range::to_string(fmt::memory_buffer &buff) { runtime::detail::to_string(seq(), buff); } - native_string obj::range::to_string() + native_persistent_string obj::range::to_string() { return runtime::detail::to_string(seq()); } native_integer obj::range::to_hash() const diff --git a/src/cpp/jank/runtime/obj/set.cpp b/src/cpp/jank/runtime/obj/set.cpp index 38a1e1bf3..29c1d50f8 100644 --- a/src/cpp/jank/runtime/obj/set.cpp +++ b/src/cpp/jank/runtime/obj/set.cpp @@ -22,11 +22,11 @@ namespace jank::runtime void obj::set::to_string(fmt::memory_buffer &buff) const { return behavior::detail::to_string(data.begin(), data.end(), "#{", '}', buff); } - native_string obj::set::to_string() const + native_persistent_string obj::set::to_string() const { fmt::memory_buffer buff; behavior::detail::to_string(data.begin(), data.end(), "#{", '}', buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } /* TODO: Cache this. */ diff --git a/src/cpp/jank/runtime/obj/string.cpp b/src/cpp/jank/runtime/obj/string.cpp index a471d682c..bd53c8c74 100644 --- a/src/cpp/jank/runtime/obj/string.cpp +++ b/src/cpp/jank/runtime/obj/string.cpp @@ -9,11 +9,11 @@ namespace jank::runtime { - obj::string::static_object(native_string const &d) + obj::string::static_object(native_persistent_string const &d) : data{ d } { } - obj::string::static_object(native_string &&d) + obj::string::static_object(native_persistent_string &&d) : data{ std::move(d) } { } @@ -26,7 +26,7 @@ namespace jank::runtime return data == s->data; } - native_string const& obj::string::to_string() const + native_persistent_string const& obj::string::to_string() const { return data; } void obj::string::to_string(fmt::memory_buffer &buff) const @@ -35,10 +35,10 @@ namespace jank::runtime native_integer obj::string::to_hash() const { return data.to_hash(); } - result obj::string::substring(native_integer start) const + result obj::string::substring(native_integer start) const { return substring(start, static_cast(data.size())); } - result obj::string::substring + result obj::string::substring ( native_integer const start, native_integer const end @@ -59,8 +59,8 @@ namespace jank::runtime native_integer obj::string::first_index_of(object_ptr const c) const { auto const s(runtime::detail::to_string(c)); - auto const found(data.find_first_of(s)); - if(found == native_string::npos) + auto const found(data.find(s)); + if(found == native_persistent_string::npos) { return -1; } return static_cast(found); } @@ -68,8 +68,8 @@ namespace jank::runtime native_integer obj::string::last_index_of(object_ptr const c) const { auto const s(runtime::detail::to_string(c)); - auto const found(data.find_last_of(s)); - if(found == native_string::npos) + auto const found(data.rfind(s)); + if(found == native_persistent_string::npos) { return -1; } return static_cast(found); } diff --git a/src/cpp/jank/runtime/obj/symbol.cpp b/src/cpp/jank/runtime/obj/symbol.cpp index 90b127977..ac9f6d7b3 100644 --- a/src/cpp/jank/runtime/obj/symbol.cpp +++ b/src/cpp/jank/runtime/obj/symbol.cpp @@ -11,7 +11,7 @@ namespace jank::runtime void separate(obj::symbol &sym, S &&s) { auto const found(s.find('/')); - if(found != native_string::npos && s.size() > 1) + if(found != native_persistent_string::npos && s.size() > 1) { sym.ns = s.substr(0, found); sym.name = s.substr(found + 1); @@ -20,15 +20,15 @@ namespace jank::runtime { sym.name = std::forward(s); } } - obj::symbol::static_object(native_string const &d) + obj::symbol::static_object(native_persistent_string const &d) { separate(*this, d); } - obj::symbol::static_object(native_string &&d) + obj::symbol::static_object(native_persistent_string &&d) { separate(*this, std::move(d)); } - obj::symbol::static_object(native_string const &ns, native_string const &n) + obj::symbol::static_object(native_persistent_string const &ns, native_persistent_string const &n) : ns{ ns }, name{ n } { } - obj::symbol::static_object(native_string &&ns, native_string &&n) + obj::symbol::static_object(native_persistent_string &&ns, native_persistent_string &&n) : ns{ std::move(ns) }, name{ std::move(n) } { } @@ -46,8 +46,8 @@ namespace jank::runtime void to_string_impl ( - native_string const &ns, - native_string const &name, + native_persistent_string const &ns, + native_persistent_string const &name, fmt::memory_buffer &buff ) { @@ -58,11 +58,11 @@ namespace jank::runtime } void obj::symbol::to_string(fmt::memory_buffer &buff) const { to_string_impl(ns, name, buff); } - native_string obj::symbol::to_string() const + native_persistent_string obj::symbol::to_string() const { fmt::memory_buffer buff; to_string_impl(ns, name, buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } native_integer obj::symbol::to_hash() const /* TODO: Cache this. */ @@ -76,10 +76,10 @@ namespace jank::runtime return ret; } - native_string const& obj::symbol::get_name() const + native_persistent_string const& obj::symbol::get_name() const { return name; } - native_string const& obj::symbol::get_namespace() const + native_persistent_string const& obj::symbol::get_namespace() const { return ns; } bool obj::symbol::operator ==(obj::symbol const &rhs) const diff --git a/src/cpp/jank/runtime/obj/vector.cpp b/src/cpp/jank/runtime/obj/vector.cpp index f9aff3a14..0783a4acb 100644 --- a/src/cpp/jank/runtime/obj/vector.cpp +++ b/src/cpp/jank/runtime/obj/vector.cpp @@ -47,11 +47,11 @@ namespace jank::runtime void obj::vector::to_string(fmt::memory_buffer &buff) const { return behavior::detail::to_string(data.begin(), data.end(), "[", ']', buff); } - native_string obj::vector::to_string() const + native_persistent_string obj::vector::to_string() const { fmt::memory_buffer buff; behavior::detail::to_string(data.begin(), data.end(), "[", ']', buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } /* TODO: Cache this. */ diff --git a/src/cpp/jank/runtime/util.cpp b/src/cpp/jank/runtime/util.cpp index 0cf5204bb..4e945be1f 100644 --- a/src/cpp/jank/runtime/util.cpp +++ b/src/cpp/jank/runtime/util.cpp @@ -36,7 +36,7 @@ namespace jank::runtime { return o; } } - static native_unordered_map const munge_chars + static native_unordered_map const munge_chars { { '-', "_" }, { ':', "_COLON_" }, @@ -65,7 +65,7 @@ namespace jank::runtime }; /* https://en.cppreference.com/w/cpp/keyword */ - static native_set const cpp_keywords + static native_set const cpp_keywords { "alignas", "alignof", "and", "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", "auto", "bitand", "bitor", "bool", "break", @@ -89,9 +89,9 @@ namespace jank::runtime "xor", "xor_eq" }; - native_string munge(native_string const &o) + native_persistent_string munge(native_persistent_string const &o) { - native_string munged; + native_transient_string munged; for(auto const &c : o) { auto const &replacement(munge_chars.find(c)); diff --git a/src/cpp/jank/runtime/var.cpp b/src/cpp/jank/runtime/var.cpp index c7aa7be18..c799aa48c 100644 --- a/src/cpp/jank/runtime/var.cpp +++ b/src/cpp/jank/runtime/var.cpp @@ -31,12 +31,12 @@ namespace jank::runtime { format_to(std::back_inserter(buff), FMT_COMPILE("#'{}/{}"), n->name->name, name->name); } void var::to_string(fmt::memory_buffer &buff) const { to_string_impl(n, name, buff); } - native_string var::to_string() const + native_persistent_string var::to_string() const /* TODO: Maybe cache this. */ { fmt::memory_buffer buff; to_string_impl(n, name, buff); - return native_string{ buff.data(), buff.size() }; + return native_persistent_string{ buff.data(), buff.size() }; } native_integer var::to_hash() const /* TODO: Cache this. */ diff --git a/src/cpp/jank/util/mapped_file.cpp b/src/cpp/jank/util/mapped_file.cpp index 63f35965d..61c9ea536 100644 --- a/src/cpp/jank/util/mapped_file.cpp +++ b/src/cpp/jank/util/mapped_file.cpp @@ -31,7 +31,7 @@ namespace jank::util { ::close(fd); } } - result map_file(native_string_view const &path) + result map_file(native_persistent_string_view const &path) { if(!boost::filesystem::exists(path.data())) { return err("file doesn't exist"); } diff --git a/src/cpp/main.cpp b/src/cpp/main.cpp index d305a5c31..b6a40f5fc 100644 --- a/src/cpp/main.cpp +++ b/src/cpp/main.cpp @@ -12,6 +12,8 @@ #include #include +#include + #include #include #include @@ -20,40 +22,359 @@ #include #include #include +#include namespace jank { void run(util::cli::options const &opts, runtime::context &rt_ctx) { - { - profile::timer timer{ "require clojure.core" }; - rt_ctx.load_module("/clojure.core").expect_ok(); - } - - { - profile::timer timer{ "eval user code" }; - std::cout << runtime::detail::to_string(rt_ctx.eval_file(opts.target_file)) << std::endl; - } + static_cast(opts); + static_cast(rt_ctx); + //{ + // profile::timer timer{ "require clojure.core" }; + // rt_ctx.load_module("/clojure.core").expect_ok(); + //} //{ - // ankerl::nanobench::Config config; - // config.mMinEpochIterations = 5000000; - // config.mOut = &std::cout; - // config.mWarmup = 10000; - - // auto const hf1(rt_ctx.find_var("clojure.core", "highest-fixed-1").unwrap()->get_root()); - // auto const kw1(rt_ctx.intern_keyword("", "a", true).expect_ok()); - // auto const kw2(rt_ctx.intern_keyword("", "b", true).expect_ok()); - // ankerl::nanobench::Bench().config(config).run - // ( - // "bitmap", - // [&] - // { - // auto const ret(runtime::dynamic_call(hf1, kw1)); - // ankerl::nanobench::doNotOptimizeAway(ret); - // } - // ); + // profile::timer timer{ "eval user code" }; + // std::cout << runtime::detail::to_string(rt_ctx.eval_file(opts.target_file)) << std::endl; //} + + { + ankerl::nanobench::Config config; + config.mMinEpochIterations = 10000000; + config.mOut = &std::cout; + config.mWarmup = 10000; + + auto small("foo"); + auto medium("p0aeoka13scfq4ufg27xlse0y07gjg9v29nonktptjd36jnmlfzpze4qaxztkewq8v36hivq7ieuecvjhp9myn52ubvplrq7ip62oj7qo0n2s8xqgaxc38n70jo3cwdq"); + + using std_string = std::basic_string, native_allocator>; + + std_string small_std{ small }; + native_persistent_string small_persistent{ small }; + folly::fbstring small_folly{ small }; + std_string medium_std{ medium }; + native_persistent_string medium_persistent{ medium }; + folly::fbstring medium_folly{ medium }; + + ankerl::nanobench::Bench().config(config).run + ( + "native_persistent_string small allocation", + [&] + { + native_persistent_string ret{ small }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "std_string small allocation", + [&] + { + std_string ret{ small }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "folly::string small allocation", + [&] + { + folly::fbstring ret{ small }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "native_persistent_string medium allocation", + [&] + { + native_persistent_string ret{ medium }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "std_string medium allocation", + [&] + { + std_string ret{ medium }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "folly::string medium allocation", + [&] + { + folly::fbstring ret{ medium }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + /*** Copy ctor ***/ + + ankerl::nanobench::Bench().config(config).run + ( + "native_persistent_string small copy ctor", + [&] + { + native_persistent_string ret{ small_persistent }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "std_string small copy ctor", + [&] + { + std_string ret{ small_std }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "folly::string small copy ctor", + [&] + { + folly::fbstring ret{ small_folly }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "native_persistent_string medium copy ctor", + [&] + { + native_persistent_string ret{ medium_persistent }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "std_string medium copy ctor", + [&] + { + std_string ret{ medium_std }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "folly::string medium copy ctor", + [&] + { + folly::fbstring ret{ medium_folly }; + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + /*** Find ***/ + + ankerl::nanobench::Bench().config(config).run + ( + "native_persistent_string small find", + [&] + { + auto const ret(small_persistent.find("fo")); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "std_string small find", + [&] + { + auto const ret(small_std.find("fo")); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "folly::string small find", + [&] + { + auto const ret(small_folly.find("fo")); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "native_persistent_string medium find", + [&] + { + auto const ret(medium_persistent.find("kewq")); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "std_string medium find", + [&] + { + auto const ret(medium_std.find("kewq")); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "folly::string medium find", + [&] + { + auto const ret(medium_folly.find("kewq")); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + /*** Substrings. ***/ + + ankerl::nanobench::Bench().config(config).run + ( + "native_persistent_string small substr", + [&] + { + auto const ret(small_persistent.substr(1)); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "std_string small substr", + [&] + { + auto const ret(small_std.substr(1)); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "folly_string small substr", + [&] + { + auto const ret(small_folly.substr(1)); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "native_persistent_string medium substr", + [&] + { + auto const ret(medium_persistent.substr(4, 100)); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "std_string medium substr", + [&] + { + auto const ret(medium_std.substr(4, 100)); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "folly_string medium substr", + [&] + { + auto const ret(medium_folly.substr(4, 100)); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + /*** Comparisons. ***/ + + std_string another_medium_std{ medium }; + native_persistent_string another_medium_persistent{ medium }; + folly::fbstring another_medium_folly{ medium }; + + auto different_medium("p0aeoka13scfq4ufg27xlse0y07gjg9v29nonktptjd36jnmlfzpze4qaxztkewq8v36hivq7ieuecvjhp9myn52ubvplrq7ip62oj7qo0n2s8xqgaxc38nXXXXXXXXX"); + std_string different_medium_std{ different_medium }; + native_persistent_string different_medium_persistent{ different_medium }; + folly::fbstring different_medium_folly{ different_medium }; + + ankerl::nanobench::Bench().config(config).run + ( + "native_persistent_string medium compare same", + [&] + { + auto const ret(medium_persistent == another_medium_persistent); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "std_string medium compare same", + [&] + { + auto const ret(medium_std == another_medium_std); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "folly_string medium compare same", + [&] + { + auto const ret(medium_folly == another_medium_folly); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "native_persistent_string medium compare different", + [&] + { + auto const ret(medium_persistent == different_medium_persistent); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "std_string medium compare different", + [&] + { + auto const ret(medium_std == different_medium_std); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + + ankerl::nanobench::Bench().config(config).run + ( + "folly_string medium compare different", + [&] + { + auto const ret(medium_folly == different_medium_folly); + ankerl::nanobench::doNotOptimizeAway(ret); + } + ); + } } void compile(util::cli::options const &opts, runtime::context &rt_ctx) @@ -81,7 +402,7 @@ namespace jank /* TODO: Multi-line input. */ while(auto const buf = readline("> ")) { - native_string line{ buf }; + std::string line{ buf }; boost::trim(line); if(line.empty()) { continue; } @@ -99,7 +420,7 @@ namespace jank { fmt::println("Exception: {}", e.what()); } catch(jank::runtime::object_ptr const o) { fmt::println("Exception: {}", jank::runtime::detail::to_string(o)); } - catch(jank::native_string const &s) + catch(jank::native_persistent_string const &s) { fmt::println("Exception: {}", s); } catch(jank::read::error const &e) { fmt::println("Read error: {}", e.message); } @@ -149,7 +470,7 @@ catch(std::exception const &e) { fmt::println("Exception: {}", e.what()); } catch(jank::runtime::object_ptr const o) { fmt::println("Exception: {}", jank::runtime::detail::to_string(o)); } -catch(jank::native_string const &s) +catch(jank::native_persistent_string const &s) { fmt::println("Exception: {}", s); } catch(jank::read::error const &e) { fmt::println("Read error: {}", e.message); } diff --git a/src/jank/clojure/core.jank b/src/jank/clojure/core.jank index ef02b8f3a..4445cba5f 100644 --- a/src/jank/clojure/core.jank +++ b/src/jank/clojure/core.jank @@ -307,10 +307,10 @@ x) ([x & more] (let [x-res (gensym)] - (list 'let [x-res x] + (list 'clojure.core/let [x-res x] (list 'if x-res x-res - (cons 'or more)))))) + (cons 'clojure.core/or more)))))) (defmacro or [& args] @@ -323,9 +323,9 @@ x) ([x & more] (let [x-res (gensym)] - (list 'let [x-res x] + (list 'clojure.core/let [x-res x] (list 'if x-res - (cons 'and more) + (cons 'clojure.core/and more) x-res))))) (defmacro and @@ -333,7 +333,7 @@ (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 +; time. If a test returns logical true, cond evaluates and returns ; the value of the corresponding expr and doesn't evaluate any of the ; other tests or exprs. (cond) returns nil. (defmacro cond [& clauses] @@ -462,7 +462,7 @@ 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() }); + return make_box(native_persistent_string{ buff.data(), buff.size() }); } else { throw #{ (ex-info :invalid-seq {:args args}) }#; } @@ -711,7 +711,7 @@ ( visit_object ( - [=](auto const typed_o) -> native_string + [=](auto const typed_o) -> native_persistent_string { using T = typename decltype(typed_o)::value_type; @@ -729,7 +729,7 @@ ( visit_object ( - [=](auto const typed_o) -> native_string + [=](auto const typed_o) -> native_persistent_string { using T = typename decltype(typed_o)::value_type; diff --git a/test/cpp/jank/analyze/box.cpp b/test/cpp/jank/analyze/box.cpp index 67726640b..97fa038a7 100644 --- a/test/cpp/jank/analyze/box.cpp +++ b/test/cpp/jank/analyze/box.cpp @@ -50,7 +50,7 @@ namespace jank::analyze CHECK(equal(runtime::get(c_binding, make_box("has_unboxed_usage")), make_box(false))); } - SUBCASE("Boxed usage") + SUBCASE("Boxed usage, directly") { auto const res(rt_ctx.analyze_string("(let* [a 1 b a] a)")); CHECK_EQ(res.size(), 1); @@ -63,6 +63,24 @@ namespace jank::analyze CHECK(equal(runtime::get(a_binding, make_box("has_unboxed_usage")), make_box(true))); } + SUBCASE("Boxed usage, indirectly") + { + auto const res(rt_ctx.analyze_string("(let* [a 1 b a] b)")); + CHECK_EQ(res.size(), 1); + + auto const map(res[0]->to_runtime_data()); + auto const a_binding(runtime::get_in(map, rt_ctx.eval_string(R"(["pairs" 0 0])"))); + auto const b_binding(runtime::get_in(map, rt_ctx.eval_string(R"(["pairs" 0 1])"))); + + CHECK(equal(runtime::get(a_binding, make_box("needs_box")), make_box(false))); + CHECK(equal(runtime::get(a_binding, make_box("has_boxed_usage")), make_box(true))); + CHECK(equal(runtime::get(a_binding, make_box("has_unboxed_usage")), make_box(false))); + + CHECK(equal(runtime::get(b_binding, make_box("needs_box")), make_box(false))); + CHECK(equal(runtime::get(b_binding, make_box("has_boxed_usage")), make_box(true))); + CHECK(equal(runtime::get(b_binding, make_box("has_unboxed_usage")), make_box(false))); + } + SUBCASE("Captured, no unboxed usage") { auto const res(rt_ctx.analyze_string("(let* [a 1 f (fn* [] a)] (f))", false)); @@ -104,6 +122,24 @@ namespace jank::analyze CHECK(equal(runtime::get(captured_a_binding, make_box("has_unboxed_usage")), make_box(false))); } + SUBCASE("Captured, box usage indirectly") + { + auto const res(rt_ctx.analyze_string("(let* [a 1 b a] (fn* [] b))")); + CHECK_EQ(res.size(), 1); + + auto const map(res[0]->to_runtime_data()); + auto const a_binding(runtime::get_in(map, rt_ctx.eval_string(R"(["pairs" 0 0])"))); + auto const b_binding(runtime::get_in(map, rt_ctx.eval_string(R"(["pairs" 0 1])"))); + + CHECK(equal(runtime::get(a_binding, make_box("needs_box")), make_box(false))); + CHECK(equal(runtime::get(a_binding, make_box("has_boxed_usage")), make_box(true))); + CHECK(equal(runtime::get(a_binding, make_box("has_unboxed_usage")), make_box(false))); + + CHECK(equal(runtime::get(b_binding, make_box("needs_box")), make_box(false))); + CHECK(equal(runtime::get(b_binding, make_box("has_boxed_usage")), make_box(true))); + CHECK(equal(runtime::get(b_binding, make_box("has_unboxed_usage")), make_box(false))); + } + SUBCASE("Sub-expression of a boxed call which doesn't require boxed inputs") { runtime::context rt_ctx; diff --git a/test/cpp/jank/jit/processor.cpp b/test/cpp/jank/jit/processor.cpp index c14cf8e82..12ca9bb52 100644 --- a/test/cpp/jank/jit/processor.cpp +++ b/test/cpp/jank/jit/processor.cpp @@ -27,7 +27,7 @@ namespace jank::jit struct failure { boost::filesystem::path path; - native_string error; + native_persistent_string error; }; TEST_CASE("Files") diff --git a/test/cpp/jank/native_persistent_string.cpp b/test/cpp/jank/native_persistent_string.cpp new file mode 100644 index 000000000..3a6724acb --- /dev/null +++ b/test/cpp/jank/native_persistent_string.cpp @@ -0,0 +1,189 @@ +#include + +/* This must go last; doctest and glog both define CHECK and family. */ +#include + +namespace jank +{ + TEST_SUITE("native_persistent_string") + { + TEST_CASE("Constructor") + { + SUBCASE("Default") + { + native_persistent_string s; + CHECK(s.empty()); + } + + SUBCASE("Copy") + { + SUBCASE("SSO") + { + native_persistent_string s{ "foo bar" }; + native_persistent_string c{ s }; + CHECK_EQ(c.size(), 7); + CHECK_NE(c.data(), s.data()); + } + + SUBCASE("Long") + { + native_persistent_string s{ "foo bar spam meow foo bar spam meow" }; + native_persistent_string c{ s }; + CHECK_EQ(c.size(), 35); + CHECK_EQ(c.data(), s.data()); + } + } + + SUBCASE("C string") + { + SUBCASE("SSO") + { + native_persistent_string s{ "foo bar" }; + CHECK_EQ(s.size(), 7); + } + + SUBCASE("Long") + { + native_persistent_string s{ "foo bar spam foo bar spam foo bar spam" }; + CHECK_EQ(s.size(), 38); + } + } + } + + TEST_CASE("Find") + { + SUBCASE("Empty corpus, empty pattern") + { + native_persistent_string s; + CHECK_EQ(s.find(""), 0); + } + + SUBCASE("Empty corpus") + { + native_persistent_string s; + CHECK_EQ(s.find("something"), native_persistent_string::npos); + } + + SUBCASE("Empty corpus, high pos") + { + native_persistent_string s; + CHECK_EQ(s.find("something", 10), native_persistent_string::npos); + } + + SUBCASE("Non-empty corpus, empty pattern") + { + native_persistent_string s{ "foo bar" }; + CHECK_EQ(s.find(""), 0); + } + + SUBCASE("Non-empty corpus, missing pattern") + { + native_persistent_string s{ "I'm not Abel. I'm just Cain." }; + CHECK_EQ(s.find("p"), native_persistent_string::npos); + } + + SUBCASE("Non-empty corpus, missing pattern longer than corpus") + { + native_persistent_string s{ "I'm not Abel. I'm just Cain." }; + CHECK_EQ(s.find("Cupiditate eveniet at alias amet. Placeat facere qui sunt vel voluptas tenetur. Sunt molestias exercitationem repellat aut non qui. Exercitationem iste similique similique ut."), native_persistent_string::npos); + } + } + + TEST_CASE("Substring") + { + SUBCASE("Empty corpus, empty substring") + { + native_persistent_string s; + auto const sub(s.substr()); + CHECK(sub.empty()); + } + + SUBCASE("Empty corpus, pos > 0") + { + native_persistent_string s; + CHECK_THROWS(s.substr(1)); + } + + SUBCASE("Empty corpus, pos = count, high count") + { + native_persistent_string s; + auto const sub(s.substr(0, 100)); + CHECK(sub.empty()); + } + + SUBCASE("Non-empty corpus, pos = count, high count") + { + native_persistent_string s{ "foo bar" }; + auto const sub(s.substr(7, 100)); + CHECK(sub.empty()); + } + + SUBCASE("Non-empty corpus, pos = count, high count") + { + native_persistent_string s{ "foo bar" }; + auto const sub(s.substr(7, 100)); + CHECK(sub.empty()); + } + + SUBCASE("SSO") + { + SUBCASE("Non-empty corpus, prefix") + { + native_persistent_string s{ "foo bar" }; + auto const sub(s.substr(0, 3)); + CHECK_EQ(sub.size(), 3); + CHECK_EQ(sub, "foo"); + } + + SUBCASE("Non-empty corpus, suffix") + { + native_persistent_string s{ "foo bar" }; + auto const sub(s.substr(4, 3)); + CHECK_EQ(sub.size(), 3); + CHECK_EQ(sub, "bar"); + auto const sub2(s.substr(4)); + CHECK_EQ(sub2.size(), 3); + CHECK_EQ(sub, sub2); + } + + SUBCASE("Non-empty corpus, middle") + { + native_persistent_string s{ "foo bar" }; + auto const sub(s.substr(2, 3)); + CHECK_EQ(sub.size(), 3); + CHECK_EQ(sub, "o b"); + } + } + + SUBCASE("Long") + { + SUBCASE("Non-empty corpus, prefix") + { + native_persistent_string s{ "foo bar spam meow foo bar spam meow" }; + auto const sub(s.substr(0, 3)); + CHECK_EQ(sub.size(), 3); + CHECK_EQ(sub, "foo"); + } + + SUBCASE("Non-empty corpus, suffix") + { + native_persistent_string s{ "foo bar spam meow foo bar spam meow" }; + auto const sub(s.substr(4, 3)); + CHECK_EQ(sub.size(), 3); + CHECK_EQ(sub, "bar"); + auto const sub2(s.substr(4)); + CHECK_EQ(sub2, "bar spam meow foo bar spam meow"); + CHECK_EQ(sub2.substr(0, 3), sub); + } + + SUBCASE("Non-empty corpus, middle") + { + native_persistent_string s{ "foo bar spam meow foo bar spam meow" }; + auto const sub(s.substr(2, 3)); + CHECK_EQ(sub.size(), 3); + CHECK_EQ(sub, "o b"); + } + } + } + }; +} diff --git a/test/cpp/jank/read/parse.cpp b/test/cpp/jank/read/parse.cpp index 8ea9a69a2..331b445c2 100644 --- a/test/cpp/jank/read/parse.cpp +++ b/test/cpp/jank/read/parse.cpp @@ -226,7 +226,7 @@ namespace jank::read::parse { auto const r(p.next()); CHECK(r.is_ok()); - CHECK(runtime::detail::equal(r.expect_ok(), rt_ctx.intern_keyword(runtime::obj::symbol{ "", native_string{ s } }, false).expect_ok())); + CHECK(runtime::detail::equal(r.expect_ok(), rt_ctx.intern_keyword(runtime::obj::symbol{ "", native_persistent_string{ s } }, false).expect_ok())); } } diff --git a/third-party/vcpkg b/third-party/vcpkg index d9beea7d6..7f3d87ca0 160000 --- a/third-party/vcpkg +++ b/third-party/vcpkg @@ -1 +1 @@ -Subproject commit d9beea7d67f8551dfa4d22489fb7c78acf7de30f +Subproject commit 7f3d87ca095a512c48332e3c7e5044cdc242c0d9