use crate::rvm::instructions::{ComprehensionMode, LoopMode};
use crate::value::Value;
use crate::value::{Object, ObjectCursor};
use crate::Rc;
use alloc::collections::BTreeSet;
use alloc::vec::Vec;
#[derive(Debug, Clone)]
pub struct LoopContext {
pub mode: LoopMode,
pub iteration_state: IterationState,
pub key_reg: u8,
pub value_reg: u8,
pub result_reg: u8,
pub body_start: u16,
pub loop_end: u16,
pub loop_next_pc: u16, pub body_resume_pc: usize,
pub success_count: usize,
pub total_iterations: usize,
pub current_iteration_failed: bool, }
#[derive(Debug, Clone)]
pub enum IterationState {
Array {
items: Rc<Vec<Value>>,
index: usize,
},
Object {
obj: Rc<Object>,
cursor: ObjectCursor,
},
Set {
items: Rc<BTreeSet<Value>>,
current_item: Option<Value>,
first_iteration: bool,
},
Single {
consumed: bool,
},
}
impl IterationState {
pub(super) const fn advance(&mut self) {
match *self {
Self::Array { ref mut index, .. } => {
debug_assert!(
*index < usize::MAX,
"IterationState::Array index already at usize::MAX on advance"
);
*index = index.saturating_add(1);
}
Self::Object { .. } => {}
Self::Set {
ref mut first_iteration,
..
} => {
*first_iteration = false;
}
Self::Single {
ref mut consumed, ..
} => {
debug_assert!(
!*consumed,
"IterationState::Single advanced after consumption"
);
*consumed = true;
}
}
}
}
#[allow(unused)]
#[derive(Debug, Clone)]
pub struct CallRuleContext {
pub return_pc: usize,
pub dest_reg: u8,
pub result_reg: u8,
pub rule_index: u16,
pub rule_type: crate::rvm::program::RuleType,
pub current_definition_index: usize,
pub current_body_index: usize,
}
#[derive(Debug, Clone)]
pub(super) struct ComprehensionContext {
pub(super) mode: ComprehensionMode,
pub(super) result_reg: u8,
pub(super) key_reg: u8,
pub(super) value_reg: u8,
pub(super) body_start: u16,
pub(super) comprehension_end: u16,
pub(super) iteration_state: Option<IterationState>,
pub(super) resume_pc: usize,
}
#[cfg(test)]
#[allow(
clippy::expect_used,
clippy::unwrap_used,
clippy::unreachable,
clippy::pattern_type_mismatch,
clippy::shadow_unrelated,
clippy::panic
)]
mod tests {
use super::*;
use crate::value::Object;
#[test]
fn iteration_state_object_is_snapshot_independent_of_source() {
let mut obj = Object::new();
obj.insert(Value::from("a"), Value::from(1));
obj.insert(Value::from("b"), Value::from(2));
obj.insert(Value::from("c"), Value::from(3));
let source = Value::Object(Rc::new(obj));
let snapshot_obj = match &source {
Value::Object(o) => Rc::clone(o),
_ => unreachable!(),
};
let state = IterationState::Object {
obj: Rc::clone(&snapshot_obj),
cursor: snapshot_obj.cursor(),
};
let mut alias = source.clone();
let inner = alias.as_object_mut().expect("object");
inner.insert(Value::from("a"), Value::from(999));
inner.insert(Value::from("d"), Value::from(4));
inner.remove(&Value::from("b"));
let mut collected: Vec<(Value, Value)> = Vec::new();
if let IterationState::Object {
ref obj,
mut cursor,
} = state
{
while let Some((k, v)) = obj.next(&mut cursor) {
collected.push((k.clone(), v.clone()));
}
} else {
unreachable!();
}
assert_eq!(collected.len(), 3);
assert!(collected.contains(&(Value::from("a"), Value::from(1))));
assert!(collected.contains(&(Value::from("b"), Value::from(2))));
assert!(collected.contains(&(Value::from("c"), Value::from(3))));
assert!(!collected.iter().any(|kv| kv.0 == Value::from("d")));
let src_obj = source.as_object().expect("object");
assert_eq!(src_obj.len(), 3);
assert_eq!(src_obj.get(&Value::from("a")), Some(&Value::from(1)));
}
}