///|
/// Expression evaluation helpers.
///|
fn lookup_name_value(
name : String,
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
) -> Result[Value, RuntimeError] {
fn read_local_cell() -> Result[Value, RuntimeError] {
match get_local_value(locals, name) {
Some(v) =>
if is_cell_value(v) {
match cell_get_value(v) {
Some(value) => Ok(value)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnboundLocalError: local variable '" +
name +
"' referenced before assignment",
),
)
}
} else {
Ok(v)
}
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Name,
"name '" + name + "' is not defined",
),
)
}
}
fn read_closure_cell(cell : Value) -> Result[Value, RuntimeError] {
match cell_get_value(cell) {
Some(value) => Ok(value)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Name,
"free variable '" +
name +
"' referenced before assignment in enclosing scope",
),
)
}
}
if scope_name_is_global(name) {
match get_global_value(globals, name) {
Some(v) => Ok(v)
None =>
match get_global_value(builtins, name) {
Some(v) => Ok(v)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Name,
"name '" + name + "' is not defined",
),
)
}
}
} else if scope_name_is_nonlocal(name) {
match lookup_closure_cell(name) {
Some(cell) => read_closure_cell(cell)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Name,
"name '" + name + "' is not defined",
),
)
}
} else {
match get_local_value(locals, name) {
Some(_) => read_local_cell()
None =>
match lookup_closure_cell(name) {
Some(cell) => read_closure_cell(cell)
None =>
match get_global_value(globals, name) {
Some(v) => Ok(v)
None =>
match get_global_value(builtins, name) {
Some(v) => Ok(v)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Name,
"name '" + name + "' is not defined",
),
)
}
}
}
}
}
}
///|
fn eval_expr_with_env(
expr : Expr,
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
match expr {
Expr::WithSpan(span~, expr~) => {
// Expression-level spans are used to provide better caret ranges in
// tracebacks (PEP 657 tests in the CPython stdlib).
push_active_span(span)
let result = eval_expr_with_env(expr, locals, globals, builtins, io)
pop_active_span()
result
}
Expr::Literal(literal) =>
match literal {
Literal::None => Ok(Value::None)
Literal::Bool(value) => Ok(Value::Bool(value))
Literal::Int(value) => Ok(Value::Int(value))
Literal::Float(value) => Ok(Value::Float(value))
Literal::Complex(real, imag) => Ok(Value::Complex(real, imag))
Literal::Str(value) => Ok(Value::Str(value))
Literal::Bytes(value) => Ok(Value::Bytes(value))
}
Expr::Name(name) => lookup_name_value(name, locals, globals, builtins)
Expr::FString(text) =>
match eval_fstring_text(text, locals, globals, builtins, io) {
Ok(value) => Ok(Value::Str(value))
Err(err) => Err(err)
}
Expr::NamedExpr(name~, value~) => {
let evaluated = match
eval_expr_with_env(value, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
set_scoped_value(locals, globals, name, evaluated)
Ok(evaluated)
}
Expr::Await(expr) =>
if !coroutine_is_active() {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: 'await' outside async function".to_string(),
),
)
} else {
let awaitable = match
eval_expr_with_env(expr, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
match awaitable {
Value::Instance(inst) =>
if inst.class.name == "coroutine" {
coroutine_await(awaitable)
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" +
type_name_from_value(awaitable) +
"' object can't be used in 'await' expression",
),
)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" +
type_name_from_value(awaitable) +
"' object can't be used in 'await' expression",
),
)
}
}
Expr::Yield(_) | Expr::YieldFrom(_) =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: 'yield' outside function".to_string(),
),
)
Expr::List(items) => {
let values : Array[Value] = []
for item in items {
match item {
Expr::Starred(value~) => {
let iter_value = match
eval_expr_with_env(value, locals, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
let items = match iterable_values(iter_value) {
Ok(v) => v
Err(err) => return Err(err)
}
for v in items {
values.push(v)
}
}
_ => {
let value = match
eval_expr_with_env(item, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
values.push(value)
}
}
}
Ok(Value::List(values))
}
Expr::ListComp(elt~, clauses~, filters~) => {
let mut has_async = false
for clause in clauses {
if clause.is_async {
has_async = true
break
}
}
if has_async && !coroutine_is_active() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: 'async for' outside async function".to_string(),
),
)
}
let comp_locals = clone_locals(locals)
let adjusted_clauses : Array[CompClause] = []
if clauses.length() > 0 {
let first_iter_value = match
eval_expr_with_env(clauses[0].iter, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let iter_name = fresh_genexp_local(comp_locals)
set_local_value(comp_locals, iter_name, first_iter_value)
adjusted_clauses.push(CompClause::{
is_async: clauses[0].is_async,
targets: clauses[0].targets,
target: clauses[0].target,
filters: clauses[0].filters,
iter: Expr::Name(iter_name),
})
for i = 1; i < clauses.length(); i = i + 1 {
adjusted_clauses.push(clauses[i])
}
}
let clauses = if clauses.length() > 0 {
adjusted_clauses
} else {
clauses
}
for clause in clauses {
for name in clause.targets {
shadow_local_cell(comp_locals, name, Value::None)
}
}
let values : Array[Value] = []
scope_decls_stack.val.push(comprehension_scope_decls(clauses))
let result = eval_list_comp_walk(
0, elt, clauses, filters, comp_locals, globals, builtins, io, values,
)
pop_scope_decls()
match result {
Ok(_) => ()
Err(err) => return Err(err)
}
Ok(Value::List(values))
}
Expr::GenExp(elt~, clauses~, filters~) => {
fn expr_contains_await(expr : Expr) -> Bool {
expr_has_await(expr)
}
let gen_locals = clone_locals(locals)
let scope = comprehension_scope_decls(clauses)
let mut has_async_clause = false
for clause in clauses {
if clause.is_async {
has_async_clause = true
}
for name in clause.targets {
shadow_local_cell(gen_locals, name, Value::None)
}
}
let mut has_await = expr_contains_await(elt)
if !has_await {
for clause in clauses {
if expr_contains_await(clause.iter) {
has_await = true
break
}
}
}
if !has_await {
for filter in filters {
if expr_contains_await(filter) {
has_await = true
break
}
}
}
let is_async_genexp = has_async_clause || has_await
if is_async_genexp {
if clauses.length() == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: invalid async generator expression".to_string(),
),
)
}
let first_iter_value = match
eval_expr_with_env(clauses[0].iter, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let first_iterator = if clauses[0].is_async {
let aiter_value = match
get_attr_from_value(
first_iter_value, "__aiter__", globals, builtins, io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
match
call_callable_with_env(aiter_value, [], [], globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
} else {
match
iter_value_to_iterator(first_iter_value, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
}
let iter_name = fresh_genexp_local(gen_locals)
set_local_value(gen_locals, iter_name, first_iterator)
let adjusted_clauses : Array[CompClause] = []
adjusted_clauses.push(CompClause::{
is_async: clauses[0].is_async,
targets: clauses[0].targets,
target: clauses[0].target,
filters: clauses[0].filters,
iter: Expr::Name(iter_name),
})
for i = 1; i < clauses.length(); i = i + 1 {
adjusted_clauses.push(clauses[i])
}
return Ok(
async_genexp_new(
elt, adjusted_clauses, filters, gen_locals, globals, builtins, io,
),
)
}
if clauses.length() == 0 {
let body = genexp_to_generator_body(elt, clauses, filters)
let filename = current_traceback_filename()
return Ok(
generator_new(
body,
gen_locals,
globals,
builtins,
io,
scope.globals,
scope.nonlocals,
current_closure_env(),
"".to_string(),
filename,
),
)
}
let first_iter_value = match
eval_expr_with_env(clauses[0].iter, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let first_iterator = match
iter_value_to_iterator(first_iter_value, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let iter_name = fresh_genexp_local(gen_locals)
set_local_value(gen_locals, iter_name, first_iterator)
let adjusted_clauses : Array[CompClause] = []
adjusted_clauses.push(CompClause::{
is_async: false,
targets: clauses[0].targets,
target: clauses[0].target,
filters: clauses[0].filters,
iter: Expr::Name(iter_name),
})
for i = 1; i < clauses.length(); i = i + 1 {
adjusted_clauses.push(clauses[i])
}
let body = genexp_to_generator_body(elt, adjusted_clauses, filters)
let filename = current_traceback_filename()
Ok(
generator_new(
body,
gen_locals,
globals,
builtins,
io,
scope.globals,
scope.nonlocals,
current_closure_env(),
"".to_string(),
filename,
),
)
}
Expr::SetComp(elt~, clauses~, filters~) => {
let mut has_async = false
for clause in clauses {
if clause.is_async {
has_async = true
break
}
}
if has_async && !coroutine_is_active() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: 'async for' outside async function".to_string(),
),
)
}
let comp_locals = clone_locals(locals)
let adjusted_clauses : Array[CompClause] = []
if clauses.length() > 0 {
let first_iter_value = match
eval_expr_with_env(clauses[0].iter, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let iter_name = fresh_genexp_local(comp_locals)
set_local_value(comp_locals, iter_name, first_iter_value)
adjusted_clauses.push(CompClause::{
is_async: clauses[0].is_async,
targets: clauses[0].targets,
target: clauses[0].target,
filters: clauses[0].filters,
iter: Expr::Name(iter_name),
})
for i = 1; i < clauses.length(); i = i + 1 {
adjusted_clauses.push(clauses[i])
}
}
let clauses = if clauses.length() > 0 {
adjusted_clauses
} else {
clauses
}
for clause in clauses {
for name in clause.targets {
shadow_local_cell(comp_locals, name, Value::None)
}
}
let values : Array[Value] = []
scope_decls_stack.val.push(comprehension_scope_decls(clauses))
let result = eval_list_comp_walk(
0, elt, clauses, filters, comp_locals, globals, builtins, io, values,
)
pop_scope_decls()
match result {
Ok(_) => ()
Err(err) => return Err(err)
}
let unique : Array[Value] = []
for item in values {
let _ = match set_add_unique(unique, item) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
Ok(Value::Set(unique))
}
Expr::DictComp(key~, value~, clauses~, filters~) => {
let mut has_async = false
for clause in clauses {
if clause.is_async {
has_async = true
break
}
}
if has_async && !coroutine_is_active() {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"SyntaxError: 'async for' outside async function".to_string(),
),
)
}
let comp_locals = clone_locals(locals)
let adjusted_clauses : Array[CompClause] = []
if clauses.length() > 0 {
let first_iter_value = match
eval_expr_with_env(clauses[0].iter, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let iter_name = fresh_genexp_local(comp_locals)
set_local_value(comp_locals, iter_name, first_iter_value)
adjusted_clauses.push(CompClause::{
is_async: clauses[0].is_async,
targets: clauses[0].targets,
target: clauses[0].target,
filters: clauses[0].filters,
iter: Expr::Name(iter_name),
})
for i = 1; i < clauses.length(); i = i + 1 {
adjusted_clauses.push(clauses[i])
}
}
let clauses = if clauses.length() > 0 {
adjusted_clauses
} else {
clauses
}
for clause in clauses {
for name in clause.targets {
shadow_local_cell(comp_locals, name, Value::None)
}
}
let values : Array[(Value, Value)] = []
scope_decls_stack.val.push(comprehension_scope_decls(clauses))
let result = eval_dict_comp_walk(
0, key, value, clauses, filters, comp_locals, globals, builtins, io, values,
)
pop_scope_decls()
match result {
Ok(_) => ()
Err(err) => return Err(err)
}
Ok(Value::Dict(values))
}
Expr::Dict(items) => {
let values : Array[(Value, Value)] = []
fn set_pair(
pairs : Array[(Value, Value)],
key : Value,
value : Value,
) -> Result[Unit, RuntimeError] {
match dict_set_item(pairs, key, value) {
Ok(v) => Ok(v)
Err(err) => Err(err)
}
}
for item in items {
match item.0 {
Expr::DoubleStarred(value~) => {
let mapping = match
eval_expr_with_env(value, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
match mapping {
Value::Dict(pairs) =>
for pair in pairs {
let _ = match set_pair(values, pair.0, pair.1) {
Ok(v) => v
Err(err) => return Err(err)
}
}
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"dict update sequence element has wrong length".to_string(),
),
)
}
}
_ => {
let key = match
eval_expr_with_env(item.0, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let value = match
eval_expr_with_env(item.1, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let _ = match set_pair(values, key, value) {
Ok(v) => v
Err(err) => return Err(err)
}
}
}
}
Ok(Value::Dict(values))
}
Expr::Set(items) => {
let values : Array[Value] = []
for item in items {
match item {
Expr::Starred(value~) => {
let iter_value = match
eval_expr_with_env(value, locals, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
let items = match iterable_values(iter_value) {
Ok(v) => v
Err(err) => return Err(err)
}
for value in items {
let _ = match set_add_unique(values, value) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
}
_ => {
let value = match
eval_expr_with_env(item, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let _ = match set_add_unique(values, value) {
Ok(_) => ()
Err(err) => return Err(err)
}
}
}
}
Ok(Value::Set(values))
}
Expr::Tuple(items) => {
let values : Array[Value] = []
for item in items {
match item {
Expr::Starred(value~) => {
let iter_value = match
eval_expr_with_env(value, locals, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
let items = match iterable_values(iter_value) {
Ok(v) => v
Err(err) => return Err(err)
}
for v in items {
values.push(v)
}
}
_ => {
let value = match
eval_expr_with_env(item, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
values.push(value)
}
}
}
Ok(Value::Tuple(values))
}
Expr::Lambda(params~, defaults~, body~) => {
let default_values : Array[Value] = []
for expr in defaults {
let value = match
eval_expr_with_env(expr, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
default_values.push(value)
}
let is_generator = match body {
Expr::Yield(_) | Expr::YieldFrom(_) => true
_ => false
}
let func_body : Array[Stmt] = if is_generator {
[Stmt::ExprStmt(body)]
} else {
[Stmt::Return(Some(body))]
}
let closure = capture_cell_closure(locals)
let globals_marker = Value::Instance(InstanceValue::{
class: ClassValue::{ name: "__mpython_globals__", bases: [], dict: [], },
dict: globals,
})
closure.push(("$__mpython_globals__", globals_marker))
Ok(
Value::Function(FunctionValue::{
name: "",
params,
defaults: default_values,
body: func_body,
is_generator,
is_async: false,
closure,
}),
)
}
Expr::Attribute(value~, attr~) => {
let target = match
eval_expr_with_env(value, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
get_attr_from_value(target, attr, globals, builtins, io)
}
Expr::Subscript(value~, index~) => {
let target = match
eval_expr_with_env(value, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
match index {
Expr::Slice(start~, end~, step~) => {
let start_value = match start {
Some(expr) =>
match eval_expr_with_env(expr, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
None => Value::None
}
let end_value = match end {
Some(expr) =>
match eval_expr_with_env(expr, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
None => Value::None
}
let step_value = match step {
Some(expr) =>
match eval_expr_with_env(expr, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
None => Value::None
}
fn slice_list(values : Array[Value]) -> Result[Value, RuntimeError] {
let indices = match
slice_indices_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::List(slice_values_by_indices(values, indices)))
}
match target {
Value::List(values) => slice_list(values)
Value::Instance(inst) => {
match get_named_value(inst.dict, list_storage_name) {
Some(Value::List(values)) => return slice_list(values)
_ => ()
}
match get_named_value(inst.dict, tuple_storage_name) {
Some(Value::Tuple(values)) => {
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::Tuple(slice_values_by_indices(values, indices)),
)
}
_ => ()
}
// General Python protocol: obj.__getitem__(slice(...))
let slice_dict : Array[(String, Value)] = []
slice_dict.push(("start", start_value))
slice_dict.push(("stop", end_value))
slice_dict.push(("step", step_value))
let slice_obj = Value::Instance(InstanceValue::{
class: builtin_class_from_name("slice", builtins),
dict: slice_dict,
})
match
get_attr_from_value(
Value::Instance(inst),
"__getitem__",
globals,
builtins,
io,
) {
Ok(getitem) =>
call_callable_with_env(
getitem,
[slice_obj],
[],
globals,
builtins,
io,
)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" +
inst.class.name +
"' object is not subscriptable",
),
)
_ => Err(err)
}
}
}
Value::Tuple(values) => {
let indices = match
slice_indices_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::Tuple(slice_values_by_indices(values, indices)))
}
Value::Str(text) => {
let chars = text.to_array()
let indices = match
slice_indices_from_values(
chars.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::Str(slice_string_by_indices(chars, indices)))
}
Value::Bytes(bytes) => {
let indices = match
slice_indices_from_values(
bytes.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::Bytes(slice_ints_by_indices(bytes, indices)))
}
Value::ByteArray(bytes) => {
let indices = match
slice_indices_from_values(
bytes.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::ByteArray(slice_ints_by_indices(bytes, indices)))
}
Value::MemoryView(bytes) => {
let indices = match
slice_indices_from_values(
bytes.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
// In CPython, memoryview slices with a non-unit step are typically
// non-contiguous. Some stdlib consumers (e.g. `binascii`) require
// a C-contiguous buffer and raise BufferError for such views.
let step_bigint = match step_value {
Value::None => 1N
Value::Int(v) => v
Value::Bool(v) => if v { 1N } else { 0N }
_ => 1N
}
let sliced = Value::MemoryView(
slice_ints_by_indices(bytes, indices),
)
if step_bigint != 1N {
let dict : Array[(String, Value)] = [
("__mpython_bytes__", sliced),
("__mpython_contiguous__", Value::Bool(false)),
]
Ok(
Value::Instance(InstanceValue::{
class: builtin_class_from_name("memoryview", builtins),
dict,
}),
)
} else {
Ok(sliced)
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"subscript requires sequence".to_string(),
),
)
}
}
_ => {
let idx_value = match
eval_expr_with_env(index, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
match target {
Value::Function(func) =>
if func.body.length() == 0 &&
is_generic_alias_origin_name(func.name) {
let args = match idx_value {
Value::Tuple(values) => values
_ => [idx_value]
}
return Ok(
make_generic_alias(
Value::Class(builtin_class_from_name(func.name, builtins)),
args,
),
)
}
Value::Class(klass) =>
if is_generic_alias_origin_name(klass.name) {
let args = match idx_value {
Value::Tuple(values) => values
_ => [idx_value]
}
return Ok(make_generic_alias(Value::Class(klass), args))
}
_ => ()
}
match target {
Value::Class(_) | Value::Function(_) =>
match
get_attr_from_value(
target, "__class_getitem__", globals, builtins, io,
) {
Ok(class_getitem) => {
let args = match class_getitem {
Value::BoundMethod(_) => [idx_value]
Value::Function(_) => [target, idx_value]
Value::Class(_) => [target, idx_value]
_ => [idx_value]
}
return call_callable_with_env(
class_getitem,
args,
[],
globals,
builtins,
io,
)
}
Err(_) => ()
}
_ => ()
}
match idx_value {
Value::Instance(inst) =>
if inst.class.name == "slice" {
let start_value = match get_named_value(inst.dict, "start") {
Some(v) => v
None => Value::None
}
let end_value = match get_named_value(inst.dict, "stop") {
Some(v) => v
None => Value::None
}
let step_value = match get_named_value(inst.dict, "step") {
Some(v) => v
None => Value::None
}
fn slice_list(
values : Array[Value],
) -> Result[Value, RuntimeError] {
let indices = match
slice_indices_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::List(slice_values_by_indices(values, indices)))
}
return match target {
Value::List(values) => slice_list(values)
Value::Instance(list_inst) =>
match get_named_value(list_inst.dict, list_storage_name) {
Some(Value::List(values)) => slice_list(values)
_ =>
match
get_attr_from_value(
Value::Instance(list_inst),
"__getitem__",
globals,
builtins,
io,
) {
Ok(getitem) =>
call_callable_with_env(
getitem,
[idx_value],
[],
globals,
builtins,
io,
)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" +
list_inst.class.name +
"' object is not subscriptable",
),
)
_ => Err(err)
}
}
}
Value::Tuple(values) => {
let indices = match
slice_indices_from_values(
values.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::Tuple(slice_values_by_indices(values, indices)))
}
Value::Str(text) => {
let chars = text.to_array()
let indices = match
slice_indices_from_values(
chars.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::Str(slice_string_by_indices(chars, indices)))
}
Value::Bytes(bytes) => {
let indices = match
slice_indices_from_values(
bytes.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::Bytes(slice_ints_by_indices(bytes, indices)))
}
Value::ByteArray(bytes) => {
let indices = match
slice_indices_from_values(
bytes.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::ByteArray(slice_ints_by_indices(bytes, indices)))
}
Value::MemoryView(bytes) => {
let indices = match
slice_indices_from_values(
bytes.length(),
start_value,
end_value,
step_value,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let step_bigint = match step_value {
Value::None => 1N
Value::Int(v) => v
Value::Bool(v) => if v { 1N } else { 0N }
_ => 1N
}
let sliced = Value::MemoryView(
slice_ints_by_indices(bytes, indices),
)
if step_bigint != 1N {
let dict : Array[(String, Value)] = [
("__mpython_bytes__", sliced),
("__mpython_contiguous__", Value::Bool(false)),
]
Ok(
Value::Instance(InstanceValue::{
class: builtin_class_from_name("memoryview", builtins),
dict,
}),
)
} else {
Ok(sliced)
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"subscript requires sequence".to_string(),
),
)
}
}
_ => ()
}
match target {
// User-defined `obj[key]` should dispatch to `obj.__getitem__(key)`.
Value::Instance(_) =>
match
get_attr_from_value(
target, "__getitem__", globals, builtins, io,
) {
Ok(getitem) =>
return call_callable_with_env(
getitem,
[idx_value],
[],
globals,
builtins,
io,
)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute => ()
_ => return Err(err)
}
}
_ => ()
}
match target {
Value::Dict(values) => {
let index = match dict_find_index(values, idx_value) {
Ok(v) => v
Err(err) => return Err(err)
}
match index {
Some(i) => Ok(values[i].1)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Key,
"key not found".to_string(),
),
)
}
}
_ => {
let idx = match index_from_value(idx_value, 0) {
Ok(value) => value
Err(err) => return Err(err)
}
fn index_list(
values : Array[Value],
idx : Int32,
) -> Result[Value, RuntimeError] {
let norm = normalize_index(idx, values.length())
if norm >= 0 && norm < values.length() {
Ok(values[norm])
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
}
}
match target {
Value::List(values) => index_list(values, idx)
Value::Instance(inst) =>
match get_named_value(inst.dict, list_storage_name) {
Some(Value::List(values)) => index_list(values, idx)
_ =>
match get_named_value(inst.dict, tuple_storage_name) {
Some(Value::Tuple(values)) => {
let norm = normalize_index(idx, values.length())
if norm >= 0 && norm < values.length() {
Ok(values[norm])
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"subscript requires sequence".to_string(),
),
)
}
}
Value::Tuple(values) => {
let norm = normalize_index(idx, values.length())
if norm >= 0 && norm < values.length() {
Ok(values[norm])
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
}
}
Value::Str(text) => {
let chars = text.to_array()
let norm = normalize_index(idx, chars.length())
if norm >= 0 && norm < chars.length() {
Ok(Value::Str(char_to_string(chars[norm])))
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
}
}
Value::Bytes(bytes)
| Value::ByteArray(bytes)
| Value::MemoryView(bytes) => {
let norm = normalize_index(idx, bytes.length())
if norm >= 0 && norm < bytes.length() {
Ok(Value::Int(@bigint.BigInt::from_int(bytes[norm])))
} else {
Err(
make_runtime_error(
RuntimeErrorKind::Index,
"index out of range".to_string(),
),
)
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"subscript requires sequence".to_string(),
),
)
}
}
}
}
}
}
Expr::IfExpr(condition~, then_expr~, else_expr~) => {
let cond_value = match
eval_expr_with_env(condition, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let ok = match
truthy_from_value_with_env(cond_value, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
if ok {
eval_expr_with_env(then_expr, locals, globals, builtins, io)
} else {
eval_expr_with_env(else_expr, locals, globals, builtins, io)
}
}
Expr::Unary(op~, expr~) => {
let value = match
eval_expr_with_env(expr, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
match op {
UnaryOp::Neg => {
// User-defined unary operators: fall back to numeric handling only if
// the instance doesn't implement the corresponding dunder.
match value {
Value::Instance(_) =>
match
get_attr_from_value(value, "__neg__", globals, builtins, io) {
Ok(dunder) =>
return call_callable_with_env(
dunder,
[],
[],
globals,
builtins,
io,
)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute => ()
_ => return Err(err)
}
}
_ => ()
}
match value {
Value::Complex(real, imag) =>
return Ok(Value::Complex(-real, -imag))
_ => ()
}
let (is_float, num) = match number_value(value) {
Ok(value) => value
Err(err) => return Err(err)
}
if is_float {
Ok(Value::Float(-num))
} else {
match value {
Value::Int(v) => Ok(Value::Int(-v))
Value::Bool(v) => Ok(Value::Int(-(if v { 1N } else { 0N })))
_ => Ok(Value::Int(-@bigint.BigInt::from_int64(num.to_int64())))
}
}
}
UnaryOp::Pos =>
match value {
Value::Instance(_) =>
match
get_attr_from_value(value, "__pos__", globals, builtins, io) {
Ok(dunder) =>
call_callable_with_env(dunder, [], [], globals, builtins, io)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute => Ok(value)
_ => Err(err)
}
}
Value::Bool(v) => Ok(Value::Int(if v { 1N } else { 0N }))
_ => Ok(value)
}
UnaryOp::Not => {
let ok = match
truthy_from_value_with_env(value, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::Bool(!ok))
}
UnaryOp::Invert => {
match value {
Value::Instance(_) =>
match
get_attr_from_value(value, "__invert__", globals, builtins, io) {
Ok(dunder) =>
return call_callable_with_env(
dunder,
[],
[],
globals,
builtins,
io,
)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute => ()
_ => return Err(err)
}
}
_ => ()
}
match value {
Value::Int(v) => Ok(Value::Int(-(v + 1N)))
Value::Bool(v) => {
// CPython 3.13+: `~bool` is deprecated.
let import_value = match get_named_value(builtins, "__import__") {
Some(v) => v
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"missing builtins.__import__".to_string(),
),
)
}
let warnings_module = match
call_callable_with_env(
import_value,
[Value::Str("warnings")],
[],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let warn_fn = match
get_attr_from_value(
warnings_module, "warn", globals, builtins, io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
let category = match
get_named_value(builtins, "DeprecationWarning") {
Some(Value::Class(k)) => Value::Class(k)
_ => Value::None
}
let _ = match
call_callable_with_env(
warn_fn,
[
Value::Str(
"Bitwise inversion on bool is deprecated".to_string(),
),
category,
Value::Int(2N),
],
[],
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(Value::Int(-((if v { 1N } else { 0N }) + 1N)))
}
Value::Float(_) =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bad operand type for unary ~: 'float'".to_string(),
),
)
Value::Function(func) => {
let type_name = if func.name == "int" ||
func.name == "str" ||
func.name == "bytes" ||
func.name == "dict" {
"type"
} else {
"function"
}
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bad operand type for unary ~: '" + type_name + "'",
),
)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"bad operand type for unary ~: '" +
type_name_from_value(value) +
"'",
),
)
}
}
}
}
Expr::Binary(op~, left~, right~) => {
let left_val = match
eval_expr_with_env(left, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let right_val = match
eval_expr_with_env(right, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
eval_binary_op_values(op, left_val, right_val, globals, builtins, io)
}
Expr::BoolOp(op~, values~) =>
if values.length() == 0 {
Ok(Value::Bool(false))
} else {
let mut result = match
eval_expr_with_env(values[0], locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
for i = 1; i < values.length(); i = i + 1 {
let ok = match
truthy_from_value_with_env(result, globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
match op {
BoolOp::And => if !ok { return Ok(result) }
BoolOp::Or => if ok { return Ok(result) }
}
result = match
eval_expr_with_env(values[i], locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
}
Ok(result)
}
Expr::Compare(left~, ops~, comparators~) => {
let mut previous = match
eval_expr_with_env(left, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
for i = 0; i < ops.length(); i = i + 1 {
let current = match
eval_expr_with_env(comparators[i], locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let ok = match
compare_values_with_env(
ops[i],
previous,
current,
globals,
builtins,
io,
) {
Ok(v) => v
Err(err) => return Err(err)
}
if !ok {
return Ok(Value::Bool(false))
}
previous = current
}
Ok(Value::Bool(true))
}
Expr::Call(callee~, args~) =>
match callee {
Expr::Attribute(value~, attr~) =>
if attr == "join" {
let sep_value = match
eval_expr_with_env(value, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
match sep_value {
Value::Str(sep) => {
if args.length() != 1 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"join() takes exactly one argument".to_string(),
),
)
}
let iter_value = match
eval_expr_with_env(args[0], locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let iterator_value = match
iter_value_to_iterator(iter_value, globals, builtins, io) {
Ok(v) => v
Err(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"can only join an iterable".to_string(),
),
)
}
let buf = StringBuilder()
let mut first = true
while true {
match
iterator_next(iterator_value, None, globals, builtins, io) {
Ok(item) => {
let part = match item {
Value::Str(text) => text
other => value_to_string(other)
}
if first {
first = false
} else {
buf.write_string(sep)
}
buf.write_string(part)
}
Err(err) =>
if err.exc_type == "StopIteration" {
break
} else {
return Err(err)
}
}
}
Ok(Value::Str(buf.to_string()))
}
_ => {
let callee_value = match
get_attr_from_value(sep_value, attr, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let (arg_values, keywords) = match
eval_call_args(args, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
match callee_value {
Value::Function(func) =>
if func.body.length() == 0 {
match
eval_builtin_call(
func.name,
arg_values,
keywords,
locals,
globals,
builtins,
io,
) {
Ok(Some(value)) => Ok(value)
Ok(None) =>
call_callable_with_env(
callee_value, arg_values, keywords, globals, builtins,
io,
)
Err(err) => Err(err)
}
} else {
call_callable_with_env(
callee_value, arg_values, keywords, globals, builtins, io,
)
}
_ =>
call_callable_with_env(
callee_value, arg_values, keywords, globals, builtins, io,
)
}
}
}
} else {
let callee_value = match
eval_expr_with_env(
Expr::Attribute(value~, attr~),
locals,
globals,
builtins,
io,
) {
Ok(value) => value
Err(err) => return Err(err)
}
let (arg_values, keywords) = match
eval_call_args(args, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
match callee_value {
Value::Function(func) =>
if func.body.length() == 0 {
match
eval_builtin_call(
func.name,
arg_values,
keywords,
locals,
globals,
builtins,
io,
) {
Ok(Some(value)) => Ok(value)
Ok(None) =>
call_callable_with_env(
callee_value, arg_values, keywords, globals, builtins, io,
)
Err(err) => Err(err)
}
} else {
call_callable_with_env(
callee_value, arg_values, keywords, globals, builtins, io,
)
}
_ =>
call_callable_with_env(
callee_value, arg_values, keywords, globals, builtins, io,
)
}
}
Expr::Name(name) => {
let callee_value = match
lookup_name_value(name, locals, globals, builtins) {
Ok(v) => v
Err(err) => return Err(err)
}
match callee_value {
Value::Function(func) =>
if func.body.length() == 0 {
let (arg_values, keywords) = match
eval_call_args(args, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
match
eval_builtin_call(
func.name,
arg_values,
keywords,
locals,
globals,
builtins,
io,
) {
Ok(Some(value)) => Ok(value)
Ok(None) =>
call_callable_with_env(
callee_value, arg_values, keywords, globals, builtins, io,
)
Err(err) => Err(err)
}
} else {
let (arg_values, keywords) = match
eval_call_args(args, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
call_callable_with_env(
callee_value, arg_values, keywords, globals, builtins, io,
)
}
_ => {
let (arg_values, keywords) = match
eval_call_args(args, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
call_callable_with_env(
callee_value, arg_values, keywords, globals, builtins, io,
)
}
}
}
_ => {
let callee_value = match
eval_expr_with_env(callee, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
let (arg_values, keywords) = match
eval_call_args(args, locals, globals, builtins, io) {
Ok(value) => value
Err(err) => return Err(err)
}
call_callable_with_env(
callee_value, arg_values, keywords, globals, builtins, io,
)
}
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::NotImplemented,
"expression not implemented".to_string(),
),
)
}
}