Rework +mink scry failure logic

This commit is contained in:
Alex Shelkovnykov 2023-10-23 21:49:37 -06:00
parent 7e4bf3ae5d
commit 7992d02766
8 changed files with 228 additions and 207 deletions

View File

@ -230,7 +230,6 @@ struct Nock12 {
reff: Noun,
path: Noun,
hand: Noun,
flag: Option<bool>,
}
#[derive(Copy, Clone)]
@ -267,41 +266,42 @@ pub struct Context {
pub scry_stack: Noun,
}
#[derive(Clone, Copy, Debug)]
pub enum Error {
Nock(NockError),
Scry(ScryError),
}
// #[derive(Clone, Copy, Debug)]
// pub enum Error {
// Nock(NockError),
// Scry(ScryError),
// }
#[derive(Clone, Copy, Debug)]
pub enum NockError {
Deterministic(Noun), // trace
NonDeterministic(Noun), // trace
}
// #[derive(Clone, Copy, Debug)]
// pub enum NockError {
// Deterministic(Noun), // trace
// NonDeterministic(Noun), // trace
// }
#[derive(Clone, Copy, Debug)]
pub enum ScryError {
Blocked(Noun), // path
Deterministic(Noun), // trace
NonDeterministic(Noun), // trace
}
// #[derive(Clone, Copy, Debug)]
// pub enum ScryError {
// Blocked(Noun), // path
// Deterministic(Noun), // trace
// NonDeterministic(Noun), // trace
// }
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Failure {
Blocked(Noun), // path
Deterministic,
NonDeterministic,
pub enum Error {
ScryBlocked(Noun), // path
ScryCrashed(Noun), // trace
Deterministic(Noun), // trace
NonDeterministic(Noun), // trace
}
impl From<noun::Error> for Failure {
impl From<noun::Error> for Error {
fn from(_: noun::Error) -> Self {
Failure::Deterministic
Error::Deterministic(D(0))
}
}
impl From<cold::Error> for Failure {
impl From<cold::Error> for Error {
fn from(_: cold::Error) -> Self {
Failure::Deterministic
Error::Deterministic(D(0))
}
}
@ -318,7 +318,6 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
let orig_subject = subject; // for debugging
let virtual_frame: *const u64 = context.stack.get_frame_pointer();
let mut res: Noun = D(0);
let mut scry_flag: bool = false;
// Setup stack for Nock computation
unsafe {
@ -403,7 +402,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
context.stack.pop::<NockWork>();
} else {
// Axis invalid for input Noun
break Err(Failure::Deterministic);
break Err(Error::Deterministic(D(0)));
}
}
NockWork::Work1(once) => {
@ -412,7 +411,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
NockWork::Work2(mut vale) => {
if (*terminator).load(Ordering::Relaxed) {
break Err(Failure::NonDeterministic);
break Err(Error::NonDeterministic(D(0)));
}
match vale.todo {
@ -502,7 +501,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
context.stack.pop::<NockWork>();
} else {
// Cannot increment (Nock 4) a cell
break Err(Failure::Deterministic);
break Err(Error::Deterministic(D(0)));
}
}
},
@ -545,11 +544,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
push_formula(stack, cond.once, cond.tail)?;
} else {
// Test branch of Nock 6 must return 0 or 1
break Err(Failure::Deterministic);
break Err(Error::Deterministic(D(0)));
}
} else {
// Test branch of Nock 6 must return a direct atom
break Err(Failure::Deterministic);
break Err(Error::Deterministic(D(0)));
}
}
},
@ -605,7 +604,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
},
NockWork::Work9(mut kale) => {
if (*terminator).load(Ordering::Relaxed) {
break Err(Failure::NonDeterministic);
break Err(Error::NonDeterministic(D(0)));
}
match kale.todo {
@ -657,7 +656,7 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
}
} else {
// Axis into core must be atom
break Err(Failure::Deterministic);
break Err(Error::Deterministic(D(0)));
}
}
Todo9::RestoreSubject => {
@ -799,54 +798,64 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
push_formula(stack, scry.path, false)?;
}
Todo12::Scry => {
let stack = &mut context.stack;
if let Some(cell) = context.scry_stack.cell() {
let scry_stack = context.scry_stack;
let scry_handler = cell.head();
let scry_gate = scry_handler.as_cell()?;
let payload = T(stack, &[scry.reff, res]);
let payload = T(&mut context.stack, &[scry.reff, res]);
let scry_core = T(
stack,
&mut context.stack,
&[
scry_gate.head(),
payload,
scry_gate.tail().as_cell()?.tail(),
],
);
let scry_form = T(stack, &[D(9), D(2), D(1), scry_core]);
let scry_form = T(&mut context.stack, &[D(9), D(2), D(1), scry_core]);
scry.todo = Todo12::Done;
scry.path = res;
scry.hand = context.scry_stack;
scry.flag = Some(scry_flag);
scry_flag = true;
context.scry_stack = cell.tail();
*stack.top() = NockWork::Work12(scry);
push_formula(stack, scry_form, false)?;
match interpret(context, subject, scry_form) {
Ok(noun) => {
scry.todo = Todo12::Done;
scry.path = res;
scry.hand = scry_stack;
*context.stack.top() = NockWork::Work12(scry);
res = noun;
}
Err(error) => match error {
Error::Deterministic(trace) | Error::ScryCrashed(trace) => {
break Err(Error::ScryCrashed(trace));
}
Error::NonDeterministic(_) => {
break Err(error);
}
Error::ScryBlocked(_) => {
break Err(Error::NonDeterministic(D(0)));
}
},
}
} else {
// No scry handler
break Err(Failure::Deterministic);
break Err(Error::Deterministic(D(0)));
}
}
Todo12::Done => match res.as_either_atom_cell() {
Left(atom) => {
if atom.as_noun().raw_equals(D(0)) {
break Err(Failure::Blocked(scry.path));
break Err(Error::ScryBlocked(scry.path));
} else {
// XX: mink crash
break Err(Failure::Deterministic);
break Err(Error::ScryCrashed(D(0)));
}
}
Right(cell) => match cell.tail().as_either_atom_cell() {
Left(_) => {
let stack = &mut context.stack;
scry_flag = scry.flag.unwrap();
let hunk = T(stack, &[D(tas!(b"hunk")), scry.reff, scry.path]);
mean_push(stack, hunk);
break Err(Failure::Deterministic);
break Err(Error::ScryCrashed(D(0)));
}
Right(cell) => {
res = cell.tail();
scry_flag = scry.flag.unwrap();
context.scry_stack = scry.hand;
context.stack.pop::<NockWork>();
}
@ -859,11 +868,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res
match nock {
Ok(res) => Ok(res),
Err(err) => Err(exit(context, virtual_frame, err, scry_flag)),
Err(err) => Err(exit(context, virtual_frame, err)),
}
}
fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), Failure> {
fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), Error> {
unsafe {
if let Ok(formula_cell) = formula.as_cell() {
// Formula
@ -883,7 +892,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(),
*stack.push() = NockWork::Work0(Nock0 { axis: axis_atom });
} else {
// Axis for Nock 0 must be an atom
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
}
}
1 => {
@ -901,7 +910,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(),
});
} else {
// Argument to Nock 2 must be cell
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
};
}
3 => {
@ -925,7 +934,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(),
});
} else {
// Argument to Nock 5 must be cell
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
};
}
6 => {
@ -940,11 +949,11 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(),
});
} else {
// Argument tail to Nock 6 must be cell
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
};
} else {
// Argument to Nock 6 must be cell
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
}
}
7 => {
@ -957,7 +966,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(),
});
} else {
// Argument to Nock 7 must be cell
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
};
}
8 => {
@ -970,7 +979,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(),
});
} else {
// Argument to Nock 8 must be cell
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
};
}
9 => {
@ -984,11 +993,11 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(),
});
} else {
// Axis for Nock 9 must be an atom
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
}
} else {
// Argument to Nock 9 must be cell
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
};
}
10 => {
@ -1003,15 +1012,15 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(),
});
} else {
// Axis for Nock 10 must be an atom
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
}
} else {
// Heah of argument to Nock 10 must be a cell
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
};
} else {
// Argument to Nock 10 must be a cell
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
};
}
11 => {
@ -1036,13 +1045,13 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(),
});
} else {
// Hint tag must be an atom
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
}
}
};
} else {
// Argument for Nock 11 must be cell
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
};
}
12 => {
@ -1052,44 +1061,42 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(),
reff: arg_cell.head(),
path: arg_cell.tail(),
hand: D(0),
flag: None,
});
} else {
// Argument for Nock 12 must be cell
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
}
}
_ => {
// Invalid formula opcode
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
}
}
} else {
// Formula opcode must be direct atom
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
}
}
}
} else {
// Bad formula: atoms are not formulas
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
}
}
Ok(())
}
pub fn exit(
context: &mut Context,
virtual_frame: *const u64,
error: Failure,
scry_flag: bool,
) -> Error {
pub fn exit(context: &mut Context, virtual_frame: *const u64, error: Error) -> Error {
unsafe {
let stack = &mut context.stack;
let mut preserve = match error {
Failure::Blocked(path) => path,
_ => *(stack.local_noun_pointer(0)),
Error::ScryBlocked(path) => path,
Error::Deterministic(t) | Error::NonDeterministic(t) | Error::ScryCrashed(t) => {
// Return $tang of traces
let h = *(stack.local_noun_pointer(0));
T(stack, &[h, t])
}
};
while (stack).get_frame_pointer() != virtual_frame {
@ -1098,27 +1105,10 @@ pub fn exit(
}
match error {
Failure::Blocked(_) => {
if scry_flag {
Error::Scry(ScryError::Blocked(preserve))
} else {
panic!("serf: blocked outside of scry");
}
}
Failure::Deterministic => {
if scry_flag {
Error::Scry(ScryError::Deterministic(preserve))
} else {
Error::Nock(NockError::Deterministic(preserve))
}
}
Failure::NonDeterministic => {
if scry_flag {
Error::Scry(ScryError::NonDeterministic(preserve))
} else {
Error::Nock(NockError::NonDeterministic(preserve))
}
}
Error::Deterministic(_) => Error::Deterministic(preserve),
Error::NonDeterministic(_) => Error::NonDeterministic(preserve),
Error::ScryCrashed(_) => Error::ScryCrashed(preserve),
Error::ScryBlocked(_) => error,
}
}
}
@ -1256,7 +1246,7 @@ mod hint {
tag: Atom,
hint: Noun,
body: Noun,
) -> Result<Option<Noun>, Failure> {
) -> Result<Option<Noun>, Error> {
// XX: handle IndirectAtom tags
match tag.as_direct()?.data() {
tas!(b"sham") => {
@ -1292,7 +1282,7 @@ mod hint {
let tape = tape(stack, "jet mismatch");
let mean = T(stack, &[D(tas!(b"mean")), tape]);
mean_push(stack, mean);
Err(Failure::Deterministic)
Err(Error::Deterministic(D(0)))
} else {
Ok(Some(nock_res))
}
@ -1310,10 +1300,10 @@ mod hint {
mean_push(stack, mean);
match error {
Error::Nock(NockError::NonDeterministic(_)) => {
Err(Failure::NonDeterministic)
Error::NonDeterministic(_) => {
Err(Error::NonDeterministic(D(0)))
}
_ => Err(Failure::Deterministic),
_ => Err(Error::Deterministic(D(0))),
}
}
}
@ -1355,14 +1345,14 @@ mod hint {
tag: Atom,
hint: Option<(Noun, Noun)>,
_body: Noun,
) -> Result<Option<Noun>, Failure> {
) -> Result<Option<Noun>, Error> {
// XX: handle IndirectAtom tags
match tag.as_direct()?.data() {
tas!(b"slog") => {
let stack = &mut context.stack;
let newt = &mut context.newt;
let (_form, clue) = hint.ok_or(Failure::Deterministic)?;
let (_form, clue) = hint.ok_or(Error::Deterministic(D(0)))?;
let slog_cell = clue.as_cell()?;
let pri = slog_cell.head().as_direct()?.data();
let tank = slog_cell.tail();
@ -1373,19 +1363,19 @@ mod hint {
tas!(b"hand") | tas!(b"hunk") | tas!(b"lose") | tas!(b"mean") | tas!(b"spot") => {
let terminator = Arc::clone(&TERMINATOR);
if (*terminator).load(Ordering::Relaxed) {
return Err(Failure::NonDeterministic);
return Err(Error::NonDeterministic(D(0)));
}
let stack = &mut context.stack;
let (_form, clue) = hint.ok_or(Failure::Deterministic)?;
let (_form, clue) = hint.ok_or(Error::Deterministic(D(0)))?;
let noun = T(stack, &[tag.as_noun(), clue]);
mean_push(stack, noun);
Ok(None)
}
tas!(b"hela") => {
// XX: should this be virtualized?
// pretty sure we should be bailing on error
// might need to switch return type to Result<Option<Noun>, Failure>
// XX: This only prints the trace down to the bottom of THIS
// interpret call. We'll need to recursively work down
// frames to get the stack trace all the way to the root.
let mean = unsafe { *(context.stack.local_noun_pointer(0)) };
let tone = Cell::new(&mut context.stack, D(2), mean);
@ -1398,7 +1388,7 @@ mod hint {
let tape = tape(stack, "%hela failed: toon not %2");
let mean = T(stack, &[D(tas!(b"mean")), tape]);
mean_push(stack, mean);
return Err(Failure::Deterministic);
return Err(Error::Deterministic(D(0)));
}
let mut list = toon.tail();
@ -1436,7 +1426,7 @@ mod hint {
hint: Option<Noun>,
body: Noun,
res: Noun,
) -> Result<Option<Noun>, Failure> {
) -> Result<Option<Noun>, Error> {
let stack = &mut context.stack;
let newt = &mut context.newt;
let cold = &mut context.cold;

View File

@ -10,7 +10,7 @@ pub mod nock;
pub mod text;
pub mod tree;
use crate::interpreter::{Context, Failure};
use crate::interpreter::{Context, Error};
use crate::jets::bits::*;
use crate::jets::cold::Cold;
use crate::jets::form::*;
@ -23,7 +23,7 @@ use crate::jets::tree::*;
use crate::jets::warm::Warm;
use crate::mem::NockStack;
use crate::newt::Newt;
use crate::noun::{self, Noun, Slots};
use crate::noun::{self, Noun, Slots, D};
use ares_macros::tas;
use std::cmp;
@ -39,13 +39,13 @@ pub type Jet = fn(&mut Context, Noun) -> Result;
*/
#[derive(Debug, PartialEq)]
pub enum JetErr {
Punt, // Retry with the raw nock
Fail(Failure), // Error; do not retry
Punt, // Retry with the raw nock
Fail(Error), // Error; do not retry
}
impl From<noun::Error> for JetErr {
fn from(_err: noun::Error) -> Self {
Self::Fail(Failure::Deterministic)
Self::Fail(Error::Deterministic(D(0)))
}
}
@ -53,7 +53,7 @@ impl From<JetErr> for () {
fn from(_: JetErr) -> Self {}
}
impl From<JetErr> for Failure {
impl From<JetErr> for Error {
fn from(e: JetErr) -> Self {
match e {
JetErr::Fail(f) => f,
@ -138,13 +138,13 @@ pub mod util {
pub fn checked_add(a: usize, b: usize) -> result::Result<usize, JetErr> {
a.checked_add(b)
.filter(|x| x <= &MAX_BIT_LENGTH)
.ok_or(JetErr::Fail(Failure::NonDeterministic))
.ok_or(JetErr::Fail(Error::NonDeterministic(D(0))))
}
/// Performs addition that returns None on Noun size overflow
pub fn checked_sub(a: usize, b: usize) -> result::Result<usize, JetErr> {
a.checked_sub(b)
.ok_or(JetErr::Fail(Failure::NonDeterministic))
.ok_or(JetErr::Fail(Error::NonDeterministic(D(0))))
}
pub fn checked_left_shift(bloq: usize, a: usize) -> result::Result<usize, JetErr> {
@ -152,7 +152,7 @@ pub mod util {
// Catch overflow
if (res >> bloq) < a || res > MAX_BIT_LENGTH {
Err(JetErr::Fail(Failure::NonDeterministic))
Err(JetErr::Fail(Error::NonDeterministic(D(0))))
} else {
Ok(res)
}
@ -170,14 +170,14 @@ pub mod util {
pub fn slot(noun: Noun, axis: u64) -> Result {
noun.slot(axis)
.map_err(|_e| JetErr::Fail(Failure::Deterministic))
.map_err(|_e| JetErr::Fail(Error::Deterministic(D(0))))
}
/// Extract a bloq and check that it's computable by the current system
pub fn bloq(a: Noun) -> result::Result<usize, JetErr> {
let bloq = a.as_direct()?.data() as usize;
if bloq >= 47 {
Err(JetErr::Fail(Failure::NonDeterministic))
Err(JetErr::Fail(Error::NonDeterministic(D(0))))
} else {
Ok(bloq)
}

View File

@ -1,6 +1,6 @@
/** Bit arithmetic & logic jets
*/
use crate::interpreter::{Context, Failure};
use crate::interpreter::{Context, Error};
use crate::jets::util::*;
use crate::jets::{JetErr, Result};
use crate::noun::{DirectAtom, IndirectAtom, Noun, D};
@ -245,7 +245,7 @@ pub fn jet_rev(context: &mut Context, subject: Noun) -> Result {
let boz = slot(arg, 2)?.as_atom()?.as_direct()?.data();
if boz >= 64 {
return Err(JetErr::Fail(Failure::Deterministic));
return Err(JetErr::Fail(Error::Deterministic(D(0))));
}
let boz = boz as usize;

View File

@ -12,7 +12,7 @@
* Another approach is use a global custom allocator. This is fairly involved, but it would allow
* us to use any library without worrying whether it allocates.
*/
use crate::interpreter::{Context, Failure};
use crate::interpreter::{Context, Error};
use crate::jets::util::*;
use crate::jets::{JetErr, Result};
use crate::noun::{Atom, DirectAtom, IndirectAtom, Noun, D, DIRECT_MAX, NO, T, YES};
@ -44,7 +44,7 @@ pub fn jet_dec(context: &mut Context, subject: Noun) -> Result {
match atom.as_either() {
Left(direct) => {
if direct.data() == 0 {
Err(JetErr::Fail(Failure::Deterministic))
Err(JetErr::Fail(Error::Deterministic(D(0))))
} else {
Ok(unsafe { DirectAtom::new_unchecked(direct.data() - 1) }.as_noun())
}
@ -72,7 +72,7 @@ pub fn jet_dec(context: &mut Context, subject: Noun) -> Result {
}
}
} else {
Err(JetErr::Fail(Failure::Deterministic))
Err(JetErr::Fail(Error::Deterministic(D(0))))
}
}
@ -83,7 +83,7 @@ pub fn jet_div(context: &mut Context, subject: Noun) -> Result {
let b = slot(arg, 3)?.as_atom()?;
if unsafe { b.as_noun().raw_equals(D(0)) } {
Err(JetErr::Fail(Failure::Deterministic))
Err(JetErr::Fail(Error::Deterministic(D(0))))
} else if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) {
Ok(unsafe { DirectAtom::new_unchecked(a.data() / b.data()) }.as_noun())
} else {
@ -101,7 +101,7 @@ pub fn jet_dvr(context: &mut Context, subject: Noun) -> Result {
let b = slot(arg, 3)?.as_atom()?;
if unsafe { b.as_noun().raw_equals(D(0)) } {
Err(JetErr::Fail(Failure::Deterministic))
Err(JetErr::Fail(Error::Deterministic(D(0))))
} else {
let (div, rem) = if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) {
let (div, rem) = (a.data() / b.data(), a.data() % b.data());
@ -222,7 +222,7 @@ pub fn jet_mod(context: &mut Context, subject: Noun) -> Result {
let b = slot(arg, 3)?.as_atom()?;
if unsafe { b.as_noun().raw_equals(D(0)) } {
Err(JetErr::Fail(Failure::Deterministic))
Err(JetErr::Fail(Error::Deterministic(D(0))))
} else if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) {
Ok(unsafe { DirectAtom::new_unchecked(a.data() % b.data()) }.as_noun())
} else {
@ -270,7 +270,7 @@ pub fn jet_sub(context: &mut Context, subject: Noun) -> Result {
#[cfg(test)]
mod tests {
use super::*;
use crate::interpreter::Failure;
use crate::interpreter::Error;
use crate::jets::util::test::{
assert_jet, assert_jet_err, assert_jet_ubig, assert_nary_jet_ubig, init_context, A,
};
@ -377,7 +377,7 @@ mod tests {
let (a0, _a24, a63, _a96, a128) = atoms(s);
assert_jet_ubig(c, jet_dec, a128, ubig!(0xdeadbeef12345678fedcba987654320f));
assert_jet(c, jet_dec, a63, D(0x7ffffffffffffffe));
assert_jet_err(c, jet_dec, a0, JetErr::Fail(Failure::Deterministic));
assert_jet_err(c, jet_dec, a0, JetErr::Fail(Error::Deterministic(D(0))));
}
#[test]
@ -403,13 +403,13 @@ mod tests {
c,
jet_div,
&[atom_63, atom_0],
JetErr::Fail(Failure::Deterministic),
JetErr::Fail(Error::Deterministic(D(0))),
);
assert_math_jet_err(
c,
jet_div,
&[atom_0, atom_0],
JetErr::Fail(Failure::Deterministic),
JetErr::Fail(Error::Deterministic(D(0))),
);
}
@ -460,7 +460,7 @@ mod tests {
c,
jet_dvr,
&[atom_63, atom_0],
JetErr::Fail(Failure::Deterministic),
JetErr::Fail(Error::Deterministic(D(0))),
);
}
@ -540,13 +540,13 @@ mod tests {
c,
jet_mod,
&[atom_63, atom_0],
JetErr::Fail(Failure::Deterministic),
JetErr::Fail(Error::Deterministic(D(0))),
);
assert_math_jet_err(
c,
jet_mod,
&[atom_0, atom_0],
JetErr::Fail(Failure::Deterministic),
JetErr::Fail(Error::Deterministic(D(0))),
);
}
@ -597,7 +597,7 @@ mod tests {
c,
jet_sub,
&[atom_63, atom_96],
JetErr::Fail(Failure::Deterministic),
JetErr::Fail(Error::Deterministic(D(0))),
);
}
}

View File

@ -1,14 +1,33 @@
use crate::hamt::Hamt;
/** Virtualization jets
*/
use crate::interpreter::Context;
use crate::hamt::Hamt;
use crate::interpreter::{Context, Error};
use crate::jets::util::slot;
use crate::jets::Result;
use crate::jets::{JetErr, Result};
use crate::noun::{Noun, T};
crate::gdb!();
// XX: interpret should accept optional scry function and potentially produce blocked
// Determinist scry crashes should have the following behaviour:
//
// root <-- Deterministic
// mink <-- Deterministic
// scry <-- ScryCrashed
//
// root <-- Deterministic
// mink <-- Deterministic
// mink <-- ScryCrashed
// scry <-- ScryCrashed
// scry <-- ScryCrashed
//
// root
// mink <-- Tone
// mink <-- Deterministic
// mink <-- ScryCrashed
// scry <-- ScryCrashed
// scry <-- ScryCrashed
// scry
//
pub fn jet_mink(context: &mut Context, subject: Noun) -> Result {
let arg = slot(subject, 6)?;
// mink sample = [nock scry_namespace]
@ -23,14 +42,30 @@ pub fn jet_mink(context: &mut Context, subject: Noun) -> Result {
context.cache = Hamt::<Noun>::new();
context.scry_stack = T(&mut context.stack, &[scry_handler, old_scry_stack]);
let res = util::mink(context, v_subject, v_formula);
context.cache = old_cache;
context.scry_stack = old_scry_stack;
res
match util::mink(context, v_subject, v_formula) {
Ok(noun) => {
context.cache = old_cache;
context.scry_stack = old_scry_stack;
Ok(noun)
}
Err(error) => match error {
Error::NonDeterministic(_) => Err(JetErr::Fail(error)),
Error::ScryCrashed(trace) => {
if unsafe { context.scry_stack.raw_equals(old_scry_stack) } {
Err(JetErr::Fail(Error::Deterministic(trace)))
} else {
Err(JetErr::Fail(error))
}
}
Error::Deterministic(_) | Error::ScryBlocked(_) => {
panic!("scry: mink: unhandled errors in helper")
}
},
}
}
pub mod util {
use crate::interpreter::{interpret, Context, Error, Failure, NockError, ScryError};
use crate::interpreter::{interpret, Context, Error};
use crate::jets;
use crate::jets::form::util::scow;
use crate::jets::util::rip;
@ -44,26 +79,13 @@ pub mod util {
pub const LEAF: Noun = D(tas!(b"leaf"));
pub const ROSE: Noun = D(tas!(b"rose"));
pub fn mink(context: &mut Context, subject: Noun, formula: Noun) -> jets::Result {
pub fn mink(context: &mut Context, subject: Noun, formula: Noun) -> Result<Noun, Error> {
match interpret(context, subject, formula) {
Ok(res) => Ok(T(&mut context.stack, &[D(0), res])),
Err(err) => match err {
Error::Nock(nock_err) => match nock_err {
NockError::Deterministic(trace) => Ok(T(&mut context.stack, &[D(2), trace])),
// XX: trace is unused; needs to be printed or welded to trace from outer context
NockError::NonDeterministic(_trace) => {
Err(JetErr::Fail(Failure::NonDeterministic))
}
},
Error::Scry(scry_err) => match scry_err {
ScryError::Blocked(path) => Ok(T(&mut context.stack, &[D(1), path])),
// XX: trace is unused; needs to be printed or welded to trace from outer context
ScryError::Deterministic(_trace) => Err(JetErr::Fail(Failure::Deterministic)),
// XX: trace is unused; needs to be printed or welded to trace from outer context
ScryError::NonDeterministic(_trace) => {
Err(JetErr::Fail(Failure::NonDeterministic))
}
},
Error::ScryBlocked(path) => Ok(T(&mut context.stack, &[D(1), path])),
Error::Deterministic(trace) => Ok(T(&mut context.stack, &[D(2), trace])),
Error::NonDeterministic(_) | Error::ScryCrashed(_) => Err(err),
},
}
}
@ -77,14 +99,14 @@ pub mod util {
match tag.data() {
x if x < 2 => return Ok(tone),
x if x > 2 => return Err(JetErr::Fail(Failure::Deterministic)),
x if x > 2 => return Err(JetErr::Fail(Error::Deterministic(D(0)))),
_ => {}
}
if unsafe { original_list.raw_equals(D(0)) } {
return Ok(tone);
} else if original_list.atom().is_some() {
return Err(JetErr::Fail(Failure::Deterministic));
return Err(JetErr::Fail(Error::Deterministic(D(0))));
}
// XX: trim traces longer than 1024 frames
@ -134,15 +156,21 @@ pub mod util {
T(stack, &[LEAF, tape])
}
Right(cell) => {
let tone = mink(context, dat, cell.head())?.as_cell()?;
if !tone.head().raw_equals(D(0)) {
'tank: {
if let Ok(tone) = mink(context, dat, cell.head()) {
if let Some(cell) = tone.cell() {
if cell.head().raw_equals(D(0)) {
// XX: need to check that this is
// actually a path;
// return leaf+"mook.mean" if not
break 'tank cell.tail();
}
}
}
let stack = &mut context.stack;
let tape = tape(stack, "####");
T(stack, &[LEAF, tape])
} else {
// XX: need to check that this is actually a tank
// return leaf+"mook.mean" if not
tone.tail()
}
}
},
@ -326,7 +354,7 @@ mod tests {
let nock = T(stack, &[subj, form]);
let scry = D(0);
let samp = T(stack, &[nock, scry]);
let rest = T(stack, &[D(2), D(0)]);
let rest = T(stack, &[D(2), D(0), D(0)]);
assert_jet(context, jet_mink, samp, rest);
}
@ -412,7 +440,7 @@ mod tests {
let t1 = T(stack, &[tt1, tt2, D(0)]);
let rest = T(stack, &[D(2), t1]);
let rest = T(stack, &[D(2), t1, D(0)]);
assert_jet(context, jet_mink, samp, rest);
}

View File

@ -13,9 +13,9 @@ pub fn jet_lent(_context: &mut Context, subject: Noun) -> Result {
}
pub mod util {
use crate::interpreter::Failure;
use crate::interpreter::Error;
use crate::jets::JetErr;
use crate::noun::Noun;
use crate::noun::{Noun, D};
pub fn lent(tape: Noun) -> Result<usize, JetErr> {
let mut len = 0usize;
@ -25,7 +25,7 @@ pub mod util {
if atom.as_bitslice().first_one().is_none() {
break;
} else {
return Err(JetErr::Fail(Failure::Deterministic));
return Err(JetErr::Fail(Error::Deterministic(D(0))));
}
}
let cell = list.as_cell()?;
@ -40,7 +40,7 @@ pub mod util {
#[cfg(test)]
mod tests {
use super::*;
use crate::interpreter::Failure;
use crate::interpreter::Error;
use crate::jets::util::test::{assert_jet, assert_jet_err, init_context};
use crate::jets::JetErr;
use crate::noun::{D, T};
@ -54,8 +54,8 @@ mod tests {
assert_jet(c, jet_lent, sam, D(3));
let sam = T(&mut c.stack, &[D(3), D(2), D(1), D(0)]);
assert_jet(c, jet_lent, sam, D(3));
assert_jet_err(c, jet_lent, D(1), JetErr::Fail(Failure::Deterministic));
assert_jet_err(c, jet_lent, D(1), JetErr::Fail(Error::Deterministic(D(0))));
let sam = T(&mut c.stack, &[D(3), D(2), D(1)]);
assert_jet_err(c, jet_lent, sam, JetErr::Fail(Failure::Deterministic));
assert_jet_err(c, jet_lent, sam, JetErr::Fail(Error::Deterministic(D(0))));
}
}

View File

@ -1,6 +1,6 @@
/** Tree jets
*/
use crate::interpreter::{Context, Failure};
use crate::interpreter::{Context, Error};
use crate::jets::util::*;
use crate::jets::{JetErr, Result};
use crate::noun::{Noun, D};
@ -14,7 +14,7 @@ pub fn jet_cap(_context: &mut Context, subject: Noun) -> Result {
unsafe {
if met < 2 {
Err(JetErr::Fail(Failure::Deterministic))
Err(JetErr::Fail(Error::Deterministic(D(0))))
} else if *(tom.as_bitslice().get_unchecked(met - 2)) {
Ok(D(3))
} else {
@ -30,7 +30,7 @@ pub fn jet_mas(context: &mut Context, subject: Noun) -> Result {
let met = met(0, tom);
if met < 2 {
Err(JetErr::Fail(Failure::Deterministic))
Err(JetErr::Fail(Error::Deterministic(D(0))))
} else {
let c = bex(stack, met - 1);
let d = bex(stack, met - 2);
@ -43,7 +43,7 @@ pub fn jet_mas(context: &mut Context, subject: Noun) -> Result {
#[cfg(test)]
mod tests {
use super::*;
use crate::interpreter::Failure;
use crate::interpreter::Error;
use crate::jets::util::test::{assert_jet, assert_jet_err, init_context};
use crate::jets::JetErr;
use crate::noun::D;
@ -52,8 +52,8 @@ mod tests {
fn test_cap() {
let c = &mut init_context();
assert_jet_err(c, jet_cap, D(0), JetErr::Fail(Failure::Deterministic));
assert_jet_err(c, jet_cap, D(1), JetErr::Fail(Failure::Deterministic));
assert_jet_err(c, jet_cap, D(0), JetErr::Fail(Error::Deterministic(D(0))));
assert_jet_err(c, jet_cap, D(1), JetErr::Fail(Error::Deterministic(D(0))));
assert_jet(c, jet_cap, D(2), D(2));
assert_jet(c, jet_cap, D(3), D(3));
@ -68,8 +68,8 @@ mod tests {
fn test_mas() {
let c = &mut init_context();
assert_jet_err(c, jet_mas, D(0), JetErr::Fail(Failure::Deterministic));
assert_jet_err(c, jet_mas, D(1), JetErr::Fail(Failure::Deterministic));
assert_jet_err(c, jet_mas, D(0), JetErr::Fail(Error::Deterministic(D(0))));
assert_jet_err(c, jet_mas, D(1), JetErr::Fail(Error::Deterministic(D(0))));
assert_jet(c, jet_mas, D(2), D(1));
assert_jet(c, jet_mas, D(3), D(1));

View File

@ -1,6 +1,6 @@
use crate::hamt::Hamt;
use crate::interpreter;
use crate::interpreter::{inc, interpret, Error, NockError};
use crate::interpreter::{inc, interpret, Error};
use crate::jets::cold::Cold;
use crate::jets::hot::Hot;
use crate::jets::nock::util::mook;
@ -264,7 +264,10 @@ fn slam(context: &mut Context, axis: u64, ovo: Noun) -> Result<Noun, Error> {
}
fn goof(context: &mut Context, trace: Noun) -> Noun {
let tone = Cell::new(&mut context.nock_context.stack, D(2), trace);
// XX: trace is a $tang with at least one $tank, which we pick off the top.
// Actual input to +mook should be (roll trace weld) ( or (zing trace)).
let head = trace.cell().unwrap().head();
let tone = Cell::new(&mut context.nock_context.stack, D(2), head);
let tang = mook(&mut context.nock_context, tone, false)
.expect("serf: goof: +mook crashed on bail")
.tail();
@ -279,12 +282,12 @@ fn soft(context: &mut Context, ovo: Noun) -> Result<Noun, Noun> {
match slam(context, POKE_AXIS, ovo) {
Ok(res) => Ok(res),
Err(error) => match error {
Error::Nock(nock_err) => match nock_err {
NockError::Deterministic(trace) | NockError::NonDeterministic(trace) => {
Err(goof(context, trace))
}
},
Error::Scry(_) => panic!("serf: soft: .^ invalid outside of virtual Nock"),
Error::Deterministic(trace) | Error::NonDeterministic(trace) => {
Err(goof(context, trace))
}
Error::ScryBlocked(_) | Error::ScryCrashed(_) => {
panic!("serf: soft: .^ invalid outside of virtual Nock")
}
},
}
}
@ -302,13 +305,13 @@ fn play_life(context: &mut Context, eve: Noun) {
context.play_done();
}
Err(error) => match error {
Error::Nock(nock_err) => match nock_err {
NockError::Deterministic(trace) | NockError::NonDeterministic(trace) => {
let goof = goof(context, trace);
context.play_bail(goof);
}
},
Error::Scry(_) => panic!("serf: soft: .^ invalid outside of virtual Nock"),
Error::Deterministic(trace) | Error::NonDeterministic(trace) => {
let goof = goof(context, trace);
context.play_bail(goof);
}
Error::ScryBlocked(_) | Error::ScryCrashed(_) => {
panic!("serf: soft: .^ invalid outside of virtual Nock")
}
},
}
}