Skip to content
2 changes: 1 addition & 1 deletion docs/reference/feature-matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ my @copy = @{$z}; # ERROR
- ✅ **Backreferences to Named Groups**: Using `\k<name>` or `\g{name}` for backreferences to named groups is supported.
- ✅ **Relative Backreferences**: Using `\g{-n}` for relative backreferences.
- ✅ **Basic Unicode Properties**: Common `\p{...}` and `\P{...}` forms such as `\p{L}` execute through Joni. General_Category assignments now enter the forked Joni parser unchanged and resolve to pinned Perl ranges there.
- 🟡 **Perl Unicode Property Syntax**: Perl-specific properties execute through Joni. `Age`, cumulative `In`/`Present_In`, General_Category, Canonical_Combining_Class, Bidi_Class, Decomposition_Type, East_Asian_Width, Numeric_Value, Joining_Group, Block, Script, and Script_Extensions assignments are generated from pinned Perl 5.44 Unicode 17.0 data with loose and wildcard aliases, ordered missing defaults, official compact aliases and shortcuts, Script-versus-Script_Extensions policy, reserved or composite values, exact rationals, and Perl's generated decimal keyword aliases; `ASCII_Hex_Digit`/`AHex` accepts Perl's eight boolean value aliases. General_Category uses Joni's range-resolver API; the other families remain adapter-translated until their family-specific case-fold and wildcard semantics are represented natively. Other generated property/value aliases still have pinned acceptance/rejection gaps.
- 🟡 **Perl Unicode Property Syntax**: Perl-specific properties execute through Joni. `Age`, cumulative `In`/`Present_In`, General_Category, Canonical_Combining_Class, Bidi_Class, Decomposition_Type, East_Asian_Width, Numeric_Value, Joining_Group, Block, Script, and Script_Extensions assignments are generated from pinned Perl 5.44 Unicode 17.0 data with loose and wildcard aliases, ordered missing defaults, official compact aliases and shortcuts, Script-versus-Script_Extensions policy, reserved or composite values, exact rationals, and Perl's generated decimal keyword aliases; `ASCII_Hex_Digit`/`AHex` accepts Perl's eight boolean value aliases. General_Category and standalone exact Age, `In`/`Present_In`, Block, Script, Script_Extensions, Canonical_Combining_Class, Bidi_Class, Decomposition_Type, East_Asian_Width, Numeric_Value, and Joining_Group assignments use Joni's range-resolver API with explicit per-family case-fold policy. No-fold properties inside composed character classes and wildcard values remain adapter-translated until Joni represents those syntax semantics natively. Other generated property/value aliases still have pinned acceptance/rejection gaps.
- ✅ **Possessive Quantifiers**: Quantifiers like `*+`, `++`, `?+`, and `{n,m}+`, which disable backtracking, are supported.
- ✅ **Atomic Grouping**: Use of `(?>...)` for atomic groups is supported.
- ✅ **`\K` assertion**: Keep left — in `s///`, text before `\K` is preserved; match variables reflect only the portion after `\K`. Ordinary KEEP assertions route through native Joni and no longer use the Java marker rewrite; the adapter still rejects KEEP inside lookaround until the Joni analyser emits Perl's diagnostic directly.
Expand Down
17 changes: 11 additions & 6 deletions src/main/java/org/perlonjava/runtime/regex/JoniRegexPattern.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.joni.Matcher;
import org.joni.CalloutHandler;
import org.joni.CalloutResult;
import org.joni.CharacterPropertyResolver;
import org.joni.DynamicPatternResult;
import org.joni.MatchView;
import org.joni.NameEntry;
Expand Down Expand Up @@ -60,12 +61,13 @@ private static int resolveNamedCharacter(byte[] bytes, int p, int end,
? StandardCharsets.ISO_8859_1 : StandardCharsets.UTF_8));
}

private static int[] resolveCharacterProperty(byte[] bytes, int p, int end,
Encoding encoding) {
private static CharacterPropertyResolver.Result resolveCharacterProperty(
byte[] bytes, int p, int end, Encoding encoding,
boolean inCharacterClass) {
String property = new String(bytes, p, end - p,
encoding == ISO8859_1Encoding.INSTANCE
? StandardCharsets.ISO_8859_1 : StandardCharsets.UTF_8);
return UnicodeResolver.resolveJoniPropertyRanges(property);
return UnicodeResolver.resolveJoniProperty(property, inCharacterClass);
}

private final Regex regex;
Expand Down Expand Up @@ -231,8 +233,9 @@ private static UserPropertyTranslation translateUserDefinedProperties(
boolean frontendProperty = unnegated.matches(
"(?i)^(?:script|sc|block|blk|age|in|present[_ ]?in)\\s*(?:=|:(?!:)).*");
boolean perlBuiltInAlias = UnicodeResolver.isPerlBuiltInPropertyAlias(unnegated);
boolean joniResolvedProperty = UnicodeResolver.resolveJoniPropertyRanges(
unnegated) != null;
boolean joniResolvedProperty = UnicodeResolver.resolveJoniProperty(
unnegated, extendedClassBracketDepth > 0
|| standardClassBracketDepth > 0) != null;
if (!userDefined && joniResolvedProperty
&& (frontendProperty || scriptExtensions || perlBuiltInAlias)) {
translated.append(pattern, i, end + 1);
Expand Down Expand Up @@ -270,7 +273,9 @@ private static UserPropertyTranslation translateUserDefinedProperties(
.append(')');
} catch (IllegalArgumentException error) {
String message = error.getMessage();
if (!userDefined || message != null && message.contains("in expansion of")) {
if (!userDefined || message != null
&& (message.contains("in expansion of")
|| message.startsWith("Can't find Unicode property definition"))) {
throw error;
}
translated.append("[\\s\\S]");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -355,11 +355,12 @@ static int handleEscapeSequences(String s, StringBuilder sb, int c, int offset,
// Perl allows user-defined properties (InFoo/IsFoo) to be unknown at compile time;
// they are resolved at runtime when the property sub is available.
// If it's currently undefined, emit a placeholder that compiles in Java and mark for recompilation.
// But if the error already contains "in expansion of", it is a real user-property definition error
// that should be reported (not deferred).
// Explicit missing-property and "in expansion of" diagnostics are
// real user-property errors that should be reported, not deferred.
String msg = e.getMessage();
if (UnicodeResolver.isUserDefinedPropertyName(property)
&& (msg == null || !msg.contains("in expansion of"))) {
&& (msg == null || (!msg.contains("in expansion of")
&& !msg.startsWith("Can't find Unicode property definition")))) {
RegexPreprocessor.markDeferredUnicodePropertyEncountered();
sb.setLength(sb.length() - 1); // Remove the backslash
// Placeholder: match any single character, including newline
Expand Down
Loading