# Copyright (c) 2025 Yuki Kimoto
# MIT License
class Re {
version_from Regex;
use Regex;
use Regex::Match;
use Sync::Mutex;
# Fields
our $MUTEX : Sync::Mutex;
our $REGEXES_H : cache Hash of Regex;
INIT {
$MUTEX = Sync::Mutex->new;
}
private static method new_regex_use_cache : Regex ($pattern : string, $flags : string, $is_global_ref : int*) {
unless ($pattern) {
die "The regex pattern $pattern must be defined.";
}
unless ($flags) {
$flags = "";
}
my $is_global = 0;
my $flags_without_g = (string)undef;
if (Fn->contains($flags, "g")) {
$is_global = 1;
my $flags_length = length $flags;
my $flags_without_g_tmp = (mutable string)new_string_len $flags_length;
my $char_offset = 0;
for (my $i = 0; $i < $flags_length; $i++) {
my $char = $flags->[$i];
unless ($char == 'g') {
$flags_without_g_tmp->[$char_offset] = $char;
$char_offset++;
}
}
Fn->shorten_null_char($flags_without_g_tmp);
$flags_without_g = $flags_without_g_tmp;
}
else {
$flags_without_g = $flags;
}
$$is_global_ref = $is_global;
my $regex_key = "(?$flags_without_g)$pattern";
my $regex = (Regex)undef;
{
Fn->defer(method : void () {
$MUTEX->reader_unlock;
});
$MUTEX->reader_lock;
if ($REGEXES_H) {
$regex = (Regex)$REGEXES_H->get($regex_key);
}
}
unless ($regex) {
Fn->defer(method : void () {
$MUTEX->unlock;
});
$MUTEX->lock;
unless ($REGEXES_H) {
$REGEXES_H = Hash->new;
}
$regex = Regex->new($pattern, $flags_without_g);
$REGEXES_H->set($regex_key, $regex);
}
return $regex;
}
static method m : Regex::Match ($string : string, $pattern_and_flags : string|string[], $offset_ref : int* = undef, $length : int = -1) {
my $pattern = (string)undef;
my $flags = (string)undef;
if ($pattern_and_flags isa string) {
$pattern = (string)$pattern_and_flags;
}
elsif ($pattern_and_flags isa string[]) {
$pattern = $pattern_and_flags->(string[])->[0];
$flags = $pattern_and_flags->(string[])->[1];
}
else {
die "The type of \$pattern_and_flags must be string or string[].";
}
my $is_global = 0;
my $regex = &new_regex_use_cache($pattern, $flags, \$is_global);
my $match = $regex->match($string, $offset_ref, $length);
return $match;
}
static method s : Regex::ReplaceInfo ($string : mutable string, $pattern_and_flags : string|string[], $replace : string|Regex::Replacer, $offset_ref : int* = undef, $length : int = -1) {
my $pattern = (string)undef;
my $flags = (string)undef;
if ($pattern_and_flags isa string) {
$pattern = (string)$pattern_and_flags;
}
elsif ($pattern_and_flags isa string[]) {
$pattern = $pattern_and_flags->(string[])->[0];
$flags = $pattern_and_flags->(string[])->[1];
}
else {
die "The type of \$pattern_and_flags must be string or string[].";
}
my $is_global = 0;
my $regex = &new_regex_use_cache($pattern, $flags, \$is_global);
my $options = Fn->merge_options(undef, {global => $is_global});
my $replace_info = $regex->replace($string, $replace, $offset_ref, $length, $options);
return $replace_info;
}
static method split : string[] ($pattern_and_flags : string|string[], $string : string, $limit : int = 0) {
my $pattern = (string)undef;
my $flags = (string)undef;
if ($pattern_and_flags isa string) {
$pattern = (string)$pattern_and_flags;
}
elsif ($pattern_and_flags isa string[]) {
$pattern = $pattern_and_flags->(string[])->[0];
$flags = $pattern_and_flags->(string[])->[1];
}
else {
die "The type of \$pattern_and_flags must be string or string[].";
}
my $is_global = 0;
my $regex = &new_regex_use_cache($pattern, $flags, \$is_global);
my $parts = $regex->split($string, $limit);
return $parts;
}
}