///|
/// Text/encoding builtins (str + bytes/bytearray/memoryview + sys encoding hooks).
///|
fn builtin_str(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
// CPython-like overloads:
// - str() -> ""
// - str(obj) -> obj.__str__() if defined (instances), else fallback
// - str(bytes, encoding[, errors]) -> decode
let mut encoding_value : Value? = None
let mut errors_value : Value? = None
for pair in keywords {
if pair.0 == "encoding" {
encoding_value = Some(pair.1)
} else if pair.0 == "errors" {
errors_value = Some(pair.1)
} else {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"str() got unexpected keyword argument".to_string(),
),
)
}
}
if positional.length() == 0 {
return Ok(Value::Str(""))
}
if positional.length() >= 2 || encoding_value is Some(_) {
let bytes = match positional[0] {
Value::Bytes(items)
| Value::ByteArray(items)
| Value::MemoryView(items) => items
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"str() bytes argument must be a bytes-like object".to_string(),
),
)
}
let encoding = if positional.length() >= 2 {
positional[1]
} else {
match encoding_value {
Some(v) => v
None => Value::Str("utf-8".to_string())
}
}
let errors = if positional.length() >= 3 {
Some(positional[2])
} else {
errors_value
}
let text = match
decode_bytes_with_encoding("str", bytes, encoding, errors) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::Str(text))
}
let value = positional[0]
match value {
Value::Str(text) => Ok(Value::Str(text))
Value::Instance(inst) =>
match get_attr_from_value(value, "__str__", globals, builtins, io) {
Ok(callable) =>
match callable {
Value::BoundMethod(bound) =>
if bound.function.body.length() > 0 {
match
(eval_function_with_kwargs_ref.val)(
bound.function,
[bound.self],
[],
globals,
builtins,
io,
) {
Ok(Value::Str(text)) => Ok(Value::Str(text))
Ok(_) =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__str__ returned non-string".to_string(),
),
)
Err(err) => Err(err)
}
// For most objects, object.__str__ falls back to __repr__.
// Exceptions are special-cased to keep BaseException.__str__
// compatible with CPython (message from `.args`).
} else if is_base_exception_class_value(inst.class) {
Ok(Value::Str(value_to_string(value)))
} else {
match
eval_builtin_call(
"repr",
[value],
[],
locals,
globals,
builtins,
io,
) {
Ok(Some(Value::Str(text))) => Ok(Value::Str(text))
Ok(Some(_)) =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__repr__ returned non-string".to_string(),
),
)
Ok(None) => Ok(Value::Str(value_to_string(value)))
Err(err) => Err(err)
}
}
Value::Function(func) =>
if func.body.length() > 0 {
match
(eval_function_with_kwargs_ref.val)(
func,
[value],
[],
globals,
builtins,
io,
) {
Ok(Value::Str(text)) => Ok(Value::Str(text))
Ok(_) =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__str__ returned non-string".to_string(),
),
)
Err(err) => Err(err)
}
} else if is_base_exception_class_value(inst.class) {
Ok(Value::Str(value_to_string(value)))
} else {
match
eval_builtin_call(
"repr",
[value],
[],
locals,
globals,
builtins,
io,
) {
Ok(Some(Value::Str(text))) => Ok(Value::Str(text))
Ok(Some(_)) =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__repr__ returned non-string".to_string(),
),
)
Ok(None) => Ok(Value::Str(value_to_string(value)))
Err(err) => Err(err)
}
}
_ => Ok(Value::Str(value_to_string(value)))
}
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute =>
if is_base_exception_class_value(inst.class) {
Ok(Value::Str(value_to_string(value)))
} else {
match
eval_builtin_call(
"repr",
[value],
[],
locals,
globals,
builtins,
io,
) {
Ok(Some(Value::Str(text))) => Ok(Value::Str(text))
Ok(Some(_)) =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__repr__ returned non-string".to_string(),
),
)
Ok(None) => Ok(Value::Str(value_to_string(value)))
Err(err) => Err(err)
}
}
_ => Err(err)
}
}
_ => Ok(Value::Str(value_to_string(value)))
}
}
///|
fn builtin_str_maketrans(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("str.maketrans", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"maketrans() takes 1 to 3 arguments".to_string(),
),
)
}
if positional.length() == 1 {
match positional[0] {
Value::Dict(pairs) => {
let out : Array[(Value, Value)] = []
for pair in pairs {
let key = match pair.0 {
Value::Int(v) => Value::Int(v)
Value::Bool(v) =>
Value::Int(@bigint.BigInt::from_int(if v { 1 } else { 0 }))
Value::Str(text) =>
if text.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"string keys in maketrans() must be length 1".to_string(),
),
)
} else {
let code = text.to_array()[0].to_int()
Value::Int(@bigint.BigInt::from_int(code))
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"maketrans() keys must be int or str".to_string(),
),
)
}
let value = match pair.1 {
Value::Int(v) => Value::Int(v)
Value::Bool(v) =>
Value::Int(@bigint.BigInt::from_int(if v { 1 } else { 0 }))
Value::Str(text) => Value::Str(text)
Value::None => Value::None
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"maketrans() values must be int, str, or None".to_string(),
),
)
}
let _ = match dict_set_item(out, key, value) {
Ok(v) => v
Err(err) => return Err(err)
}
}
Ok(Value::Dict(out))
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"maketrans() expects a dict".to_string(),
),
)
}
} else {
let from_text = match positional[0] {
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"maketrans() expects string arguments".to_string(),
),
)
}
let to_text = match positional[1] {
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"maketrans() expects string arguments".to_string(),
),
)
}
if from_text.length() != to_text.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: the first two maketrans arguments must have equal length".to_string(),
),
)
}
let out : Array[(Value, Value)] = []
let from_chars = from_text.to_array()
let to_chars = to_text.to_array()
for i = 0; i < from_chars.length(); i = i + 1 {
let key = Value::Int(@bigint.BigInt::from_int(from_chars[i].to_int()))
let value = Value::Int(@bigint.BigInt::from_int(to_chars[i].to_int()))
let _ = match dict_set_item(out, key, value) {
Ok(v) => v
Err(err) => return Err(err)
}
}
if positional.length() == 3 {
let delete_text = match positional[2] {
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"maketrans() expects string arguments".to_string(),
),
)
}
let delete_chars = delete_text.to_array()
for ch in delete_chars {
let key = Value::Int(@bigint.BigInt::from_int(ch.to_int()))
let _ = match dict_set_item(out, key, Value::None) {
Ok(v) => v
Err(err) => return Err(err)
}
}
}
Ok(Value::Dict(out))
}
}
///|
fn builtin_str_casefold(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = locals
let _ = globals
let _ = builtins
let _ = io
let _ = match ensure_no_keywords("str.casefold", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"casefold() takes exactly one argument".to_string(),
),
)
}
match positional[0] {
Value::Str(text) => Ok(Value::Str(text.to_lower()))
other =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"casefold() expects a str, not '" + type_name_from_value(other) + "'",
),
)
}
}
///|
fn builtin_str_join(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("str.join", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"join() takes exactly one argument".to_string(),
),
)
}
let sep = match positional[0] {
Value::Str(text) => text
other =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"join() expects a str separator, not '" +
type_name_from_value(other) +
"'",
),
)
}
let iterator_value = match
iter_value_to_iterator(positional[1], globals, builtins, io) {
Ok(v) => v
Err(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"can only join an iterable".to_string(),
),
)
}
let buf = StringBuilder()
let mut first = true
while true {
match iterator_next(iterator_value, None, globals, builtins, io) {
Ok(item) => {
let part = match item {
Value::Str(text) => text
other => value_to_string(other)
}
if first {
first = false
} else {
buf.write_string(sep)
}
buf.write_string(part)
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
Ok(Value::Str(buf.to_string()))
}
///|
fn builtin_sys_intern(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("sys.intern", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"intern() takes exactly one argument".to_string(),
),
)
}
match positional[0] {
Value::Str(_) => Ok(positional[0])
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"intern() argument must be str".to_string(),
),
)
}
}
///|
fn builtin_sys_getfilesystemencoding(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("sys.getfilesystemencoding", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"getfilesystemencoding() takes no arguments".to_string(),
),
)
}
Ok(Value::Str("utf-8"))
}
///|
fn builtin_sys_getdefaultencoding(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("sys.getdefaultencoding", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"getdefaultencoding() takes no arguments".to_string(),
),
)
}
Ok(Value::Str("utf-8"))
}
///|
fn builtin_sys_getfilesystemencodeerrors(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("sys.getfilesystemencodeerrors", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"getfilesystemencodeerrors() takes no arguments".to_string(),
),
)
}
Ok(Value::Str("surrogateescape"))
}
///|
fn builtin_sys_getrecursionlimit(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("sys.getrecursionlimit", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
let _ = locals
let _ = globals
let _ = builtins
let _ = io
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"getrecursionlimit() takes no arguments".to_string(),
),
)
}
Ok(Value::Int(@bigint.BigInt::from_int(current_config().max_recursion)))
}
///|
fn builtin_sys_setrecursionlimit(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("sys.setrecursionlimit", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
let _ = locals
let _ = globals
let _ = builtins
let _ = io
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"setrecursionlimit() takes exactly one argument".to_string(),
),
)
}
let limit = match positional[0] {
Value::Int(v) =>
match bigint_to_int_checked(v) {
Ok(n) => n
Err(err) => return Err(err)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"recursion limit must be int".to_string(),
),
)
}
if limit < 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: recursion limit must be >= 1".to_string(),
),
)
}
let cfg = current_config()
let updated = Config::{
max_recursion: limit,
traceback_limit: cfg.traceback_limit,
track_spans: cfg.track_spans,
bytecode_strict: cfg.bytecode_strict,
allow_filesystem_imports: cfg.allow_filesystem_imports,
import_paths: cfg.import_paths,
argv: cfg.argv,
main_path: cfg.main_path,
}
if active_config_stack.val.length() == 0 {
active_config_stack.val.push(updated)
} else {
active_config_stack.val[active_config_stack.val.length() - 1] = updated
}
Ok(Value::None)
}
///|
fn copy_ints(values : Array[Int32]) -> Array[Int32] {
let out : Array[Int32] = []
for value in values {
out.push(value)
}
out
}
///|
fn encode_string_utf8(text : String) -> Array[Int32] {
let out : Array[Int32] = []
for ch in text.to_array() {
let code = ch.to_int()
if code <= 0x7F {
out.push(code)
} else if code <= 0x7FF {
out.push(0xC0 | (code >> 6))
out.push(0x80 | (code & 0x3F))
} else if code <= 0xFFFF {
out.push(0xE0 | (code >> 12))
out.push(0x80 | ((code >> 6) & 0x3F))
out.push(0x80 | (code & 0x3F))
} else {
out.push(0xF0 | (code >> 18))
out.push(0x80 | ((code >> 12) & 0x3F))
out.push(0x80 | ((code >> 6) & 0x3F))
out.push(0x80 | (code & 0x3F))
}
}
out
}
///|
let surrogateescape_pua_base = 0xF0000
///|
fn surrogateescape_char_from_byte(b : Int32) -> Char {
// MoonBit strings cannot represent surrogate code points (U+DCxx),
// so we use a private-use marker range to round-trip surrogateescape.
(surrogateescape_pua_base + (b & 0xFF)).to_char().unwrap()
}
///|
fn surrogateescape_byte_from_char(ch : Char) -> Int32? {
let code = ch.to_int()
if code >= surrogateescape_pua_base && code < surrogateescape_pua_base + 256 {
Some(code - surrogateescape_pua_base)
} else {
None
}
}
///|
fn encode_string_utf8_with_errors(
text : String,
errors : String,
) -> Array[Int32] {
if errors != "surrogateescape" {
return encode_string_utf8(text)
}
let out : Array[Int32] = []
for ch in text.to_array() {
match surrogateescape_byte_from_char(ch) {
Some(b) => out.push(b)
None => {
let code = ch.to_int()
if code <= 0x7F {
out.push(code)
} else if code <= 0x7FF {
out.push(0xC0 | (code >> 6))
out.push(0x80 | (code & 0x3F))
} else if code <= 0xFFFF {
out.push(0xE0 | (code >> 12))
out.push(0x80 | ((code >> 6) & 0x3F))
out.push(0x80 | (code & 0x3F))
} else {
out.push(0xF0 | (code >> 18))
out.push(0x80 | ((code >> 12) & 0x3F))
out.push(0x80 | ((code >> 6) & 0x3F))
out.push(0x80 | (code & 0x3F))
}
}
}
}
out
}
///|
fn encode_string_ascii(text : String) -> Result[Array[Int32], RuntimeError] {
let out : Array[Int32] = []
for ch in text.to_array() {
let code = ch.to_int()
if code > 0x7F {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnicodeEncodeError: 'ascii' codec can't encode character".to_string(),
),
)
}
out.push(code)
}
Ok(out)
}
///|
fn encode_string_ascii_with_errors(
name : String,
text : String,
errors : String,
) -> Result[Array[Int32], RuntimeError] {
let _ = name
if errors == "strict" {
return encode_string_ascii(text)
}
if errors != "surrogateescape" &&
errors != "surrogatepass" &&
errors != "xmlcharrefreplace" {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"LookupError: unknown error handler".to_string(),
),
)
}
let out : Array[Int32] = []
for ch in text.to_array() {
match surrogateescape_byte_from_char(ch) {
Some(b) => out.push(b)
None => {
let code = ch.to_int()
if code > 0x7F {
if errors == "xmlcharrefreplace" {
// Replace with a decimal character reference, e.g. "☃".
for b in "".to_array() {
out.push(b.to_int())
}
let digits = code.to_string()
for digit in digits.to_array() {
out.push(digit.to_int())
}
out.push(0x3B) // ';'
continue
}
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnicodeEncodeError: 'ascii' codec can't encode character".to_string(),
),
)
}
out.push(code)
}
}
}
Ok(out)
}
///|
fn encode_string_with_encoding(
name : String,
text : String,
encoding_value : Value,
errors_value : Value?,
) -> Result[Array[Int32], RuntimeError] {
fn normalize_encoding_name(raw : String) -> String {
// CPython normalizes encoding names case-insensitively and treats "_" as "-".
let lower = raw.to_lower()
let buf = StringBuilder()
for ch in lower {
if ch == '_' {
buf.write_char('-')
} else {
buf.write_char(ch)
}
}
buf.to_string()
}
let encoding = match encoding_value {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
name + "() encoding must be str".to_string(),
),
)
}
let encoding = normalize_encoding_name(encoding)
let errors = match errors_value {
Some(Value::Str(errs)) => errs
Some(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
name + "() errors must be str".to_string(),
),
)
None => "strict"
}
if encoding == "utf-8" || encoding == "utf8" {
if errors != "strict" &&
errors != "surrogateescape" &&
errors != "surrogatepass" &&
errors != "xmlcharrefreplace" {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"LookupError: unknown error handler".to_string(),
),
)
}
Ok(encode_string_utf8_with_errors(text, errors))
} else if encoding == "ascii" ||
encoding == "us-ascii" ||
encoding == "usascii" {
encode_string_ascii_with_errors(name, text, errors)
} else if encoding == "latin-1" ||
encoding == "latin1" ||
encoding == "iso-8859-1" ||
encoding == "iso8859-1" {
if errors != "strict" &&
errors != "ignore" &&
errors != "replace" &&
errors != "surrogateescape" &&
errors != "surrogatepass" &&
errors != "xmlcharrefreplace" {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"LookupError: unknown error handler".to_string(),
),
)
}
let out : Array[Int32] = []
for ch in text {
let code = ch.to_int()
if code <= 0xFF {
out.push(code)
} else if errors == "ignore" {
continue
} else if errors == "replace" {
out.push(0x3F) // '?'
} else {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnicodeEncodeError: 'latin-1' codec can't encode character".to_string(),
),
)
}
}
Ok(out)
} else if encoding == "raw-unicode-escape" || encoding == "raw_unicode_escape" {
fn push_hex(out : Array[Int32], value : Int32, width : Int32) -> Unit {
let mut shift = (width - 1) * 4
while shift >= 0 {
let digit = (value >> shift) & 0xF
let ch = if digit < 10 { 0x30 + digit } else { 0x61 + (digit - 10) }
out.push(ch)
shift = shift - 4
}
}
let out : Array[Int32] = []
for ch in text {
let code = ch.to_int()
if code <= 0xFF {
out.push(code)
} else if code <= 0xFFFF {
out.push(0x5C) // '\'
out.push(0x75) // 'u'
push_hex(out, code, 4)
} else {
out.push(0x5C) // '\'
out.push(0x55) // 'U'
push_hex(out, code, 8)
}
}
Ok(out)
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"LookupError: unknown encoding".to_string(),
),
)
}
}
///|
fn decode_bytes_utf8(
name : String,
bytes : Array[Int32],
errors : String,
) -> Result[String, RuntimeError] {
let _ = name
let buf = StringBuilder()
let mut i = 0
while i < bytes.length() {
let b0 = bytes[i] & 0xFF
if b0 < 0x80 {
buf.write_char(b0.to_char().unwrap())
i += 1
continue
}
let (need, code0) = if (b0 & 0xE0) == 0xC0 {
(1, b0 & 0x1F)
} else if (b0 & 0xF0) == 0xE0 {
(2, b0 & 0x0F)
} else if (b0 & 0xF8) == 0xF0 {
(3, b0 & 0x07)
} else {
if errors == "surrogateescape" {
// Map invalid bytes to U+DCxx, matching Python's surrogateescape.
buf.write_char(surrogateescape_char_from_byte(b0))
i += 1
continue
}
if errors == "replace" {
buf.write_char((65533).to_char().unwrap())
i += 1
continue
}
if errors == "ignore" {
i += 1
continue
}
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnicodeDecodeError: invalid start byte".to_string(),
),
)
}
let mut code = code0
if i + need >= bytes.length() {
if errors == "surrogateescape" {
// Truncated sequence: treat remaining bytes as undecodable.
while i < bytes.length() {
let b = bytes[i] & 0xFF
buf.write_char(surrogateescape_char_from_byte(b))
i += 1
}
break
}
if errors == "replace" {
buf.write_char((65533).to_char().unwrap())
break
}
if errors == "ignore" {
break
}
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnicodeDecodeError: unexpected end of data".to_string(),
),
)
}
let mut bad_lead = false
let mut j = 0
while j < need {
let b = bytes[i + 1 + j] & 0xFF
if (b & 0xC0) != 0x80 {
if errors == "surrogateescape" {
// Bad continuation: treat the lead byte as undecodable and retry from next byte.
buf.write_char(surrogateescape_char_from_byte(b0))
i += 1
bad_lead = true
break
}
if errors == "replace" {
buf.write_char((65533).to_char().unwrap())
i += 1
bad_lead = true
break
}
if errors == "ignore" {
i += 1
bad_lead = true
break
}
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnicodeDecodeError: invalid continuation byte".to_string(),
),
)
}
code = (code << 6) | (b & 0x3F)
j += 1
}
if bad_lead {
continue
}
buf.write_char(code.to_char().unwrap())
i += 1 + need
}
Ok(buf.to_string())
}
///|
fn decode_bytes_ascii(
name : String,
bytes : Array[Int32],
errors : String,
) -> Result[String, RuntimeError] {
let _ = name
let buf = StringBuilder()
for b in bytes {
let v = b & 0xFF
if v > 0x7F {
if errors == "surrogateescape" {
buf.write_char(surrogateescape_char_from_byte(v))
continue
}
if errors == "replace" {
buf.write_char((65533).to_char().unwrap())
continue
}
if errors == "ignore" {
continue
}
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnicodeDecodeError: 'ascii' codec can't decode byte".to_string(),
),
)
}
buf.write_char(v.to_char().unwrap())
}
Ok(buf.to_string())
}
///|
fn decode_bytes_latin1(
name : String,
bytes : Array[Int32],
errors : String,
) -> Result[String, RuntimeError] {
let _ = name
let _ = errors
let buf = StringBuilder()
for b in bytes {
let v = b & 0xFF
buf.write_char(v.to_char().unwrap())
}
Ok(buf.to_string())
}
///|
fn decode_bytes_with_encoding(
name : String,
bytes : Array[Int32],
encoding_value : Value,
errors_value : Value?,
) -> Result[String, RuntimeError] {
fn normalize_encoding_name(raw : String) -> String {
let lower = raw.to_lower()
let buf = StringBuilder()
for ch in lower {
if ch == '_' {
buf.write_char('-')
} else {
buf.write_char(ch)
}
}
buf.to_string()
}
let encoding = match encoding_value {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
name + "() encoding must be str".to_string(),
),
)
}
let encoding = normalize_encoding_name(encoding)
let errors = match errors_value {
Some(Value::Str(errs)) => errs
Some(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
name + "() errors must be str".to_string(),
),
)
None => "strict"
}
if encoding == "utf-8" || encoding == "utf8" {
if errors != "strict" &&
errors != "surrogateescape" &&
errors != "surrogatepass" &&
errors != "ignore" &&
errors != "replace" {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"LookupError: unknown error handler".to_string(),
),
)
}
decode_bytes_utf8(name, bytes, errors)
} else if encoding == "ascii" ||
encoding == "us-ascii" ||
encoding == "usascii" {
if errors != "strict" &&
errors != "surrogateescape" &&
errors != "surrogatepass" &&
errors != "ignore" &&
errors != "replace" {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"LookupError: unknown error handler".to_string(),
),
)
}
decode_bytes_ascii(name, bytes, errors)
} else if encoding == "latin-1" ||
encoding == "latin1" ||
encoding == "iso-8859-1" ||
encoding == "iso8859-1" {
// For latin-1, every byte maps directly to the same codepoint.
// Accept a small set of error handlers for compatibility.
if errors != "strict" &&
errors != "surrogateescape" &&
errors != "surrogatepass" &&
errors != "ignore" &&
errors != "replace" {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"LookupError: unknown error handler".to_string(),
),
)
}
decode_bytes_latin1(name, bytes, errors)
} else if encoding == "raw-unicode-escape" || encoding == "raw_unicode_escape" {
let _ = errors
fn hex_value(b : Int32) -> Int32? {
if b >= 0x30 && b <= 0x39 {
Some(b - 0x30)
} else if b >= 0x41 && b <= 0x46 {
Some(b - 0x41 + 10)
} else if b >= 0x61 && b <= 0x66 {
Some(b - 0x61 + 10)
} else {
None
}
}
fn parse_hex(bytes : Array[Int32], start : Int32, count : Int32) -> Int32? {
if start + count > bytes.length() {
return None
}
let mut acc = 0
for i = 0; i < count; i = i + 1 {
let b = bytes[start + i] & 0xFF
match hex_value(b) {
Some(v) => acc = (acc << 4) | v
None => return None
}
}
Some(acc)
}
let buf = StringBuilder()
let mut i = 0
while i < bytes.length() {
let b0 = bytes[i] & 0xFF
if b0 == 0x5C && i + 1 < bytes.length() { // '\'
let b1 = bytes[i + 1] & 0xFF
if b1 == 0x75 { // 'u'
match parse_hex(bytes, i + 2, 4) {
Some(code) => {
buf.write_char(code.to_char().unwrap())
i += 6
continue
}
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnicodeDecodeError: invalid \\u escape".to_string(),
),
)
}
} else if b1 == 0x55 { // 'U'
match parse_hex(bytes, i + 2, 8) {
Some(code) =>
if code > 0x10FFFF {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnicodeDecodeError: invalid \\U escape".to_string(),
),
)
} else {
buf.write_char(code.to_char().unwrap())
i += 10
continue
}
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnicodeDecodeError: invalid \\U escape".to_string(),
),
)
}
}
}
buf.write_char(b0.to_char().unwrap())
i += 1
}
Ok(buf.to_string())
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"LookupError: unknown encoding".to_string(),
),
)
}
}
///|
fn byte_from_value(name : String, value : Value) -> Result[Int32, RuntimeError] {
match value {
Value::Int(v) => {
let int_val = match bigint_to_int_checked(v) {
Ok(value) => value
Err(err) => return Err(err)
}
if int_val < 0 || int_val > 255 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: byte must be in range(0, 256)".to_string(),
),
)
} else {
Ok(int_val)
}
}
Value::Bool(v) => Ok(if v { 1 } else { 0 })
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
name + "() an integer is required".to_string(),
),
)
}
}
///|
fn bytes_from_iterable(
name : String,
value : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Array[Int32], RuntimeError] {
let iterator_value = match
iter_value_to_iterator(value, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
let out : Array[Int32] = []
while true {
match iterator_next(iterator_value, None, globals, builtins, io) {
Ok(item) => {
let byte = match byte_from_value(name, item) {
Ok(value) => value
Err(err) => return Err(err)
}
out.push(byte)
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
Ok(out)
}
///|
fn bytes_from_count(
name : String,
count : @bigint.BigInt,
) -> Result[Array[Int32], RuntimeError] {
let _ = name
if count < 0N {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: negative count".to_string(),
),
)
}
let count_i = match bigint_to_int_checked(count) {
Ok(value) => value
Err(err) => return Err(err)
}
let out : Array[Int32] = []
for i = 0; i < count_i; i = i + 1 {
out.push(0)
}
Ok(out)
}
///|
fn hex_digit_value(ch : Char) -> Int32? {
let code = ch.to_int()
if code >= '0'.to_int() && code <= '9'.to_int() {
Some(code - '0'.to_int())
} else if code >= 'a'.to_int() && code <= 'f'.to_int() {
Some(10 + (code - 'a'.to_int()))
} else if code >= 'A'.to_int() && code <= 'F'.to_int() {
Some(10 + (code - 'A'.to_int()))
} else {
None
}
}
///|
fn bytes_fromhex_parse(text : String) -> Result[Array[Int32], RuntimeError] {
let out : Array[Int32] = []
let chars = text.to_array()
let mut have_hi = false
let mut hi = 0
for i = 0; i < chars.length(); i = i + 1 {
let ch = chars[i]
if ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r' {
continue
}
match hex_digit_value(ch) {
Some(n) =>
if have_hi {
out.push((hi << 4) | n)
have_hi = false
} else {
hi = n
have_hi = true
}
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: non-hexadecimal number found in fromhex() arg at position " +
i.to_string(),
),
)
}
}
if have_hi {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: non-hexadecimal number found in fromhex() arg at position " +
chars.length().to_string(),
),
)
}
Ok(out)
}
///|
fn builtin_bytes(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = locals
let _ = match ensure_no_keywords("bytes", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() == 0 {
return Ok(Value::Bytes([]))
}
if positional.length() > 3 {
return Err(too_many_arguments_error("bytes", positional.length()))
}
if positional.length() == 1 {
match positional[0] {
Value::Int(count) =>
return Ok(
Value::Bytes(
match bytes_from_count("bytes", count) {
Ok(value) => value
Err(err) => return Err(err)
},
),
)
Value::Bytes(values) => return Ok(Value::Bytes(copy_ints(values)))
Value::ByteArray(values) => return Ok(Value::Bytes(copy_ints(values)))
Value::MemoryView(values) => return Ok(Value::Bytes(copy_ints(values)))
Value::Str(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"string argument without an encoding".to_string(),
),
)
other => {
// CPython: bytes(x) first tries x.__bytes__() if present.
match other {
Value::Instance(_) => {
let maybe_bytes = match
call_method0_on_instance(
other, "__bytes__", globals, builtins, io,
) {
Ok(v) => v
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute => None
_ => return Err(err)
}
}
match maybe_bytes {
Some(v) =>
match v {
Value::Bytes(values)
| Value::ByteArray(values)
| Value::MemoryView(values) =>
return Ok(Value::Bytes(copy_ints(values)))
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__bytes__ returned non-bytes".to_string(),
),
)
}
None => ()
}
}
_ => ()
}
return Ok(
Value::Bytes(
match bytes_from_iterable("bytes", other, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
},
),
)
}
}
}
match positional[0] {
Value::Str(text) => {
let encoding = positional[1]
let errors_value = if positional.length() > 2 {
Some(positional[2])
} else {
None
}
let bytes = match
encode_string_with_encoding("bytes", text, encoding, errors_value) {
Ok(value) => value
Err(err) => return Err(err)
}
Ok(Value::Bytes(bytes))
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"encoding without a string argument".to_string(),
),
)
}
}
///|
fn builtin_bytes_fromhex(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = locals
let _ = globals
let _ = builtins
let _ = io
let _ = match ensure_no_keywords("bytes.fromhex", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"fromhex() takes exactly one argument".to_string(),
),
)
}
let text = match positional[1] {
Value::Str(s) => s
other =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"fromhex() argument must be str, not '" +
type_name_from_value(other) +
"'",
),
)
}
Ok(
Value::Bytes(
match bytes_fromhex_parse(text) {
Ok(v) => v
Err(err) => return Err(err)
},
),
)
}
///|
fn bytes_like_ints_for_translate(
name : String,
value : Value,
) -> Result[Array[Int32], RuntimeError] {
match value {
Value::Bytes(bytes) | Value::ByteArray(bytes) | Value::MemoryView(bytes) =>
Ok(bytes)
Value::Instance(inst) =>
if inst.class.name == "array" {
let typecode = match get_named_value(inst.dict, "typecode") {
Some(Value::Str(v)) => v
_ => "".to_string()
}
if typecode != "b" && typecode != "B" {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"a bytes-like object is required".to_string(),
),
)
}
let data = match get_named_value(inst.dict, "_data") {
Some(Value::List(values)) => values
_ => []
}
let out : Array[Int32] = []
for v in data {
let int_val = match v {
Value::Int(bi) =>
match bigint_to_int_checked(bi) {
Ok(iv) => iv
Err(err) => return Err(err)
}
Value::Bool(b) => if b { 1 } else { 0 }
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
name + "() an integer is required".to_string(),
),
)
}
out.push(int_val & 0xFF)
}
Ok(out)
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"a bytes-like object is required".to_string(),
),
)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"a bytes-like object is required".to_string(),
),
)
}
}
///|
fn builtin_bytes_maketrans(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = locals
let _ = globals
let _ = builtins
let _ = io
let _ = match ensure_no_keywords("bytes.maketrans", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"maketrans() takes exactly two arguments".to_string(),
),
)
}
let from_bytes = match
bytes_like_ints_for_translate("bytes.maketrans", positional[0]) {
Ok(v) => v
Err(err) => return Err(err)
}
let to_bytes = match
bytes_like_ints_for_translate("bytes.maketrans", positional[1]) {
Ok(v) => v
Err(err) => return Err(err)
}
if from_bytes.length() != to_bytes.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: maketrans arguments must have equal length".to_string(),
),
)
}
let table : Array[Int32] = []
for i = 0; i < 256; i = i + 1 {
table.push(i)
}
for i = 0; i < from_bytes.length(); i = i + 1 {
let from_b = from_bytes[i] & 0xFF
let to_b = to_bytes[i] & 0xFF
if from_b >= 0 && from_b < 256 {
table[from_b] = to_b
}
}
Ok(Value::Bytes(table))
}
///|
fn builtin_bytes_translate(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = locals
let _ = globals
let _ = builtins
let _ = io
let _ = match ensure_no_keywords("bytes.translate", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"translate() takes exactly one argument".to_string(),
),
)
}
let src = match positional[0] {
Value::Bytes(bytes) | Value::MemoryView(bytes) => bytes
Value::ByteArray(bytes) => bytes
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"translate() expects a bytes-like object".to_string(),
),
)
}
let table = match
bytes_like_ints_for_translate("bytes.translate", positional[1]) {
Ok(v) => v
Err(err) => return Err(err)
}
if table.length() != 256 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: translation table must be 256 bytes long".to_string(),
),
)
}
let out : Array[Int32] = []
for byte in src {
let b = byte & 0xFF
out.push(table[b])
}
Ok(Value::Bytes(out))
}
///|
fn builtin_bytes_upper(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("bytes.upper", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"upper() takes no arguments".to_string(),
),
)
}
let bytes = match positional[0] {
Value::Bytes(values) | Value::MemoryView(values) => values
Value::ByteArray(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"upper() expects a bytes-like object".to_string(),
),
)
}
let out : Array[Int32] = []
for b in bytes {
let v = b & 0xFF
if v >= 'a'.to_int() && v <= 'z'.to_int() {
out.push(v - 32)
} else {
out.push(v)
}
}
Ok(Value::Bytes(out))
}
///|
fn builtin_bytes_rstrip(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("bytes.rstrip", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 1 && positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rstrip() takes at most one argument".to_string(),
),
)
}
let bytes = match positional[0] {
Value::Bytes(values) | Value::MemoryView(values) => values
Value::ByteArray(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rstrip() expects a bytes-like object".to_string(),
),
)
}
let strip_set : Array[Bool] = []
for _i = 0; _i < 256; _i = _i + 1 {
strip_set.push(false)
}
if positional.length() == 1 || positional[1] is Value::None {
// Default: ASCII whitespace.
for
ch in [
' '.to_int(),
'\t'.to_int(),
'\n'.to_int(),
'\r'.to_int(),
0x0b,
0x0c,
] {
strip_set[ch] = true
}
} else {
let chars = match positional[1] {
Value::Bytes(values)
| Value::ByteArray(values)
| Value::MemoryView(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rstrip() chars must be a bytes-like object".to_string(),
),
)
}
for b in chars {
strip_set[b & 0xFF] = true
}
}
let mut end = bytes.length()
while end > 0 {
let b = bytes[end - 1] & 0xFF
if strip_set[b] {
end -= 1
} else {
break
}
}
let out : Array[Int32] = []
for i = 0; i < end; i = i + 1 {
out.push(bytes[i] & 0xFF)
}
Ok(Value::Bytes(out))
}
///|
fn builtin_bytes_join(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("bytes.join", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"join() takes exactly one argument".to_string(),
),
)
}
let sep = match positional[0] {
Value::Bytes(values) | Value::MemoryView(values) => values
Value::ByteArray(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"join() expects a bytes-like separator".to_string(),
),
)
}
let iterator_value = match
iter_value_to_iterator(positional[1], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
let out : Array[Int32] = []
let mut first = true
while true {
match iterator_next(iterator_value, None, globals, builtins, io) {
Ok(item) => {
let part = match item {
Value::Bytes(values)
| Value::ByteArray(values)
| Value::MemoryView(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"sequence item is not a bytes-like object".to_string(),
),
)
}
if first {
first = false
} else {
for b in sep {
out.push(b & 0xFF)
}
}
for b in part {
out.push(b & 0xFF)
}
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
Ok(Value::Bytes(out))
}
///|
fn builtin_bytearray_upper(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("bytearray.upper", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"upper() takes no arguments".to_string(),
),
)
}
let bytes = match positional[0] {
Value::ByteArray(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"upper() expects a bytearray".to_string(),
),
)
}
let out : Array[Int32] = []
for b in bytes {
let v = b & 0xFF
if v >= 'a'.to_int() && v <= 'z'.to_int() {
out.push(v - 32)
} else {
out.push(v)
}
}
Ok(Value::ByteArray(out))
}
///|
fn builtin_bytearray_rstrip(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("bytearray.rstrip", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 1 && positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rstrip() takes at most one argument".to_string(),
),
)
}
let bytes = match positional[0] {
Value::ByteArray(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rstrip() expects a bytearray".to_string(),
),
)
}
let strip_set : Array[Bool] = []
for _i = 0; _i < 256; _i = _i + 1 {
strip_set.push(false)
}
if positional.length() == 1 || positional[1] is Value::None {
for
ch in [
' '.to_int(),
'\t'.to_int(),
'\n'.to_int(),
'\r'.to_int(),
0x0b,
0x0c,
] {
strip_set[ch] = true
}
} else {
let chars = match positional[1] {
Value::Bytes(values)
| Value::ByteArray(values)
| Value::MemoryView(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rstrip() chars must be a bytes-like object".to_string(),
),
)
}
for b in chars {
strip_set[b & 0xFF] = true
}
}
let mut end = bytes.length()
while end > 0 {
let b = bytes[end - 1] & 0xFF
if strip_set[b] {
end -= 1
} else {
break
}
}
let out : Array[Int32] = []
for i = 0; i < end; i = i + 1 {
out.push(bytes[i] & 0xFF)
}
Ok(Value::ByteArray(out))
}
///|
fn builtin_bytearray_translate(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = locals
let _ = globals
let _ = builtins
let _ = io
let _ = match ensure_no_keywords("bytearray.translate", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"translate() takes exactly one argument".to_string(),
),
)
}
let src = match positional[0] {
Value::ByteArray(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"translate() expects a bytearray".to_string(),
),
)
}
let table = match
bytes_like_ints_for_translate("bytearray.translate", positional[1]) {
Ok(v) => v
Err(err) => return Err(err)
}
if table.length() != 256 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: translation table must be 256 bytes long".to_string(),
),
)
}
let out : Array[Int32] = []
for byte in src {
let b = byte & 0xFF
out.push(table[b] & 0xFF)
}
Ok(Value::ByteArray(out))
}
///|
fn builtin_bytearray(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = locals
let _ = match ensure_no_keywords("bytearray", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() == 0 {
return Ok(Value::ByteArray([]))
}
if positional.length() > 3 {
return Err(too_many_arguments_error("bytearray", positional.length()))
}
if positional.length() == 1 {
match positional[0] {
Value::Int(count) =>
return Ok(
Value::ByteArray(
match bytes_from_count("bytearray", count) {
Ok(value) => value
Err(err) => return Err(err)
},
),
)
Value::Bytes(values) => return Ok(Value::ByteArray(copy_ints(values)))
Value::ByteArray(values) => return Ok(Value::ByteArray(copy_ints(values)))
Value::MemoryView(values) =>
return Ok(Value::ByteArray(copy_ints(values)))
Value::Str(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"string argument without an encoding".to_string(),
),
)
other =>
return Ok(
Value::ByteArray(
match
bytes_from_iterable("bytearray", other, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
},
),
)
}
}
match positional[0] {
Value::Str(text) => {
let encoding = positional[1]
let errors_value = if positional.length() > 2 {
Some(positional[2])
} else {
None
}
let bytes = match
encode_string_with_encoding("bytearray", text, encoding, errors_value) {
Ok(value) => value
Err(err) => return Err(err)
}
Ok(Value::ByteArray(bytes))
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"encoding without a string argument".to_string(),
),
)
}
}
///|
fn builtin_bytearray_fromhex(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = locals
let _ = globals
let _ = builtins
let _ = io
let _ = match ensure_no_keywords("bytearray.fromhex", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"fromhex() takes exactly one argument".to_string(),
),
)
}
let text = match positional[1] {
Value::Str(s) => s
other =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"fromhex() argument must be str, not '" +
type_name_from_value(other) +
"'",
),
)
}
Ok(
Value::ByteArray(
match bytes_fromhex_parse(text) {
Ok(v) => v
Err(err) => return Err(err)
},
),
)
}
///|
fn builtin_bytearray_copy(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("bytearray.copy", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"copy() takes exactly one argument".to_string(),
),
)
}
match positional[0] {
Value::ByteArray(values) => Ok(Value::ByteArray(copy_ints(values)))
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"copy() expects a bytearray".to_string(),
),
)
}
}
///|
fn builtin_bytearray_setitem(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("bytearray.__setitem__", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytearray.__setitem__() takes exactly two arguments".to_string(),
),
)
}
let values = match positional[0] {
Value::ByteArray(v) => v
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytearray.__setitem__() expects a bytearray".to_string(),
),
)
}
let key = positional[1]
let assigned = positional[2]
fn replacement_bytes(
value : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Array[Int32], RuntimeError] {
match value {
Value::Bytes(bytes)
| Value::ByteArray(bytes)
| Value::MemoryView(bytes) => Ok(copy_ints(bytes))
other =>
bytes_from_iterable(
"bytearray.__setitem__", other, globals, builtins, io,
)
}
}
match key {
Value::Instance(slice_inst) =>
if slice_inst.class.name == "slice" {
let slice_start = match get_named_value(slice_inst.dict, "start") {
Some(v) => v
None => Value::None
}
let slice_stop = match get_named_value(slice_inst.dict, "stop") {
Some(v) => v
None => Value::None
}
let slice_step = match get_named_value(slice_inst.dict, "step") {
Some(v) => v
None => Value::None
}
let len_big = @bigint.BigInt::from_int(values.length())
let (istart, istop, istep) = match
range_slice_indices(len_big, slice_start, slice_stop, slice_step) {
Ok(v) => v
Err(err) => return Err(err)
}
let repl = match replacement_bytes(assigned, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
if istep == 1N {
let start_idx = match bigint_to_int_checked(istart) {
Ok(v) => v
Err(err) => return Err(err)
}
let stop_idx = match bigint_to_int_checked(istop) {
Ok(v) => v
Err(err) => return Err(err)
}
let old_len = values.length()
let out : Array[Int32] = []
for i = 0; i < start_idx; i = i + 1 {
out.push(values[i] & 0xFF)
}
for b in repl {
out.push(b & 0xFF)
}
for i = stop_idx; i < old_len; i = i + 1 {
out.push(values[i] & 0xFF)
}
while values.length() > 0 {
let _ = values.remove(values.length() - 1)
}
for b in out {
values.push(b & 0xFF)
}
return Ok(Value::None)
}
let expected_big = range_slice_length(istart, istop, istep)
let expected = match bigint_to_int_checked(expected_big) {
Ok(v) => v
Err(err) => return Err(err)
}
if repl.length() != expected {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: attempt to assign sequence of size " +
repl.length().to_string() +
" to extended slice of size " +
expected.to_string(),
),
)
}
for j = 0; j < expected; j = j + 1 {
let idx_big = range_element_at(
istart,
istep,
@bigint.BigInt::from_int(j),
)
let idx = match bigint_to_int_checked(idx_big) {
Ok(v) => v
Err(err) => return Err(err)
}
values[idx] = repl[j] & 0xFF
}
return Ok(Value::None)
}
_ => ()
}
let idx = match index_from_value(key, 0) {
Ok(v) => v
Err(err) => return Err(err)
}
let idx = normalize_index(idx, values.length())
if idx < 0 || idx >= values.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"IndexError: bytearray index out of range".to_string(),
),
)
}
let b = match byte_from_value("bytearray.__setitem__", assigned) {
Ok(v) => v
Err(err) => return Err(err)
}
values[idx] = b & 0xFF
Ok(Value::None)
}
///|
fn builtin_bytearray_delitem(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("bytearray.__delitem__", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytearray.__delitem__() takes exactly one argument".to_string(),
),
)
}
let values = match positional[0] {
Value::ByteArray(v) => v
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bytearray.__delitem__() expects a bytearray".to_string(),
),
)
}
let key = positional[1]
match key {
Value::Instance(slice_inst) =>
if slice_inst.class.name == "slice" {
let slice_start = match get_named_value(slice_inst.dict, "start") {
Some(v) => v
None => Value::None
}
let slice_stop = match get_named_value(slice_inst.dict, "stop") {
Some(v) => v
None => Value::None
}
let slice_step = match get_named_value(slice_inst.dict, "step") {
Some(v) => v
None => Value::None
}
let len_big = @bigint.BigInt::from_int(values.length())
let (istart, istop, istep) = match
range_slice_indices(len_big, slice_start, slice_stop, slice_step) {
Ok(v) => v
Err(err) => return Err(err)
}
if istep == 1N {
let start_idx = match bigint_to_int_checked(istart) {
Ok(v) => v
Err(err) => return Err(err)
}
let stop_idx = match bigint_to_int_checked(istop) {
Ok(v) => v
Err(err) => return Err(err)
}
let old_len = values.length()
let out : Array[Int32] = []
for i = 0; i < start_idx; i = i + 1 {
out.push(values[i] & 0xFF)
}
for i = stop_idx; i < old_len; i = i + 1 {
out.push(values[i] & 0xFF)
}
while values.length() > 0 {
let _ = values.remove(values.length() - 1)
}
for b in out {
values.push(b & 0xFF)
}
return Ok(Value::None)
}
let expected_big = range_slice_length(istart, istop, istep)
let expected = match bigint_to_int_checked(expected_big) {
Ok(v) => v
Err(err) => return Err(err)
}
let idxs : Array[Int32] = []
for j = 0; j < expected; j = j + 1 {
let idx_big = range_element_at(
istart,
istep,
@bigint.BigInt::from_int(j),
)
let idx = match bigint_to_int_checked(idx_big) {
Ok(v) => v
Err(err) => return Err(err)
}
idxs.push(idx)
}
let mut i = idxs.length()
while i > 0 {
i = i - 1
let _ = values.remove(idxs[i])
}
return Ok(Value::None)
}
_ => ()
}
let idx = match index_from_value(key, 0) {
Ok(v) => v
Err(err) => return Err(err)
}
let idx = normalize_index(idx, values.length())
if idx < 0 || idx >= values.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"IndexError: bytearray index out of range".to_string(),
),
)
}
let _ = values.remove(idx)
Ok(Value::None)
}
///|
fn builtin_memoryview(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = locals
let _ = match ensure_no_keywords("memoryview", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"memoryview() takes exactly one argument".to_string(),
),
)
}
match positional[0] {
Value::Bytes(values) => Ok(Value::MemoryView(copy_ints(values)))
Value::ByteArray(values) => Ok(Value::MemoryView(copy_ints(values)))
Value::MemoryView(values) => Ok(Value::MemoryView(copy_ints(values)))
Value::Instance(inst) =>
if inst.class.name == "memoryview" {
match get_named_value(inst.dict, "__mpython_bytes__") {
Some(Value::MemoryView(_))
| Some(Value::Bytes(_))
| Some(Value::ByteArray(_)) => Ok(Value::Instance(inst))
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"memoryview: a bytes-like object is required, not '" +
type_name_from_value(positional[0]) +
"'".to_string(),
),
)
}
} else if inst.class.name == "array" {
let typecode = match get_named_value(inst.dict, "typecode") {
Some(Value::Str(v)) => v
_ => "".to_string()
}
if typecode != "b" && typecode != "B" {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"memoryview: a bytes-like object is required, not '" +
type_name_from_value(positional[0]) +
"'".to_string(),
),
)
}
let data = match get_named_value(inst.dict, "_data") {
Some(Value::List(values)) => values
_ => []
}
let out : Array[Int32] = []
for v in data {
let b = match byte_from_value("memoryview", v) {
Ok(b) => b
Err(err) => return Err(err)
}
out.push(b & 0xFF)
}
Ok(Value::MemoryView(out))
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"memoryview: a bytes-like object is required, not '" +
type_name_from_value(positional[0]) +
"'".to_string(),
),
)
}
other =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"memoryview: a bytes-like object is required, not '" +
type_name_from_value(other) +
"'".to_string(),
),
)
}
}
///|
fn builtin_memoryview_tobytes(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("memoryview.tobytes", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"tobytes() takes no arguments".to_string(),
),
)
}
match positional[0] {
Value::MemoryView(values) => Ok(Value::Bytes(copy_ints(values)))
Value::Instance(inst) =>
if inst.class.name == "memoryview" {
match get_named_value(inst.dict, "__mpython_bytes__") {
Some(Value::MemoryView(values))
| Some(Value::Bytes(values))
| Some(Value::ByteArray(values)) =>
Ok(Value::Bytes(copy_ints(values)))
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"tobytes() expects a memoryview".to_string(),
),
)
}
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"tobytes() expects a memoryview".to_string(),
),
)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"tobytes() expects a memoryview".to_string(),
),
)
}
}
///|
fn builtin_memoryview_cast(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = locals
let _ = globals
let _ = builtins
let _ = io
let _ = match ensure_no_keywords("memoryview.cast", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 2 && positional.length() != 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"cast() takes 1 or 2 arguments".to_string(),
),
)
}
match positional[1] {
Value::Str(_) => ()
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"cast() format must be str".to_string(),
),
)
}
let format = match positional[1] {
Value::Str(v) => v
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"cast() format must be str".to_string(),
),
)
}
let mut ndim = 1
if positional.length() == 3 && !(positional[2] is Value::None) {
match positional[2] {
Value::Tuple(values) => {
ndim = values.length()
for v in values {
match v {
Value::Int(_) => ()
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"cast() shape must be a tuple of ints".to_string(),
),
)
}
}
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"cast() shape must be a tuple".to_string(),
),
)
}
}
let raw = match positional[0] {
Value::MemoryView(values) => copy_ints(values)
Value::Instance(inst) =>
if inst.class.name == "memoryview" {
match get_named_value(inst.dict, "__mpython_bytes__") {
Some(Value::MemoryView(values))
| Some(Value::Bytes(values))
| Some(Value::ByteArray(values)) => copy_ints(values)
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"cast() expects a memoryview".to_string(),
),
)
}
} else {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"cast() expects a memoryview".to_string(),
),
)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"cast() expects a memoryview".to_string(),
),
)
}
let cls = builtin_class_from_name("memoryview", builtins)
Ok(
Value::Instance(InstanceValue::{
class: cls,
dict: [
("__mpython_bytes__", Value::MemoryView(raw)),
("format", Value::Str(format)),
("ndim", Value::Int(@bigint.BigInt::from_int(ndim))),
],
}),
)
}