From 27a28e6324b8f3849c7ee4e6f902cef654b4069d Mon Sep 17 00:00:00 2001 From: Chris Badahdah Date: Sat, 4 Jul 2026 23:07:34 -0700 Subject: [PATCH 01/10] Enable `clojure.string-test.reverse` --- .../src/cpp/clojure/string_native.cpp | 41 ++++++++++++++++++- .../src/jank_test/run_clojure_test_suite.cljc | 2 +- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/compiler+runtime/src/cpp/clojure/string_native.cpp b/compiler+runtime/src/cpp/clojure/string_native.cpp index 65e3283fd..2f85e7124 100644 --- a/compiler+runtime/src/cpp/clojure/string_native.cpp +++ b/compiler+runtime/src/cpp/clojure/string_native.cpp @@ -24,9 +24,48 @@ namespace clojure::string_native return s_str.is_blank(); } + static bool is_continuation_byte(jtl::immutable_string const &s, usize const i) + { + if(i == jtl::immutable_string::npos) + { + throw std::runtime_error{ "Invalid unicode string." }; + } + + return (s[i] & 0xC0) == 0x80; + } + jtl::immutable_string reverse(jtl::immutable_string const &s) { - return { s.rbegin(), s.rend() }; + usize i{ s.size() }; + jtl::string_builder buff{ s.size() }; + while(i > 0) + { + if(!is_continuation_byte(s, i - 1)) + { + buff(s[i - 1]); + i -= 1; + } + else if(!is_continuation_byte(s, i - 2)) + { + buff(s.substr(i - 2, 2)); + i -= 2; + } + else if(!is_continuation_byte(s, i - 3)) + { + buff(s.substr(i - 3, 3)); + i -= 3; + } + else if(!is_continuation_byte(s, i - 4)) + { + buff(s.substr(i - 4, 4)); + i -= 4; + } + else + { + throw std::runtime_error{ "Invalid unicode string." }; + } + } + return buff.release(); } jtl::immutable_string lower_case(jtl::immutable_string const &s) diff --git a/compiler+runtime/test/bash/clojure-test-suite/src/jank_test/run_clojure_test_suite.cljc b/compiler+runtime/test/bash/clojure-test-suite/src/jank_test/run_clojure_test_suite.cljc index cec7e682c..a62966fe6 100644 --- a/compiler+runtime/test/bash/clojure-test-suite/src/jank_test/run_clojure_test_suite.cljc +++ b/compiler+runtime/test/bash/clojure-test-suite/src/jank_test/run_clojure_test_suite.cljc @@ -233,7 +233,7 @@ clojure.string-test.ends-with-qmark ; clojure.string-test.escape ; Uncaught exception: Can't convert character to integer. clojure.string-test.lower-case - ; clojure.string-test.reverse ; issues with unicode + clojure.string-test.reverse clojure.string-test.starts-with-qmark clojure.string-test.upper-case ]) From cd041cf1388511972e48187fcdb734c2f07a9cf0 Mon Sep 17 00:00:00 2001 From: Chris Badahdah Date: Sun, 5 Jul 2026 14:57:19 -0700 Subject: [PATCH 02/10] Update reverse to use range based for loop --- compiler+runtime/include/cpp/jtl/utf8.hpp | 110 ++++++++++++++++++ .../src/cpp/clojure/string_native.cpp | 40 +------ 2 files changed, 114 insertions(+), 36 deletions(-) create mode 100644 compiler+runtime/include/cpp/jtl/utf8.hpp diff --git a/compiler+runtime/include/cpp/jtl/utf8.hpp b/compiler+runtime/include/cpp/jtl/utf8.hpp new file mode 100644 index 000000000..c33eab93a --- /dev/null +++ b/compiler+runtime/include/cpp/jtl/utf8.hpp @@ -0,0 +1,110 @@ +#include + +namespace jtl +{ + constexpr bool + is_continuation_byte(immutable_string const &s, immutable_string::size_type const i) + { + if(i == immutable_string::npos) + { + throw std::runtime_error{ "Invalid unicode string." }; + } + + return (s[i] & 0xC0) == 0x80; + } + + struct utf8_reverse_range + { + utf8_reverse_range(immutable_string const &s) + : data{ s } + { + } + + struct iterator + { + using iterator_category = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = immutable_string; + using reference = value_type const &; + + iterator() + : data{} + , i{ 0 } + , n{ 0 } + { + } + + iterator(immutable_string const &s) + : data{ s } + , i{ s.size() } + , n{ prev_char_size() } + { + } + + constexpr value_type::size_type prev_char_size() + { + if(!is_continuation_byte(data, i - 1)) + { + return 1; + } + else if(!is_continuation_byte(data, i - 2)) + { + return 2; + } + else if(!is_continuation_byte(data, i - 3)) + { + return 3; + } + else if(!is_continuation_byte(data, i - 4)) + { + return 4; + } + else + { + throw std::runtime_error{ "Invalid unicode string." }; + } + } + + value_type operator*() const + { + return data.substr(i - n, n); + } + + iterator &operator++() + { + i -= n; + if(i > n) + { + n = prev_char_size(); + } + return *this; + } + + bool operator==(iterator const &it) const + { + return i == it.i; + } + + value_type data; + value_type::size_type i{}; + value_type::size_type n{}; + }; + + iterator begin() const + { + if(data.empty()) + { + return {}; + } + + return { data }; + } + + iterator end() const + { + return {}; + } + + immutable_string data; + }; +} diff --git a/compiler+runtime/src/cpp/clojure/string_native.cpp b/compiler+runtime/src/cpp/clojure/string_native.cpp index 2f85e7124..33706bdd6 100644 --- a/compiler+runtime/src/cpp/clojure/string_native.cpp +++ b/compiler+runtime/src/cpp/clojure/string_native.cpp @@ -1,3 +1,5 @@ +#include + #include #include #include @@ -24,46 +26,12 @@ namespace clojure::string_native return s_str.is_blank(); } - static bool is_continuation_byte(jtl::immutable_string const &s, usize const i) - { - if(i == jtl::immutable_string::npos) - { - throw std::runtime_error{ "Invalid unicode string." }; - } - - return (s[i] & 0xC0) == 0x80; - } - jtl::immutable_string reverse(jtl::immutable_string const &s) { - usize i{ s.size() }; jtl::string_builder buff{ s.size() }; - while(i > 0) + for(auto const &c : jtl::utf8_reverse_range(s)) { - if(!is_continuation_byte(s, i - 1)) - { - buff(s[i - 1]); - i -= 1; - } - else if(!is_continuation_byte(s, i - 2)) - { - buff(s.substr(i - 2, 2)); - i -= 2; - } - else if(!is_continuation_byte(s, i - 3)) - { - buff(s.substr(i - 3, 3)); - i -= 3; - } - else if(!is_continuation_byte(s, i - 4)) - { - buff(s.substr(i - 4, 4)); - i -= 4; - } - else - { - throw std::runtime_error{ "Invalid unicode string." }; - } + buff(c); } return buff.release(); } From 07c682f1dc4af0bf697d3786407e4361cf8508fc Mon Sep 17 00:00:00 2001 From: Chris Badahdah Date: Sun, 5 Jul 2026 15:15:05 -0700 Subject: [PATCH 03/10] Fix CI errors --- compiler+runtime/include/cpp/jtl/utf8.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler+runtime/include/cpp/jtl/utf8.hpp b/compiler+runtime/include/cpp/jtl/utf8.hpp index c33eab93a..956ab3168 100644 --- a/compiler+runtime/include/cpp/jtl/utf8.hpp +++ b/compiler+runtime/include/cpp/jtl/utf8.hpp @@ -29,8 +29,8 @@ namespace jtl iterator() : data{} - , i{ 0 } - , n{ 0 } + , i{} + , n{} { } @@ -41,7 +41,7 @@ namespace jtl { } - constexpr value_type::size_type prev_char_size() + constexpr value_type::size_type prev_char_size() const { if(!is_continuation_byte(data, i - 1)) { From 5511d81d23e61f747fe076ac31d43f24fa8b0cc5 Mon Sep 17 00:00:00 2001 From: Chris Badahdah Date: Sun, 5 Jul 2026 15:28:23 -0700 Subject: [PATCH 04/10] Fix CI errors --- compiler+runtime/include/cpp/jtl/utf8.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler+runtime/include/cpp/jtl/utf8.hpp b/compiler+runtime/include/cpp/jtl/utf8.hpp index 956ab3168..6628d86d5 100644 --- a/compiler+runtime/include/cpp/jtl/utf8.hpp +++ b/compiler+runtime/include/cpp/jtl/utf8.hpp @@ -86,8 +86,8 @@ namespace jtl } value_type data; - value_type::size_type i{}; - value_type::size_type n{}; + value_type::size_type i; + value_type::size_type n; }; iterator begin() const From f21aa03b4a906384d89d4bdb9adb51ef2dd0d02f Mon Sep 17 00:00:00 2001 From: Chris Badahdah Date: Sat, 11 Jul 2026 23:53:41 -0700 Subject: [PATCH 05/10] Address PR feedback --- compiler+runtime/CMakeLists.txt | 1 + compiler+runtime/include/cpp/jtl/utf8.hpp | 125 ++++------------------ compiler+runtime/src/cpp/jtl/utf8.cpp | 90 ++++++++++++++++ 3 files changed, 113 insertions(+), 103 deletions(-) create mode 100644 compiler+runtime/src/cpp/jtl/utf8.cpp diff --git a/compiler+runtime/CMakeLists.txt b/compiler+runtime/CMakeLists.txt index 457914a94..c601681fd 100644 --- a/compiler+runtime/CMakeLists.txt +++ b/compiler+runtime/CMakeLists.txt @@ -517,6 +517,7 @@ add_library( src/cpp/jtl/panic.cpp src/cpp/jtl/assert.cpp src/cpp/jtl/string_builder.cpp + src/cpp/jtl/utf8.cpp src/cpp/jank/c_api.cpp src/cpp/jank/hash.cpp src/cpp/jank/util/cli.cpp diff --git a/compiler+runtime/include/cpp/jtl/utf8.hpp b/compiler+runtime/include/cpp/jtl/utf8.hpp index 6628d86d5..94a35813d 100644 --- a/compiler+runtime/include/cpp/jtl/utf8.hpp +++ b/compiler+runtime/include/cpp/jtl/utf8.hpp @@ -1,110 +1,29 @@ +#include + #include namespace jtl { - constexpr bool - is_continuation_byte(immutable_string const &s, immutable_string::size_type const i) + struct reverse_iterator { - if(i == immutable_string::npos) - { - throw std::runtime_error{ "Invalid unicode string." }; - } - - return (s[i] & 0xC0) == 0x80; - } - - struct utf8_reverse_range - { - utf8_reverse_range(immutable_string const &s) - : data{ s } - { - } - - struct iterator - { - using iterator_category = std::input_iterator_tag; - using difference_type = std::ptrdiff_t; - using value_type = immutable_string; - using reference = value_type const &; - - iterator() - : data{} - , i{} - , n{} - { - } - - iterator(immutable_string const &s) - : data{ s } - , i{ s.size() } - , n{ prev_char_size() } - { - } - - constexpr value_type::size_type prev_char_size() const - { - if(!is_continuation_byte(data, i - 1)) - { - return 1; - } - else if(!is_continuation_byte(data, i - 2)) - { - return 2; - } - else if(!is_continuation_byte(data, i - 3)) - { - return 3; - } - else if(!is_continuation_byte(data, i - 4)) - { - return 4; - } - else - { - throw std::runtime_error{ "Invalid unicode string." }; - } - } - - value_type operator*() const - { - return data.substr(i - n, n); - } - - iterator &operator++() - { - i -= n; - if(i > n) - { - n = prev_char_size(); - } - return *this; - } - - bool operator==(iterator const &it) const - { - return i == it.i; - } - - value_type data; - value_type::size_type i; - value_type::size_type n; - }; - - iterator begin() const - { - if(data.empty()) - { - return {}; - } - - return { data }; - } - - iterator end() const - { - return {}; - } - - immutable_string data; + using reverse_iterator_category = std::input_iterator_tag; + using difference_type = std::ptrdiff_t; + using value_type = immutable_string; + using reference = value_type const &; + + reverse_iterator() = default; + reverse_iterator(immutable_string const &s); + reverse_iterator(immutable_string const &s, value_type::size_type const i); + + value_type operator*() const; + reverse_iterator &operator++(); + reverse_iterator operator++(int); + bool operator==(reverse_iterator const &it) const; + + value_type data; + value_type::size_type i{}; + value_type::size_type n{}; }; + + std::ranges::subrange utf8_reverse_range(immutable_string const &s); } diff --git a/compiler+runtime/src/cpp/jtl/utf8.cpp b/compiler+runtime/src/cpp/jtl/utf8.cpp new file mode 100644 index 000000000..0edb7013f --- /dev/null +++ b/compiler+runtime/src/cpp/jtl/utf8.cpp @@ -0,0 +1,90 @@ +#include + +namespace jtl +{ + using value_type = reverse_iterator::value_type; + using size_type = value_type::size_type; + + static bool is_continuation_byte(immutable_string const &s, size_type const i) + { + if(i == immutable_string::npos) + { + throw std::runtime_error{ "Invalid UTF-8 string." }; + } + + return (s[i] & 0xC0) == 0x80; + } + + static size_type prev_char_size(immutable_string const &data, size_type const i) + { + if(!is_continuation_byte(data, i - 1)) + { + return 1; + } + else if(!is_continuation_byte(data, i - 2)) + { + return 2; + } + else if(!is_continuation_byte(data, i - 3)) + { + return 3; + } + else if(!is_continuation_byte(data, i - 4)) + { + return 4; + } + else + { + throw std::runtime_error{ "Invalid UTF-8 string." }; + } + } + + reverse_iterator::reverse_iterator(immutable_string const &s) + : data{ s } + , i{ s.size() } + , n{ prev_char_size(s, i) } + { + } + + reverse_iterator::reverse_iterator(immutable_string const &s, value_type::size_type const i) + : data{ s } + , i{ i } + , n{ i == 0 ? 0 : prev_char_size(s, i) } + { + } + + value_type reverse_iterator::operator*() const + { + return data.substr(i - n, n); + } + + reverse_iterator &reverse_iterator::operator++() + { + i -= n; + if(i > n) + { + n = prev_char_size(data, i); + } + return *this; + } + + reverse_iterator reverse_iterator::operator++(int) + { + auto tmp{ *this }; + ++*this; + return tmp; + } + + bool reverse_iterator::operator==(reverse_iterator const &it) const + { + return data.data() == it.data.data() && i == it.i; + } + + std::ranges::subrange utf8_reverse_range(immutable_string const &s) + { + reverse_iterator end{ s, 0 }; + auto begin(s.empty() ? end : reverse_iterator{ s }); + return { begin, end }; + } + +} From 0b832db91078cb29611b0c5ec225453f55d98439 Mon Sep 17 00:00:00 2001 From: Chris Badahdah Date: Sun, 12 Jul 2026 01:24:48 -0700 Subject: [PATCH 06/10] Make utf8 iterator bidirectional --- compiler+runtime/include/cpp/jtl/utf8.hpp | 25 ++-- .../src/cpp/clojure/string_native.cpp | 4 +- compiler+runtime/src/cpp/jtl/utf8.cpp | 109 ++++++++++++++---- 3 files changed, 105 insertions(+), 33 deletions(-) diff --git a/compiler+runtime/include/cpp/jtl/utf8.hpp b/compiler+runtime/include/cpp/jtl/utf8.hpp index 94a35813d..37f128d01 100644 --- a/compiler+runtime/include/cpp/jtl/utf8.hpp +++ b/compiler+runtime/include/cpp/jtl/utf8.hpp @@ -1,29 +1,30 @@ -#include - #include namespace jtl { - struct reverse_iterator + struct utf8_iterator { - using reverse_iterator_category = std::input_iterator_tag; + using iterator_category = std::bidirectional_iterator_tag; using difference_type = std::ptrdiff_t; using value_type = immutable_string; using reference = value_type const &; - reverse_iterator() = default; - reverse_iterator(immutable_string const &s); - reverse_iterator(immutable_string const &s, value_type::size_type const i); + utf8_iterator() = default; + utf8_iterator(immutable_string const &s); + utf8_iterator(immutable_string const &s, value_type::size_type const i); value_type operator*() const; - reverse_iterator &operator++(); - reverse_iterator operator++(int); - bool operator==(reverse_iterator const &it) const; + utf8_iterator &operator++(); + utf8_iterator operator++(int); + utf8_iterator &operator--(); + utf8_iterator operator--(int); + bool operator==(utf8_iterator const &it) const; + + utf8_iterator begin(); + utf8_iterator end(); value_type data; value_type::size_type i{}; value_type::size_type n{}; }; - - std::ranges::subrange utf8_reverse_range(immutable_string const &s); } diff --git a/compiler+runtime/src/cpp/clojure/string_native.cpp b/compiler+runtime/src/cpp/clojure/string_native.cpp index 33706bdd6..0dbe5e91d 100644 --- a/compiler+runtime/src/cpp/clojure/string_native.cpp +++ b/compiler+runtime/src/cpp/clojure/string_native.cpp @@ -1,3 +1,5 @@ +#include + #include #include @@ -29,7 +31,7 @@ namespace clojure::string_native jtl::immutable_string reverse(jtl::immutable_string const &s) { jtl::string_builder buff{ s.size() }; - for(auto const &c : jtl::utf8_reverse_range(s)) + for(auto const &c : jtl::utf8_iterator(s) | std::views::reverse) { buff(c); } diff --git a/compiler+runtime/src/cpp/jtl/utf8.cpp b/compiler+runtime/src/cpp/jtl/utf8.cpp index 0edb7013f..6ee0e201a 100644 --- a/compiler+runtime/src/cpp/jtl/utf8.cpp +++ b/compiler+runtime/src/cpp/jtl/utf8.cpp @@ -2,9 +2,34 @@ namespace jtl { - using value_type = reverse_iterator::value_type; + using value_type = utf8_iterator::value_type; using size_type = value_type::size_type; + static size_type next_char_size(immutable_string const &s, size_type const i) + { + auto const c(s[i]); + if(c <= 0x7f) + { + return 1; + } + else if((c & 0xE0) == 0xC0) + { + return 2; + } + else if((c & 0xF0) == 0xE0) + { + return 3; + } + else if((c & 0xF8) == 0xF0) + { + return 4; + } + else + { + throw std::runtime_error{ "Invalid UTF-8 string." }; + } + } + static bool is_continuation_byte(immutable_string const &s, size_type const i) { if(i == immutable_string::npos) @@ -39,52 +64,96 @@ namespace jtl } } - reverse_iterator::reverse_iterator(immutable_string const &s) + utf8_iterator::utf8_iterator(immutable_string const &s) : data{ s } - , i{ s.size() } - , n{ prev_char_size(s, i) } + , i{ s.size() == 0 ? value_type::npos : 0 } + , n{ i == value_type::npos ? 0 : next_char_size(s, i) } { } - reverse_iterator::reverse_iterator(immutable_string const &s, value_type::size_type const i) + utf8_iterator::utf8_iterator(immutable_string const &s, value_type::size_type const i) : data{ s } - , i{ i } - , n{ i == 0 ? 0 : prev_char_size(s, i) } + , i{ s.size() == 0 ? value_type::npos : i } + , n{ i == value_type::npos ? 0 : next_char_size(s, i) } { } - value_type reverse_iterator::operator*() const + value_type utf8_iterator::operator*() const { - return data.substr(i - n, n); + return data.substr(i, n); } - reverse_iterator &reverse_iterator::operator++() + utf8_iterator &utf8_iterator::operator++() { - i -= n; - if(i > n) + if(i == value_type::npos) { - n = prev_char_size(data, i); + i = 0; + n = next_char_size(data, i); } + else + { + i += n; + if(i >= data.size()) + { + i = value_type::npos; + n = 0; + } + else + { + n = next_char_size(data, i); + } + } + return *this; } - reverse_iterator reverse_iterator::operator++(int) + utf8_iterator utf8_iterator::operator++(int) { auto tmp{ *this }; ++*this; return tmp; } - bool reverse_iterator::operator==(reverse_iterator const &it) const + utf8_iterator &utf8_iterator::operator--() { - return data.data() == it.data.data() && i == it.i; + if(i == 0) + { + i = value_type::npos; + n = 0; + } + else if(i == value_type::npos) + { + auto const size(data.size()); + n = prev_char_size(data, size); + i = size - n; + } + else + { + n = prev_char_size(data, i); + i -= n; + } + return *this; } - std::ranges::subrange utf8_reverse_range(immutable_string const &s) + utf8_iterator utf8_iterator::operator--(int) { - reverse_iterator end{ s, 0 }; - auto begin(s.empty() ? end : reverse_iterator{ s }); - return { begin, end }; + auto tmp{ *this }; + --*this; + return tmp; + } + + bool utf8_iterator::operator==(utf8_iterator const &it) const + { + return i == it.i && (data.data() == it.data.data() || data == it.data); } + utf8_iterator utf8_iterator::begin() + { + return { data, 0 }; + } + + utf8_iterator utf8_iterator::end() + { + return { data, value_type::npos }; + } } From 3b6ed41cb5d967c16651de500551f37f1aad7319 Mon Sep 17 00:00:00 2001 From: Chris Badahdah Date: Sun, 12 Jul 2026 02:17:40 -0700 Subject: [PATCH 07/10] Fix CI --- compiler+runtime/include/cpp/jtl/utf8.hpp | 4 ++-- compiler+runtime/src/cpp/jtl/utf8.cpp | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler+runtime/include/cpp/jtl/utf8.hpp b/compiler+runtime/include/cpp/jtl/utf8.hpp index 37f128d01..ccbb24162 100644 --- a/compiler+runtime/include/cpp/jtl/utf8.hpp +++ b/compiler+runtime/include/cpp/jtl/utf8.hpp @@ -20,8 +20,8 @@ namespace jtl utf8_iterator operator--(int); bool operator==(utf8_iterator const &it) const; - utf8_iterator begin(); - utf8_iterator end(); + utf8_iterator begin() const; + utf8_iterator end() const; value_type data; value_type::size_type i{}; diff --git a/compiler+runtime/src/cpp/jtl/utf8.cpp b/compiler+runtime/src/cpp/jtl/utf8.cpp index 6ee0e201a..3280f9429 100644 --- a/compiler+runtime/src/cpp/jtl/utf8.cpp +++ b/compiler+runtime/src/cpp/jtl/utf8.cpp @@ -66,14 +66,14 @@ namespace jtl utf8_iterator::utf8_iterator(immutable_string const &s) : data{ s } - , i{ s.size() == 0 ? value_type::npos : 0 } + , i{ s.empty() ? value_type::npos : 0 } , n{ i == value_type::npos ? 0 : next_char_size(s, i) } { } utf8_iterator::utf8_iterator(immutable_string const &s, value_type::size_type const i) : data{ s } - , i{ s.size() == 0 ? value_type::npos : i } + , i{ s.empty() ? value_type::npos : i } , n{ i == value_type::npos ? 0 : next_char_size(s, i) } { } @@ -147,12 +147,12 @@ namespace jtl return i == it.i && (data.data() == it.data.data() || data == it.data); } - utf8_iterator utf8_iterator::begin() + utf8_iterator utf8_iterator::begin() const { return { data, 0 }; } - utf8_iterator utf8_iterator::end() + utf8_iterator utf8_iterator::end() const { return { data, value_type::npos }; } From 07bfa3800c829310d195d606f119c004288e8781 Mon Sep 17 00:00:00 2001 From: Chris Badahdah Date: Sun, 12 Jul 2026 11:38:42 -0700 Subject: [PATCH 08/10] Cleanup --- compiler+runtime/include/cpp/jtl/utf8.hpp | 2 +- compiler+runtime/src/cpp/jtl/utf8.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/compiler+runtime/include/cpp/jtl/utf8.hpp b/compiler+runtime/include/cpp/jtl/utf8.hpp index ccbb24162..6b30c586e 100644 --- a/compiler+runtime/include/cpp/jtl/utf8.hpp +++ b/compiler+runtime/include/cpp/jtl/utf8.hpp @@ -11,7 +11,7 @@ namespace jtl utf8_iterator() = default; utf8_iterator(immutable_string const &s); - utf8_iterator(immutable_string const &s, value_type::size_type const i); + utf8_iterator(immutable_string const &s, value_type::size_type const position); value_type operator*() const; utf8_iterator &operator++(); diff --git a/compiler+runtime/src/cpp/jtl/utf8.cpp b/compiler+runtime/src/cpp/jtl/utf8.cpp index 3280f9429..a426803e4 100644 --- a/compiler+runtime/src/cpp/jtl/utf8.cpp +++ b/compiler+runtime/src/cpp/jtl/utf8.cpp @@ -71,9 +71,9 @@ namespace jtl { } - utf8_iterator::utf8_iterator(immutable_string const &s, value_type::size_type const i) + utf8_iterator::utf8_iterator(immutable_string const &s, value_type::size_type const position) : data{ s } - , i{ s.empty() ? value_type::npos : i } + , i{ s.empty() ? value_type::npos : position } , n{ i == value_type::npos ? 0 : next_char_size(s, i) } { } From 867a6ce57728aa69bc6590a5e7c49e71d6ac8d07 Mon Sep 17 00:00:00 2001 From: Chris Badahdah Date: Sun, 12 Jul 2026 21:58:39 -0700 Subject: [PATCH 09/10] Add `utf8_range` --- compiler+runtime/include/cpp/jtl/utf8.hpp | 13 ++++++++++--- compiler+runtime/src/cpp/clojure/string_native.cpp | 2 +- compiler+runtime/src/cpp/jtl/utf8.cpp | 9 +++++++-- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/compiler+runtime/include/cpp/jtl/utf8.hpp b/compiler+runtime/include/cpp/jtl/utf8.hpp index 6b30c586e..2acad16f5 100644 --- a/compiler+runtime/include/cpp/jtl/utf8.hpp +++ b/compiler+runtime/include/cpp/jtl/utf8.hpp @@ -20,11 +20,18 @@ namespace jtl utf8_iterator operator--(int); bool operator==(utf8_iterator const &it) const; - utf8_iterator begin() const; - utf8_iterator end() const; - value_type data; value_type::size_type i{}; value_type::size_type n{}; }; + + struct utf8_range + { + utf8_range(immutable_string const &s); + + utf8_iterator begin() const; + utf8_iterator end() const; + + immutable_string data; + }; } diff --git a/compiler+runtime/src/cpp/clojure/string_native.cpp b/compiler+runtime/src/cpp/clojure/string_native.cpp index 0dbe5e91d..d519d7eb5 100644 --- a/compiler+runtime/src/cpp/clojure/string_native.cpp +++ b/compiler+runtime/src/cpp/clojure/string_native.cpp @@ -31,7 +31,7 @@ namespace clojure::string_native jtl::immutable_string reverse(jtl::immutable_string const &s) { jtl::string_builder buff{ s.size() }; - for(auto const &c : jtl::utf8_iterator(s) | std::views::reverse) + for(auto const &c : jtl::utf8_range(s) | std::views::reverse) { buff(c); } diff --git a/compiler+runtime/src/cpp/jtl/utf8.cpp b/compiler+runtime/src/cpp/jtl/utf8.cpp index a426803e4..ce47a5f79 100644 --- a/compiler+runtime/src/cpp/jtl/utf8.cpp +++ b/compiler+runtime/src/cpp/jtl/utf8.cpp @@ -147,12 +147,17 @@ namespace jtl return i == it.i && (data.data() == it.data.data() || data == it.data); } - utf8_iterator utf8_iterator::begin() const + utf8_range::utf8_range(immutable_string const &s) + : data{ s } + { + } + + utf8_iterator utf8_range::begin() const { return { data, 0 }; } - utf8_iterator utf8_iterator::end() const + utf8_iterator utf8_range::end() const { return { data, value_type::npos }; } From 4038c594acbdd4af6698b2f6400dd9953e2b4876 Mon Sep 17 00:00:00 2001 From: Chris Badahdah Date: Mon, 13 Jul 2026 14:44:38 -0700 Subject: [PATCH 10/10] Bump `clojure-test-suite` --- .../test/bash/clojure-test-suite/clojure-test-suite | 2 +- .../src/jank_test/run_clojure_test_suite.cljc | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/compiler+runtime/test/bash/clojure-test-suite/clojure-test-suite b/compiler+runtime/test/bash/clojure-test-suite/clojure-test-suite index cde6c8235..5e22883bf 160000 --- a/compiler+runtime/test/bash/clojure-test-suite/clojure-test-suite +++ b/compiler+runtime/test/bash/clojure-test-suite/clojure-test-suite @@ -1 +1 @@ -Subproject commit cde6c823568e9561bb5ff1157db3bbc6f6ae9dee +Subproject commit 5e22883bf442ec896326614763f0f4e655e36f18 diff --git a/compiler+runtime/test/bash/clojure-test-suite/src/jank_test/run_clojure_test_suite.cljc b/compiler+runtime/test/bash/clojure-test-suite/src/jank_test/run_clojure_test_suite.cljc index 1aebb7923..48504d419 100644 --- a/compiler+runtime/test/bash/clojure-test-suite/src/jank_test/run_clojure_test_suite.cljc +++ b/compiler+runtime/test/bash/clojure-test-suite/src/jank_test/run_clojure_test_suite.cljc @@ -32,7 +32,7 @@ clojure.core-test.boolean-qmark clojure.core-test.bound-fn clojure.core-test.bound-fn-star - clojure.core-test.butlast + ; clojure.core-test.butlast ; "TODO: port int-array" ; clojure.core-test.byte ; TODO: port byte, Expecting whitespace after the last token. due to M. ; clojure.core-test.case ; analyze/invalid-case error: Unable to resolve symbol 'of'. ; clojure.core-test.char ; FIXME: Failing unicode character tests. @@ -71,7 +71,7 @@ clojure.core-test.false-qmark clojure.core-test.ffirst clojure.core-test.find - clojure.core-test.first + ; clojure.core-test.first ; "TODO: port to-array" clojure.core-test.float clojure.core-test.float-qmark clojure.core-test.fn-qmark @@ -169,7 +169,7 @@ ; clojure.core-test.rem ; FIXME: Failing tests. ; clojure.core-test.remove-watch ; TODO: port sync clojure.core-test.repeat - clojure.core-test.rest + ; clojure.core-test.rest ; "TODO: port int-array" clojure.core-test.reverse ; clojure.core-test.reversible-qmark ; TODO: port reversible?, TODO: port object-array ; clojure.core-test.rseq ; TODO: port rseq @@ -195,7 +195,7 @@ ; clojure.core-test.sorted-qmark ; TODO: port sorted-map-by, not yet implemented: sorted-set-by, TODO: port array-map, TODO: port object-array ; clojure.core-test.special-symbol-qmark ; TODO: port special-symbol? ; clojure.core-test.star ; FIXME: Failing tests. - clojure.core-test.star-squote + ; clojure.core-test.star-squote ; Assertion failed! is_tagged_pointer(val) clojure.core-test.str clojure.core-test.string-qmark ; clojure.core-test.subs ; FIXME: Failing tests.