use smallvec::smallvec;
use crate::{
args::ArgValues,
bytecode::VM,
defer_drop,
exception_private::{ExcType, ExcTypeExt, RunResult, SimpleException},
heap::{DropGuard, DropWithContext, HeapData},
types::{List, allocate_tuple},
value::Value,
};
pub fn builtin_enumerate(vm: &mut VM<'_>, args: ArgValues) -> RunResult<Value> {
let (iterable, start) = extract_enumerate_args(args, vm)?;
defer_drop!(start, vm);
let iter = iterable.into_py_iter(vm)?;
defer_drop!(iter, vm);
let mut iter = iter.read(vm);
let mut index: i64 = match start {
Some(Value::Int(n)) => *n,
Some(Value::Bool(b)) => i64::from(*b),
Some(v) => {
let type_name = v.py_type_name(vm);
return Err(SimpleException::new_msg(
ExcType::TypeError,
format!("'{type_name}' object cannot be interpreted as an integer"),
)
.into());
}
None => 0,
};
let result: Vec<Value> = Vec::new();
let mut result_guard = DropGuard::new(result, vm);
let (result, vm) = result_guard.as_parts_mut();
while let Some(item) = iter.py_next(vm)? {
let tuple_val = allocate_tuple(smallvec![Value::Int(index), item], vm.heap);
result.push(tuple_val);
index += 1;
}
let (result, vm) = result_guard.into_parts();
let heap_id = vm.heap.allocate(HeapData::List(List::new(result)));
Ok(Value::Ref(heap_id))
}
fn extract_enumerate_args(args: ArgValues, vm: &mut VM<'_>) -> RunResult<(Value, Option<Value>)> {
let (mut pos, kwargs) = args.into_parts();
let n_pos = pos.len();
let total = n_pos + kwargs.len();
let mut kw = kwargs.into_iter();
match (pos.next(), pos.next(), kw.next(), kw.next()) {
(Some(iterable), start, None, None) if total <= 2 => Ok((iterable, start)),
(Some(iterable), None, Some(kv), None) => match take_kwarg(kv, "start", vm) {
Ok(start) => Ok((iterable, Some(start))),
Err(err) => {
iterable.drop_with(vm);
Err(err)
}
},
(None, None, Some(kv), None) => Ok((take_kwarg(kv, "iterable", vm)?, None)),
(None, None, Some(kv0), Some(kv1)) if total == 2 => two_kwarg_form(kv0, kv1, vm),
(p0, p1, k0, k1) => {
((p0, p1), (k0, k1)).drop_with(vm);
pos.drop_with(vm);
kw.drop_with(vm);
if n_pos == 0 {
Err(ExcType::type_error_missing_required_no_pos("enumerate", "iterable"))
} else {
Err(ExcType::type_error_method_at_most("enumerate", 2, total, false))
}
}
}
}
fn two_kwarg_form(kv0: (Value, Value), kv1: (Value, Value), vm: &mut VM<'_>) -> RunResult<(Value, Option<Value>)> {
let swapped = key_check(&kv0.0, "start", vm).is_ok();
let checked = if swapped {
key_check(&kv1.0, "iterable", vm)
} else {
key_check(&kv0.0, "iterable", vm).and(key_check(&kv1.0, "start", vm))
};
match checked {
Ok(()) => {
let ((key0, val0), (key1, val1)) = (kv0, kv1);
(key0, key1).drop_with(vm);
Ok(if swapped {
(val1, Some(val0))
} else {
(val0, Some(val1))
})
}
Err(err) => {
(kv0, kv1).drop_with(vm);
Err(err)
}
}
}
fn take_kwarg(kv: (Value, Value), expected: &str, vm: &mut VM<'_>) -> RunResult<Value> {
match key_check(&kv.0, expected, vm) {
Ok(()) => {
let (key, value) = kv;
key.drop_with(vm);
Ok(value)
}
Err(err) => {
kv.drop_with(vm);
Err(err)
}
}
}
fn key_check(key: &Value, expected: &str, vm: &VM<'_>) -> RunResult<()> {
let Some(key) = key.as_either_str(vm.heap) else {
return Err(ExcType::type_error_kwargs_nonstring_key());
};
let key = key.as_str(vm.interns);
if key == expected {
Ok(())
} else {
Err(ExcType::type_error_invalid_keyword_argument("enumerate", key))
}
}