# Copyright (c) 2023 Yuki Kimoto
# MIT License
class Fn {
use StringBuffer;
use StringList;
use IntList;
enum {
GET_CODE_POINT_ERROR_OVER_STRING_RANGE = -1,
GET_CODE_POINT_ERROR_INVALID_UTF8 = -2,
}
static method BYTE_MAX : int () { return 127; }
static method BYTE_MIN : int () { return -128; }
native static method DBL_MAX : double ();
native static method DBL_MIN : double ();
native static method DOUBLE_MAX : double ();
native static method DOUBLE_MIN : double ();
native static method FLOAT_MAX : float ();
native static method FLOAT_MIN : float();
native static method FLT_MAX : float ();
native static method FLT_MIN : float();
static method INT16_MAX : int () { return 32767; }
static method INT16_MIN : int () { return -32768; }
static method INT32_MAX : int () { return 2147483647; }
static method INT32_MIN : int () { return -2147483648; }
static method INT64_MAX : long () { return 9223372036854775807L; }
static method INT64_MIN : long () { return -9223372036854775808L; }
static method INT8_MAX : int () { return 127; }
static method INT8_MIN : int () { return -128; }
static method INT_MAX : int () { return 2147483647; }
static method INT_MIN : int () { return -2147483648; }
static method LONG_MAX : long () { return 9223372036854775807L; }
static method LONG_MIN : long () { return -9223372036854775808L; }
static method RAND_MAX : int () { return 2147483647; }
static method SHORT_MAX : int () { return 32767; }
static method SHORT_MIN : int () { return -32768; }
static method UBYTE_MAX : int () { return (byte)0xFF; }
static method UINT16_MAX : int () { return (short)0xFFFF; }
static method UINT32_MAX : int () { return 0xFFFFFFFF; }
static method UINT64_MAX : long () { return 0xFFFFFFFFFFFFFFFFL; }
static method UINT8_MAX : int () { return (byte)0xFF; }
static method UINT_MAX : int () { return 0xFFFFFFFF; }
static method ULONG_MAX : long () { return 0xFFFFFFFFFFFFFFFFL; }
static method USHORT_MAX : int () { return (short)0xFFFF; }
static method abs : int ($value : int) {
my $abs = 0;
if ($value > 0) {
$abs = $value;
}
else {
$abs = -$value;
}
return $abs;
}
static method chomp : void ($string : mutable string) {
unless ($string) {
die "The \$string must be defined";
}
my $length = length $string;
if ($length >= 2 && $string->[$length - 2] == '\r' && $string->[$length - 1] == '\n') {
&shorten($string, $length - 2);
}
elsif ($length >= 1 && $string->[$length - 1] == '\n') {
&shorten($string, $length - 1);
}
}
static method chompr : string ($string : string) {
unless ($string) {
die "The \$string must be defined";
}
my $new_string = (mutable string)copy $string;
&chomp($new_string);
return $new_string;
}
static method chr : string ($code_point : int) {
my $is_unicode_scalar_value = &is_unicode_scalar_value($code_point);
my $utf8_char = (string)undef;
if ($is_unicode_scalar_value) {
$utf8_char = &_chr_native($code_point);
}
return $utf8_char;
}
static method contains : int ($string : string, $substring : string, $string_offset = 0 : int, $string_length = -1 : int) {
return &index($string, $substring, $string_offset, $string_length) >= 0;
}
static method copy_string : string ($string : string) {
return copy $string;
}
native static method crand : int ($seed : int*);
precompile static method equals_string_range : int ($string1 : string, $string1_offset : int, $string2 : string, $string2_offset : int, $length : int) {
unless ($string1) {
die "The \$string1 must be defined";
}
unless ($string2) {
die "The \$string2 must be defined";
}
unless ($string1_offset >= 0) {
die "The \$string1_offset must be greater than or equal to 0";
}
unless ($string2_offset >= 0) {
die "The \$string2_offset must be greater than or equal to 0";
}
my $string1_length = length $string1;
unless ($string1_offset + $length <= $string1_length) {
return 0;
}
my $string2_length = length $string2;
unless ($string1_offset + $length <= $string2_length) {
return 0;
}
my $match = 1;
for (my $i = 0; $i < $length; $i++) {
my $char1 = $string1->[$string1_offset + $i];
my $char2 = $string2->[$string2_offset + $i];
unless ($char1 == $char2) {
$match = 0;
last;
}
}
return $match;
}
native static method get_code_point : int ($string : string, $offset_ref : int*);
precompile static method hex : int ($hex_string : string) {
unless ($hex_string) {
die "The \$hex string must be defined";
}
my $hex_value = 0;
my $digit = 0;
my $value = 0;
my $length = length $hex_string;
unless ($length >= 1 && $length <= 8) {
die "The length of the \$hex string must be 1 to 8";
}
for (my $i = $length - 1; $i >= 0; $i--) {
my $ascii_code = $hex_string->[$i];
unless (&is_hex_digit($ascii_code)) {
die "The \$hex string must contain only hex characters";
}
my $digit_value = 0;
if ($ascii_code >= '0' && $ascii_code <= '9') {
$digit_value = $ascii_code - 48;
}
elsif ($ascii_code >= 'a' && $ascii_code <= 'f') {
$digit_value = $ascii_code - 87;
}
elsif ($ascii_code >= 'A' && $ascii_code <= 'F') {
$digit_value = $ascii_code - 55;
}
$value += $digit_value * &powi(16, $digit);
$digit += 1;
}
return $value;
}
precompile static method index : int ($string : string, $substring : string, $string_offset = 0 : int, $string_length = -1 : int) {
unless ($string) {
die "The \$string must be defined";
}
unless ($substring) {
die "The \$substring must be defined";
}
unless ($string_offset >= 0) {
die "The \$string_offset must be greater than or equal to 0";
}
if ($string_length < 0) {
$string_length = length $string - $string_offset;
}
unless ($string_offset + $string_length <= length $string) {
die "The \$string_offset + the \$string_length must be less than or equal to the length of the \$string";
}
my $substring_length = length $substring;
if ($substring_length == 0) {
return 0;
}
my $first : byte = $substring->[0];
my $max : int = ($string_offset + $string_length - $substring_length);
for (my $i = $string_offset; $i <= $max; $i++) {
if ($string->[$i] != $first) {
while (++$i <= $max && $string->[$i] != $first) {}
}
if ($i <= $max) {
my $j = $i + 1;
my $end = $j + $substring_length - 1;
for (my $k = 01; $j < $end && $string->[$j] == $substring->[$k]; ($j++, $k++)) {}
if ($j == $end) {
return $i;
}
}
}
return -1;
}
precompile static method init_string : void ($string : mutable string, $ascii_code = 0 : int, $offset = 0 : int, $length = -1 : int) {
unless ($string) {
die "The \$string must be defined";
}
my $string_length = length $string;
if ($length < 0) {
$length = $string_length - $offset;
}
unless ($offset + $length <= $string_length) {
die "The \$offset + the \$length must be less than or equal to the length of the \$string";
}
for (my $i = $offset; $i < $offset + $length; $i++) {
$string->[$i] = (byte)$ascii_code;
}
}
static method is_alnum : int ($code_point : int) {
if (($code_point >= 'A' && $code_point <= 'Z') || ($code_point >= 'a' && $code_point <= 'z') || ($code_point >= '0' && $code_point <= '9')) {
return 1;
}
else {
return 0;
}
}
static method is_alpha : int ($code_point : int) {
if (($code_point >= 'A' && $code_point <= 'Z') || ($code_point >= 'a' && $code_point <= 'z')) {
return 1;
}
else {
return 0;
}
}
native static method is_array : int ($object : object);
static method is_blank : int ($code_point : int) {
# SP or HT
if ($code_point >= '\x20' || $code_point <= '\x9') {
return 1;
}
else {
return 0;
}
}
native static method is_class : int ($object : object);
static method is_cntrl : int ($code_point : int) {
if (($code_point >= 0x00 && $code_point <= 0x1f) || $code_point == 0x7f) {
return 1;
}
else {
return 0;
}
}
static method is_digit : int ($code_point : int) {
if ($code_point >= '0' && $code_point <= '9') {
return 1;
}
else {
return 0;
}
}
static method is_graph : int ($code_point : int) {
if ($code_point >= 0x21 && $code_point <= 0x7E) {
return 1;
}
else {
return 0;
}
}
static method is_hex_digit : int ($code_point : int) {
if (($code_point >= '0' && $code_point <= '9') || ($code_point >= 'a' && $code_point <= 'f') || ($code_point >= 'A' && $code_point <= 'F')) {
return 1;
}
else {
return 0;
}
}
static method is_lower : int ($code_point : int) {
if ($code_point >= 'a' && $code_point <= 'z') {
return 1;
}
else {
return 0;
}
}
native static method is_mulnum_array : int ($object : object);
native static method is_numeric_array : int ($object : object);
native static method is_object_array : int ($object : object);
# This is same as Perl ASCII mode \s
static method is_perl_space : int ($code_point : int) {
my $is_perl_space = 0;
switch ($code_point) {
case 0x20: # ' ' SP
case 0x0D: # '\r' CR
case 0x0A: # '\n' LF
case 0x09: # '\t' HT
case 0x0C: # '\f' FF
{
$is_perl_space = 1;
break;
}
}
return $is_perl_space;
}
static method is_perl_word : int ($code_point : int) {
my $ispword = 0;
if ($code_point >= 'a' && $code_point <= 'z') {
$ispword = 1;
}
elsif ($code_point >= 'A' && $code_point <= 'Z') {
$ispword = 1;
}
elsif ($code_point == '_') {
$ispword = 1;
}
elsif ($code_point >= '0' && $code_point <= '9') {
$ispword = 1;
}
return $ispword;
}
native static method is_pointer_class : int ($object : object);
static method is_print : int ($code_point : int) {
if ($code_point >= 0x20 && $code_point <= 0x7E) {
return 1;
}
else {
return 0;
}
}
static method is_punct : int ($code_point : int) {
if (($code_point >= 0x21 && $code_point <= 0x2F) || ($code_point >= 0x3A && $code_point <= 0x40) || ($code_point >= 0x5B && $code_point <= 0x60) || ($code_point >= 0x7B && $code_point <= 0x7E)) {
return 1;
}
else {
return 0;
}
}
static method is_space : int ($code_point : int) {
if (($code_point >= 0x09 && $code_point <= 0x0D) || $code_point == 0x20) {
return 1;
}
else {
return 0;
}
}
private static method is_unicode_scalar_value : int ($code_point: int) {
my $is_unicode_scalar_value = 0;
# The range of Unicde code points
if ($code_point >= 0 && $code_point <= 0x10FFFF) {
# Not surrogate code points
unless ($code_point >= 0xD800 && $code_point <= 0xDFFF) {
$is_unicode_scalar_value = 1;
}
}
return $is_unicode_scalar_value;
}
static method is_upper : int ($code_point : int) {
if ($code_point >= 'A' && $code_point <= 'Z') {
return 1;
}
else {
return 0;
}
}
static method is_xdigit : int ($code_point : int) {
if (($code_point >= 'A' && $code_point <= 'F') || ($code_point >= 'a' && $code_point <= 'f') || ($code_point >= '0' && $code_point <= '9')) {
return 1;
}
else {
return 0;
}
}
precompile static method join : string ($separator : string, $strings : string[]) {
unless ($separator) {
die "The \$separator must be defined";
}
unless ($strings) {
die "The \$strings must be defined";
}
my $join_buffer = StringBuffer->new;
for (my $i = 0; $i < @$strings; $i++) {
my $string = $strings->[$i];
if ($string) {
$join_buffer->push($string);
}
else {
$join_buffer->push("");
}
if ($i != @$strings - 1) {
$join_buffer->push($separator);
}
}
my $join = $join_buffer->to_string;
return $join;
}
static method labs : long ($value : long) {
my $labs = 0L;
if ($value > 0) {
$labs = $value;
}
else {
$labs = -$value;
}
return $labs;
}
precompile static method lc : string ($string : string) {
unless ($string) {
die "The \$string must be defined";
}
my $length = length $string;
my $new_string = (mutable string)new_string_len($length);
for (my $i = 0; $i < $length; $i++) {
my $char = $string->[$i];
if ($char >= 'A' && $char <= 'Z') {
$new_string->[$i] = (byte)($string->[$i] + 32);
}
else {
$new_string->[$i] = $string->[$i];
}
}
return $new_string;
}
static method lcfirst : string ($string : string) {
unless ($string) {
die "The \$string must be defined";
}
my $length = length $string;
my $new_string = (mutable string)new_string_len($length);
if ($length > 0) {
my $char = $string->[0];
if ($char >= 'A' && $char <= 'Z') {
$new_string->[0] = (byte)($char + 32);
}
else {
$new_string->[0] = $char;
}
}
Fn->memcpy($new_string, 1, $string, 1, $length - 1);
return $new_string;
}
static method look_code_point : int ($string : string, $offset_ref : int*) {
my $save_offset = $$offset_ref;
my $code_point = &get_code_point($string, $offset_ref);
$$offset_ref = $save_offset;
return $code_point;
}
native static method memcpy : void ($dest : object, $dest_offset : int, $source : object, $source_offset : int, $length : int);
native static method memmove : void ($dest : object, $dest_offset : int, $source : object, $source_offset : int, $length : int);
static method ord : int ($string : string) {
my $offset = 0;
my $code_point = &get_code_point($string, \$offset);
return $code_point;
}
precompile static method powi : int ($base : int, $exponant : int) {
unless ($exponant >= 0) {
die "The \$exponant number must be greater than or equal to 0";
}
if ($base == 0) {
unless ($exponant != 0) {
die "If the \$base number is 0, the \$exponant number can't be 0";
}
}
my $ret = 1;
for (my $i = 0; $i < $exponant; $i++) {
$ret = $ret * $base;
}
return $ret;
}
precompile static method powl : long ($base : long, $exponant : long) {
unless ($exponant >= 0) {
die "The \$exponant number must be greater than or equal to 0";
}
if ($base == 0) {
unless ($exponant != 0) {
die "If the \$base number is 0, the \$exponant number can't be 0";
}
}
my $ret = 1L;
for (my $i = 0; $i < $exponant; $i++) {
$ret = $ret * $base;
}
return $ret;
}
static method rand : double ($seed : int*, $max = 1 : int) {
# 0 <= random_number < 1
my $random_number = (double)&crand($seed) / ((double)&RAND_MAX() + 1);
$random_number *= $max;
return $random_number;
}
precompile static method repeat : string ($string : string, $count : int) {
unless ($string) {
die "The \$string must be defined";
}
unless ($count >= 0) {
die "The \$repeat count must be greater than or equal to 0";
}
my $buffer = StringBuffer->new;
for (my $i = 0; $i < $count; $i++) {
$buffer->push($string);
}
my $repeat_string = $buffer->to_string;
return $repeat_string;
}
precompile static method replace_chars : void ($string : mutable string, $from_ch : int, $to_ch : int) {
unless ($string) {
die "The \$string must be defined";
}
my $string_length = length $string;
for (my $i = 0; $i < $string_length; $i++) {
if ($string->[$i] == $from_ch) {
$string->[$i] = (byte)$to_ch;
}
}
}
precompile static method rindex : int ($string : string, $substring : string, $offset = 0 : int, $length = -1 : int) {
unless ($string) {
die "The \$string must be defined";
}
unless ($substring) {
die "The \$substring must be defined";
}
unless ($offset >= 0) {
die "The \$offset must be greater than or equal to 0";
}
my $string_length = length $string;
if ($length < 0) {
$length = $string_length - $offset;
}
unless ($offset + $length <= $string_length) {
die "The \$offset + the \$length must be less than or equal to the length of the \$string";
}
my $substring_length = length $substring;
my $start_offset = $offset + $length - $substring_length;
for (my $i = $start_offset; $i >= $offset; --$i) {
my $match = 1;
for (my $j = 0; $j < $substring_length; ++$j) {
if ($string->[$i + $j] != $substring->[$j]) {
$match = 0;
last;
}
}
if ($match) {
return $i;
}
}
return -1;
}
native static method sizeof_native_int : int ();
native static method sizeof_native_pointer : int ();
native static method shorten : void ($string : mutable string, $length : int);
precompile static method shorten_null_char : void ($string : mutable string) {
if (!$string) {
die "The \$string must be defined";
}
my $null_char_offset = -1;
for (my $i = 0; $i < length $string; $i++) {
my $char = $string->[$i];
if ($char == '\0') {
$null_char_offset = $i;
last;
}
}
if ($null_char_offset >= 0) {
Fn->shorten($string, $null_char_offset);
}
}
precompile static method split : string[] ($separator : string, $string : string, $limit = 0 : int) {
unless ($separator) {
die "The \$separator must be defined";
}
unless ($string) {
die "The \$string must be defined";
}
my $string_length = length $string;
my $separator_length = length $separator;
unless ($separator_length > 0) {
die "The length of the \$separator must be greater than 0";
}
my $parts_list = StringList->new_len(0);
my $offset = 0;
my $match_count = 0;
for (my $i = 0; $i < $string_length; $i++) {
if ($limit > 0 && $match_count >= $limit - 1) {
last;
}
my $match_offset = &index($string, $separator, $offset);
my $match = $match_offset >= 0;
if ($match) {
$match_count++;
my $part = &substr($string, $offset, $match_offset - $offset);
$parts_list->push($part);
my $match_legnth = $separator_length;
$offset = $match_offset + $match_legnth;
}
}
if ($offset == $string_length) {
$parts_list->push("");
}
else {
my $part = &substr($string, $offset, $string_length - $offset);
$parts_list->push($part);
}
if ($limit == 0) {
while ($parts_list->length > 0) {
if ($parts_list->get($parts_list->length - 1) eq "") {
$parts_list->pop;
}
else {
last;
}
}
}
my $parts = $parts_list->to_array;
return $parts;
}
static method substr : string ($string : string, $offset : int, $length = -1 : int) {
unless ($string) {
die "The \$string must be defined";
}
unless ($offset >= 0) {
die "The \$offset must be greater than or equal to 0";
}
my $string_length = length $string;
if ($length < 0) {
$length = $string_length - $offset;
}
unless ($offset + $length <= $string_length) {
die "The \$offset + the \$length must be less than or equal to the length of the \$string";
}
my $substring = (mutable string)new_string_len($length);
Fn->memcpy($substring, 0, $string, $offset, $length);
return $substring;
}
precompile static method to_code_points : int[] ($string : string) {
unless ($string) {
die "The \$string must be defined";
}
my $string_length = length $string;
my $offset = 0;
my $utf8_length = 0;
my $code_points_list = IntList->new;
while ($offset < $string_length) {
my $code_point = &get_code_point($string, \$offset);
if ($code_point < 0) {
if ($code_point == &GET_CODE_POINT_ERROR_INVALID_UTF8) {
die "The \$string contains a invalid Unicode code point";
}
else {
die "An unexpected exception is thrown";
}
}
$code_points_list->push($code_point);
}
my $code_points = $code_points_list->to_array;
return $code_points;
}
native static method to_double : double ($string : string);
native static method to_float : float ($string : string);
static method to_int : int ($string : string) {
return &to_int_with_base($string, 10);
}
native static method to_int_with_base : int ($string : string, $digit : int);
static method to_long : long ($string : string) {
return &to_long_with_base($string, 10);
}
native static method to_long_with_base : long ($string : string, $digit : int);
static method to_lower : int ($code_point : int) {
if ($code_point >= 'A' && $code_point <= 'Z') {
$code_point = $code_point + 0x20;
}
return $code_point;
}
static method to_upper : int ($code_point : int) {
if ($code_point >= 'a' && $code_point <= 'z') {
$code_point = $code_point - 0x20;
}
return $code_point;
}
precompile static method to_utf8_chars : string[] ($string : string) {
unless ($string) {
die "The \$string must be defined";
}
my $string_length = length $string;
my $offset = 0;
my $utf8_length = 0;
my $utf8_chars_list = StringList->new;
while ($offset < $string_length) {
my $code_point = &get_code_point($string, \$offset);
if ($code_point < 0) {
if ($code_point == &GET_CODE_POINT_ERROR_INVALID_UTF8) {
die "The \$string contains a invalid Unicode code point";
}
else {
die "An unexpected exception is thrown";
}
}
my $utf8_char = &chr($code_point);
$utf8_chars_list->push($utf8_char);
}
my $utf8_chars = $utf8_chars_list->to_array;
return $utf8_chars;
}
precompile static method tr : string ($string : string, $pattern : string, $replace : string) {
unless ($string) {
die "The \$string must be defined";
}
unless ($pattern) {
die "The \$pattern must be defined";
}
unless ($replace) {
die "The \$replace must be defined";
}
my $replace_length = length $pattern;
my $pattern_range = &_parse_range($pattern, "$pattern");
my $replace_range = &_parse_range($replace, "$replace");
my $string_length = length $string;
my $offset = 0;
my $buffer = StringBuffer->new;
while ($offset < $string_length) {
my $code_point = &get_code_point($string, \$offset);
if ($code_point < 0) {
if ($code_point == &GET_CODE_POINT_ERROR_INVALID_UTF8) {
die "The \$string contains a invalid Unicode code point";
}
else {
die "An unexpected exception is thrown";
}
}
my $pattern_offset = 0;
my $match = 0;
my $replace_pos = 0;
my $match_pattern_index = -1;
my $match_code_point_offset = -1;
my $min_code_point = $pattern_range->[0];
my $max_code_point = $pattern_range->[1];
if ($code_point >= $min_code_point && $code_point <= $max_code_point) {
$match = 1;
$match_code_point_offset = $code_point - $min_code_point;
}
if ($match) {
my $replace_code_point = $replace_range->[0] + $match_code_point_offset;
my $char = &chr($replace_code_point);
$buffer->push($char);
}
else {
my $char = &chr($code_point);
$buffer->push($char);
}
}
my $ret = $buffer->to_string;
return $ret;
}
precompile static method trim : string ($string : string) {
unless ($string) {
die "The \$string must be defined";
}
my $length = length $string;
my $start_string_index = -1;
my $end_string_index = -1;
for (my $i = 0; $i < $length; $i++) {
if ($start_string_index == -1) {
if (&is_space($string->[$i])) {
# Skip
}
else {
$start_string_index = $i;
last;
}
}
}
for (my $i = $length - 1; $i >= 0; $i--) {
if ($end_string_index == -1) {
if (&is_space($string->[$i])) {
# Skip
}
else {
$end_string_index = $i;
last;
}
}
}
my $trimed_string : string;
if ($start_string_index == -1 && $end_string_index == -1) {
return "";
}
elsif ($end_string_index == -1) {
$trimed_string = &substr($string, $start_string_index, $length - $start_string_index);
}
elsif ($end_string_index == -1) {
$trimed_string = &substr($string, 0, $end_string_index + 1);
}
else {
$trimed_string = &substr($string, $start_string_index, $end_string_index - $start_string_index + 1);
}
return $trimed_string;
}
precompile static method uc : string ($string : string) {
unless ($string) {
die "The \$string must be defined";
}
my $length = length $string;
my $new_string = (mutable string)new_string_len($length);
for (my $i = 0; $i < $length; $i++) {
my $char = $string->[$i];
if ($char >= 'a' && $char <= 'z') {
$new_string->[$i] = (byte)($string->[$i] - 32);
}
else {
$new_string->[$i] = $string->[$i];
}
}
return $new_string;
}
static method ucfirst : string ($string : string) {
unless ($string) {
die "The \$string must be defined";
}
my $length = length $string;
my $new_string = (mutable string)new_string_len($length);
if ($length > 0) {
my $char = $string->[0];
if ($char >= 'a' && $char <= 'z') {
$new_string->[0] = (byte)($char - 32);
}
else {
$new_string->[0] = $char;
}
}
Fn->memcpy($new_string, 1, $string, 1, $length - 1);
return $new_string;
}
precompile static method utf8_length : int ($string : string) {
unless ($string) {
die "The \$string must be defined";
}
my $string_length = length $string;
my $offset = 0;
my $utf8_length = 0;
while ($offset < $string_length) {
my $code_point = &get_code_point($string, \$offset);
if ($code_point < 0) {
if ($code_point == &GET_CODE_POINT_ERROR_INVALID_UTF8) {
die "The \$string contains a invalid Unicode code point";
}
else {
die "An unexpected exception is thrown";
}
}
$utf8_length++;
}
return $utf8_length;
}
precompile static method utf8_substr : string ($string : string, $utf8_offset : int, $utf8_length = -1 : int) {
unless ($string) {
die "The \$string must be defined";
}
unless ($utf8_offset >= 0) {
die "The \$utf8_offset must be greater than or equal to 0";
}
my $string_length = length $string;
my $offset = 0;
my $current_utf8_offset = 0;
my $buffer = StringBuffer->new;
while ($offset < $string_length) {
my $code_point = &get_code_point($string, \$offset);
if ($code_point < 0) {
if ($code_point == &GET_CODE_POINT_ERROR_INVALID_UTF8) {
die "The \$string contains a invalid Unicode code point";
}
else {
die "An unexpected exception is thrown";
}
}
# -1:before, 0:range, 1:after
my $range = 0;
if ($current_utf8_offset < $utf8_offset) {
$range = -1;
}
else {
if ($utf8_length < 0) {
$range = 0;
}
else {
if ($current_utf8_offset < $utf8_offset + $utf8_length) {
$range = 0;
}
else {
$range = 1;
}
}
}
if ($range == 0) {
my $utf8_char = &chr($code_point);
$buffer->push($utf8_char);
}
elsif ($range == 1) {
last;
}
$current_utf8_offset++;
}
if ($utf8_length >= 0) {
unless ($utf8_offset + $utf8_length <= $current_utf8_offset) {
die "The \$utf8_offset + the \$utf8_length must be less than or equal to the UTF-8 length of the \$string";
}
}
my $substring = $buffer->to_string;
return $substring;
}
native static method _chr_native : string ($uchar : int);
private precompile static method _parse_range : int[] ($range_format : string, $arg_name : string) {
my $range = IntList->new;
my $range_format_length = length $range_format;
my $offset = 0;
my $min_code_point = -1;
my $max_code_point = -1;
my $code_points_index = 0;
while ($offset < $range_format_length) {
my $code_point = &get_code_point($range_format, \$offset);
if ($code_point < 0) {
if ($code_point == &GET_CODE_POINT_ERROR_INVALID_UTF8) {
die "The range format of the $arg_name can't be contain a invalid Unicode code point";
}
else {
die "Calling the get_code_point method in the Fn class to the $arg_name returns an error code: $code_point";
}
}
if ($code_points_index == 0) {
$min_code_point = $code_point;
}
elsif ($code_points_index == 1) {
unless ($code_point == '-') {
die "The second character ot the range format of the $arg_name must be \"-\"";
}
}
elsif ($code_points_index == 2) {
$max_code_point = $code_point;
}
$code_points_index++;
}
my $code_points_length = $code_points_index;
unless ($code_points_length == 3) {
if ($code_points_length == 1) {
$max_code_point = $min_code_point;
}
if ($max_code_point < 0) {
die "The range format of the $arg_name must be 1 or 3 characters";
}
}
unless ($min_code_point <= $max_code_point) {
die "The code point of the ending character in the $arg_name must be greater than or equal to the code point of the begining caharater";
}
return [$min_code_point, $max_code_point];
}
}