diff --git a/docs/about/changelog.md b/docs/about/changelog.md index d1746669a..f7211d34d 100644 --- a/docs/about/changelog.md +++ b/docs/about/changelog.md @@ -6,6 +6,7 @@ Release history of PerlOnJava. See [Roadmap](roadmap.md) for future plans. - Fix localization of numbered regex captures. - Fix IO-handle type checks and uninitialized-value warning locations. +- Fix Unicode regex interpolation in substitutions. - Fix numeric-zero results from failed `s///` substitutions. - Bundle the complete CPAN `File::Path` 2.18 implementation, including modern `rmtree`/`remove_tree` options such as `keep_root`, `error`, `result`, diff --git a/src/main/java/org/perlonjava/runtime/regex/RuntimeRegexTemplate.java b/src/main/java/org/perlonjava/runtime/regex/RuntimeRegexTemplate.java index 1097060ce..74e1ceed9 100644 --- a/src/main/java/org/perlonjava/runtime/regex/RuntimeRegexTemplate.java +++ b/src/main/java/org/perlonjava/runtime/regex/RuntimeRegexTemplate.java @@ -136,7 +136,12 @@ private static boolean isByteCompatiblePatternPart(RuntimeScalar scalar) { } static RuntimeScalar patternScalar(String pattern, boolean byteBackedPattern) { - return byteBackedPattern + // A byte-backed scalar cannot represent characters outside the Latin-1 + // byte range. This matters when a Unicode qr// is stringified and + // embedded in an interpolated substitution. + boolean byteCompatible = byteBackedPattern + && pattern.chars().allMatch(ch -> ch <= 0xFF); + return byteCompatible ? new RuntimeScalar(pattern.getBytes(StandardCharsets.ISO_8859_1)) : new RuntimeScalar(pattern); } diff --git a/src/test/resources/unit/regex/unicode_qr_substitution_template.t b/src/test/resources/unit/regex/unicode_qr_substitution_template.t new file mode 100644 index 000000000..a4d6de0fa --- /dev/null +++ b/src/test/resources/unit/regex/unicode_qr_substitution_template.t @@ -0,0 +1,15 @@ +use strict; +use warnings; +use utf8; +use Test::More tests => 2; + +my $tld = qr/(?:com|みんな|中国|테스트|भारत|москва)/i; + +my $text = "Visit site.みんな today"; +$text =~ s{ (.*?) ($tld) | (.+?)$ }{defined $1 ? $1 : $3}gsex; +is($text, 'Visit site. today', 'substitution compiles an interpolated Unicode qr'); + +my $ascii = qr/(?:com|org)/i; +my $ascii_text = 'Visit site.com today'; +$ascii_text =~ s{ (.*?) ($ascii) | (.+?)$ }{defined $1 ? $1 : $3}gsex; +is($ascii_text, 'Visit site. today', 'ASCII qr interpolation remains compatible');