///|
/// Iterator protocol helpers (internal).
///|
let iterator_class_ref : Ref[ClassValue?] = { val: None, }
///|
let repeat_class_ref : Ref[ClassValue?] = { val: None, }
///|
let count_class_ref : Ref[ClassValue?] = { val: None, }
///|
let enumerate_class_ref : Ref[ClassValue?] = { val: None, }
///|
let rangeiter_class_ref : Ref[ClassValue?] = { val: None, }
///|
let cycle_class_ref : Ref[ClassValue?] = { val: None, }
///|
let seqiter_class_ref : Ref[ClassValue?] = { val: None, }
///|
let zip_class_ref : Ref[ClassValue?] = { val: None, }
///|
fn internal_iter_class(name : String, cache : Ref[ClassValue?]) -> ClassValue {
match cache.val {
Some(value) => return value
None => ()
}
let dict : Array[(String, Value)] = [
(
"__iter__",
Value::Function(FunctionValue::{
name: "__mpython.internal_iter",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
(
"__next__",
Value::Function(FunctionValue::{
name: "__mpython.internal_next",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
]
if name == "rangeiter" {
dict.push(
(
"__setstate__",
Value::Function(FunctionValue::{
name: "rangeiter.__setstate__",
params: ["self", "state"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
let klass = ClassValue::{ name, bases: [], dict, }
cache.val = Some(klass)
klass
}
///|
fn make_iterator(items_in : Array[Value]) -> Value {
let items : Array[Value] = []
for item in items_in {
items.push(item)
}
let dict : Array[(String, Value)] = []
dict.push(("items", Value::List(items)))
dict.push(("pos", Value::Int(0N)))
Value::Instance(InstanceValue::{
class: internal_iter_class("iterator", iterator_class_ref),
dict,
})
}
///|
fn make_repeat_iterator(value : Value, remaining : @bigint.BigInt?) -> Value {
let dict : Array[(String, Value)] = []
dict.push(("value", value))
match remaining {
Some(v) => dict.push(("remaining", Value::Int(v)))
None => dict.push(("remaining", Value::None))
}
Value::Instance(InstanceValue::{
class: internal_iter_class("repeat", repeat_class_ref),
dict,
})
}
///|
fn make_count_iterator(start : Value, step : Value) -> Value {
let dict : Array[(String, Value)] = []
dict.push(("current", start))
dict.push(("step", step))
Value::Instance(InstanceValue::{
class: internal_iter_class("count", count_class_ref),
dict,
})
}
///|
fn make_cycle_iterator(iterator : Value) -> Value {
let dict : Array[(String, Value)] = []
dict.push(("iterator", iterator))
dict.push(("saved", Value::List([])))
dict.push(("pos", Value::Int(0N)))
Value::Instance(InstanceValue::{
class: internal_iter_class("cycle", cycle_class_ref),
dict,
})
}
///|
fn make_enumerate_iterator(iterator : Value, start : @bigint.BigInt) -> Value {
let dict : Array[(String, Value)] = []
dict.push(("iterator", iterator))
dict.push(("index", Value::Int(start)))
Value::Instance(InstanceValue::{
class: internal_iter_class("enumerate", enumerate_class_ref),
dict,
})
}
///|
fn make_range_iterator(
start : @bigint.BigInt,
stop : @bigint.BigInt,
step : @bigint.BigInt,
) -> Value {
let dict : Array[(String, Value)] = []
dict.push(("start", Value::Int(start)))
dict.push(("current", Value::Int(start)))
dict.push(("stop", Value::Int(stop)))
dict.push(("step", Value::Int(step)))
dict.push(("index", Value::Int(0N)))
Value::Instance(InstanceValue::{
class: internal_iter_class("rangeiter", rangeiter_class_ref),
dict,
})
}
///|
fn make_seqiter_iterator(target : Value) -> Value {
let dict : Array[(String, Value)] = []
dict.push(("target", target))
dict.push(("index", Value::Int(0N)))
Value::Instance(InstanceValue::{
class: internal_iter_class("seqiter", seqiter_class_ref),
dict,
})
}
///|
fn make_zip_iterator(iterators_in : Array[Value], strict : Bool) -> Value {
let iterators : Array[Value] = []
for it in iterators_in {
iterators.push(it)
}
let dict : Array[(String, Value)] = []
dict.push(("iterators", Value::List(iterators)))
dict.push(("strict", Value::Bool(strict)))
Value::Instance(InstanceValue::{
class: internal_iter_class("zip", zip_class_ref),
dict,
})
}
///|
fn iter_value_to_iterator(
value : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
match value {
Value::Instance(inst) => {
// Treat list subclasses (instances carrying `$__list__`) as iterables.
match get_named_value(inst.dict, list_storage_name) {
Some(Value::List(values)) => return Ok(make_iterator(values))
_ => ()
}
// Treat tuple subclasses (instances carrying `$__tuple__`) as iterables.
match get_named_value(inst.dict, tuple_storage_name) {
Some(Value::Tuple(values)) => return Ok(make_iterator(values))
_ => ()
}
// Treat set/frozenset subclasses as iterables.
match get_named_value(inst.dict, set_storage_name) {
Some(Value::Set(values)) => return Ok(make_iterator(values))
_ => ()
}
if inst.class.name == "dict_keys" ||
inst.class.name == "dict_values" ||
inst.class.name == "dict_items" {
let items = match iterable_values(Value::Instance(inst)) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(make_iterator(items))
} else if inst.class.name == "iterator" ||
inst.class.name == "generator" ||
inst.class.name == "repeat" ||
inst.class.name == "count" ||
inst.class.name == "cycle" ||
inst.class.name == "enumerate" ||
inst.class.name == "rangeiter" ||
inst.class.name == "zip" {
Ok(Value::Instance(inst))
} else {
// Try the Python protocol first: obj.__iter__() -> iterator
match
get_attr_from_value(
Value::Instance(inst),
"__iter__",
globals,
builtins,
io,
) {
Ok(iter_method) =>
call_callable_with_env(iter_method, [], [], globals, builtins, io)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute => {
match
get_attr_from_value(
Value::Instance(inst),
"__getitem__",
globals,
builtins,
io,
) {
Ok(_) =>
return Ok(make_seqiter_iterator(Value::Instance(inst)))
Err(err2) =>
match err2.kind {
RuntimeErrorKind::Attribute => ()
_ => return Err(err2)
}
}
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" + inst.class.name + "' object is not iterable",
),
)
}
_ => Err(err)
}
}
}
}
Value::Class(klass) => {
let class_value = Value::Class(klass)
match
get_attr_from_value(class_value, "__iter__", globals, builtins, io) {
Ok(iter_method) =>
call_callable_with_env(iter_method, [], [], globals, builtins, io)
Err(err) =>
match err.kind {
RuntimeErrorKind::Attribute => {
let names_value = get_named_value(klass.dict, "_member_names_")
let map_value = get_named_value(klass.dict, "_member_map_")
match (names_value, map_value) {
(Some(Value::List(names)), Some(Value::Dict(pairs))) => {
let items : Array[Value] = []
for name_value in names {
match name_value {
Value::Str(name) => {
let idx = match
dict_find_index(pairs, Value::Str(name)) {
Ok(v) => v
Err(err) => return Err(err)
}
match idx {
Some(i) => items.push(pairs[i].1)
None => ()
}
}
_ => ()
}
}
if items.length() == 0 && pairs.length() > 0 {
for pair in pairs {
items.push(pair.1)
}
}
Ok(make_iterator(items))
}
_ => {
let mut is_enum = false
match class_mro(klass) {
Ok(mro) =>
for cls in mro {
if cls.name == "Enum" ||
cls.name == "Flag" ||
cls.name == "IntFlag" ||
cls.name == "StrEnum" {
is_enum = true
break
}
}
Err(err) => return Err(err)
}
if is_enum {
let items : Array[Value] = []
for pair in klass.dict {
if !pair.0.has_prefix("_") {
items.push(pair.1)
}
}
if items.length() > 0 {
return Ok(make_iterator(items))
}
}
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"object is not iterable".to_string(),
),
)
}
}
}
_ => Err(err)
}
}
}
other => {
let items = match iterable_values(other) {
Ok(v) => v
Err(err) => return Err(err)
}
Ok(make_iterator(items))
}
}
}
///|
fn iterator_next(
iterator_value : Value,
default_value : Value?,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
match iterator_value {
Value::Instance(inst) =>
if inst.class.name == "iterator" {
let items_value = get_named_value(inst.dict, "items")
let pos_value = get_named_value(inst.dict, "pos")
let items = match items_value {
Some(Value::List(items)) => items
_ => []
}
let pos = match pos_value {
Some(Value::Int(v)) => v
Some(Value::Bool(v)) => if v { 1N } else { 0N }
_ => 0N
}
if pos < @bigint.BigInt::from_int(items.length()) {
let idx = match bigint_to_int_checked(pos) {
Ok(v) => v
Err(err) => return Err(err)
}
let item = items[idx]
set_named_value(inst.dict, "pos", Value::Int(pos + 1N))
Ok(item)
} else {
match default_value {
Some(v) => Ok(v)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"StopIteration".to_string(),
),
)
}
}
} else if inst.class.name == "repeat" {
let value = match get_named_value(inst.dict, "value") {
Some(v) => v
None => Value::None
}
match get_named_value(inst.dict, "remaining") {
Some(Value::None) => Ok(value)
Some(Value::Int(v)) =>
if v <= 0N {
match default_value {
Some(v2) => Ok(v2)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"StopIteration".to_string(),
),
)
}
} else {
set_named_value(inst.dict, "remaining", Value::Int(v - 1N))
Ok(value)
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"StopIteration".to_string(),
),
)
}
} else if inst.class.name == "count" {
let current = match get_named_value(inst.dict, "current") {
Some(v) => v
None => Value::Int(0N)
}
let step = match get_named_value(inst.dict, "step") {
Some(v) => v
None => Value::Int(1N)
}
match (current, step) {
(Value::Int(current_i), Value::Int(step_i)) => {
set_named_value(
inst.dict,
"current",
Value::Int(current_i + step_i),
)
Ok(Value::Int(current_i))
}
(Value::Float(current_f), Value::Float(step_f)) => {
set_named_value(
inst.dict,
"current",
Value::Float(current_f + step_f),
)
Ok(Value::Float(current_f))
}
(Value::Int(current_i), Value::Float(step_f)) => {
let current_f = match bigint_to_double_checked(current_i) {
Ok(v) => v
Err(err) => return Err(err)
}
set_named_value(
inst.dict,
"current",
Value::Float(current_f + step_f),
)
Ok(Value::Float(current_f))
}
(Value::Float(current_f), Value::Int(step_i)) => {
let step_f = match bigint_to_double_checked(step_i) {
Ok(v) => v
Err(err) => return Err(err)
}
set_named_value(
inst.dict,
"current",
Value::Float(current_f + step_f),
)
Ok(Value::Float(current_f))
}
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"count() expects numeric values".to_string(),
),
)
}
} else if inst.class.name == "zip" {
let strict = match get_named_value(inst.dict, "strict") {
Some(Value::Bool(v)) => v
_ => false
}
let iterators = match get_named_value(inst.dict, "iterators") {
Some(Value::List(values)) => values
_ => ([] : Array[Value])
}
if iterators.length() == 0 {
match default_value {
Some(v) => Ok(v)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"StopIteration".to_string(),
),
)
}
} else {
let items : Array[Value] = []
let mut stop_index : Int32? = None
for i = 0; i < iterators.length(); i = i + 1 {
match iterator_next(iterators[i], None, globals, builtins, io) {
Ok(v) => items.push(v)
Err(err) =>
if err.exc_type == "StopIteration" {
stop_index = Some(i)
break
} else {
return Err(err)
}
}
}
match stop_index {
None => Ok(Value::Tuple(items))
Some(i) =>
if strict {
for j = i + 1; j < iterators.length(); j = j + 1 {
match
iterator_next(iterators[j], None, globals, builtins, io) {
Ok(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: zip() argument lengths differ".to_string(),
),
)
Err(err) =>
if err.exc_type == "StopIteration" {
continue
} else {
return Err(err)
}
}
}
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: zip() argument lengths differ".to_string(),
),
)
} else {
match default_value {
Some(v) => Ok(v)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"StopIteration".to_string(),
),
)
}
}
}
}
} else if inst.class.name == "cycle" {
let saved = match get_named_value(inst.dict, "saved") {
Some(Value::List(values)) => values
_ => []
}
let pos0 = match get_named_value(inst.dict, "pos") {
Some(Value::Int(v)) => v
Some(Value::Bool(v)) => if v { 1N } else { 0N }
_ => 0N
}
let iter_value_opt = get_named_value(inst.dict, "iterator")
let mut iter_done = false
match iter_value_opt {
Some(Value::None) => iter_done = true
None => iter_done = true
Some(v) =>
match iterator_next(v, None, globals, builtins, io) {
Ok(item) => {
saved.push(item)
return Ok(item)
}
Err(err) =>
if err.exc_type == "StopIteration" {
iter_done = true
} else {
return Err(err)
}
}
}
if iter_done {
// Mark the source iterator as done so we keep cycling over the saved list.
set_named_value(inst.dict, "iterator", Value::None)
if saved.length() == 0 {
match default_value {
Some(v) => Ok(v)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"StopIteration".to_string(),
),
)
}
} else {
let len_i = @bigint.BigInt::from_int(saved.length())
let idx_big = if len_i == 0N { 0N } else { pos0 % len_i }
let idx = match bigint_to_int_checked(idx_big) {
Ok(v) => v
Err(err) => return Err(err)
}
set_named_value(inst.dict, "pos", Value::Int(pos0 + 1N))
Ok(saved[idx])
}
} else {
// Should be unreachable due to early return in Ok(item) branch.
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"internal cycle iterator error".to_string(),
),
)
}
} else if inst.class.name == "rangeiter" {
let current = match get_named_value(inst.dict, "current") {
Some(Value::Int(v)) => v
Some(Value::Bool(v)) => if v { 1N } else { 0N }
_ => 0N
}
let start0 = match get_named_value(inst.dict, "start") {
Some(Value::Int(v)) => v
Some(Value::Bool(v)) => if v { 1N } else { 0N }
_ => current
}
let stop = match get_named_value(inst.dict, "stop") {
Some(Value::Int(v)) => v
Some(Value::Bool(v)) => if v { 1N } else { 0N }
_ => 0N
}
let step = match get_named_value(inst.dict, "step") {
Some(Value::Int(v)) => v
Some(Value::Bool(v)) => if v { 1N } else { 0N }
_ => 1N
}
let index0 = match get_named_value(inst.dict, "index") {
Some(Value::Int(v)) => v
Some(Value::Bool(v)) => if v { 1N } else { 0N }
_ => (current - start0) / step
}
let done = if step > 0N { current >= stop } else { current <= stop }
if done {
match default_value {
Some(v) => Ok(v)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"StopIteration".to_string(),
),
)
}
} else {
set_named_value(inst.dict, "current", Value::Int(current + step))
set_named_value(inst.dict, "index", Value::Int(index0 + 1N))
Ok(Value::Int(current))
}
} else if inst.class.name == "seqiter" {
let target = match get_named_value(inst.dict, "target") {
Some(v) => v
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"invalid seqiter iterator".to_string(),
),
)
}
let index0 = match get_named_value(inst.dict, "index") {
Some(Value::Int(v)) => v
Some(Value::Bool(v)) => if v { 1N } else { 0N }
_ => 0N
}
let getitem = match
get_attr_from_value(target, "__getitem__", globals, builtins, io) {
Ok(v) => v
Err(err) => return Err(err)
}
match
call_callable_with_env(
getitem,
[Value::Int(index0)],
[],
globals,
builtins,
io,
) {
Ok(item) => {
set_named_value(inst.dict, "index", Value::Int(index0 + 1N))
Ok(item)
}
Err(err) =>
if err.exc_type == "IndexError" {
match default_value {
Some(v) => Ok(v)
None =>
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"StopIteration".to_string(),
),
)
}
} else {
Err(err)
}
}
} else if inst.class.name == "generator" {
generator_next(Value::Instance(inst), default_value)
} else if inst.class.name == "enumerate" {
let inner = match get_named_value(inst.dict, "iterator") {
Some(v) => v
None =>
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"invalid enumerate iterator".to_string(),
),
)
}
let index0 = match get_named_value(inst.dict, "index") {
Some(Value::Int(v)) => v
Some(Value::Bool(v)) => if v { 1N } else { 0N }
_ => 0N
}
match iterator_next(inner, None, globals, builtins, io) {
Ok(item) => {
set_named_value(inst.dict, "index", Value::Int(index0 + 1N))
Ok(Value::Tuple([Value::Int(index0), item]))
}
Err(err) =>
if err.exc_type == "StopIteration" {
match default_value {
Some(v) => Ok(v)
None => Err(err)
}
} else {
Err(err)
}
}
} else {
let next_method = match
get_attr_from_value(
Value::Instance(inst),
"__next__",
globals,
builtins,
io,
) {
Ok(v) => v
Err(_) =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" + inst.class.name + "' object is not an iterator",
),
)
}
match
call_callable_with_env(next_method, [], [], globals, builtins, io) {
Ok(v) => Ok(v)
Err(err) =>
if err.exc_type == "StopIteration" {
match default_value {
Some(v) => Ok(v)
None => Err(err)
}
} else {
Err(err)
}
}
}
other =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" + type_name_from_value(other) + "' object is not an iterator",
),
)
}
}
///|
fn iterator_send(
iterator_value : Value,
sent_value : Value,
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
match iterator_value {
Value::Instance(inst) =>
if inst.class.name == "generator" {
generator_send(Value::Instance(inst), sent_value)
} else if inst.class.name == "iterator" {
match sent_value {
Value::None =>
iterator_next(Value::Instance(inst), None, globals, builtins, io)
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" + inst.class.name + "' object does not support send()",
),
)
}
} else {
match sent_value {
Value::None =>
iterator_next(Value::Instance(inst), None, globals, builtins, io)
_ =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" + inst.class.name + "' object does not support send()",
),
)
}
}
other =>
Err(
make_runtime_error(
RuntimeErrorKind::Type,
"'" + type_name_from_value(other) + "' object is not an iterator",
),
)
}
}
///|
fn builtin_rangeiter_setstate(
positional : Array[Value],
keywords : Array[(String, Value)],
locals : Array[(String, Value)],
globals : Array[(String, Value)],
builtins : Array[(String, Value)],
io : MockIO,
) -> Result[Value, RuntimeError] {
touch_env(locals, globals, builtins, io)
let _ = match ensure_no_keywords("rangeiter.__setstate__", keywords) {
Ok(v) => v
Err(err) => return Err(err)
}
if positional.length() != 2 {
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rangeiter.__setstate__() takes exactly one argument".to_string(),
),
)
}
let inst = match positional[0] {
Value::Instance(inst) if inst.class.name == "rangeiter" => inst
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rangeiter.__setstate__ expects a range iterator".to_string(),
),
)
}
let state = match positional[1] {
Value::Int(v) => v
Value::Bool(v) => if v { 1N } else { 0N }
_ =>
return Err(
make_runtime_error(
RuntimeErrorKind::Type,
"rangeiter.__setstate__ state must be int".to_string(),
),
)
}
let start0 = match get_named_value(inst.dict, "start") {
Some(Value::Int(v)) => v
Some(Value::Bool(v)) => if v { 1N } else { 0N }
_ => 0N
}
let step0 = match get_named_value(inst.dict, "step") {
Some(Value::Int(v)) => v
Some(Value::Bool(v)) => if v { 1N } else { 0N }
_ => 1N
}
set_named_value(inst.dict, "index", Value::Int(state))
set_named_value(inst.dict, "current", Value::Int(start0 + step0 * state))
Ok(Value::None)
}