///|
priv enum MethodFormatValue {
Scalar(NamedFormatValue)
Integer(Int64)
Text(String)
NoneValue
TypeValue(String)
}
///|
fn method_decimal_digit(character : Char) -> Int32? {
let code = character.to_int()
for
zero in [
0x30, 0x660, 0x6f0, 0x7c0, 0x966, 0x9e6, 0xa66, 0xae6, 0xb66, 0xbe6, 0xc66,
0xce6, 0xd66, 0xde6, 0xe50, 0xed0, 0xf20, 0x1040, 0x1090, 0x17e0, 0x1810, 0x1946,
0x19d0, 0x1a80, 0x1a90, 0x1b50, 0x1bb0, 0x1c40, 0x1c50, 0xa620, 0xa8d0, 0xa900,
0xa9d0, 0xa9f0, 0xaa50, 0xabf0, 0xff10, 0x104a0, 0x10d30, 0x11066, 0x110f0,
0x11136, 0x111d0, 0x112f0, 0x11450, 0x114d0, 0x11650, 0x116c0, 0x11730, 0x118e0,
0x11950, 0x11c50, 0x11d50, 0x11da0, 0x11f50, 0x16a60, 0x16ac0, 0x16b50, 0x1d7ce,
0x1d7d8, 0x1d7e2, 0x1d7ec, 0x1d7f6, 0x1e140, 0x1e2f0, 0x1e4f0, 0x1e950, 0x1fbf0,
] {
if code >= zero && code < zero + 10 {
return Some(code - zero)
}
}
None
}
///|
fn method_quote(value : String, ascii~ : Bool = false) -> String {
let quote = if value.contains("'") && !value.contains("\"") {
'"'
} else {
'\''
}
let result = StringBuilder()
result.write_char(quote)
for character in value.to_array() {
let code = character.to_int()
if character == quote || character == '\\' {
result.write_char('\\')
result.write_char(character)
} else if character == '\n' {
result.write_string("\\n")
} else if character == '\r' {
result.write_string("\\r")
} else if character == '\t' {
result.write_string("\\t")
} else if !named_format_python_printable(character) ||
(ascii && code >= 128) {
let (prefix, width) = if code <= 255 {
("\\x", 2)
} else if code <= 65535 {
("\\u", 4)
} else {
("\\U", 8)
}
result.write_string(
prefix +
code.reinterpret_as_uint().to_string(radix=16).pad_start(width, '0'),
)
} else {
result.write_char(character)
}
}
result.write_char(quote)
result.to_string()
}
///|
/// Represent a String using the existing Python quoting and Unicode profile.
pub fn python_string_repr(value : String) -> String {
method_quote(value)
}
///|
fn method_type_name(value : MethodFormatValue) -> String {
match value {
Scalar(PythonFloat(_)) => "float"
Scalar(PythonInteger(_)) => "int"
Scalar(NumpyFloat64(_)) => "float64"
Scalar(NumpyInt64(_)) => "int64"
Integer(_) => "int"
Text(_) => "str"
NoneValue => "NoneType"
TypeValue(_) => "type"
}
}
///|
fn method_str(value : MethodFormatValue) -> String raise @core.DataError {
match value {
Scalar(value) => named_format_value_str(value)
Integer(value) => value.to_string()
Text(value) => value
NoneValue => "None"
TypeValue(name) => {
let name = if name == "float64" || name == "int64" {
"numpy." + name
} else {
name
}
""
}
}
}
///|
fn method_repr(
value : MethodFormatValue,
ascii~ : Bool = false,
) -> String raise @core.DataError {
match value {
Scalar(value) => named_format_value_repr(value)
Text(value) => method_quote(value, ascii~)
_ => method_str(value)
}
}
///|
fn method_attribute(
value : MethodFormatValue,
name : String,
) -> MethodFormatValue raise @core.DataError {
if name == "__class__" {
return TypeValue(method_type_name(value))
}
match (value, name) {
(TypeValue(name), "__name__") => Text(name)
(Scalar(PythonFloat(number)), "real") => Scalar(PythonFloat(number))
(Scalar(NumpyFloat64(number)), "real") => Scalar(NumpyFloat64(number))
(Scalar(PythonFloat(_)), "imag") => Scalar(PythonFloat(0.0))
(Scalar(NumpyFloat64(_)), "imag") => Scalar(NumpyFloat64(0.0))
(Scalar(PythonInteger(number)), "real" | "numerator") =>
Scalar(PythonInteger(number))
(Scalar(PythonInteger(_)), "imag") =>
Scalar(PythonInteger(@bigint.BigInt::from_int(0)))
(Scalar(PythonInteger(_)), "denominator") => Integer(1L)
(Scalar(NumpyInt64(number)), "real" | "numerator") =>
Scalar(NumpyInt64(number))
(Scalar(NumpyInt64(_)), "imag") => Scalar(NumpyInt64(0L))
(Scalar(NumpyInt64(_)), "denominator") => Integer(1L)
(Integer(number), "real" | "numerator") => Integer(number)
(Integer(_), "imag") => Integer(0L)
(Integer(_), "denominator") => Integer(1L)
_ => {
ignore(
named_format_error(
"Unsupported",
"dynamic attribute " +
method_quote(name) +
" on " +
method_type_name(value),
),
)
value
}
}
}
///|
// Parse Python's decimal Nd integer tokens, retaining Py_ssize_t overflow order.
fn method_decimal_token(chars : Array[Char]) -> Int64? raise @core.DataError {
if chars.length() == 0 {
return None
}
let mut result = 0L
for character in chars {
let digit = match method_decimal_digit(character) {
Some(digit) => digit
None => return None
}
if result > (9223372036854775807L - Int64::from_int(digit)) / 10L {
ignore(
named_format_error(
"ValueError", "Too many decimal digits in format string",
),
)
}
result = result * 10L + Int64::from_int(digit)
}
Some(result)
}
///|
fn method_subscript(
value : MethodFormatValue,
token : String,
) -> MethodFormatValue raise @core.DataError {
let index = method_decimal_token(token.to_array())
match value {
Text(text) =>
match index {
None => {
ignore(
named_format_error(
"TypeError", "string indices must be integers, not 'str'",
),
)
value
}
Some(index) => {
let chars = text.to_array()
if index >= Int64::from_int(chars.length()) {
ignore(
named_format_error("IndexError", "string index out of range"),
)
return value
}
Text(String::from_array([chars[index.to_int()]]))
}
}
_ => {
let name = match value {
Scalar(NumpyFloat64(_)) => "numpy.float64"
Scalar(NumpyInt64(_)) => "numpy.int64"
_ => method_type_name(value)
}
if name == "numpy.float64" || name == "numpy.int64" {
ignore(
named_format_error("IndexError", "invalid index to scalar variable."),
)
} else {
ignore(
named_format_error(
"TypeError",
"'" + name + "' object is not subscriptable",
),
)
}
value
}
}
}
///|
fn method_resolve_field(
name : String,
value : NamedFormatValue,
position : Int32?,
) -> MethodFormatValue raise @core.DataError {
let chars = name.to_array()
let mut index = 0
while index < chars.length() && chars[index] != '.' && chars[index] != '[' {
index += 1
}
let first = String::from_array(chars[:index])
let numeric = method_decimal_token(chars[:index].to_owned())
if first == "" || numeric is Some(_) {
ignore(named_format_error("IndexError", "tuple index out of range"))
}
let mut result : MethodFormatValue = if first == "x" {
MethodFormatValue::Scalar(value)
} else if first == "pos" {
match position {
Some(position) => Integer(Int64::from_int(position))
None => NoneValue
}
} else {
ignore(named_format_error("KeyError", method_quote(first)))
NoneValue
}
while index < chars.length() {
let is_attribute = chars[index] == '.'
if !is_attribute && chars[index] != '[' {
ignore(
named_format_error(
"ValueError", "Only '.' or '[' may follow ']' in format field specifier",
),
)
}
index += 1
let start = index
if is_attribute {
while index < chars.length() && chars[index] != '.' && chars[index] != '[' {
index += 1
}
if start == index {
ignore(
named_format_error("ValueError", "Empty attribute in format string"),
)
}
result = method_attribute(result, String::from_array(chars[start:index]))
} else {
while index < chars.length() && chars[index] != ']' {
index += 1
}
if index == chars.length() {
ignore(named_format_error("ValueError", "Missing ']' in format string"))
}
if start == index {
ignore(
named_format_error("ValueError", "Empty attribute in format string"),
)
}
result = method_subscript(result, String::from_array(chars[start:index]))
index += 1
}
}
result
}