///|
/// Callable dispatch and runtime call hooks.
///|
fn eval_function_with_kwargs_uninitialized(
func : FunctionValue,
positional : Array[Value],
keywords : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
let _ = func
let _ = positional
let _ = keywords
let _ = globals
let _ = builtins
let _ = io
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"RuntimeError: internal call callback not initialized".to_string(),
),
)
}
///|
let eval_function_with_kwargs_ref : Ref[
(
FunctionValue,
Array[Value],
Array[(String, Value)],
Array[(String, Value)],
Array[(String, Value)],
MockIO,
) -> Result[Value, RuntimeError],
] = { val: eval_function_with_kwargs_uninitialized, }
///|
fn eval_generator_with_kwargs_uninitialized(
func : FunctionValue,
positional : Array[Value],
keywords : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
let _ = func
let _ = positional
let _ = keywords
let _ = globals
let _ = builtins
let _ = io
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"RuntimeError: internal generator callback not initialized".to_string(),
),
)
}
///|
let eval_generator_with_kwargs_ref : Ref[
(
FunctionValue,
Array[Value],
Array[(String, Value)],
Array[(String, Value)],
Array[(String, Value)],
MockIO,
) -> Result[Value, RuntimeError],
] = { val: eval_generator_with_kwargs_uninitialized, }
///|
fn set_eval_function_with_kwargs_callback(
callback : (
FunctionValue,
Array[Value],
Array[(String, Value)],
Array[(String, Value)],
Array[(String, Value)],
MockIO,
) -> Result[Value, RuntimeError],
) -> Unit {
eval_function_with_kwargs_ref.val = callback
}
///|
fn set_eval_generator_with_kwargs_callback(
callback : (
FunctionValue,
Array[Value],
Array[(String, Value)],
Array[(String, Value)],
Array[(String, Value)],
MockIO,
) -> Result[Value, RuntimeError],
) -> Unit {
eval_generator_with_kwargs_ref.val = callback
}
///|
fn call_callable_with_env(
callee : Value,
positional : Array[Value],
keywords : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
match callee {
Value::Function(func) =>
if func.is_async {
if func.is_generator {
(eval_generator_with_kwargs_ref.val)(
func, positional, keywords, globals, builtins, io,
)
} else {
(eval_function_with_kwargs_ref.val)(
func, positional, keywords, globals, builtins, io,
)
}
} else if func.is_generator {
(eval_generator_with_kwargs_ref.val)(
func, positional, keywords, globals, builtins, io,
)
} else if func.name == "__getattribute__" && func.body.length() == 0 {
// Intrinsic: object.__getattribute__(self, name)
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__getattribute__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__getattribute__() takes exactly two arguments".to_string(),
),
)
}
let target = positional[0]
let name = match positional[1] {
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"attribute name must be str".to_string(),
),
)
}
get_attr_raw_from_value(target, name, globals, builtins, io)
} else if func.name == "__setattr__" && func.body.length() == 0 {
// Intrinsic: object.__setattr__(self, name, value)
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__setattr__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__setattr__() takes exactly three arguments".to_string(),
),
)
}
let target = positional[0]
let name = match positional[1] {
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"attribute name must be str".to_string(),
),
)
}
let value = positional[2]
let _ = match
set_attr_raw_on_value(target, name, value, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::None)
} else if func.name == "__delattr__" && func.body.length() == 0 {
// Intrinsic: object.__delattr__(self, name)
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__delattr__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__delattr__() takes exactly two arguments".to_string(),
),
)
}
let target = positional[0]
let name = match positional[1] {
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"attribute name must be str".to_string(),
),
)
}
let _ = match
delete_attr_raw_on_value(target, name, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::None)
} else if func.body.length() == 0 &&
(func.name == "__repr__" || func.name.has_suffix(".__repr__")) {
// Intrinsic: built-in `__repr__` implementations are modeled as stubs.
// They should return the `repr()` string of their sole argument.
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__repr__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__repr__() takes exactly one argument".to_string(),
),
)
}
Ok(Value::Str(repr_fallback(positional[0])))
} else if func.name == "__new__" && func.body.length() == 0 {
// Intrinsic: object.__new__(cls)
if positional.length() < 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__new__() expects a type".to_string(),
),
)
}
let cls_value = positional[0]
match cls_value {
Value::Class(klass) => {
let inst_dict : Array[(String, Value)] = []
inst_dict.push(("hashvalue", Value::Int(fresh_object_hashvalue())))
if class_includes_list(klass) {
inst_dict.push((list_storage_name, Value::List([])))
}
if class_includes_dict(klass) {
inst_dict.push((dict_storage_name, Value::Dict([])))
}
if class_includes_set(klass) || class_includes_frozenset(klass) {
inst_dict.push((set_storage_name, Value::Set([])))
}
if class_includes_tuple(klass) {
inst_dict.push((tuple_storage_name, Value::Tuple([])))
}
let inst_value = Value::Instance(InstanceValue::{
class: klass,
dict: inst_dict,
})
gc_register_instance(inst_value)
Ok(inst_value)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__new__() expects a type".to_string(),
),
)
}
} else if func.name == "__hash__" && func.body.length() == 0 {
// Intrinsic: object.__hash__(self)
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__hash__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__hash__() takes exactly one argument".to_string(),
),
)
}
match hash_value(positional[0]) {
Ok(hash) => Ok(Value::Int(hash))
Err(err) => Err(err)
}
} else if func.name == "__eq__" && func.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__eq__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__eq__() takes exactly two arguments".to_string(),
),
)
}
Ok(Value::Bool(eq_value(positional[0], positional[1])))
} else if func.name == "__ne__" && func.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__ne__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__ne__() takes exactly two arguments".to_string(),
),
)
}
Ok(Value::Bool(!eq_value(positional[0], positional[1])))
} else if func.body.length() == 0 {
match
eval_builtin_call(
func.name,
positional,
keywords,
[],
globals,
builtins,
io,
) {
Ok(Some(value)) => Ok(value)
Ok(None) =>
(eval_function_with_kwargs_ref.val)(
func, positional, keywords, globals, builtins, io,
)
Err(err) => Err(err)
}
} else {
(eval_function_with_kwargs_ref.val)(
func, positional, keywords, globals, builtins, io,
)
}
Value::BoundMethod(bound_method) => {
// Intrinsic: type.mro(cls)
match bound_method.self {
Value::Class(klass) =>
if bound_method.function.name == "mro" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"mro() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"mro() takes no arguments".to_string(),
),
)
}
let mro = match class_mro(klass) {
Ok(v) => v
Err(err) => return Err(err)
}
let out : Array[Value] = []
for cls in mro {
out.push(Value::Class(cls))
}
return Ok(Value::List(out))
}
_ => ()
}
// Intrinsic: dict.__init__(self, mapping=(), /, **kwargs)
//
// Pure-Python stdlib types like `collections.defaultdict` call
// `dict.__init__(self, ...)` directly; make sure the backing storage is
// populated instead of treating this as a no-op stub.
if bound_method.function.name == "__init__" &&
bound_method.function.body.length() == 0 {
match bound_method.self {
Value::Instance(inst) =>
match get_named_value(inst.dict, dict_storage_name) {
Some(Value::Dict(pairs)) =>
if class_includes_dict(inst.class) {
fn replace_pairs(
pairs : Array[(Value, Value)],
next : Array[(Value, Value)],
) -> Unit {
let mut i = pairs.length()
while i > 0 {
i = i - 1
let _ = pairs.remove(i)
}
for item in next {
pairs.push(item)
}
}
let created = match
builtin_dict(
positional,
keywords,
[],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
match created {
Value::Dict(next_pairs) => replace_pairs(pairs, next_pairs)
_ => ()
}
return Ok(Value::None)
}
_ => ()
}
_ => ()
}
}
// Intrinsic: object.__getattribute__(self, name)
if bound_method.function.name == "__getattribute__" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__getattribute__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__getattribute__() takes exactly one argument".to_string(),
),
)
}
let name = match positional[0] {
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"attribute name must be str".to_string(),
),
)
}
return get_attr_raw_from_value(
bound_method.self,
name,
globals,
builtins,
io,
)
}
// Intrinsic: object.__setattr__(self, name, value)
if bound_method.function.name == "__setattr__" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__setattr__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__setattr__() takes exactly two arguments".to_string(),
),
)
}
let name = match positional[0] {
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"attribute name must be str".to_string(),
),
)
}
let value = positional[1]
let _ = match
set_attr_raw_on_value(
bound_method.self,
name,
value,
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::None)
}
// Intrinsic: object.__delattr__(self, name)
if bound_method.function.name == "__delattr__" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__delattr__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__delattr__() takes exactly one argument".to_string(),
),
)
}
let name = match positional[0] {
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"attribute name must be str".to_string(),
),
)
}
let _ = match
delete_attr_raw_on_value(
bound_method.self,
name,
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::None)
}
// Intrinsic: object.__hash__(self)
if bound_method.function.name == "__hash__" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__hash__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__hash__() takes no arguments".to_string(),
),
)
}
match hash_value(bound_method.self) {
Ok(hash) => return Ok(Value::Int(hash))
Err(err) => return Err(err)
}
}
// Intrinsic: built-in `__repr__` stubs (e.g. set.__repr__) must return a string.
if bound_method.function.body.length() == 0 &&
(
bound_method.function.name == "__repr__" ||
bound_method.function.name.has_suffix(".__repr__")
) {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__repr__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__repr__() takes no arguments".to_string(),
),
)
}
return Ok(Value::Str(repr_fallback(bound_method.self)))
}
// Intrinsic: code.co_positions(self)
if bound_method.function.name == "code.co_positions" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"co_positions() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"co_positions() takes no arguments".to_string(),
),
)
}
match bound_method.self {
Value::Instance(inst) =>
match get_named_value(inst.dict, "__mpython_co_positions") {
Some(Value::List(items)) => return Ok(Value::List(items))
_ => ()
}
_ => ()
}
return Ok(
Value::List([
Value::Tuple([Value::None, Value::None, Value::None, Value::None]),
]),
)
}
// Intrinsic: frame.clear(self)
if bound_method.function.name == "frame.clear" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"clear() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"clear() takes no arguments".to_string(),
),
)
}
match bound_method.self {
Value::Instance(inst) => {
match get_named_value(inst.dict, "f_locals") {
Some(Value::Dict(pairs)) =>
while pairs.length() > 0 {
let _ = pairs.remove(pairs.length() - 1)
}
_ => set_named_value(inst.dict, "f_locals", Value::Dict([]))
}
return Ok(Value::None)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"clear() expects a frame".to_string(),
),
)
}
}
// Intrinsic: BaseException.__setstate__(self, state)
if bound_method.function.name == "__setstate__" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__setstate__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__setstate__() takes exactly one argument".to_string(),
),
)
}
let state = positional[0]
match (bound_method.self, state) {
(Value::Instance(inst), Value::Dict(pairs)) => {
fn key_to_attr_name(key : Value) -> Result[String, RuntimeError] {
match key {
Value::Str(text) => Ok(text)
Value::Instance(key_inst) => {
let mut is_str_base = false
for base in key_inst.class.bases {
match base {
Value::Class(c) =>
if c.name == "str" {
is_str_base = true
break
}
_ => ()
}
}
if is_str_base {
// moonpython doesn't model `str` subclasses as primitive strings yet.
// Treat `str`-subclass attribute keys as empty strings (covers
// `HashThisKeyWillClearTheDict()` in CPython's `test_baseexception`).
Ok("".to_string())
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"attribute name must be str".to_string(),
),
)
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"attribute name must be str".to_string(),
),
)
}
}
for pair in pairs {
let name = match key_to_attr_name(pair.0) {
Ok(v) => v
Err(err) => return Err(err)
}
set_named_value(inst.dict, name, pair.1)
}
return Ok(Value::None)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__setstate__() expects a dict".to_string(),
),
)
}
}
// Intrinsic: BaseException.__reduce_ex__(self, protocol)
//
// CPython does not pickle the attached traceback by default. MoonPython
// stores `__traceback__` as a normal instance attribute, so we must
// explicitly exclude it from the serialized state to keep exceptions
// picklable (unittest discovery relies on this).
if bound_method.function.name == "__reduce_ex__" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__reduce_ex__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__reduce_ex__() takes exactly one argument".to_string(),
),
)
}
match bound_method.self {
Value::Instance(inst) => {
let args_value = match get_named_value(inst.dict, "args") {
Some(Value::Tuple(values)) => Value::Tuple(values)
Some(v) => Value::Tuple([v])
None => Value::Tuple([])
}
let state_pairs : Array[(Value, Value)] = []
for pair in inst.dict {
if pair.0 == "__traceback__" {
continue
}
state_pairs.push((Value::Str(pair.0), pair.1))
}
return Ok(
Value::Tuple([
Value::Class(inst.class),
args_value,
Value::Dict(state_pairs),
]),
)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__reduce_ex__() expects an exception instance".to_string(),
),
)
}
}
// Intrinsic: BaseException.__reduce__(self)
if bound_method.function.name == "__reduce__" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__reduce__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__reduce__() takes no arguments".to_string(),
),
)
}
match bound_method.self {
Value::Instance(inst) => {
let args_value = match get_named_value(inst.dict, "args") {
Some(Value::Tuple(values)) => Value::Tuple(values)
Some(v) => Value::Tuple([v])
None => Value::Tuple([])
}
let state_pairs : Array[(Value, Value)] = []
for pair in inst.dict {
if pair.0 == "__traceback__" {
continue
}
state_pairs.push((Value::Str(pair.0), pair.1))
}
return Ok(
Value::Tuple([
Value::Class(inst.class),
args_value,
Value::Dict(state_pairs),
]),
)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__reduce__() expects an exception instance".to_string(),
),
)
}
}
// Intrinsic: BaseException.add_note(self, note)
if bound_method.function.name == "add_note" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"add_note() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"add_note() takes exactly one argument".to_string(),
),
)
}
let note = match positional[0] {
Value::Str(text) => Value::Str(text)
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"add_note() argument must be str".to_string(),
),
)
}
match bound_method.self {
Value::Instance(inst) =>
match get_named_value(inst.dict, "__notes__") {
None | Some(Value::None) => {
set_named_value(inst.dict, "__notes__", Value::List([note]))
return Ok(Value::None)
}
Some(Value::List(items)) => {
items.push(note)
return Ok(Value::None)
}
Some(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__notes__ must be a list".to_string(),
),
)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"add_note() expects an exception instance".to_string(),
),
)
}
}
// Intrinsic: object.__init_subclass__(cls, **kwargs)
if bound_method.function.name == "__init_subclass__" &&
bound_method.function.body.length() == 0 {
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__init_subclass__() takes no arguments".to_string(),
),
)
}
return Ok(Value::None)
}
// Intrinsic: type.__subclasses__(cls)
if bound_method.function.name == "__subclasses__" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__subclasses__() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__subclasses__() takes no arguments".to_string(),
),
)
}
match bound_method.self {
Value::Class(klass) =>
match get_named_value(klass.dict, "__mpython_subclasses__") {
Some(Value::List(values)) => return Ok(Value::List(values))
_ => return Ok(Value::List([]))
}
_ => return Ok(Value::List([]))
}
}
// Intrinsic: importlib meta_path finder
if bound_method.function.name == "__mpython_find_spec" &&
bound_method.function.body.length() == 0 {
// Signature: find_spec(fullname, path=None, target=None)
// We accept a subset used by the stdlib `importlib` implementation.
let mut fullname_value : Value? = None
let mut path_value : Value = Value::None
if positional.length() >= 1 {
fullname_value = Some(positional[0])
}
if positional.length() >= 2 {
path_value = positional[1]
}
for pair in keywords {
if pair.0 == "path" {
path_value = pair.1
}
}
let fullname = match fullname_value {
Some(Value::Str(text)) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"find_spec() fullname must be str".to_string(),
),
)
}
fn root_from_sys_path_entry(value : Value) -> String? {
match value {
Value::Str(text) =>
if text.length() == 0 {
Some(current_workdir())
} else if text.has_prefix("/") {
Some(text)
} else {
Some(resolve_path_from_initial_cwd(text))
}
_ => None
}
}
fn cache_tag_from_sys() -> String {
let mut tag = "cpython-312".to_string()
match module_cache_get("sys") {
Some(Value::Instance(sys_inst)) =>
match get_named_value(sys_inst.dict, "implementation") {
Some(Value::Instance(impl_inst)) =>
match get_named_value(impl_inst.dict, "cache_tag") {
Some(Value::Str(v)) => tag = v
_ => ()
}
_ => ()
}
_ => ()
}
tag
}
fn cached_from_source_path(source_path : String) -> String {
let tag = cache_tag_from_sys()
let slash = find_last_char(source_path, '/')
let (dir, file) = match slash {
Some(idx) =>
(
substring(source_path, 0, idx),
substring(source_path, idx + 1, source_path.length()),
)
None => ("".to_string(), source_path)
}
let base = if file.has_suffix(".py") && file.length() >= 3 {
substring(file, 0, file.length() - 3)
} else {
file
}
let pycache_dir = if dir.length() == 0 {
"__pycache__".to_string()
} else {
dir + "/__pycache__"
}
pycache_dir + "/" + base + "." + tag + ".pyc"
}
let roots : Array[String] = []
match path_value {
Value::None => {
// Use `sys.path` when `path` is not provided.
let sys_value_opt = module_cache_get("sys")
match sys_value_opt {
Some(Value::Instance(sys_inst)) =>
match get_named_value(sys_inst.dict, "path") {
Some(Value::List(values)) =>
for item in values {
match root_from_sys_path_entry(item) {
Some(root) => roots.push(root)
None => ()
}
}
_ => ()
}
_ => ()
}
}
Value::List(values) =>
for item in values {
match root_from_sys_path_entry(item) {
Some(root) => roots.push(root)
None => ()
}
}
Value::Str(text) =>
match root_from_sys_path_entry(Value::Str(text)) {
Some(root) => roots.push(root)
None => ()
}
_ => ()
}
let mut rel = module_rel_path(fullname)
// When `path` is provided (i.e. we're searching inside an already
// resolved package), only join the leaf module name onto each entry in
// that package's `__path__`.
match path_value {
Value::None => ()
_ => {
let parts = module_name_parts(fullname)
if parts.length() > 0 {
rel = parts[parts.length() - 1]
}
}
}
// Prefer packages over modules (matches CPython importlib behavior).
for root in roots {
let pkg_dir = @path.Path(root).join(@path.Path(rel)).to_string()
let init_path = @path.Path(pkg_dir)
.join(@path.Path("__init__.py"))
.to_string()
if @fs.path_exists(init_path) {
let loader = make_importlib_loader()
match loader {
Value::Instance(loader_inst) =>
set_named_value(loader_inst.dict, "path", Value::Str(init_path))
_ => ()
}
// ModuleSpec.parent behaves differently for packages: it is the
// package's own name (not the dotted parent) when the module has
// submodule_search_locations.
let parent = fullname
let cached = cached_from_source_path(init_path)
let spec_class = ClassValue::{
name: "ModuleSpec",
bases: [],
dict: [],
}
let spec = Value::Instance(InstanceValue::{
class: spec_class,
dict: [
("name", Value::Str(fullname)),
("origin", Value::Str(init_path)),
("loader", loader),
("parent", Value::Str(parent)),
(
"submodule_search_locations",
Value::List([Value::Str(pkg_dir)]),
),
("cached", Value::Str(cached)),
("loader_state", Value::None),
("_uninitialized_submodules", Value::List([])),
("has_location", Value::Bool(true)),
],
})
return Ok(spec)
}
}
for root in roots {
let module_path = @path.Path(root)
.join(@path.Path(rel + ".py"))
.to_string()
if @fs.path_exists(module_path) {
let loader = make_importlib_loader()
match loader {
Value::Instance(loader_inst) =>
set_named_value(
loader_inst.dict,
"path",
Value::Str(module_path),
)
_ => ()
}
let parent = module_parent_name(fullname)
let cached = cached_from_source_path(module_path)
let spec_class = ClassValue::{
name: "ModuleSpec",
bases: [],
dict: [],
}
let spec = Value::Instance(InstanceValue::{
class: spec_class,
dict: [
("name", Value::Str(fullname)),
("origin", Value::Str(module_path)),
("loader", loader),
("parent", Value::Str(parent)),
("submodule_search_locations", Value::None),
("cached", Value::Str(cached)),
("loader_state", Value::None),
("_uninitialized_submodules", Value::List([])),
("has_location", Value::Bool(true)),
],
})
return Ok(spec)
}
}
return Ok(Value::None)
}
// Intrinsic: sys.meta_path invalidate_caches
if bound_method.function.name == "__mpython_invalidate_caches" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"invalidate_caches() got unexpected keyword arguments".to_string(),
),
)
}
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"invalidate_caches() takes no arguments".to_string(),
),
)
}
let sys_value_opt = module_cache_get("sys")
match sys_value_opt {
Some(Value::Instance(sys_inst)) =>
match get_named_value(sys_inst.dict, "path_importer_cache") {
Some(Value::Dict(pairs)) =>
for pair in pairs {
let importer = pair.1
match
get_attr_raw_from_value(
importer,
"invalidate_caches".to_string(),
globals,
builtins,
io,
) {
Ok(inv) => {
let _ = match
call_callable_with_env(
inv,
[],
[],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
}
Err(_) => ()
}
}
_ => ()
}
_ => ()
}
return Ok(Value::None)
}
// Intrinsic: importlib loader create_module(spec)
if bound_method.function.name == "__mpython_create_module" &&
bound_method.function.body.length() == 0 {
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"create_module() takes exactly one argument".to_string(),
),
)
}
let spec = positional[0]
let (name, origin, parent, locations, loader, cached) = match spec {
Value::Instance(spec_inst) => {
let name = match get_named_value(spec_inst.dict, "name") {
Some(Value::Str(v)) => v
_ => "".to_string()
}
let origin = match get_named_value(spec_inst.dict, "origin") {
Some(Value::Str(v)) => v
_ => "".to_string()
}
let parent = match get_named_value(spec_inst.dict, "parent") {
Some(Value::Str(v)) => v
_ => "".to_string()
}
let locations = match
get_named_value(spec_inst.dict, "submodule_search_locations") {
Some(v) => v
None => Value::None
}
let loader = match get_named_value(spec_inst.dict, "loader") {
Some(v) => v
None => Value::None
}
let cached = match get_named_value(spec_inst.dict, "cached") {
Some(v) => v
None => Value::None
}
(name, origin, parent, locations, loader, cached)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"create_module() expects a spec".to_string(),
),
)
}
let is_package = match locations {
Value::List(_) => true
_ => false
}
let package_name = if is_package { name } else { parent }
let module_class = ClassValue::{ name: "module", bases: [], dict: [], }
let dict : Array[(String, Value)] = [
("hashvalue", Value::Int(fresh_object_hashvalue())),
("__name__", Value::Str(name)),
("__package__", Value::Str(package_name)),
("__file__", Value::Str(origin)),
("__cached__", cached),
("__loader__", loader),
("__spec__", spec),
("__doc__", Value::None),
]
let module_dict = dict
match locations {
Value::List(_) => module_dict.push(("__path__", locations))
_ => ()
}
return Ok(
Value::Instance(InstanceValue::{
class: module_class,
dict: module_dict,
}),
)
}
// Intrinsic: importlib loader exec_module(module)
if bound_method.function.name == "__mpython_exec_module" &&
bound_method.function.body.length() == 0 {
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"exec_module() takes exactly one argument".to_string(),
),
)
}
let module_value = positional[0]
let (name, filename, module_globals, is_pkg, pkg_path) = match
module_value {
Value::Instance(inst) => {
let name = match get_named_value(inst.dict, "__name__") {
Some(Value::Str(v)) => v
_ => "".to_string()
}
let filename = match get_named_value(inst.dict, "__file__") {
Some(Value::Str(v)) => v
_ => "".to_string()
}
let mut is_pkg = false
let mut pkg_path = ""
match get_named_value(inst.dict, "__path__") {
Some(Value::List(values)) =>
if values.length() > 0 {
match values[0] {
Value::Str(v) => {
is_pkg = true
pkg_path = v
}
_ => ()
}
}
_ => ()
}
(name, filename, inst.dict, is_pkg, pkg_path)
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"exec_module() expects a module".to_string(),
),
)
}
// Mirror CPython importlib behavior: a module should be present in
// `sys.modules` while its body executes so bootstrap helpers can
// reference it (e.g. importlib._bootstrap._setup).
register_module_hierarchy(name, module_value)
let source = @fs.read_file_to_string(filename) catch {
_ => {
module_cache_delete(name)
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ImportError: cannot read file: " + filename,
),
)
}
}
let config = current_config()
let saved_closure_env_stack = closure_env_stack.val
let saved_scope_decls_stack = scope_decls_stack.val
closure_env_stack.val = []
scope_decls_stack.val = []
let _ = match ensure_recursion_available() {
Ok(_) => ()
Err(err) => return Err(err)
}
push_traceback_frame("".to_string(), filename)
set_traceback_frame_env(module_globals, module_globals)
let program = match parse(source) {
Ok(v) => v
Err(err) => {
pop_traceback_frame()
closure_env_stack.val = saved_closure_env_stack
scope_decls_stack.val = saved_scope_decls_stack
module_cache_delete(name)
return Err(make_parse_runtime_error(err))
}
}
// Ensure package modules have an importable `__path__`.
if is_pkg {
set_named_value(
module_globals,
"__path__",
Value::List([Value::Str(pkg_path)]),
)
}
let _ = match
eval_block(
program.body,
module_globals,
module_globals,
builtins,
io,
config,
) {
Ok(v) => v
Err(err) => {
pop_traceback_frame()
closure_env_stack.val = saved_closure_env_stack
scope_decls_stack.val = saved_scope_decls_stack
module_cache_delete(name)
return Err(err)
}
}
pop_traceback_frame()
closure_env_stack.val = saved_closure_env_stack
scope_decls_stack.val = saved_scope_decls_stack
// Importlib expects `exec_module` to return None.
let _ = name
return Ok(Value::None)
}
// Intrinsic: importlib loader get_source(fullname)
if bound_method.function.name == "__mpython_get_source" &&
bound_method.function.body.length() == 0 {
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"get_source() takes exactly one argument".to_string(),
),
)
}
let fullname = match positional[0] {
Value::Str(v) => v
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"get_source() fullname must be str".to_string(),
),
)
}
let module_value_opt = module_cache_get(fullname)
let filename_opt = match module_value_opt {
Some(Value::Instance(inst)) =>
match get_named_value(inst.dict, "__file__") {
Some(Value::Str(v)) => Some(v)
_ => None
}
_ => None
}
match filename_opt {
Some(filename) =>
if filename.has_prefix("<") && filename.has_suffix(">") {
return Ok(Value::None)
} else {
let source = @fs.read_file_to_string(filename) catch { _ => "" }
if source.length() == 0 {
return Ok(Value::None)
}
return Ok(Value::Str(source))
}
None => return Ok(Value::None)
}
}
match bound_method.self {
Value::Instance(inst) =>
if (
inst.class.name == "dict_keys" ||
inst.class.name == "dict_values" ||
inst.class.name == "dict_items"
) &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
let dict_value = match get_named_value(inst.dict, "dict") {
Some(v) => v
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"RuntimeError: bad dict view".to_string(),
),
)
}
let pairs = match dict_value {
Value::Dict(pairs) => pairs
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"dict view expects a dict".to_string(),
),
)
}
match bound_method.function.name {
"__len__" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__len__() takes no arguments".to_string(),
),
)
} else {
return Ok(
Value::Int(@bigint.BigInt::from_int(pairs.length())),
)
}
"__iter__" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__iter__() takes no arguments".to_string(),
),
)
} else {
let items : Array[Value] = []
if inst.class.name == "dict_keys" {
for pair in pairs {
items.push(pair.0)
}
} else if inst.class.name == "dict_values" {
for pair in pairs {
items.push(pair.1)
}
} else {
for pair in pairs {
items.push(Value::Tuple([pair.0, pair.1]))
}
}
return Ok(make_iterator(items))
}
"__contains__" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__contains__() takes exactly one argument".to_string(),
),
)
} else {
let item = positional[0]
if inst.class.name == "dict_keys" {
let index = match dict_find_index(pairs, item) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::Bool(index is Some(_)))
} else if inst.class.name == "dict_values" {
for pair in pairs {
if eq_value(pair.1, item) {
return Ok(Value::Bool(true))
}
}
return Ok(Value::Bool(false))
} else {
let _ = match ensure_hashable(item) {
Ok(v) => v
Err(err) => return Err(err)
}
match item {
Value::Tuple(values) | Value::List(values) =>
if values.length() != 2 {
return Ok(Value::Bool(false))
} else {
for pair in pairs {
if eq_value(pair.0, values[0]) &&
eq_value(pair.1, values[1]) {
return Ok(Value::Bool(true))
}
}
return Ok(Value::Bool(false))
}
_ => return Ok(Value::Bool(false))
}
}
}
_ => ()
}
} else if bound_method.function.body.length() == 0 &&
get_named_value(inst.dict, list_storage_name) is Some(_) {
// Support list subclasses: instances created via object.__new__ for a class
// that includes `list` in its MRO store their backing array in `$__list__`.
match get_named_value(inst.dict, list_storage_name) {
Some(Value::List(values)) => {
if keywords.length() > 0 && bound_method.function.name != "sort" {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
match bound_method.function.name {
"__getitem__" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__getitem__() takes exactly one argument".to_string(),
),
)
} else {
let index_value = positional[0]
match index_value {
Value::Instance(slice_inst) =>
if slice_inst.class.name == "slice" {
let start_value = match
get_named_value(slice_inst.dict, "start") {
Some(v) => v
None => Value::None
}
let end_value = match
get_named_value(slice_inst.dict, "stop") {
Some(v) => v
None => Value::None
}
let step_value = match
get_named_value(slice_inst.dict, "step") {
Some(v) => v
None => Value::None
}
let indices = match
slice_indices_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(
Value::List(
slice_values_by_indices(values, indices),
),
)
} else {
()
}
_ => ()
}
let idx = match index_from_value(index_value, 0) {
Ok(v) => v
Err(err) => return Err(err)
}
let norm = normalize_index(idx, values.length())
if norm < 0 || norm >= values.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Index,
"list index out of range".to_string(),
),
)
}
return Ok(values[norm])
}
"__setitem__" =>
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__setitem__() takes exactly two arguments".to_string(),
),
)
} else {
let index_value = positional[0]
let assigned = positional[1]
match index_value {
Value::Instance(slice_inst) =>
if slice_inst.class.name == "slice" {
let start_value = match
get_named_value(slice_inst.dict, "start") {
Some(v) => v
None => Value::None
}
let end_value = match
get_named_value(slice_inst.dict, "stop") {
Some(v) => v
None => Value::None
}
let step_value = match
get_named_value(slice_inst.dict, "step") {
Some(v) => v
None => Value::None
}
let new_items = match iterable_values(assigned) {
Ok(v) => v
Err(err) => return Err(err)
}
let (start, end, step) = match
slice_params_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
if step == 1 {
let merged : Array[Value] = []
for i = 0; i < start; i = i + 1 {
merged.push(values[i])
}
for item in new_items {
merged.push(item)
}
for i = end; i < values.length(); i = i + 1 {
merged.push(values[i])
}
replace_array_values(values, merged)
return Ok(Value::None)
} else {
let indices = match
slice_indices_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
if new_items.length() != indices.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: attempt to assign sequence of size " +
new_items.length().to_string() +
" to extended slice of size " +
indices.length().to_string(),
),
)
}
for i = 0; i < indices.length(); i = i + 1 {
values[indices[i]] = new_items[i]
}
return Ok(Value::None)
}
} else {
()
}
_ => ()
}
let idx = match index_from_value(index_value, 0) {
Ok(v) => v
Err(err) => return Err(err)
}
let norm = normalize_index(idx, values.length())
if norm < 0 || norm >= values.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Index,
"list assignment index out of range".to_string(),
),
)
}
values[norm] = assigned
return Ok(Value::None)
}
"__delitem__" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__delitem__() takes exactly one argument".to_string(),
),
)
} else {
let index_value = positional[0]
match index_value {
Value::Instance(slice_inst) =>
if slice_inst.class.name == "slice" {
let start_value = match
get_named_value(slice_inst.dict, "start") {
Some(v) => v
None => Value::None
}
let end_value = match
get_named_value(slice_inst.dict, "stop") {
Some(v) => v
None => Value::None
}
let step_value = match
get_named_value(slice_inst.dict, "step") {
Some(v) => v
None => Value::None
}
let (_, _, step) = match
slice_params_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
if step == 1 {
let (start, end, _) = match
slice_params_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let merged : Array[Value] = []
for i = 0; i < start; i = i + 1 {
merged.push(values[i])
}
for i = end; i < values.length(); i = i + 1 {
merged.push(values[i])
}
replace_array_values(values, merged)
return Ok(Value::None)
}
let indices = match
slice_indices_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
// Remove from the end so indices stay valid.
let mut i = indices.length()
while i > 0 {
i = i - 1
let _ = values.remove(indices[i])
}
return Ok(Value::None)
} else {
()
}
_ => ()
}
let idx = match index_from_value(index_value, 0) {
Ok(v) => v
Err(err) => return Err(err)
}
let norm = normalize_index(idx, values.length())
if norm < 0 || norm >= values.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Index,
"list assignment index out of range".to_string(),
),
)
}
let _ = values.remove(norm)
return Ok(Value::None)
}
"append" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"append() takes exactly one argument".to_string(),
),
)
} else {
values.push(positional[0])
return Ok(Value::None)
}
"extend" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"extend() takes exactly one argument".to_string(),
),
)
} else {
let to_add : Array[Value] = []
let iterator = match
iter_value_to_iterator(
positional[0],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
while true {
match
iterator_next(iterator, None, globals, builtins, io) {
Ok(item) => to_add.push(item)
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
for item in to_add {
values.push(item)
}
return Ok(Value::None)
}
"insert" =>
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"insert() takes exactly two arguments".to_string(),
),
)
} else {
let idx = match index_from_value(positional[0], 0) {
Ok(v) => v
Err(err) => return Err(err)
}
let len = values.length()
let mut pos = normalize_index(idx, len)
if pos < 0 {
pos = 0
}
if pos > len {
pos = len
}
values.push(Value::None)
let mut i = len
while i > pos {
values[i] = values[i - 1]
i = i - 1
}
values[pos] = positional[1]
return Ok(Value::None)
}
"remove" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"remove() takes exactly one argument".to_string(),
),
)
} else {
let needle = positional[0]
for i = 0; i < values.length(); i = i + 1 {
if eq_value(values[i], needle) {
let _ = values.remove(i)
return Ok(Value::None)
}
}
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: list.remove(x): x not in list".to_string(),
),
)
}
"reverse" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"reverse() takes no arguments".to_string(),
),
)
} else {
if values.length() == 0 {
return Ok(Value::None)
}
let mut i = 0
let mut j = values.length() - 1
while i < j {
let tmp = values[i]
values[i] = values[j]
values[j] = tmp
i += 1
j -= 1
}
return Ok(Value::None)
}
"clear" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"clear() takes no arguments".to_string(),
),
)
} else {
let mut i = values.length()
while i > 0 {
i = i - 1
let _ = values.remove(i)
}
return Ok(Value::None)
}
"pop" => {
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"pop() takes at most one argument".to_string(),
),
)
}
if values.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Index,
"pop from empty list".to_string(),
),
)
}
let mut idx = values.length() - 1
if positional.length() == 1 {
let raw = match positional[0] {
Value::Int(v) =>
match bigint_to_int_checked(v) {
Ok(i) => i
Err(err) => return Err(err)
}
Value::Bool(b) => if b { 1 } else { 0 }
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"invalid index".to_string(),
),
)
}
let normalized = normalize_index(raw, values.length())
if normalized < 0 || normalized >= values.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Index,
"pop index out of range".to_string(),
),
)
}
idx = normalized
}
let item = values.remove(idx)
return Ok(item)
}
"copy" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"copy() takes no arguments".to_string(),
),
)
} else {
let out : Array[Value] = []
for item in values {
out.push(item)
}
return Ok(Value::List(out))
}
"sort" => {
if positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"sort() takes at most 2 arguments".to_string(),
),
)
}
let mut reverse = false
let mut key_value : Value? = None
for pair in keywords {
if pair.0 == "key" {
key_value = Some(pair.1)
} else if pair.0 == "reverse" {
reverse = bool_from_value(pair.1)
} else {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"sort() got unexpected keyword arguments".to_string(),
),
)
}
}
if positional.length() >= 1 {
if key_value is Some(_) {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"sort() got multiple values for argument 'key'".to_string(),
),
)
}
key_value = Some(positional[0])
}
if positional.length() == 2 {
reverse = bool_from_value(positional[1])
}
if values.length() == 0 {
return Ok(Value::None)
}
fn lt_for_sort(
a : Value,
b : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Bool, RuntimeError] {
fn tuple_items_for_sort(v : Value) -> Array[Value]? {
match v {
Value::Tuple(items) => Some(items)
Value::Instance(inst) =>
match
get_named_value(inst.dict, tuple_storage_name) {
Some(Value::Tuple(items)) => Some(items)
_ => None
}
_ => None
}
}
match (a, b) {
(Value::Str(x), Value::Str(y)) => Ok(x < y)
_ =>
match
(tuple_items_for_sort(a), tuple_items_for_sort(b)) {
(Some(xs), Some(ys)) =>
match
compare_sequence_values(
xs, ys, globals, builtins, io,
) {
Ok(cmp) => Ok(cmp < 0)
Err(err) => Err(err)
}
_ =>
match (a, b) {
(Value::List(xs), Value::List(ys)) =>
match
compare_sequence_values(
xs, ys, globals, builtins, io,
) {
Ok(cmp) => Ok(cmp < 0)
Err(err) => Err(err)
}
(
Value::Int(_)
| Value::Float(_)
| Value::Bool(_),
_,
)
| (
_,
Value::Int(_)
| Value::Float(_)
| Value::Bool(_),
) => {
let (_, left_num) = match number_value(a) {
Ok(v) => v
Err(err) => return Err(err)
}
let (_, right_num) = match number_value(b) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(left_num < right_num)
}
_ => {
let lt = match
get_attr_from_value(
a, "__lt__", globals, builtins, io,
) {
Ok(v) => v
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"sort() cannot compare values".to_string(),
),
)
_ => return Err(err)
}
}
let result = match
call_callable_with_env(
lt,
[b],
[],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(bool_from_value(result))
}
}
}
}
}
match key_value {
Some(key_fn) => {
let key_pairs : Array[(Value, Value)] = []
for item in values {
let key = if key_fn is Value::None {
item
} else {
match
call_callable_with_env(
key_fn,
[item],
[],
globals,
builtins,
io,
) {
Ok(value) => value
Err(err) => return Err(err)
}
}
key_pairs.push((key, item))
}
fn insertion_sort_pairs_by_key(
pairs : Array[(Value, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Unit, RuntimeError] {
let mut i = 1
while i < pairs.length() {
let item = pairs[i]
let mut j = i
while j > 0 {
let less = match
lt_for_sort(
item.0,
pairs[j - 1].0,
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
if !less {
break
}
pairs[j] = pairs[j - 1]
j -= 1
}
pairs[j] = item
i += 1
}
Ok(())
}
let mut kind_opt : SortKind? = None
let mut can_use_fast = true
for pair in key_pairs {
match sort_kind_for_sorted(pair.0) {
Some(kind) =>
match kind_opt {
None => kind_opt = Some(kind)
Some(prev) =>
if prev != kind {
can_use_fast = false
}
}
None => can_use_fast = false
}
}
let sorted_pairs = if can_use_fast {
let kind = match kind_opt {
Some(kind) => kind
None => return Ok(Value::None)
}
match stable_sort_pairs_by_key(key_pairs, kind) {
Ok(v) => v
Err(err) => return Err(err)
}
} else {
let _ = match
insertion_sort_pairs_by_key(
key_pairs, globals, builtins, io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
key_pairs
}
let sorted_items : Array[Value] = []
for pair in sorted_pairs {
sorted_items.push(pair.1)
}
let out = if reverse {
reverse_values(sorted_items)
} else {
sorted_items
}
replace_array_values(values, out)
return Ok(Value::None)
}
None => {
let mut kind_opt : SortKind? = None
let mut can_use_fast = true
for item in values {
match sort_kind_for_sorted(item) {
Some(kind) =>
match kind_opt {
None => kind_opt = Some(kind)
Some(prev) =>
if prev != kind {
can_use_fast = false
}
}
None => can_use_fast = false
}
}
let mut out = if can_use_fast {
let kind = match kind_opt {
Some(kind) => kind
None => return Ok(Value::None)
}
match stable_sort_for_sorted(values, kind) {
Ok(v) => v
Err(err) => return Err(err)
}
} else {
let out : Array[Value] = []
for item in values {
out.push(item)
}
let mut i = 1
while i < out.length() {
let item = out[i]
let mut j = i
while j > 0 {
let less = match
lt_for_sort(
item,
out[j - 1],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
if !less {
break
}
out[j] = out[j - 1]
j -= 1
}
out[j] = item
i += 1
}
out
}
if reverse {
out = reverse_values(out)
}
replace_array_values(values, out)
return Ok(Value::None)
}
}
}
_ => ()
}
}
_ => ()
}
} else if inst.class.name == "generator" &&
bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
match bound_method.function.name {
"send" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"send() takes exactly one argument".to_string(),
),
)
} else {
return generator_send(bound_method.self, positional[0])
}
"close" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"close() takes no arguments".to_string(),
),
)
} else {
return generator_close(bound_method.self)
}
"throw" =>
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"throw() takes 1 to 3 arguments".to_string(),
),
)
} else {
return generator_throw(bound_method.self, positional[0])
}
_ => ()
}
} else if inst.class.name == "async_generator" &&
bound_method.function.body.length() == 0 {
let has_gen_id = match get_named_value(inst.dict, "gen_id") {
Some(_) => true
None => false
}
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
match bound_method.function.name {
"__aiter__" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__aiter__() takes no arguments".to_string(),
),
)
} else {
return Ok(bound_method.self)
}
"__anext__" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__anext__() takes no arguments".to_string(),
),
)
} else {
if has_gen_id {
return Ok(
async_generator_make_anext_coroutine(
bound_method.self,
globals,
builtins,
io,
),
)
}
return Ok(
async_genexp_make_anext_coroutine(
bound_method.self,
globals,
builtins,
io,
),
)
}
"asend" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"asend() takes exactly one argument".to_string(),
),
)
} else {
if !has_gen_id {
return Err(
make_runtime_error(
RuntimeErrorKind::Attribute,
"'async_generator' object has no attribute 'asend'",
),
)
}
return Ok(
async_generator_make_asend_coroutine(
bound_method.self,
positional[0],
globals,
builtins,
io,
),
)
}
"athrow" =>
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"athrow() takes 1 to 3 arguments".to_string(),
),
)
} else {
if !has_gen_id {
return Err(
make_runtime_error(
RuntimeErrorKind::Attribute,
"'async_generator' object has no attribute 'athrow'",
),
)
}
return Ok(
async_generator_make_athrow_coroutine(
bound_method.self,
positional[0],
globals,
builtins,
io,
),
)
}
"aclose" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"aclose() takes no arguments".to_string(),
),
)
} else {
if !has_gen_id {
return Err(
make_runtime_error(
RuntimeErrorKind::Attribute,
"'async_generator' object has no attribute 'aclose'",
),
)
}
return Ok(
async_generator_make_aclose_coroutine(
bound_method.self,
globals,
builtins,
io,
),
)
}
_ => ()
}
}
Value::Str(text) =>
if bound_method.function.body.length() == 0 {
if keywords.length() > 0 &&
bound_method.function.name != "split" &&
bound_method.function.name != "splitlines" &&
bound_method.function.name != "format" {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
match bound_method.function.name {
"startswith" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"startswith() takes 1 to 3 arguments".to_string(),
),
)
}
let prefixes : Array[String] = []
match positional[0] {
Value::Str(value) => prefixes.push(value)
Value::Tuple(values) =>
for item in values {
match item {
Value::Str(value) => prefixes.push(value)
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"startswith() expects a str prefix".to_string(),
),
)
}
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"startswith() expects a str prefix".to_string(),
),
)
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
text.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, text.length())
let mut end_idx = normalize_index(end, text.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > text.length() {
start_idx = text.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > text.length() {
end_idx = text.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
let segment = substring(text, start_idx, end_idx)
let mut matched = false
for prefix in prefixes {
if segment.has_prefix(prefix) {
matched = true
break
}
}
return Ok(Value::Bool(matched))
}
"find" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"find() takes 1 to 3 arguments".to_string(),
),
)
}
let needle = match positional[0] {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"find() expects a str substring".to_string(),
),
)
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
text.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, text.length())
let mut end_idx = normalize_index(end, text.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > text.length() {
start_idx = text.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > text.length() {
end_idx = text.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
if needle.length() == 0 {
return Ok(Value::Int(@bigint.BigInt::from_int(start_idx)))
}
let max_start = end_idx - needle.length()
let mut found = -1
let mut i = start_idx
while i <= max_start {
if substring(text, i, i + needle.length()) == needle {
found = i
break
}
i += 1
}
if found < 0 {
return Ok(Value::Int(@bigint.BigInt::from_int(-1)))
} else {
return Ok(Value::Int(@bigint.BigInt::from_int(found)))
}
}
"count" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"count() takes 1 to 3 arguments".to_string(),
),
)
}
let needle = match positional[0] {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"count() expects a str substring".to_string(),
),
)
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
text.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, text.length())
let mut end_idx = normalize_index(end, text.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > text.length() {
start_idx = text.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > text.length() {
end_idx = text.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
if needle.length() == 0 {
let out = end_idx - start_idx + 1
return Ok(Value::Int(@bigint.BigInt::from_int(out)))
}
let max_start = end_idx - needle.length()
let mut found = 0
let mut i = start_idx
while i <= max_start {
if substring(text, i, i + needle.length()) == needle {
found += 1
i += needle.length()
} else {
i += 1
}
}
return Ok(Value::Int(@bigint.BigInt::from_int(found)))
}
"index" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"index() takes 1 to 3 arguments".to_string(),
),
)
}
let needle = match positional[0] {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"index() expects a str substring".to_string(),
),
)
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
text.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, text.length())
let mut end_idx = normalize_index(end, text.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > text.length() {
start_idx = text.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > text.length() {
end_idx = text.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
if needle.length() == 0 {
return Ok(Value::Int(@bigint.BigInt::from_int(start_idx)))
}
let max_start = end_idx - needle.length()
let mut found = -1
let mut i = start_idx
while i <= max_start {
if substring(text, i, i + needle.length()) == needle {
found = i
break
}
i += 1
}
if found < 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: substring not found".to_string(),
),
)
}
return Ok(Value::Int(@bigint.BigInt::from_int(found)))
}
"rfind" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rfind() takes 1 to 3 arguments".to_string(),
),
)
}
let needle = match positional[0] {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rfind() expects a str substring".to_string(),
),
)
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
text.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, text.length())
let mut end_idx = normalize_index(end, text.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > text.length() {
start_idx = text.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > text.length() {
end_idx = text.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
if needle.length() == 0 {
return Ok(Value::Int(@bigint.BigInt::from_int(end_idx)))
}
if needle.length() > end_idx - start_idx {
return Ok(Value::Int(@bigint.BigInt::from_int(-1)))
}
let mut found = -1
let mut i = end_idx - needle.length()
while i >= start_idx {
if substring(text, i, i + needle.length()) == needle {
found = i
break
}
i -= 1
}
if found < 0 {
return Ok(Value::Int(@bigint.BigInt::from_int(-1)))
} else {
return Ok(Value::Int(@bigint.BigInt::from_int(found)))
}
}
"rindex" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rindex() takes 1 to 3 arguments".to_string(),
),
)
}
let needle = match positional[0] {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rindex() expects a str substring".to_string(),
),
)
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
text.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, text.length())
let mut end_idx = normalize_index(end, text.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > text.length() {
start_idx = text.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > text.length() {
end_idx = text.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
if needle.length() == 0 {
return Ok(Value::Int(@bigint.BigInt::from_int(end_idx)))
}
if needle.length() > end_idx - start_idx {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: substring not found".to_string(),
),
)
}
let mut found = -1
let mut i = end_idx - needle.length()
while i >= start_idx {
if substring(text, i, i + needle.length()) == needle {
found = i
break
}
i -= 1
}
if found < 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: substring not found".to_string(),
),
)
}
return Ok(Value::Int(@bigint.BigInt::from_int(found)))
}
"encode" => {
if positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"encode() takes 0 to 2 arguments".to_string(),
),
)
}
let encoding_value = if positional.length() >= 1 {
positional[0]
} else {
Value::Str("utf-8")
}
let errors_value = if positional.length() == 2 {
Some(positional[1])
} else {
None
}
let bytes = match
encode_string_with_encoding(
"encode", text, encoding_value, errors_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::Bytes(bytes))
}
"endswith" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"endswith() takes 1 to 3 arguments".to_string(),
),
)
}
let suffixes : Array[String] = []
match positional[0] {
Value::Str(value) => suffixes.push(value)
Value::Tuple(values) =>
for item in values {
match item {
Value::Str(value) => suffixes.push(value)
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"endswith() expects a str suffix".to_string(),
),
)
}
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"endswith() expects a str suffix".to_string(),
),
)
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
text.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, text.length())
let mut end_idx = normalize_index(end, text.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > text.length() {
start_idx = text.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > text.length() {
end_idx = text.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
let segment = substring(text, start_idx, end_idx)
let mut matched = false
for suffix in suffixes {
if segment.has_suffix(suffix) {
matched = true
break
}
}
return Ok(Value::Bool(matched))
}
"removeprefix" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"removeprefix() takes exactly one argument".to_string(),
),
)
} else {
let prefix = match positional[0] {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"removeprefix() expects a str prefix".to_string(),
),
)
}
if text.has_prefix(prefix) {
return Ok(
Value::Str(
substring(text, prefix.length(), text.length()),
),
)
}
return Ok(Value::Str(text))
}
"removesuffix" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"removesuffix() takes exactly one argument".to_string(),
),
)
} else {
let suffix = match positional[0] {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"removesuffix() expects a str suffix".to_string(),
),
)
}
if text.has_suffix(suffix) {
return Ok(
Value::Str(
substring(text, 0, text.length() - suffix.length()),
),
)
}
return Ok(Value::Str(text))
}
"isascii" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"isascii() takes no arguments".to_string(),
),
)
} else {
for c in text.to_array() {
if c > '\u007f' {
return Ok(Value::Bool(false))
}
}
return Ok(Value::Bool(true))
}
"isdigit" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"isdigit() takes no arguments".to_string(),
),
)
} else {
let chars = text.to_array()
if chars.length() == 0 {
return Ok(Value::Bool(false))
}
for c in chars {
if !is_ascii_digit(c) {
return Ok(Value::Bool(false))
}
}
return Ok(Value::Bool(true))
}
"isalnum" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"isalnum() takes no arguments".to_string(),
),
)
} else {
let chars = text.to_array()
if chars.length() == 0 {
return Ok(Value::Bool(false))
}
for c in chars {
if !is_ascii_alnum(c) {
return Ok(Value::Bool(false))
}
}
return Ok(Value::Bool(true))
}
"isalpha" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"isalpha() takes no arguments".to_string(),
),
)
} else {
let chars = text.to_array()
if chars.length() == 0 {
return Ok(Value::Bool(false))
}
for c in chars {
if !is_ascii_alpha(c) {
return Ok(Value::Bool(false))
}
}
return Ok(Value::Bool(true))
}
"isspace" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"isspace() takes no arguments".to_string(),
),
)
} else {
let chars = text.to_array()
if chars.length() == 0 {
return Ok(Value::Bool(false))
}
for c in chars {
if !is_python_whitespace(c) {
return Ok(Value::Bool(false))
}
}
return Ok(Value::Bool(true))
}
"islower" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"islower() takes no arguments".to_string(),
),
)
} else {
let chars = text.to_array()
if chars.length() == 0 {
return Ok(Value::Bool(false))
}
let mut has_cased = false
for c in chars {
if is_ascii_alpha(c) {
has_cased = true
if is_ascii_upper(c) {
return Ok(Value::Bool(false))
}
}
}
return Ok(Value::Bool(has_cased))
}
"isupper" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"isupper() takes no arguments".to_string(),
),
)
} else {
let chars = text.to_array()
if chars.length() == 0 {
return Ok(Value::Bool(false))
}
let mut has_cased = false
for c in chars {
if is_ascii_alpha(c) {
has_cased = true
if is_ascii_lower(c) {
return Ok(Value::Bool(false))
}
}
}
return Ok(Value::Bool(has_cased))
}
"istitle" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"istitle() takes no arguments".to_string(),
),
)
} else {
let chars = text.to_array()
if chars.length() == 0 {
return Ok(Value::Bool(false))
}
let mut has_cased = false
let mut word_start = true
for c in chars {
if is_ascii_alpha(c) {
has_cased = true
if word_start {
if !is_ascii_upper(c) {
return Ok(Value::Bool(false))
}
word_start = false
} else if !is_ascii_lower(c) {
return Ok(Value::Bool(false))
}
} else {
word_start = true
}
}
return Ok(Value::Bool(has_cased))
}
"isdecimal" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"isdecimal() takes no arguments".to_string(),
),
)
} else {
let chars = text.to_array()
if chars.length() == 0 {
return Ok(Value::Bool(false))
}
for c in chars {
if !is_ascii_digit(c) {
return Ok(Value::Bool(false))
}
}
return Ok(Value::Bool(true))
}
"isnumeric" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"isnumeric() takes no arguments".to_string(),
),
)
} else {
let chars = text.to_array()
if chars.length() == 0 {
return Ok(Value::Bool(false))
}
for c in chars {
if !is_ascii_digit(c) {
return Ok(Value::Bool(false))
}
}
return Ok(Value::Bool(true))
}
"partition" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"partition() takes exactly one argument".to_string(),
),
)
} else {
let sep = match positional[0] {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"partition() expects a str separator".to_string(),
),
)
}
if sep.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: empty separator".to_string(),
),
)
}
match text.find(sep) {
Some(pos) => {
let head = substring(text, 0, pos)
let tail = substring(
text,
pos + sep.length(),
text.length(),
)
return Ok(
Value::Tuple([
Value::Str(head),
Value::Str(sep),
Value::Str(tail),
]),
)
}
None =>
return Ok(
Value::Tuple([
Value::Str(text),
Value::Str(""),
Value::Str(""),
]),
)
}
}
"rpartition" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rpartition() takes exactly one argument".to_string(),
),
)
} else {
let sep = match positional[0] {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rpartition() expects a str separator".to_string(),
),
)
}
if sep.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: empty separator".to_string(),
),
)
}
let mut found = -1
if sep.length() <= text.length() {
let mut i = text.length() - sep.length()
while i >= 0 {
if substring(text, i, i + sep.length()) == sep {
found = i
break
}
i -= 1
}
}
if found < 0 {
return Ok(
Value::Tuple([
Value::Str(""),
Value::Str(""),
Value::Str(text),
]),
)
}
let head = substring(text, 0, found)
let tail = substring(
text,
found + sep.length(),
text.length(),
)
return Ok(
Value::Tuple([
Value::Str(head),
Value::Str(sep),
Value::Str(tail),
]),
)
}
"expandtabs" => {
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"expandtabs() takes at most one argument".to_string(),
),
)
}
let mut tabsize = 8
if positional.length() == 1 {
match positional[0] {
Value::Int(v) => tabsize = v.to_int64().to_int()
Value::Bool(v) => tabsize = if v { 1 } else { 0 }
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"expandtabs() expects an int tabsize".to_string(),
),
)
}
}
if tabsize < 0 {
tabsize = 0
}
let chars = text.to_array()
let buf = StringBuilder()
let mut col = 0
for c in chars {
if c == '\t' {
if tabsize == 0 {
continue
}
let space_count = tabsize - col % tabsize
for _k = 0; _k < space_count; _k = _k + 1 {
buf.write_char(' ')
}
col += space_count
} else {
buf.write_char(c)
if c == '\n' || c == '\r' {
col = 0
} else {
col += 1
}
}
}
return Ok(Value::Str(buf.to_string()))
}
"translate" => {
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"translate() takes exactly one argument".to_string(),
),
)
}
let table = match positional[0] {
Value::Dict(pairs) => pairs
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"translate() expects a dict table".to_string(),
),
)
}
let chars = text.to_array()
let buf = StringBuilder()
for c in chars {
let code = c.to_int()
let idx = match
dict_find_index(
table,
Value::Int(@bigint.BigInt::from_int(code)),
) {
Ok(v) => v
Err(err) => return Err(err)
}
match idx {
Some(pos) =>
match table[pos].1 {
Value::None => ()
Value::Str(rep) => buf.write_string(rep)
Value::Int(v) => {
let out_code = v.to_int64().to_int()
match out_code.to_char() {
Some(ch) => buf.write_char(ch)
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: invalid character in translate table".to_string(),
),
)
}
}
Value::Bool(v) => {
let out_code = if v { 1 } else { 0 }
match out_code.to_char() {
Some(ch) => buf.write_char(ch)
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: invalid character in translate table".to_string(),
),
)
}
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"translate() table values must be int, str, or None".to_string(),
),
)
}
None => buf.write_char(c)
}
}
return Ok(Value::Str(buf.to_string()))
}
"isidentifier" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"isidentifier() takes no arguments".to_string(),
),
)
} else {
let chars = text.to_array()
if chars.length() == 0 {
return Ok(Value::Bool(false))
}
let first = chars[0]
if !(is_ascii_alpha(first) || first == '_') {
return Ok(Value::Bool(false))
}
for i = 1; i < chars.length(); i = i + 1 {
let c = chars[i]
if !(is_ascii_alnum(c) || c == '_') {
return Ok(Value::Bool(false))
}
}
return Ok(Value::Bool(true))
}
"capitalize" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"capitalize() takes no arguments".to_string(),
),
)
} else {
if text.length() == 0 {
return Ok(Value::Str(text))
}
let first = substring(text, 0, 1).to_upper()
let rest = substring(text, 1, text.length()).to_lower()
return Ok(Value::Str(first + rest))
}
"center" => {
if positional.length() == 0 || positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"center() takes 1 or 2 arguments".to_string(),
),
)
}
let width = match index_from_value(positional[0], 0) {
Ok(v) => v
Err(err) => return Err(err)
}
let fill = if positional.length() == 2 {
match positional[1] {
Value::Str(v) => v
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"center() fillchar must be str".to_string(),
),
)
}
} else {
" ".to_string()
}
if fill.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: The fill character must be exactly one character long".to_string(),
),
)
}
if width <= text.length() {
return Ok(Value::Str(text))
}
let padding = width - text.length()
let mut left = padding / 2
// Match CPython: when padding is odd, even-length strings get the
// extra fill on the left, odd-length strings get it on the right.
if padding % 2 == 1 && text.length() % 2 == 0 {
left = left + 1
}
let right = padding - left
let builder = StringBuilder()
for _i = 0; _i < left; _i = _i + 1 {
builder.write_string(fill)
}
builder.write_string(text)
for _i = 0; _i < right; _i = _i + 1 {
builder.write_string(fill)
}
return Ok(Value::Str(builder.to_string()))
}
"upper" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"upper() takes no arguments".to_string(),
),
)
} else {
return Ok(Value::Str(text.to_upper()))
}
"lower" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"lower() takes no arguments".to_string(),
),
)
} else {
return Ok(Value::Str(text.to_lower()))
}
"casefold" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"casefold() takes no arguments".to_string(),
),
)
} else {
return Ok(Value::Str(text.to_lower()))
}
"replace" => {
if positional.length() < 2 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"replace() takes 2 or 3 arguments".to_string(),
),
)
}
let old = match positional[0] {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"replace() expects a str pattern".to_string(),
),
)
}
let new_value = match positional[1] {
Value::Str(value) => value
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"replace() expects a str replacement".to_string(),
),
)
}
let count = if positional.length() == 3 {
match index_from_value(positional[2], -1) {
Ok(v) => v
Err(err) => return Err(err)
}
} else {
-1
}
if count == 0 {
return Ok(Value::Str(text))
}
if old.length() == 0 {
let chars = text.to_array()
let total = chars.length() + 1
let limit = if count < 0 || count > total {
total
} else {
count
}
let builder = StringBuilder()
let mut inserted = 0
for i = 0; i < chars.length(); i = i + 1 {
if inserted < limit {
builder.write_string(new_value)
inserted += 1
}
builder.write_char(chars[i])
}
if inserted < limit {
builder.write_string(new_value)
}
return Ok(Value::Str(builder.to_string()))
}
let builder = StringBuilder()
let mut first = true
let mut replaced = 0
for part in text.split(old) {
if first {
builder.write_string(part.to_owned())
first = false
continue
}
if count < 0 || replaced < count {
builder.write_string(new_value)
replaced += 1
} else {
builder.write_string(old)
}
builder.write_string(part.to_owned())
}
return Ok(Value::Str(builder.to_string()))
}
"strip" => {
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"strip() takes at most one argument".to_string(),
),
)
}
let chars_opt = match
parse_strip_chars(
"strip",
if positional.length() == 1 {
Some(positional[0])
} else {
None
},
) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(
Value::Str(strip_text(text, chars_opt, StripMode::Both)),
)
}
"lstrip" => {
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"lstrip() takes at most one argument".to_string(),
),
)
}
let chars_opt = match
parse_strip_chars(
"lstrip",
if positional.length() == 1 {
Some(positional[0])
} else {
None
},
) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(
Value::Str(strip_text(text, chars_opt, StripMode::Left)),
)
}
"rstrip" => {
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rstrip() takes at most one argument".to_string(),
),
)
}
let chars_opt = match
parse_strip_chars(
"rstrip",
if positional.length() == 1 {
Some(positional[0])
} else {
None
},
) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(
Value::Str(strip_text(text, chars_opt, StripMode::Right)),
)
}
"split" => {
if positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"split() takes at most 2 arguments".to_string(),
),
)
}
let mut sep_kw : Value? = None
let mut maxsplit_kw : Value? = None
for item in keywords {
if item.0 == "sep" {
sep_kw = Some(item.1)
} else if item.0 == "maxsplit" {
maxsplit_kw = Some(item.1)
} else {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"split() got unexpected keyword arguments".to_string(),
),
)
}
}
if positional.length() >= 1 && sep_kw is Some(_) {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"split() got multiple values for argument 'sep'".to_string(),
),
)
}
if positional.length() == 2 && maxsplit_kw is Some(_) {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"split() got multiple values for argument 'maxsplit'".to_string(),
),
)
}
let sep_value = if positional.length() >= 1 {
positional[0]
} else {
match sep_kw {
Some(v) => v
None => Value::None
}
}
let maxsplit_value = if positional.length() == 2 {
positional[1]
} else {
match maxsplit_kw {
Some(v) => v
None => Value::Int(@bigint.BigInt::from_int(-1))
}
}
let maxsplit = match index_from_value(maxsplit_value, -1) {
Ok(v) => v
Err(err) => return Err(err)
}
match sep_value {
Value::None => {
let chars = text.to_array()
let len = chars.length()
let mut i = 0
while i < len && is_ascii_whitespace(chars[i]) {
i += 1
}
if i >= len {
return Ok(Value::List([]))
}
if maxsplit == 0 {
let mut end = len
while end > i && is_ascii_whitespace(chars[end - 1]) {
end -= 1
}
return Ok(
Value::List([Value::Str(substring(text, i, end))]),
)
}
let parts : Array[Value] = []
let mut splits = 0
let mut start = i
while i < len {
if is_ascii_whitespace(chars[i]) {
let end = i
parts.push(Value::Str(substring(text, start, end)))
splits += 1
while i < len && is_ascii_whitespace(chars[i]) {
i += 1
}
if maxsplit >= 0 && splits >= maxsplit {
let mut tail_end = len
while tail_end > i &&
is_ascii_whitespace(chars[tail_end - 1]) {
tail_end -= 1
}
if tail_end > i {
parts.push(Value::Str(substring(text, i, tail_end)))
}
return Ok(Value::List(parts))
}
start = i
continue
}
i += 1
}
if i > start {
parts.push(Value::Str(substring(text, start, i)))
}
return Ok(Value::List(parts))
}
Value::Str(sep) => {
if sep.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: empty separator".to_string(),
),
)
}
if maxsplit == 0 {
return Ok(Value::List([Value::Str(text)]))
}
let raw_parts : Array[String] = []
for part in text.split(sep) {
raw_parts.push(part.to_owned())
}
if maxsplit < 0 || raw_parts.length() <= maxsplit + 1 {
let parts : Array[Value] = []
for part in raw_parts {
parts.push(Value::Str(part))
}
return Ok(Value::List(parts))
}
let parts : Array[Value] = []
// `maxsplit` is "maximum number of splits", so the result
// should contain at most `maxsplit + 1` pieces.
for i = 0; i < maxsplit; i = i + 1 {
parts.push(Value::Str(raw_parts[i]))
}
let builder = StringBuilder()
let mut first_tail = true
for i = maxsplit; i < raw_parts.length(); i = i + 1 {
if !first_tail {
builder.write_string(sep)
}
builder.write_string(raw_parts[i])
first_tail = false
}
parts.push(Value::Str(builder.to_string()))
return Ok(Value::List(parts))
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"split() separator must be str or None".to_string(),
),
)
}
}
"rsplit" => {
if positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rsplit() takes at most 2 arguments".to_string(),
),
)
}
let mut sep_kw : Value? = None
let mut maxsplit_kw : Value? = None
for item in keywords {
if item.0 == "sep" {
sep_kw = Some(item.1)
} else if item.0 == "maxsplit" {
maxsplit_kw = Some(item.1)
} else {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rsplit() got unexpected keyword arguments".to_string(),
),
)
}
}
if positional.length() >= 1 && sep_kw is Some(_) {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rsplit() got multiple values for argument 'sep'".to_string(),
),
)
}
if positional.length() == 2 && maxsplit_kw is Some(_) {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rsplit() got multiple values for argument 'maxsplit'".to_string(),
),
)
}
let sep_value = if positional.length() >= 1 {
positional[0]
} else {
match sep_kw {
Some(v) => v
None => Value::None
}
}
let maxsplit_value = if positional.length() == 2 {
positional[1]
} else {
match maxsplit_kw {
Some(v) => v
None => Value::Int(@bigint.BigInt::from_int(-1))
}
}
let maxsplit = match index_from_value(maxsplit_value, -1) {
Ok(v) => v
Err(err) => return Err(err)
}
match sep_value {
Value::Str(sep) => {
if sep.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: empty separator".to_string(),
),
)
}
if maxsplit == 0 {
return Ok(Value::List([Value::Str(text)]))
}
let raw_parts : Array[String] = []
for part in text.split(sep) {
raw_parts.push(part.to_owned())
}
if maxsplit < 0 || raw_parts.length() <= maxsplit + 1 {
let parts : Array[Value] = []
for part in raw_parts {
parts.push(Value::Str(part))
}
return Ok(Value::List(parts))
}
let left_end = raw_parts.length() - (maxsplit + 1)
let builder = StringBuilder()
let mut first_left = true
for i = 0; i <= left_end; i = i + 1 {
if !first_left {
builder.write_string(sep)
}
builder.write_string(raw_parts[i])
first_left = false
}
let parts : Array[Value] = [Value::Str(builder.to_string())]
for i = left_end + 1; i < raw_parts.length(); i = i + 1 {
parts.push(Value::Str(raw_parts[i]))
}
return Ok(Value::List(parts))
}
Value::None => {
let chars = text.to_array()
let len = chars.length()
let mut end = len
while end > 0 && is_ascii_whitespace(chars[end - 1]) {
end -= 1
}
if end == 0 {
return Ok(Value::List([]))
}
if maxsplit == 0 {
let mut start0 = 0
while start0 < end && is_ascii_whitespace(chars[start0]) {
start0 += 1
}
return Ok(
Value::List([Value::Str(substring(text, start0, end))]),
)
}
let raw_parts : Array[String] = []
let mut splits = 0
let mut i = end - 1
while i >= 0 {
while i >= 0 && is_ascii_whitespace(chars[i]) {
i -= 1
}
if i < 0 {
break
}
let word_end = i + 1
while i >= 0 && !is_ascii_whitespace(chars[i]) {
i -= 1
}
let word_start = i + 1
raw_parts.push(substring(text, word_start, word_end))
splits += 1
if maxsplit >= 0 && splits >= maxsplit {
while i >= 0 && is_ascii_whitespace(chars[i]) {
i -= 1
}
if i >= 0 {
let mut start0 = 0
while start0 <= i &&
is_ascii_whitespace(chars[start0]) {
start0 += 1
}
if start0 <= i {
raw_parts.push(substring(text, start0, i + 1))
}
}
break
}
}
let parts : Array[Value] = []
for j = raw_parts.length() - 1; j >= 0; j = j - 1 {
parts.push(Value::Str(raw_parts[j]))
}
return Ok(Value::List(parts))
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rsplit() separator must be str or None".to_string(),
),
)
}
}
"splitlines" => {
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"splitlines() takes at most 1 argument".to_string(),
),
)
}
let mut keepends_value : Value? = None
if positional.length() == 1 {
keepends_value = Some(positional[0])
}
for pair in keywords {
if pair.0 == "keepends" {
if keepends_value is Some(_) {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"splitlines() got multiple values for argument 'keepends'".to_string(),
),
)
}
keepends_value = Some(pair.1)
} else {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"splitlines() got unexpected keyword argument '" +
pair.0 +
"'",
),
)
}
}
let keepends = match keepends_value {
None => false
Some(Value::None) => false
Some(Value::Bool(value)) => value
Some(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"splitlines() keepends must be bool".to_string(),
),
)
}
let lines : Array[Value] = []
let chars = text.to_array()
let mut builder = StringBuilder()
let mut has_text = false
let mut i = 0
while i < chars.length() {
let c = chars[i]
if c == '\n' || c == '\r' {
if keepends {
builder.write_char(c)
has_text = true
}
if c == '\r' &&
i + 1 < chars.length() &&
chars[i + 1] == '\n' {
if keepends {
builder.write_char('\n')
has_text = true
}
i += 1
}
lines.push(Value::Str(builder.to_string()))
builder = StringBuilder()
has_text = false
} else {
builder.write_char(c)
has_text = true
}
i += 1
}
if has_text {
lines.push(Value::Str(builder.to_string()))
}
return Ok(Value::List(lines))
}
"format" => {
let formatted = match
format_string_with_args(
text, positional, keywords, globals, builtins, io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::Str(formatted))
}
_ => ()
}
}
Value::List(values) =>
if bound_method.function.body.length() == 0 {
if keywords.length() > 0 && bound_method.function.name != "sort" {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
match bound_method.function.name {
"append" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"append() takes exactly one argument".to_string(),
),
)
} else {
values.push(positional[0])
return Ok(Value::None)
}
"extend" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"extend() takes exactly one argument".to_string(),
),
)
} else {
let to_add : Array[Value] = []
let iterator = match
iter_value_to_iterator(positional[0], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
while true {
match iterator_next(iterator, None, globals, builtins, io) {
Ok(item) => to_add.push(item)
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
for item in to_add {
values.push(item)
}
return Ok(Value::None)
}
"insert" =>
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"insert() takes exactly two arguments".to_string(),
),
)
} else {
let idx = match index_from_value(positional[0], 0) {
Ok(v) => v
Err(err) => return Err(err)
}
let len = values.length()
let mut pos = normalize_index(idx, len)
if pos < 0 {
pos = 0
}
if pos > len {
pos = len
}
values.push(Value::None)
let mut i = len
while i > pos {
values[i] = values[i - 1]
i = i - 1
}
values[pos] = positional[1]
return Ok(Value::None)
}
"remove" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"remove() takes exactly one argument".to_string(),
),
)
} else {
let needle = positional[0]
for i = 0; i < values.length(); i = i + 1 {
if eq_value(values[i], needle) {
let _ = values.remove(i)
return Ok(Value::None)
}
}
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: list.remove(x): x not in list".to_string(),
),
)
}
"clear" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"clear() takes no arguments".to_string(),
),
)
} else {
let mut i = values.length()
while i > 0 {
i = i - 1
let _ = values.remove(i)
}
return Ok(Value::None)
}
"pop" => {
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"pop() takes at most one argument".to_string(),
),
)
}
if values.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Index,
"pop from empty list".to_string(),
),
)
}
let mut idx = values.length() - 1
if positional.length() == 1 {
let raw = match positional[0] {
Value::Int(v) =>
match bigint_to_int_checked(v) {
Ok(i) => i
Err(err) => return Err(err)
}
Value::Bool(b) => if b { 1 } else { 0 }
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"invalid index".to_string(),
),
)
}
let normalized = normalize_index(raw, values.length())
if normalized < 0 || normalized >= values.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Index,
"pop index out of range".to_string(),
),
)
}
idx = normalized
}
let item = values.remove(idx)
return Ok(item)
}
"__setitem__" =>
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__setitem__() takes exactly two arguments".to_string(),
),
)
} else {
let index_value = positional[0]
let assigned = positional[1]
match index_value {
Value::Instance(slice_inst) =>
if slice_inst.class.name == "slice" {
let start_value = match
get_named_value(slice_inst.dict, "start") {
Some(v) => v
None => Value::None
}
let end_value = match
get_named_value(slice_inst.dict, "stop") {
Some(v) => v
None => Value::None
}
let step_value = match
get_named_value(slice_inst.dict, "step") {
Some(v) => v
None => Value::None
}
let new_items = match iterable_values(assigned) {
Ok(v) => v
Err(err) => return Err(err)
}
let (start, end, step) = match
slice_params_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
if step == 1 {
let merged : Array[Value] = []
for i = 0; i < start; i = i + 1 {
merged.push(values[i])
}
for item in new_items {
merged.push(item)
}
for i = end; i < values.length(); i = i + 1 {
merged.push(values[i])
}
replace_array_values(values, merged)
return Ok(Value::None)
} else {
let indices = match
slice_indices_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
if new_items.length() != indices.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: attempt to assign sequence of size " +
new_items.length().to_string() +
" to extended slice of size " +
indices.length().to_string(),
),
)
}
for i = 0; i < indices.length(); i = i + 1 {
values[indices[i]] = new_items[i]
}
return Ok(Value::None)
}
} else {
()
}
_ => ()
}
let idx = match index_from_value(index_value, 0) {
Ok(v) => v
Err(err) => return Err(err)
}
let norm = normalize_index(idx, values.length())
if norm < 0 || norm >= values.length() {
return Err(
make_runtime_error(
RuntimeErrorKind::Index,
"list assignment index out of range".to_string(),
),
)
}
values[norm] = assigned
return Ok(Value::None)
}
"copy" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"copy() takes no arguments".to_string(),
),
)
} else {
let out : Array[Value] = []
for item in values {
out.push(item)
}
return Ok(Value::List(out))
}
"reverse" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"reverse() takes no arguments".to_string(),
),
)
} else {
if values.length() == 0 {
return Ok(Value::None)
}
let mut i = 0
let mut j = values.length() - 1
while i < j {
let tmp = values[i]
values[i] = values[j]
values[j] = tmp
i += 1
j -= 1
}
return Ok(Value::None)
}
"sort" => {
if positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"sort() takes at most 2 arguments".to_string(),
),
)
}
let mut reverse = false
let mut key_value : Value? = None
for pair in keywords {
if pair.0 == "key" {
key_value = Some(pair.1)
} else if pair.0 == "reverse" {
reverse = bool_from_value(pair.1)
} else {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"sort() got unexpected keyword arguments".to_string(),
),
)
}
}
if positional.length() >= 1 {
if key_value is Some(_) {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"sort() got multiple values for argument 'key'".to_string(),
),
)
}
key_value = Some(positional[0])
}
if positional.length() == 2 {
reverse = bool_from_value(positional[1])
}
if values.length() == 0 {
return Ok(Value::None)
}
fn lt_for_sort(
a : Value,
b : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Bool, RuntimeError] {
fn tuple_items_for_sort(v : Value) -> Array[Value]? {
match v {
Value::Tuple(items) => Some(items)
Value::Instance(inst) =>
match get_named_value(inst.dict, tuple_storage_name) {
Some(Value::Tuple(items)) => Some(items)
_ => None
}
_ => None
}
}
match (a, b) {
(Value::Str(x), Value::Str(y)) => Ok(x < y)
_ =>
match (tuple_items_for_sort(a), tuple_items_for_sort(b)) {
(Some(xs), Some(ys)) =>
match
compare_sequence_values(
xs, ys, globals, builtins, io,
) {
Ok(cmp) => Ok(cmp < 0)
Err(err) => Err(err)
}
_ =>
match (a, b) {
(Value::List(xs), Value::List(ys)) =>
match
compare_sequence_values(
xs, ys, globals, builtins, io,
) {
Ok(cmp) => Ok(cmp < 0)
Err(err) => Err(err)
}
(Value::Int(_) | Value::Float(_) | Value::Bool(_), _
)
| (
_,
Value::Int(_)
| Value::Float(_)
| Value::Bool(_),
) => {
let (_, left_num) = match number_value(a) {
Ok(v) => v
Err(err) => return Err(err)
}
let (_, right_num) = match number_value(b) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(left_num < right_num)
}
_ => {
let lt = match
get_attr_from_value(
a, "__lt__", globals, builtins, io,
) {
Ok(v) => v
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"sort() cannot compare values".to_string(),
),
)
_ => return Err(err)
}
}
let result = match
call_callable_with_env(
lt,
[b],
[],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(bool_from_value(result))
}
}
}
}
}
match key_value {
Some(key_fn) => {
let key_pairs : Array[(Value, Value)] = []
for item in values {
let key = if key_fn is Value::None {
item
} else {
match
call_callable_with_env(
key_fn,
[item],
[],
globals,
builtins,
io,
) {
Ok(value) => value
Err(err) => return Err(err)
}
}
key_pairs.push((key, item))
}
fn insertion_sort_pairs_by_key(
pairs : Array[(Value, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Unit, RuntimeError] {
let mut i = 1
while i < pairs.length() {
let item = pairs[i]
let mut j = i
while j > 0 {
let less = match
lt_for_sort(
item.0,
pairs[j - 1].0,
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
if !less {
break
}
pairs[j] = pairs[j - 1]
j -= 1
}
pairs[j] = item
i += 1
}
Ok(())
}
let mut kind_opt : SortKind? = None
let mut can_use_fast = true
for pair in key_pairs {
match sort_kind_for_sorted(pair.0) {
Some(kind) =>
match kind_opt {
None => kind_opt = Some(kind)
Some(prev) =>
if prev != kind {
can_use_fast = false
}
}
None => can_use_fast = false
}
}
let sorted_pairs = if can_use_fast {
let kind = match kind_opt {
Some(kind) => kind
None => return Ok(Value::None)
}
match stable_sort_pairs_by_key(key_pairs, kind) {
Ok(v) => v
Err(err) => return Err(err)
}
} else {
let _ = match
insertion_sort_pairs_by_key(
key_pairs, globals, builtins, io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
key_pairs
}
let sorted_items : Array[Value] = []
for pair in sorted_pairs {
sorted_items.push(pair.1)
}
let out = if reverse {
reverse_values(sorted_items)
} else {
sorted_items
}
replace_array_values(values, out)
return Ok(Value::None)
}
None => {
let mut kind_opt : SortKind? = None
let mut can_use_fast = true
for item in values {
match sort_kind_for_sorted(item) {
Some(kind) =>
match kind_opt {
None => kind_opt = Some(kind)
Some(prev) =>
if prev != kind {
can_use_fast = false
}
}
None => can_use_fast = false
}
}
let mut out = if can_use_fast {
let kind = match kind_opt {
Some(kind) => kind
None => return Ok(Value::None)
}
match stable_sort_for_sorted(values, kind) {
Ok(v) => v
Err(err) => return Err(err)
}
} else {
let out : Array[Value] = []
for item in values {
out.push(item)
}
let mut i = 1
while i < out.length() {
let item = out[i]
let mut j = i
while j > 0 {
let less = match
lt_for_sort(item, out[j - 1], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
if !less {
break
}
out[j] = out[j - 1]
j -= 1
}
out[j] = item
i += 1
}
out
}
if reverse {
out = reverse_values(out)
}
replace_array_values(values, out)
return Ok(Value::None)
}
}
}
_ => ()
}
}
Value::Tuple(values) =>
if bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
match bound_method.function.name {
"index" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"index() takes 1 to 3 arguments".to_string(),
),
)
}
let needle = positional[0]
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
values.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, values.length())
let mut end_idx = normalize_index(end, values.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > values.length() {
start_idx = values.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > values.length() {
end_idx = values.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
let mut i = start_idx
while i < end_idx {
if eq_value(values[i], needle) {
return Ok(Value::Int(@bigint.BigInt::from_int(i)))
}
i += 1
}
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: tuple.index(x): x not in tuple".to_string(),
),
)
}
_ => ()
}
}
Value::Dict(pairs) =>
if bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
match bound_method.function.name {
"get" => {
if positional.length() == 0 || positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"get() takes 1 or 2 arguments".to_string(),
),
)
}
let key = positional[0]
let default_value = if positional.length() == 2 {
positional[1]
} else {
Value::None
}
let index = match dict_find_index(pairs, key) {
Ok(v) => v
Err(err) => return Err(err)
}
return match index {
Some(i) => Ok(pairs[i].1)
None => Ok(default_value)
}
}
"pop" => {
if positional.length() == 0 || positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"pop() takes 1 or 2 arguments".to_string(),
),
)
}
let key = positional[0]
let index = match dict_find_index(pairs, key) {
Ok(v) => v
Err(err) => return Err(err)
}
if index is Some(i) {
let removed = pairs.remove(i)
return Ok(removed.1)
}
if positional.length() == 2 {
return Ok(positional[1])
}
return Err(
make_runtime_error(
RuntimeErrorKind::Key,
"key not found".to_string(),
),
)
}
"popitem" => {
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"popitem() takes no arguments".to_string(),
),
)
}
if pairs.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Key,
"popitem(): dictionary is empty".to_string(),
),
)
}
let removed = pairs.remove(pairs.length() - 1)
return Ok(Value::Tuple([removed.0, removed.1]))
}
"setdefault" => {
if positional.length() == 0 || positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"setdefault() takes 1 or 2 arguments".to_string(),
),
)
}
let key = positional[0]
let default_value = if positional.length() == 2 {
positional[1]
} else {
Value::None
}
let index = match dict_find_index(pairs, key) {
Ok(v) => v
Err(err) => return Err(err)
}
if index is Some(i) {
return Ok(pairs[i].1)
}
let _ = match dict_set_item(pairs, key, default_value) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(default_value)
}
"keys" => {
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"keys() takes no arguments".to_string(),
),
)
}
return Ok(
make_dict_view_instance("dict_keys", Value::Dict(pairs)),
)
}
"values" => {
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"values() takes no arguments".to_string(),
),
)
}
return Ok(
make_dict_view_instance("dict_values", Value::Dict(pairs)),
)
}
"items" => {
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"items() takes no arguments".to_string(),
),
)
}
return Ok(
make_dict_view_instance("dict_items", Value::Dict(pairs)),
)
}
"update" => {
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"update() takes at most one argument".to_string(),
),
)
}
if positional.length() == 0 || positional[0] is Value::None {
return Ok(Value::None)
}
let other = positional[0]
let entries : Array[(Value, Value)] = []
match other {
Value::Dict(other_pairs) =>
for pair in other_pairs {
entries.push(pair)
}
_ => {
let iterator = match
iter_value_to_iterator(other, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
while true {
let item = match
iterator_next(iterator, None, globals, builtins, io) {
Ok(v) => v
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
match item {
Value::Tuple(values) | Value::List(values) =>
if values.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"update() sequence element has wrong length".to_string(),
),
)
} else {
entries.push((values[0], values[1]))
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"update() expects (key, value) pairs".to_string(),
),
)
}
}
}
}
for entry in entries {
let key = entry.0
let value = entry.1
let _ = match dict_set_item(pairs, key, value) {
Ok(v) => v
Err(err) => return Err(err)
}
}
return Ok(Value::None)
}
"clear" => {
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"clear() takes no arguments".to_string(),
),
)
}
let mut i = pairs.length()
while i > 0 {
i = i - 1
let _ = pairs.remove(i)
}
return Ok(Value::None)
}
"copy" => {
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"copy() takes no arguments".to_string(),
),
)
}
let out_pairs : Array[(Value, Value)] = []
for pair in pairs {
out_pairs.push(pair)
}
return Ok(Value::Dict(out_pairs))
}
"__contains__" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__contains__() takes exactly one argument".to_string(),
),
)
} else {
let index = match dict_find_index(pairs, positional[0]) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::Bool(index is Some(_)))
}
"__getitem__" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__getitem__() takes exactly one argument".to_string(),
),
)
} else {
let key = positional[0]
let index = match dict_find_index(pairs, key) {
Ok(v) => v
Err(err) => return Err(err)
}
match index {
Some(i) => return Ok(pairs[i].1)
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Key,
"key not found".to_string(),
),
)
}
}
"__setitem__" =>
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__setitem__() takes exactly two arguments".to_string(),
),
)
} else {
let _ = match
dict_set_item(pairs, positional[0], positional[1]) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::None)
}
"__delitem__" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__delitem__() takes exactly one argument".to_string(),
),
)
} else {
let removed = match dict_delete_key(pairs, positional[0]) {
Ok(v) => v
Err(err) => return Err(err)
}
if !removed {
return Err(
make_runtime_error(
RuntimeErrorKind::Key,
"key not found".to_string(),
),
)
}
return Ok(Value::None)
}
"__iter__" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__iter__() takes no arguments".to_string(),
),
)
} else {
let keys : Array[Value] = []
for pair in pairs {
keys.push(pair.0)
}
return Ok(make_iterator(keys))
}
"__len__" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__len__() takes no arguments".to_string(),
),
)
} else {
return Ok(
Value::Int(@bigint.BigInt::from_int(pairs.length())),
)
}
_ => ()
}
}
Value::Set(values) =>
if bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
match bound_method.function.name {
"add" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"add() takes exactly one argument".to_string(),
),
)
} else {
let _ = match set_add_unique(values, positional[0]) {
Ok(_) => ()
Err(err) => return Err(err)
}
return Ok(Value::None)
}
"remove" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"remove() takes exactly one argument".to_string(),
),
)
} else {
let removed = match set_remove_item(values, positional[0]) {
Ok(v) => v
Err(err) => return Err(err)
}
if !removed {
return Err(
make_runtime_error(
RuntimeErrorKind::Key,
"key not found".to_string(),
),
)
}
return Ok(Value::None)
}
"discard" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"discard() takes exactly one argument".to_string(),
),
)
} else {
let _ = match set_remove_item(values, positional[0]) {
Ok(_) => ()
Err(err) => return Err(err)
}
return Ok(Value::None)
}
"pop" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"pop() takes no arguments".to_string(),
),
)
} else if values.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Key,
"pop from empty set".to_string(),
),
)
} else {
let idx = values.length() - 1
let item = values.remove(idx)
return Ok(item)
}
"clear" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"clear() takes no arguments".to_string(),
),
)
} else {
let mut i = values.length()
while i > 0 {
i = i - 1
let _ = values.remove(i)
}
return Ok(Value::None)
}
"copy" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"copy() takes no arguments".to_string(),
),
)
} else {
let out : Array[Value] = []
for item in values {
out.push(item)
}
return Ok(Value::Set(out))
}
"update" =>
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"update() takes at most one argument".to_string(),
),
)
} else if positional.length() == 0 ||
positional[0] is Value::None {
return Ok(Value::None)
} else {
let iterator = match
iter_value_to_iterator(positional[0], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
while true {
match iterator_next(iterator, None, globals, builtins, io) {
Ok(item) =>
match set_add_unique(values, item) {
Ok(_) => ()
Err(err) => return Err(err)
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
return Ok(Value::None)
}
"difference" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"difference() takes exactly one argument".to_string(),
),
)
} else {
let other_values : Array[Value] = []
let iterator = match
iter_value_to_iterator(positional[0], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
while true {
match iterator_next(iterator, None, globals, builtins, io) {
Ok(item) =>
match set_add_unique(other_values, item) {
Ok(_) => ()
Err(err) => return Err(err)
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
let out : Array[Value] = []
for item in values {
let index = match set_find_index(other_values, item) {
Ok(v) => v
Err(err) => return Err(err)
}
if index is None {
let _ = match set_add_unique(out, item) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
}
return Ok(Value::Set(out))
}
"difference_update" => {
// Remove all elements found in any of the provided iterables.
if positional.length() == 0 {
return Ok(Value::None)
}
let remove_values : Array[Value] = []
for arg in positional {
let iterator = match
iter_value_to_iterator(arg, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
while true {
match iterator_next(iterator, None, globals, builtins, io) {
Ok(item) =>
match set_add_unique(remove_values, item) {
Ok(_) => ()
Err(err) => return Err(err)
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
}
let mut i = values.length()
while i > 0 {
i = i - 1
let item = values[i]
let index = match set_find_index(remove_values, item) {
Ok(v) => v
Err(err) => return Err(err)
}
if index is Some(_) {
let _ = values.remove(i)
}
}
return Ok(Value::None)
}
"intersection" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"intersection() takes exactly one argument".to_string(),
),
)
} else {
let other_values : Array[Value] = []
let iterator = match
iter_value_to_iterator(positional[0], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
while true {
match iterator_next(iterator, None, globals, builtins, io) {
Ok(item) =>
match set_add_unique(other_values, item) {
Ok(_) => ()
Err(err) => return Err(err)
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
let out : Array[Value] = []
for item in values {
let index = match set_find_index(other_values, item) {
Ok(v) => v
Err(err) => return Err(err)
}
if index is Some(_) {
let _ = match set_add_unique(out, item) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
}
return Ok(Value::Set(out))
}
"intersection_update" => {
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"intersection_update() takes exactly one argument".to_string(),
),
)
}
let other_values : Array[Value] = []
let iterator = match
iter_value_to_iterator(positional[0], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
while true {
match iterator_next(iterator, None, globals, builtins, io) {
Ok(item) =>
match set_add_unique(other_values, item) {
Ok(_) => ()
Err(err) => return Err(err)
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
let next : Array[Value] = []
for item in values {
let index = match set_find_index(other_values, item) {
Ok(v) => v
Err(err) => return Err(err)
}
if index is Some(_) {
let _ = match set_add_unique(next, item) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
}
replace_array_values(values, next)
return Ok(Value::None)
}
"issuperset" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"issuperset() takes exactly one argument".to_string(),
),
)
} else {
let iterator = match
iter_value_to_iterator(positional[0], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut ok = true
while true {
match iterator_next(iterator, None, globals, builtins, io) {
Ok(item) =>
match set_find_index(values, item) {
Ok(Some(_)) => ()
Ok(None) => {
ok = false
break
}
Err(err) => return Err(err)
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
return Ok(Value::Bool(ok))
}
"issubset" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"issubset() takes exactly one argument".to_string(),
),
)
} else {
let other_values : Array[Value] = []
let iterator = match
iter_value_to_iterator(positional[0], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
while true {
match iterator_next(iterator, None, globals, builtins, io) {
Ok(item) =>
match set_add_unique(other_values, item) {
Ok(_) => ()
Err(err) => return Err(err)
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
let mut ok = true
for item in values {
let index = match set_find_index(other_values, item) {
Ok(v) => v
Err(err) => return Err(err)
}
if index is None {
ok = false
break
}
}
return Ok(Value::Bool(ok))
}
"__contains__" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__contains__() takes exactly one argument".to_string(),
),
)
} else {
let index = match set_find_index(values, positional[0]) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::Bool(index is Some(_)))
}
"__iter__" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__iter__() takes no arguments".to_string(),
),
)
} else {
return Ok(make_iterator(values))
}
"__len__" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"__len__() takes no arguments".to_string(),
),
)
} else {
return Ok(
Value::Int(@bigint.BigInt::from_int(values.length())),
)
}
_ => ()
}
}
Value::Bytes(values) =>
if bound_method.function.body.length() == 0 {
if keywords.length() > 0 && bound_method.function.name != "decode" {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
match bound_method.function.name {
"decode" => {
if positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"decode() takes 0 to 2 arguments".to_string(),
),
)
}
let mut encoding_value : Value = if positional.length() >= 1 {
positional[0]
} else {
Value::Str("utf-8")
}
let mut errors_value : Value? = if positional.length() == 2 {
Some(positional[1])
} else {
None
}
for kw in keywords {
if kw.0 == "encoding" {
if positional.length() >= 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"decode() got multiple values for argument 'encoding'".to_string(),
),
)
}
encoding_value = kw.1
} else if kw.0 == "errors" {
if positional.length() >= 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"decode() got multiple values for argument 'errors'".to_string(),
),
)
}
errors_value = Some(kw.1)
} else {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"decode() got an unexpected keyword argument".to_string(),
),
)
}
}
let text = match
decode_bytes_with_encoding(
"decode", values, encoding_value, errors_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::Str(text))
}
"strip" => {
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"strip() takes at most 1 argument".to_string(),
),
)
}
let strip_chars : Array[Int32] = match positional.length() {
0 => [9, 10, 11, 12, 13, 32]
_ =>
match positional[0] {
Value::None => [9, 10, 11, 12, 13, 32]
Value::Bytes(b)
| Value::ByteArray(b)
| Value::MemoryView(b) => b
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"strip() argument must be bytes-like or None".to_string(),
),
)
}
}
fn in_strip_set(ch : Int32, chars : Array[Int32]) -> Bool {
for c in chars {
if c == ch {
return true
}
}
false
}
let mut start = 0
let mut end = values.length()
while start < end && in_strip_set(values[start], strip_chars) {
start = start + 1
}
while end > start && in_strip_set(values[end - 1], strip_chars) {
end = end - 1
}
let out : Array[Int32] = []
for i = start; i < end; i = i + 1 {
out.push(values[i])
}
return Ok(Value::Bytes(out))
}
"find" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"find() takes 1 to 3 arguments".to_string(),
),
)
}
fn needle_bytes(
value : Value,
) -> Result[Array[Int32], RuntimeError] {
match value {
Value::Bytes(v)
| Value::ByteArray(v)
| Value::MemoryView(v) => Ok(v)
Value::Int(v) =>
match bigint_to_int_checked(v) {
Ok(iv) =>
if iv < 0 || iv > 255 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: byte must be in range(0, 256)".to_string(),
),
)
} else {
Ok([iv])
}
Err(err) => Err(err)
}
Value::Bool(v) => Ok([if v { 1 } else { 0 }])
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"find() expects a bytes-like object".to_string(),
),
)
}
}
let needle = match needle_bytes(positional[0]) {
Ok(v) => v
Err(err) => return Err(err)
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
values.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, values.length())
let mut end_idx = normalize_index(end, values.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > values.length() {
start_idx = values.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > values.length() {
end_idx = values.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
if needle.length() == 0 {
return Ok(Value::Int(@bigint.BigInt::from_int(start_idx)))
}
if needle.length() > end_idx - start_idx {
return Ok(Value::Int(@bigint.BigInt::from_int(-1)))
}
let max_start = end_idx - needle.length()
let mut found = -1
let mut i = start_idx
while i <= max_start {
let mut ok = true
for j = 0; j < needle.length(); j = j + 1 {
if (values[i + j] & 0xFF) != (needle[j] & 0xFF) {
ok = false
break
}
}
if ok {
found = i
break
}
i += 1
}
return Ok(Value::Int(@bigint.BigInt::from_int(found)))
}
"rfind" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rfind() takes 1 to 3 arguments".to_string(),
),
)
}
fn needle_bytes(
value : Value,
) -> Result[Array[Int32], RuntimeError] {
match value {
Value::Bytes(v)
| Value::ByteArray(v)
| Value::MemoryView(v) => Ok(v)
Value::Int(v) =>
match bigint_to_int_checked(v) {
Ok(iv) =>
if iv < 0 || iv > 255 {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: byte must be in range(0, 256)".to_string(),
),
)
} else {
Ok([iv])
}
Err(err) => Err(err)
}
Value::Bool(v) => Ok([if v { 1 } else { 0 }])
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rfind() expects a bytes-like object".to_string(),
),
)
}
}
let needle = match needle_bytes(positional[0]) {
Ok(v) => v
Err(err) => return Err(err)
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
values.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, values.length())
let mut end_idx = normalize_index(end, values.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > values.length() {
start_idx = values.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > values.length() {
end_idx = values.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
if needle.length() == 0 {
return Ok(Value::Int(@bigint.BigInt::from_int(end_idx)))
}
if needle.length() > end_idx - start_idx {
return Ok(Value::Int(@bigint.BigInt::from_int(-1)))
}
let mut found = -1
let mut i = end_idx - needle.length()
while i >= start_idx {
let mut ok = true
for j = 0; j < needle.length(); j = j + 1 {
if (values[i + j] & 0xFF) != (needle[j] & 0xFF) {
ok = false
break
}
}
if ok {
found = i
break
}
i -= 1
}
return Ok(Value::Int(@bigint.BigInt::from_int(found)))
}
"hex" => {
if positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"hex() takes 0 to 2 arguments".to_string(),
),
)
}
let sep = if positional.length() >= 1 {
match positional[0] {
Value::None => "".to_string()
Value::Str(text) => text
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"hex() sep must be str".to_string(),
),
)
}
} else {
"".to_string()
}
let bytes_per_sep = if positional.length() == 2 {
match positional[1] {
Value::Int(v) =>
match bigint_to_int_checked(v) {
Ok(iv) => iv
Err(err) => return Err(err)
}
Value::Bool(v) => if v { 1 } else { 0 }
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"hex() bytes_per_sep must be int".to_string(),
),
)
}
} else {
1
}
if bytes_per_sep == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: bytes_per_sep must not be zero".to_string(),
),
)
}
let hex = "0123456789abcdef".to_array()
let buf = StringBuilder()
if sep.length() == 0 {
for b in values {
let v = b & 0xFF
buf.write_char(hex[(v >> 4) & 0xF])
buf.write_char(hex[v & 0xF])
}
return Ok(Value::Str(buf.to_string()))
}
let bps = bytes_per_sep
let mut first_group = 0
let mut group_len = bps
if bps < 0 {
group_len = -bps
let rem = values.length() % group_len
first_group = if rem == 0 { group_len } else { rem }
}
for idx = 0; idx < values.length(); idx = idx + 1 {
if idx > 0 {
if bps > 0 {
if idx % bps == 0 {
buf.write_string(sep)
}
} else if idx == first_group ||
(
idx > first_group &&
(idx - first_group) % group_len == 0
) {
buf.write_string(sep)
}
}
let v = values[idx] & 0xFF
buf.write_char(hex[(v >> 4) & 0xF])
buf.write_char(hex[v & 0xF])
}
return Ok(Value::Str(buf.to_string()))
}
"startswith" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"startswith() takes 1 to 3 arguments".to_string(),
),
)
}
fn prefix_bytes(
value : Value,
) -> Result[Array[Int32], RuntimeError] {
match value {
Value::Bytes(v)
| Value::ByteArray(v)
| Value::MemoryView(v) => Ok(v)
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"startswith() expects a bytes-like prefix".to_string(),
),
)
}
}
let prefixes : Array[Array[Int32]] = []
match positional[0] {
Value::Tuple(items) =>
for item in items {
let prefix = match prefix_bytes(item) {
Ok(v) => v
Err(err) => return Err(err)
}
prefixes.push(prefix)
}
_ => {
let prefix = match prefix_bytes(positional[0]) {
Ok(v) => v
Err(err) => return Err(err)
}
prefixes.push(prefix)
}
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
values.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, values.length())
let mut end_idx = normalize_index(end, values.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > values.length() {
start_idx = values.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > values.length() {
end_idx = values.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
let available = end_idx - start_idx
let mut matched = false
for prefix in prefixes {
if prefix.length() <= available {
let mut ok = true
for j = 0; j < prefix.length(); j = j + 1 {
if values[start_idx + j] != prefix[j] {
ok = false
break
}
}
if ok {
matched = true
break
}
}
}
return Ok(Value::Bool(matched))
}
"endswith" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"endswith() takes 1 to 3 arguments".to_string(),
),
)
}
fn suffix_bytes(
value : Value,
) -> Result[Array[Int32], RuntimeError] {
match value {
Value::Bytes(v)
| Value::ByteArray(v)
| Value::MemoryView(v) => Ok(v)
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"endswith() expects a bytes-like suffix".to_string(),
),
)
}
}
let suffixes : Array[Array[Int32]] = []
match positional[0] {
Value::Tuple(items) =>
for item in items {
let suffix = match suffix_bytes(item) {
Ok(v) => v
Err(err) => return Err(err)
}
suffixes.push(suffix)
}
_ => {
let suffix = match suffix_bytes(positional[0]) {
Ok(v) => v
Err(err) => return Err(err)
}
suffixes.push(suffix)
}
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
values.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, values.length())
let mut end_idx = normalize_index(end, values.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > values.length() {
start_idx = values.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > values.length() {
end_idx = values.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
let available = end_idx - start_idx
let mut matched = false
for suffix in suffixes {
if suffix.length() <= available {
let offset = end_idx - suffix.length()
let mut ok = true
for j = 0; j < suffix.length(); j = j + 1 {
if values[offset + j] != suffix[j] {
ok = false
break
}
}
if ok {
matched = true
break
}
}
}
return Ok(Value::Bool(matched))
}
_ => ()
}
}
Value::MemoryView(values) =>
if bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
match bound_method.function.name {
"decode" => {
if positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"decode() takes 0 to 2 arguments".to_string(),
),
)
}
let encoding_value = if positional.length() >= 1 {
positional[0]
} else {
Value::Str("utf-8")
}
let errors_value = if positional.length() == 2 {
Some(positional[1])
} else {
None
}
let text = match
decode_bytes_with_encoding(
"decode", values, encoding_value, errors_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::Str(text))
}
"startswith" => {
if positional.length() == 0 || positional.length() > 3 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"startswith() takes 1 to 3 arguments".to_string(),
),
)
}
fn prefix_bytes(
value : Value,
) -> Result[Array[Int32], RuntimeError] {
match value {
Value::Bytes(v)
| Value::ByteArray(v)
| Value::MemoryView(v) => Ok(v)
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"startswith() expects a bytes-like prefix".to_string(),
),
)
}
}
let prefixes : Array[Array[Int32]] = []
match positional[0] {
Value::Tuple(items) =>
for item in items {
let prefix = match prefix_bytes(item) {
Ok(v) => v
Err(err) => return Err(err)
}
prefixes.push(prefix)
}
_ => {
let prefix = match prefix_bytes(positional[0]) {
Ok(v) => v
Err(err) => return Err(err)
}
prefixes.push(prefix)
}
}
let start = match
index_from_value(
if positional.length() >= 2 {
positional[1]
} else {
Value::None
},
0,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let end = match
index_from_value(
if positional.length() == 3 {
positional[2]
} else {
Value::None
},
values.length(),
) {
Ok(v) => v
Err(err) => return Err(err)
}
let mut start_idx = normalize_index(start, values.length())
let mut end_idx = normalize_index(end, values.length())
if start_idx < 0 {
start_idx = 0
}
if start_idx > values.length() {
start_idx = values.length()
}
if end_idx < 0 {
end_idx = 0
}
if end_idx > values.length() {
end_idx = values.length()
}
if end_idx < start_idx {
end_idx = start_idx
}
let available = end_idx - start_idx
let mut matched = false
for prefix in prefixes {
if prefix.length() <= available {
let mut ok = true
for j = 0; j < prefix.length(); j = j + 1 {
if values[start_idx + j] != prefix[j] {
ok = false
break
}
}
if ok {
matched = true
break
}
}
}
return Ok(Value::Bool(matched))
}
_ => ()
}
}
Value::ByteArray(values) =>
if bound_method.function.body.length() == 0 {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
bound_method.function.name +
"() got unexpected keyword arguments".to_string(),
),
)
}
match bound_method.function.name {
"decode" => {
if positional.length() > 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"decode() takes 0 to 2 arguments".to_string(),
),
)
}
let encoding_value = if positional.length() >= 1 {
positional[0]
} else {
Value::Str("utf-8")
}
let errors_value = if positional.length() == 2 {
Some(positional[1])
} else {
None
}
let text = match
decode_bytes_with_encoding(
"decode", values, encoding_value, errors_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
return Ok(Value::Str(text))
}
"append" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"append() takes exactly one argument".to_string(),
),
)
} else {
let b = match byte_from_value("append", positional[0]) {
Ok(v) => v
Err(err) => return Err(err)
}
values.push(b & 0xFF)
return Ok(Value::None)
}
"extend" =>
if positional.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"extend() takes exactly one argument".to_string(),
),
)
} else {
let other = positional[0]
match other {
Value::Bytes(bytes)
| Value::ByteArray(bytes)
| Value::MemoryView(bytes) =>
for b in bytes {
values.push(b & 0xFF)
}
_ => {
let iterator = match
iter_value_to_iterator(other, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
while true {
match
iterator_next(iterator, None, globals, builtins, io) {
Ok(item) => {
let b = match byte_from_value("extend", item) {
Ok(v) => v
Err(err) => return Err(err)
}
values.push(b)
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
}
}
return Ok(Value::None)
}
"copy" =>
if positional.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"copy() takes no arguments".to_string(),
),
)
} else {
let out : Array[Int32] = []
for item in values {
out.push(item)
}
return Ok(Value::ByteArray(out))
}
_ => ()
}
}
_ => ()
}
let args : Array[Value] = [bound_method.self]
for item in positional {
args.push(item)
}
if bound_method.function.body.length() == 0 {
match
eval_builtin_call(
bound_method.function.name,
args,
keywords,
[],
globals,
builtins,
io,
) {
Ok(Some(value)) => return Ok(value)
Ok(None) => ()
Err(err) => return Err(err)
}
}
if bound_method.function.is_async {
if bound_method.function.is_generator {
return (eval_generator_with_kwargs_ref.val)(
bound_method.function,
args,
keywords,
globals,
builtins,
io,
)
}
return (eval_function_with_kwargs_ref.val)(
bound_method.function,
args,
keywords,
globals,
builtins,
io,
)
}
if bound_method.function.is_generator {
return (eval_generator_with_kwargs_ref.val)(
bound_method.function,
args,
keywords,
globals,
builtins,
io,
)
}
(eval_function_with_kwargs_ref.val)(
bound_method.function,
args,
keywords,
globals,
builtins,
io,
)
}
Value::Class(klass) => {
if klass.name == "rangeiter" {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"cannot create 'range_iterator' instances".to_string(),
),
)
}
let is_builtin = match get_named_value(klass.dict, "__module__") {
Some(Value::Str(module_name)) =>
module_name == "builtins" && is_builtin_class_name(klass.name)
_ => false
}
if is_builtin {
match
eval_builtin_call(
klass.name,
positional,
keywords,
[],
globals,
builtins,
io,
) {
Ok(Some(value)) => return Ok(value)
Ok(None) => ()
Err(err) => return Err(err)
}
}
let abstract_values_opt = match
get_named_value(klass.dict, "__abstractmethods__") {
Some(Value::Set(values)) => Some(values)
Some(Value::Tuple(values)) => Some(values)
_ => None
}
if abstract_values_opt is Some(values) {
let names : Array[String] = []
for item in values {
match item {
Value::Str(name) => names.push(name)
_ => ()
}
}
if names.length() > 0 {
for i = 0; i < names.length(); i = i + 1 {
let mut best = i
for j = i + 1; j < names.length(); j = j + 1 {
if cmp_string_for_sorted(names[j], names[best]) < 0 {
best = j
}
}
if best != i {
let tmp = names[i]
names[i] = names[best]
names[best] = tmp
}
}
let builder = StringBuilder()
for i = 0; i < names.length(); i = i + 1 {
if i > 0 {
builder.write_string(", ")
}
builder.write_string("'" + names[i] + "'")
}
let label = if names.length() == 1 { "method" } else { "methods" }
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"class " +
klass.name +
" without an implementation for abstract " +
label +
" " +
builder.to_string(),
),
)
}
}
if klass.name == "BaseExceptionGroup" || klass.name == "ExceptionGroup" {
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
klass.name + "() requires a message and exceptions".to_string(),
),
)
}
let message = positional[0]
let raw_exceptions = match positional[1] {
Value::List(values) => values
Value::Tuple(values) => values
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
klass.name + " exceptions must be a list".to_string(),
),
)
}
if raw_exceptions.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
klass.name + " requires at least one exception".to_string(),
),
)
}
let mut require_exception = false
if klass.name == "ExceptionGroup" {
require_exception = true
}
let exceptions : Array[Value] = []
for exc in raw_exceptions {
let exc_value = match
ensure_exception_instance_from_value(exc, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
let inst = match exc_value {
Value::Instance(inst) => inst
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"exceptions must derive from BaseException".to_string(),
),
)
}
if require_exception {
let mro = match class_mro(inst.class) {
Ok(value) => value
Err(err) => return Err(err)
}
let mut ok = false
for cls in mro {
if cls.name == "Exception" {
ok = true
break
}
}
if !ok {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"ExceptionGroup exceptions must derive from Exception".to_string(),
),
)
}
}
exceptions.push(Value::Instance(inst))
}
let exception_tuple : Array[Value] = []
for item in exceptions {
exception_tuple.push(item)
}
let dict : Array[(String, Value)] = []
dict.push(("hashvalue", Value::Int(fresh_object_hashvalue())))
dict.push(("__traceback__", Value::None))
dict.push(
("args", Value::Tuple([message, Value::Tuple(exception_tuple)])),
)
dict.push(("__cause__", Value::None))
dict.push(("__context__", Value::None))
dict.push(("__suppress_context__", Value::Bool(false)))
dict.push(("message", message))
dict.push(("exceptions", Value::List(exceptions)))
let inst_value = Value::Instance(InstanceValue::{ class: klass, dict, })
gc_register_instance(inst_value)
return Ok(inst_value)
}
// `types.TracebackType(tb_next, frame, lasti, lineno)` is used by the
// CPython stdlib tests to synthesize traceback objects.
if klass.name == "traceback" {
let _ = match ensure_no_keywords("traceback", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 4 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"traceback() takes exactly 4 arguments".to_string(),
),
)
}
let tb_next = positional[0]
let tb_frame = positional[1]
let tb_lasti = positional[2]
let tb_lineno = positional[3]
let dict : Array[(String, Value)] = []
dict.push(("hashvalue", Value::Int(fresh_object_hashvalue())))
dict.push(("tb_next", tb_next))
dict.push(("tb_frame", tb_frame))
dict.push(("tb_lasti", tb_lasti))
dict.push(("tb_lineno", tb_lineno))
// MoonPython extension: keep a column value for PEP 657 helpers.
dict.push(("tb_colno", Value::Int(1N)))
let inst_value = Value::Instance(InstanceValue::{ class: klass, dict, })
gc_register_instance(inst_value)
return Ok(inst_value)
}
// Minimal exception instance payload for any BaseException subclass.
// This is relied upon by the CPython stdlib (e.g. abc/unittest) for custom
// exception classes like `class CustomError(Exception): ...`.
if klass.name == "module" {
// `types.ModuleType(name, doc=None)` expects a module object with at
// least `__name__` present.
let name = match positional {
[Value::Str(text), ..] => text
_ => "".to_string()
}
let doc_value = match positional {
[_, Value::None, ..] => Value::None
[_, v, ..] => v
_ => Value::None
}
let dict : Array[(String, Value)] = []
dict.push(("hashvalue", Value::Int(fresh_object_hashvalue())))
dict.push(("__name__", Value::Str(name)))
dict.push(("__doc__", doc_value))
// Let importlib populate `__package__`; defaulting to "" prevents
// `_init_module_attrs()` from setting it (it only checks for None).
dict.push(("__package__", Value::None))
dict.push(("__loader__", Value::None))
dict.push(("__spec__", Value::None))
let inst_value = Value::Instance(InstanceValue::{ class: klass, dict, })
gc_register_instance(inst_value)
return Ok(inst_value)
}
let ok = match is_base_exception_class(klass) {
Ok(v) => v
Err(err) => return Err(err)
}
if ok {
let dict : Array[(String, Value)] = []
dict.push(("hashvalue", Value::Int(fresh_object_hashvalue())))
dict.push(("__traceback__", Value::None))
dict.push(("args", Value::Tuple(positional)))
dict.push(("__cause__", Value::None))
dict.push(("__context__", Value::None))
dict.push(("__suppress_context__", Value::Bool(false)))
if positional.length() > 0 {
dict.push(("message", positional[0]))
}
if klass.name == "NameError" {
let mut name_value : Value = Value::None
for kw in keywords {
if kw.0 == "name" {
name_value = kw.1
}
}
dict.push(("name", name_value))
}
if klass.name == "AttributeError" {
let mut name_value : Value = Value::None
let mut obj_value : Value = Value::None
for kw in keywords {
if kw.0 == "name" {
name_value = kw.1
} else if kw.0 == "obj" {
obj_value = kw.1
}
}
dict.push(("name", name_value))
dict.push(("obj", obj_value))
}
if klass.name == "SyntaxError" ||
klass.name == "IndentationError" ||
klass.name == "TabError" {
// CPython's traceback helpers expect these attributes on all
// SyntaxError subclasses.
let mut msg_value : Value = Value::None
if positional.length() > 0 {
msg_value = positional[0]
}
dict.push(("msg", msg_value))
dict.push(("filename", Value::None))
dict.push(("lineno", Value::None))
dict.push(("offset", Value::None))
dict.push(("text", Value::None))
dict.push(("end_lineno", Value::None))
dict.push(("end_offset", Value::None))
if positional.length() > 1 {
match positional[1] {
Value::Tuple(items) | Value::List(items) => {
if items.length() > 0 {
set_named_value(dict, "filename", items[0])
}
if items.length() > 1 {
set_named_value(dict, "lineno", items[1])
}
if items.length() > 2 {
set_named_value(dict, "offset", items[2])
}
if items.length() > 3 {
set_named_value(dict, "text", items[3])
}
if items.length() > 4 {
set_named_value(dict, "end_lineno", items[4])
}
if items.length() > 5 {
set_named_value(dict, "end_offset", items[5])
}
}
_ => ()
}
}
}
if klass.name == "SystemExit" {
let code = if positional.length() > 0 {
positional[0]
} else {
Value::None
}
dict.push(("code", code))
}
if klass.name == "StopIteration" || klass.name == "StopAsyncIteration" {
let value = if positional.length() > 0 {
positional[0]
} else {
Value::None
}
dict.push(("value", value))
}
if klass.name == "ImportError" || klass.name == "ModuleNotFoundError" {
// CPython's ImportError exposes `.name` and `.path`. importlib relies on
// `.name` when probing for optional frozen modules.
let mut name_value : Value = Value::None
let mut path_value : Value = Value::None
let mut name_from_value : Value = Value::None
let mut obj_value : Value = Value::None
for kw in keywords {
if kw.0 == "name" {
name_value = kw.1
} else if kw.0 == "path" {
path_value = kw.1
} else if kw.0 == "name_from" {
name_from_value = kw.1
} else if kw.0 == "obj" {
obj_value = kw.1
}
}
if positional.length() > 0 {
match positional[0] {
Value::Str(msg) =>
if name_value is Value::None &&
msg.has_prefix("No module named") {
match msg.find("'") {
Some(pos0) => {
let rest = substring(msg, pos0 + 1, msg.length())
match rest.find("'") {
Some(pos1) =>
name_value = Value::Str(substring(rest, 0, pos1))
None => ()
}
}
None => ()
}
}
_ => ()
}
}
dict.push(("name", name_value))
dict.push(("path", path_value))
dict.push(("name_from", name_from_value))
dict.push(("obj", obj_value))
}
let inst_value = Value::Instance(InstanceValue::{ class: klass, dict, })
gc_register_instance(inst_value)
// Respect custom exception __init__ implementations (e.g. regrtest's
// TestFailedWithDetails sets extra fields like `.errors`).
let init = match lookup_class_attr(klass, "__init__") {
Ok(v) => v
Err(err) => return Err(err)
}
match init {
Some(Value::Function(func)) =>
// Avoid running BaseException.__init__ bodies when we don't model
// them; custom subclasses still get their Python __init__ executed.
if func.body.length() > 0 {
let args : Array[Value] = [inst_value]
for item in positional {
args.push(item)
}
let _ = match
(eval_function_with_kwargs_ref.val)(
func, args, keywords, globals, builtins, io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
}
_ => ()
}
return Ok(inst_value)
}
let new_value = match lookup_class_attr(klass, "__new__") {
Ok(value) => value
Err(err) => return Err(err)
}
let mut inst_value_opt : Value? = None
match new_value {
Some(value) => {
let args : Array[Value] = [Value::Class(klass)]
for item in positional {
args.push(item)
}
let created = match
call_callable_with_env(value, args, keywords, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
gc_register_instance(created)
inst_value_opt = Some(created)
}
None => ()
}
let init = match lookup_class_attr(klass, "__init__") {
Ok(value) => value
Err(err) => return Err(err)
}
let inst_value = match inst_value_opt {
Some(value) => value
None => {
let inst_dict : Array[(String, Value)] = []
// Instances default to identity semantics (and are hashable) unless a
// custom __eq__/__hash__ is provided. Track a unique per-object id.
inst_dict.push(("hashvalue", Value::Int(fresh_object_hashvalue())))
if class_includes_list(klass) {
inst_dict.push((list_storage_name, Value::List([])))
}
if class_includes_dict(klass) {
inst_dict.push((dict_storage_name, Value::Dict([])))
}
if class_includes_set(klass) || class_includes_frozenset(klass) {
inst_dict.push((set_storage_name, Value::Set([])))
}
if class_includes_tuple(klass) {
inst_dict.push((tuple_storage_name, Value::Tuple([])))
}
let inst_value = Value::Instance(InstanceValue::{
class: klass,
dict: inst_dict,
})
gc_register_instance(inst_value)
inst_value
}
}
match inst_value {
Value::Instance(inst) => {
match get_named_value(inst.dict, list_storage_name) {
Some(Value::List(values)) =>
// List subclasses (e.g. `class L(list): ...`) currently don't inherit
// `list.__init__` from the builtins class table, so initialize their
// backing storage here unless they define a custom `__init__`.
if get_named_value(klass.dict, "__init__") is None {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"list() takes no keyword arguments".to_string(),
),
)
}
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"list expected at most 1 argument, got " +
positional.length().to_string(),
),
)
}
if positional.length() == 1 {
let items = match
collect_items_from_iterable(
positional[0],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
replace_array_values(values, items)
}
}
_ => ()
}
match get_named_value(inst.dict, dict_storage_name) {
Some(Value::Dict(pairs)) =>
// Dict subclasses (e.g. `class D(dict): ...`) currently don't inherit
// `dict.__init__` from the builtins class table, so populate their
// backing storage here unless they define a custom `__init__`.
if get_named_value(klass.dict, "__init__") is None {
fn replace_pairs(
pairs : Array[(Value, Value)],
next : Array[(Value, Value)],
) -> Unit {
let mut i = pairs.length()
while i > 0 {
i = i - 1
let _ = pairs.remove(i)
}
for item in next {
pairs.push(item)
}
}
let created = match
builtin_dict(positional, keywords, [], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
match created {
Value::Dict(next_pairs) => replace_pairs(pairs, next_pairs)
_ => ()
}
}
_ => ()
}
match get_named_value(inst.dict, set_storage_name) {
Some(Value::Set(values)) =>
// Set/frozenset subclasses currently don't inherit `set.__init__`
// / `frozenset.__init__`; initialize storage here unless they
// define a custom `__init__`.
if get_named_value(klass.dict, "__init__") is None {
if keywords.length() > 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"set() takes no keyword arguments".to_string(),
),
)
}
if positional.length() > 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"set expected at most 1 argument, got " +
positional.length().to_string(),
),
)
}
let items = if positional.length() == 1 {
match
collect_items_from_iterable(
positional[0],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
} else {
([] : Array[Value])
}
let unique : Array[Value] = []
for item in items {
let _ = match set_add_unique(unique, item) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
replace_array_values(values, unique)
}
_ => ()
}
match init {
Some(Value::Function(func)) =>
// CPython calls __init__ if __new__ returned an instance of the
// requested class (including subclasses). For factory-style
// classes like pathlib.Path, __new__ returns a PosixPath/WindowsPath
// instance, and the inherited __init__ must still run.
if class_has_base_name(inst.class, klass.name) {
let args : Array[Value] = [Value::Instance(inst)]
for item in positional {
args.push(item)
}
let _ = match
call_callable_with_env(
Value::Function(func),
args,
keywords,
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
}
_ => ()
}
Ok(Value::Instance(inst))
}
_ => Ok(inst_value)
}
}
Value::Instance(inst) => {
if inst.class.name == "staticmethod" {
let func = match get_named_value(inst.dict, "func") {
Some(v) => v
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"staticmethod is missing __func__".to_string(),
),
)
}
return call_callable_with_env(
func, positional, keywords, globals, builtins, io,
)
}
if inst.class.name == "weakref" || inst.class.name == "KeyedRef" {
if positional.length() != 0 || keywords.length() != 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"weakref() takes no arguments".to_string(),
),
)
}
return match get_named_value(inst.dict, "value") {
Some(v) => Ok(v)
None => Ok(Value::None)
}
}
// Callable objects: obj(...) dispatches to obj.__call__(...)
let call_attr = match lookup_class_attr(inst.class, "__call__") {
Ok(v) => v
Err(err) => return Err(err)
}
match call_attr {
Some(_) => {
// Reuse the regular attribute lookup path so we also get descriptor
// behavior and method binding.
let bound = match
get_attr_raw_from_value(callee, "__call__", globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
call_callable_with_env(
bound, positional, keywords, globals, builtins, io,
)
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
not_callable_message(callee),
),
)
}
}
_ =>
Err(
make_runtime_error(RuntimeErrorKind::Type, not_callable_message(callee)),
)
}
}