///|
// CPython int(str, 10) does NOT accept ASCII U+001C..001F, despite isspace.
fn percent_precision_space(character : Char) -> Bool {
let code = character.to_int()
(code >= 9 && code <= 13) ||
code == 32 ||
code == 0x85 ||
code == 0xa0 ||
code == 0x1680 ||
(code >= 0x2000 && code <= 0x200a) ||
code == 0x2028 ||
code == 0x2029 ||
code == 0x202f ||
code == 0x205f ||
code == 0x3000
}
///|
// PyErr_Format uses %.200R. Bound the repr itself, preserving possible partial
// escape/quote truncation while avoiding a full escaped copy of a large input.
fn percent_precision_repr(value : String) -> String {
let quote = if value.contains("'") && !value.contains("\"") {
'"'
} else {
'\''
}
let result = StringBuilder()
result.write_char(quote)
let mut length = 1
for character in value {
if length == 200 {
break
}
let code = character.to_int()
let piece = if character == quote || character == '\\' {
"\\" + character.to_string()
} else if character == '\n' {
"\\n"
} else if character == '\r' {
"\\r"
} else if character == '\t' {
"\\t"
} else if !named_format_python_printable(character) {
let (prefix, width) = if code <= 255 {
("\\x", 2)
} else if code <= 65535 {
("\\u", 4)
} else {
("\\U", 8)
}
prefix +
code.reinterpret_as_uint().to_string(radix=16).pad_start(width, '0')
} else {
character.to_string()
}
for output in piece {
if length == 200 {
break
}
result.write_char(output)
length += 1
}
}
if length < 200 {
result.write_char(quote)
}
result.to_string()
}
///|
fn percent_invalid_precision_text(
value : String,
) -> @bigint.BigInt raise @core.DataError {
raise @core.DataError::InvalidTicks(
message="ValueError: invalid literal for int() with base 10: " +
percent_precision_repr(value),
)
}
///|
fn percent_precision_text_integer(
value : String,
) -> @bigint.BigInt raise @core.DataError {
let chars = value.to_array()
let mut first = 0
let mut last = chars.length()
while first < last && percent_precision_space(chars[first]) {
first += 1
}
while last > first && percent_precision_space(chars[last - 1]) {
last -= 1
}
let negative = first < last && chars[first] == '-'
if first < last && (chars[first] == '+' || chars[first] == '-') {
first += 1
}
let start = first
let mut count = 0
let mut previous_digit = false
while first < last {
if method_decimal_digit(chars[first]) is Some(_) {
count += 1
previous_digit = true
} else if chars[first] == '_' && previous_digit {
previous_digit = false
} else {
return percent_invalid_precision_text(value)
}
first += 1
}
if !previous_digit {
return percent_invalid_precision_text(value)
}
// Syntax errors win over the decimal digit limit, including a trailing '_'.
if count > 4300 {
raise @core.DataError::InvalidTicks(
message="ValueError: Exceeds the limit (4300 digits) for integer string conversion: value has " +
count.to_string() +
" digits; use sys.set_int_max_str_digits() to increase the limit",
)
}
let mut integer = @bigint.BigInt::from_int(0)
let ten = @bigint.BigInt::from_int(10)
for index in start.. @bigint.BigInt raise @core.DataError {
match value {
Automatic => @bigint.BigInt::from_int(0)
Integer(integer) => integer
Boolean(boolean) => @bigint.BigInt::from_int(if boolean { 1 } else { 0 })
Text(text) => percent_precision_text_integer(text)
Number(number) => {
if number.is_nan() {
raise @core.DataError::InvalidTicks(
message="ValueError: cannot convert float NaN to integer",
)
}
if number.is_inf() {
raise @core.DataError::InvalidTicks(
message="OverflowError: cannot convert float infinity to integer",
)
}
let (numerator, denominator) = decimal_ratio(number)
let integer = numerator / denominator
if number < 0.0 {
integer.neg()
} else {
integer
}
}
}
}
///|
fn percent_explicit_decimals(
value : PercentDecimals,
) -> Int32 raise @core.DataError {
let integer = percent_precision_integer(value)
let magnitude = if integer < @bigint.BigInt::from_int(0) {
integer.neg()
} else {
integer
}
// The dynamic f-string stringifies int(decimals) before parsing precision.
// Only unusually large integers need the exact decimal-string limit check.
if magnitude.bit_length() >= 14285 && magnitude >= decimal_power(4300) {
raise @core.DataError::InvalidTicks(
message="ValueError: Exceeds the limit (4300 digits) for integer string conversion; use sys.set_int_max_str_digits() to increase the limit",
)
}
if integer < @bigint.BigInt::from_int(0) {
raise @core.DataError::InvalidTicks(
message="ValueError: Format specifier missing precision",
)
}
if integer > @bigint.BigInt::from_int64(9223372036854775807L) {
raise @core.DataError::InvalidTicks(
message="ValueError: Too many decimal digits in format string",
)
}
if integer > @bigint.BigInt::from_int(2147483647) {
raise @core.DataError::InvalidTicks(message="ValueError: precision too big")
}
integer.to_int()
}