diff --git a/fix-builtins/src/lib.rs b/fix-builtins/src/lib.rs index 1028078..561e245 100644 --- a/fix-builtins/src/lib.rs +++ b/fix-builtins/src/lib.rs @@ -130,8 +130,15 @@ pub enum PrimOpPhase { Abort, Add, AddErrorContext, + All, + AllCallPred, + AllCheck, + Any, + AnyCallPred, + AnyCheck, + AppendContext, AttrNames, AttrValues, diff --git a/fix-primops/src/lib.rs b/fix-primops/src/lib.rs index e0dbe92..67297ff 100644 --- a/fix-primops/src/lib.rs +++ b/fix-primops/src/lib.rs @@ -33,6 +33,14 @@ pub fn dispatch_primop<'gc, M: Machine<'gc>>( match phase { Abort => abort(m, ctx, reader, mc), + All => all_entry(m, reader, mc), + AllCallPred => all_call_pred(m, reader, mc), + AllCheck => all_check(m, reader, mc), + + Any => any_entry(m, reader, mc), + AnyCallPred => any_call_pred(m, reader, mc), + AnyCheck => any_check(m, reader, mc), + DeepSeq => deep_seq_force_top(m, reader, mc), DeepSeqPush => deep_seq_push(m, reader, mc), DeepSeqLoop => deep_seq_loop(m, reader, mc), diff --git a/fix-primops/src/list.rs b/fix-primops/src/list.rs index f53937d..9618767 100644 --- a/fix-primops/src/list.rs +++ b/fix-primops/src/list.rs @@ -16,6 +16,7 @@ pub fn filter_force_list<'gc, M: Machine<'gc>>( }; if list.inner.borrow().is_empty() { let val = m.pop(); + let _pred = m.pop(); return m.return_from_primop(val, reader); } // prepare stack layout: [ pred list idx acc ] @@ -164,3 +165,121 @@ pub fn foldl_strict_update<'gc, M: Machine<'gc>>( reader.set_pc(PrimOpPhase::FoldlStrictCall1.ip() as usize); Step::Continue(()) } + +pub fn all_entry<'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), + }; + // FIXME: force callable + m.force_slot(1, reader, mc)?; + if list.inner.borrow().is_empty() { + let _list = m.pop(); + let _pred = m.pop(); + return m.return_from_primop(Value::new_inline(true), reader); + } + // prepare stack layout: [ pred list idx ] + m.push(Value::new_inline(0)); + reader.set_pc(PrimOpPhase::AllCallPred.ip() as usize); + Step::Continue(()) +} + +pub fn all_call_pred<'gc, M: Machine<'gc>>( + m: &mut M, + reader: &mut BytecodeReader<'_>, + mc: &Mutation<'gc>, +) -> Step { + let pred = m.peek_forced(2); + #[allow(clippy::unwrap_used)] + let idx = m.peek(0).as_inline::().unwrap(); + #[allow(clippy::unwrap_used)] + let elem = m.peek_forced(1).as_gc::().unwrap().inner.borrow()[idx as usize]; + m.push(pred.relax()); + m.call(reader, mc, elem, PrimOpPhase::AllCheck.ip() as usize) +} + +pub fn all_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(0).as_inline::().unwrap(); + #[allow(clippy::unwrap_used)] + let list = m.peek_forced(1).as_gc::().unwrap(); + let list = list.inner.borrow(); + if idx as usize == list.len() - 1 || !ret { + let _ = m.pop(); // idx + let _ = m.pop(); // list + let _ = m.pop(); // pred + return m.return_from_primop(Value::new_inline(ret), reader); + } + m.replace(0, Value::new_inline(idx + 1)); + reader.set_pc(PrimOpPhase::AllCallPred.ip() as usize); + Step::Continue(()) +} + +pub fn any_entry<'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), + }; + // FIXME: force callable + m.force_slot(1, reader, mc)?; + if list.inner.borrow().is_empty() { + let _list = m.pop(); + let _pred = m.pop(); + return m.return_from_primop(Value::new_inline(false), reader); + } + // prepare stack layout: [ pred list idx ] + m.push(Value::new_inline(0)); + reader.set_pc(PrimOpPhase::AnyCallPred.ip() as usize); + Step::Continue(()) +} + +pub fn any_call_pred<'gc, M: Machine<'gc>>( + m: &mut M, + reader: &mut BytecodeReader<'_>, + mc: &Mutation<'gc>, +) -> Step { + let pred = m.peek_forced(2); + #[allow(clippy::unwrap_used)] + let idx = m.peek(0).as_inline::().unwrap(); + #[allow(clippy::unwrap_used)] + let elem = m.peek_forced(1).as_gc::().unwrap().inner.borrow()[idx as usize]; + m.push(pred.relax()); + m.call(reader, mc, elem, PrimOpPhase::AnyCheck.ip() as usize) +} + +pub fn any_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(0).as_inline::().unwrap(); + #[allow(clippy::unwrap_used)] + let list = m.peek_forced(1).as_gc::().unwrap(); + let list = list.inner.borrow(); + if idx as usize == list.len() - 1 || ret { + let _ = m.pop(); // idx + let _ = m.pop(); // list + let _ = m.pop(); // pred + return m.return_from_primop(Value::new_inline(ret), reader); + } + m.replace(0, Value::new_inline(idx + 1)); + reader.set_pc(PrimOpPhase::AnyCallPred.ip() as usize); + Step::Continue(()) +} diff --git a/fix-vm/src/instructions/arithmetic.rs b/fix-vm/src/instructions/arithmetic.rs index 968a10d..c8864a7 100644 --- a/fix-vm/src/instructions/arithmetic.rs +++ b/fix-vm/src/instructions/arithmetic.rs @@ -258,13 +258,13 @@ impl<'gc> crate::Vm<'gc> { return Ok(()); } // TODO: compare other types - Err(crate::vm_err("cannot compare these types")) + Err(crate::vm_err(format!("cannot compare {} with {}", lhs.ty(), rhs.ty()))) } } pub(crate) fn get_num(val: StrictValue<'_>) -> Option { if let Some(i) = val.as_inline::() { - Some(NixNum::Int(i as i64)) + Some(NixNum::Int(i64::from(i))) } else if let Some(gc_i) = val.as_gc::() { Some(NixNum::Int(*gc_i)) } else {