use fix_abstract_vm::{ BytecodeReader, List, Machine, MachineExt, NixType, Step, StrictValue, Value, }; use fix_builtins::PrimOpPhase; use gc_arena::Mutation; pub fn filter_force_list<'gc, M: Machine<'gc>>( m: &mut M, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { m.force_slot(0, reader, mc)?; let list = match m.peek_forced(0).expect_gc::() { Ok(list) => list, Err(got) => return m.finish_type_err(NixType::List, got), }; if list.inner.borrow().is_empty() { let val = m.pop(); return m.return_from_primop(val, reader); } // prepare stack layout: [ pred list idx acc ] m.push(Value::new_inline(0)); m.push(Value::new_gc(List::new_gc(mc))); reader.set_pc(PrimOpPhase::FilterCallPred.ip() as usize); Step::Continue(()) } pub fn filter_call_pred<'gc, M: Machine<'gc>>( m: &mut M, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { m.force_slot(3, reader, mc)?; let pred = m.peek_forced(3); #[allow(clippy::unwrap_used)] let idx = m.peek(1).as_inline::().unwrap(); #[allow(clippy::unwrap_used)] let elem = m.peek_forced(2).as_gc::().unwrap().inner.borrow()[idx as usize]; m.push(pred.relax()); m.call(reader, mc, elem, PrimOpPhase::FilterCheck.ip() as usize) } pub fn filter_check<'gc, M: Machine<'gc>>( m: &mut M, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { let ret = m.force_and_retry::(reader, mc)?; #[allow(clippy::unwrap_used)] let idx = m.peek(1).as_inline::().unwrap(); #[allow(clippy::unwrap_used)] let list = m.peek_forced(2).as_gc::().unwrap(); let list = list.inner.borrow(); #[allow(clippy::unwrap_used)] let acc = m.peek_forced(0).as_gc::().unwrap(); if ret { let mut acc = acc.unlock(mc).borrow_mut(); acc.push(list[idx as usize]); } if idx as usize == list.len() - 1 { let acc = m.pop(); let _ = m.pop(); // idx let _ = m.pop(); // list let _ = m.pop(); // pred return m.return_from_primop(acc, reader); } m.replace(1, Value::new_inline(idx + 1)); reader.set_pc(PrimOpPhase::FilterCallPred.ip() as usize); Step::Continue(()) } // foldl' op nul list // // Stack layouts across phases: // Entry: [op, nul, list] // Empty: [op, nul] // Call1: [op, list, idx, acc] // Call2: [op, list, idx, acc, intermediate] // Update: [op, list, idx, acc, result] pub fn foldl_strict_entry<'gc, M: Machine<'gc>>( m: &mut M, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { m.force_slot(0, reader, mc)?; let list_val = m.peek_forced(0); let Some(list) = list_val.as_gc::() else { return m.finish_type_err(NixType::List, list_val.ty()); }; if list.inner.borrow().is_empty() { let _ = m.pop(); // list reader.set_pc(PrimOpPhase::FoldlStrictEmpty.ip() as usize); return Step::Continue(()); } let list_val = m.pop(); let nul_val = m.pop(); m.push(list_val); m.push(Value::new_inline(0i32)); m.push(nul_val); reader.set_pc(PrimOpPhase::FoldlStrictCall1.ip() as usize); Step::Continue(()) } pub fn foldl_strict_empty<'gc, M: Machine<'gc>>( m: &mut M, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { let nul = m.force_and_retry::(reader, mc)?; let _ = m.pop(); // op m.return_from_primop(nul.relax(), reader) } pub fn foldl_strict_call1<'gc, M: Machine<'gc>>( m: &mut M, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { m.force_slot(3, reader, mc)?; let op = m.peek_forced(3); let acc = m.peek(0); m.push(op.relax()); m.call(reader, mc, acc, PrimOpPhase::FoldlStrictCall2.ip() as usize) } pub fn foldl_strict_call2<'gc, M: Machine<'gc>>( m: &mut M, reader: &mut BytecodeReader<'_>, mc: &Mutation<'gc>, ) -> Step { #[allow(clippy::unwrap_used)] let idx = m.peek(2).as_inline::().unwrap(); #[allow(clippy::unwrap_used)] let list = m.peek_forced(3).as_gc::().unwrap(); let elem = list.inner.borrow()[idx as usize]; m.call( reader, mc, elem, PrimOpPhase::FoldlStrictUpdate.ip() as usize, ) } pub fn foldl_strict_update<'gc, M: Machine<'gc>>( m: &mut M, reader: &mut BytecodeReader<'_>, _mc: &Mutation<'gc>, ) -> Step { let result = m.pop(); m.replace(0, result); #[allow(clippy::unwrap_used)] let idx = m.peek(1).as_inline::().unwrap(); #[allow(clippy::unwrap_used)] let list = m.peek_forced(2).as_gc::().unwrap(); let len = list.inner.borrow().len(); if (idx as usize) + 1 == len { let acc = m.pop(); let _ = m.pop(); // idx let _ = m.pop(); // list let _ = m.pop(); // op return m.return_from_primop(acc, reader); } m.replace(1, Value::new_inline(idx + 1)); reader.set_pc(PrimOpPhase::FoldlStrictCall1.ip() as usize); Step::Continue(()) }