From 767682307e77153d8278583a6c5bf8732e48bf8e Mon Sep 17 00:00:00 2001 From: Philip Monk Date: Tue, 19 Sep 2023 12:22:54 -0700 Subject: [PATCH 01/19] wip add crud handling --- rust/ares/src/serf.rs | 51 ++++++++++++++++++++++++++++++++++++++----- 1 file changed, 45 insertions(+), 6 deletions(-) diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 02cbcc4..2afb828 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -1,4 +1,4 @@ -use crate::interpreter::{interpret, NockErr}; +use crate::interpreter::{interpret, NockErr, inc}; use crate::jets::nock::util::mook; use crate::jets::text::util::lent; use crate::mem::NockStack; @@ -140,6 +140,7 @@ pub fn serf() -> io::Result<()> { } tas!(b"work") => { // XX: what is in slot 6? it's mil_w in Vere Serf + // TODO: assert event numbers are continuous let job = slot(writ, 7)?; match slam(stack, newt, arvo, POKE_AXIS, job) { Ok(res) => { @@ -155,17 +156,53 @@ pub fn serf() -> io::Result<()> { } Err(NockErr::Error(trace)) => { // XX: Our Arvo can't currently handle %crud, so just bail + // + // TODO: on decryption failure in aes_siv, should bail as fast as + // possible, without rendering stack trace or injecting crud event. See + // c3__evil in vere. + // let tone = Cell::new(stack, D(2), trace); let tang = mook(stack, &mut Some(newt), tone, false) - .expect("serf: play: +mook crashed on bail") + .expect("serf: work: +mook crashed on bail") .tail(); let goof = T(stack, &[D(tas!(b"exit")), tang]); - // lud = (list goof) - let lud = T(stack, &[goof, D(0)]); - newt.work_bail(stack, lud); + + // [+(now) [%$ %arvo ~] [%crud [%exit tang] ovo]] + let job_cell = job.as_cell().expect("serf: work: job not a cell"); + let now = inc(stack, job_cell.head().as_atom().expect("serf: work: now not atom")).as_noun(); + let wire = T(stack, &[D(0), D(tas!(b"arvo")), D(0)]); + let crud = T(stack, &[now, wire, D(tas!(b"crud")), goof, job_cell.tail()]); + + match slam(stack, newt, arvo, POKE_AXIS, crud) { + Ok(res) => { + let cell = res.as_cell().expect("serf: work: crud +slam returned atom"); + let fec = cell.head(); + arvo = cell.tail(); + snap.save(stack, &mut arvo); + + current_mug = mug_u32(stack, arvo); + current_event_num += 1; + + newt.work_swap(stack, current_event_num, current_mug as u64, crud, fec); + } + Err(NockErr::Error(trace)) => { + let tone = Cell::new(stack, D(2), trace); + let tang = mook(stack, &mut Some(newt), tone, false) + .expect("serf: work: crud +mook crashed on bail") + .tail(); + let goof_crud = T(stack, &[D(tas!(b"exit")), tang]); + + // lud = (list goof) + let lud = T(stack, &[goof_crud, goof, D(0)]); + newt.work_bail(stack, lud); + } + Err(NockErr::Blocked(_)) => { + panic!("work: crud blocked err handling unimplemented") + } + } } Err(NockErr::Blocked(_)) => { - panic!("play: blocked err handling unimplemented") + panic!("work: blocked err handling unimplemented") } } } @@ -190,6 +227,8 @@ pub fn slam( interpret(stack, &mut Some(newt), sub, fol) } +// pub fn soft() + fn slot(noun: Noun, axis: u64) -> io::Result { noun.slot(axis) .map_err(|_e| io::Error::new(io::ErrorKind::InvalidInput, "Bad axis")) From fc3df952fb55342b7b91d034e2e259a0bc973939 Mon Sep 17 00:00:00 2001 From: Philip Monk Date: Tue, 19 Sep 2023 12:53:21 -0700 Subject: [PATCH 02/19] refactor to use soft() --- rust/ares/src/serf.rs | 55 +++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 2afb828..efcb487 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -142,7 +142,7 @@ pub fn serf() -> io::Result<()> { // XX: what is in slot 6? it's mil_w in Vere Serf // TODO: assert event numbers are continuous let job = slot(writ, 7)?; - match slam(stack, newt, arvo, POKE_AXIS, job) { + match soft(stack, newt, arvo, POKE_AXIS, job) { Ok(res) => { let cell = res.as_cell().expect("serf: work: +slam returned atom"); let fec = cell.head(); @@ -154,26 +154,18 @@ pub fn serf() -> io::Result<()> { newt.work_done(stack, current_event_num, current_mug as u64, fec); } - Err(NockErr::Error(trace)) => { - // XX: Our Arvo can't currently handle %crud, so just bail - // + Err(goof) => { // TODO: on decryption failure in aes_siv, should bail as fast as // possible, without rendering stack trace or injecting crud event. See // c3__evil in vere. // - let tone = Cell::new(stack, D(2), trace); - let tang = mook(stack, &mut Some(newt), tone, false) - .expect("serf: work: +mook crashed on bail") - .tail(); - let goof = T(stack, &[D(tas!(b"exit")), tang]); - - // [+(now) [%$ %arvo ~] [%crud [%exit tang] ovo]] + // crud = [+(now) [%$ %arvo ~] [%crud goof ovo]] let job_cell = job.as_cell().expect("serf: work: job not a cell"); let now = inc(stack, job_cell.head().as_atom().expect("serf: work: now not atom")).as_noun(); let wire = T(stack, &[D(0), D(tas!(b"arvo")), D(0)]); let crud = T(stack, &[now, wire, D(tas!(b"crud")), goof, job_cell.tail()]); - match slam(stack, newt, arvo, POKE_AXIS, crud) { + match soft(stack, newt, arvo, POKE_AXIS, crud) { Ok(res) => { let cell = res.as_cell().expect("serf: work: crud +slam returned atom"); let fec = cell.head(); @@ -185,25 +177,13 @@ pub fn serf() -> io::Result<()> { newt.work_swap(stack, current_event_num, current_mug as u64, crud, fec); } - Err(NockErr::Error(trace)) => { - let tone = Cell::new(stack, D(2), trace); - let tang = mook(stack, &mut Some(newt), tone, false) - .expect("serf: work: crud +mook crashed on bail") - .tail(); - let goof_crud = T(stack, &[D(tas!(b"exit")), tang]); - + Err(goof_crud) => { // lud = (list goof) let lud = T(stack, &[goof_crud, goof, D(0)]); newt.work_bail(stack, lud); } - Err(NockErr::Blocked(_)) => { - panic!("work: crud blocked err handling unimplemented") - } } } - Err(NockErr::Blocked(_)) => { - panic!("work: blocked err handling unimplemented") - } } } _ => panic!("got message with unknown tag {}", tag), @@ -227,7 +207,30 @@ pub fn slam( interpret(stack, &mut Some(newt), sub, fol) } -// pub fn soft() +/** Run slam, then process stack trace to tang */ +pub fn soft( + stack: &mut NockStack, + newt: &mut Newt, + core: Noun, + axis: u64, + ovo: Noun, +) -> Result { + match slam(stack, newt, core, axis, ovo) { + Ok(res) => Ok(res), + Err(NockErr::Error(trace)) => { + let tone = Cell::new(stack, D(2), trace); + let tang = mook(stack, &mut Some(newt), tone, false) + .expect("serf: soft: +mook crashed on bail") + .tail(); + // TODO: shouldn't always be exit, look at the comment by u3m_bail to see how vere + // handles this + let goof = T(stack, &[D(tas!(b"exit")), tang]); + Err(goof) + } + Err(NockErr::Blocked(_)) => panic!("soft: blocked err handling unimplemented"), + } +} + fn slot(noun: Noun, axis: u64) -> io::Result { noun.slot(axis) From e5e0fad56904f5c75a244154337ace18e0d6a509 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Tue, 19 Sep 2023 18:36:46 -0600 Subject: [PATCH 03/19] slog %crud events in toddler --- hoon/scaffolding/playpen.hoon | 4 ++- hoon/scaffolding/toddler.hoon | 35 ++++++++++++++++++++++--- rust/ares/src/newt.rs | 49 +++++++++++++++++++++++++++++------ rust/ares/src/serf.rs | 3 +-- 4 files changed, 76 insertions(+), 15 deletions(-) diff --git a/hoon/scaffolding/playpen.hoon b/hoon/scaffolding/playpen.hoon index 5b3c3df..0eb9e78 100644 --- a/hoon/scaffolding/playpen.hoon +++ b/hoon/scaffolding/playpen.hoon @@ -10,6 +10,7 @@ :: +$ cord @t +$ knot @ta ++$ term @tas +$ char @t +$ ship @p +$ life @ud @@ -54,9 +55,10 @@ [%palm p=(qual tape tape tape tape) q=(list tank)] [%rose p=(trel tape tape tape) q=(list tank)] == ++$ tang (list tank) +$ toon $% [%0 p=*] [%1 p=*] - [%2 p=(list tank)] + [%2 p=tang] == ++ tree |$ [node] $@(~ [n=node l=(tree node) r=(tree node)]) ++ gate $-(* *) diff --git a/hoon/scaffolding/toddler.hoon b/hoon/scaffolding/toddler.hoon index 15df09a..79be6f9 100644 --- a/hoon/scaffolding/toddler.hoon +++ b/hoon/scaffolding/toddler.hoon @@ -8,11 +8,24 @@ != => |% - ++ cask |$ [a] (pair mark a) - +$ mark @tas +$ card (cask) + ++ cask |$ [a] (pair mark a) + +$ goof [mote=term =tang] + +$ mark @tas +$ ovum [=wire =card] + +$ vere [[non=@ta rev=path] kel=wynn] +$ wire path + +$ wasp + :: %crud: reroute $ovum with $goof + :: %wack: iterate entropy + :: %wyrd: check/record runtime kelvin stack + :: + $% [%crud =goof =ovum] + [%wack p=@uvJ] + [%wyrd p=vere] + == + +$ weft [lal=@tas num=@ud] + +$ wynn (list weft) :: mutually recursive Ackermann functions :: test turning %spot hints on/off ++ wack @@ -50,8 +63,22 @@ ++ poke |= [now=@da ovo=ovum] ^- ^ - ~> %slog.[0 leaf+(scow %ud (wack 2 1))] - [~ ..poke] + :: + ?. ?=(?(%crud %wack %wyrd) p.card.ovo) + ~> %slog.[0 leaf+(scow %ud (wack 1 1))] + [~ ..poke] + :: + =/ buz + ~> %mean.'pith: bad wasp' + ;;(wasp card.ovo) + ?+ -.buz + ~> %slog.[0 leaf+"default $wasp"] + [~ ..poke] + :: + %crud + ~> %slog.[0 goof.buz] + [~ ..poke] + == -- :: |= [now=@da ovo=ovum] diff --git a/rust/ares/src/newt.rs b/rust/ares/src/newt.rs index 6a8d8d9..2b073f7 100644 --- a/rust/ares/src/newt.rs +++ b/rust/ares/src/newt.rs @@ -110,7 +110,11 @@ impl Newt { self.output.write_all(&buf).unwrap(); } - /** Send %ripe, the first event. */ + /** Send %ripe, the first event. + * + * eve = event number + * mug = mug of Arvo after above event + */ pub fn ripe(&mut self, stack: &mut NockStack, eve: u64, mug: u64) { let version = T( stack, @@ -130,7 +134,11 @@ impl Newt { self.write_noun(stack, live); } - /** Send %slog, pretty-printed debug output. */ + /** Send %slog, pretty-printed debug output. + * + * pri = debug priority + * tank = output as tank + */ pub fn slog(&mut self, stack: &mut NockStack, pri: u64, tank: Noun) { let slog = T(stack, &[D(tas!(b"slog")), D(pri), tank]); self.write_noun(stack, slog); @@ -148,19 +156,30 @@ impl Newt { self.write_noun(stack, peek); } - /** Send %peek %bail, unsuccessfully scried. */ + /** Send %peek %bail, unsuccessfully scried. + * + * dud = goof + */ pub fn peek_bail(&mut self, stack: &mut NockStack, dud: Noun) { let peek = T(stack, &[D(tas!(b"peek")), D(tas!(b"bail")), dud]); self.write_noun(stack, peek); } - /** Send %play %done, successfully replayed events. */ + /** Send %play %done, successfully replayed events. + * + * mug = mug of Arvo after full replay + */ pub fn play_done(&mut self, stack: &mut NockStack, mug: u64) { let play = T(stack, &[D(tas!(b"play")), D(tas!(b"done")), D(mug)]); self.write_noun(stack, play); } - /** Send %play %bail, failed to replay events. */ + /** Send %play %bail, failed to replay events. + * + * eve = last good event number + * mug = mug of Arvo after above event + * dud = goof when trying next event + */ pub fn play_bail(&mut self, stack: &mut NockStack, eve: u64, mug: u64, dud: Noun) { let play = T( stack, @@ -169,7 +188,12 @@ impl Newt { self.write_noun(stack, play); } - /** Send %work %done, successfully ran event. */ + /** Send %work %done, successfully ran event. + * + * eve = new event number + * mug = mug of Arvo after above event + * fec = list of effects + */ pub fn work_done(&mut self, stack: &mut NockStack, eve: u64, mug: u64, fec: Noun) { let work = T( stack, @@ -178,7 +202,13 @@ impl Newt { self.write_noun(stack, work); } - /** Send %work %swap, successfully replaced failed event. */ + /** Send %work %swap, successfully replaced failed event. + * + * eve = new event number + * mug = mug of Arvo after above event + * job = event performed instead of the one given to serf by king + * fec = list of effects + */ pub fn work_swap(&mut self, stack: &mut NockStack, eve: u64, mug: u64, job: Noun, fec: Noun) { let work = T( stack, @@ -187,7 +217,10 @@ impl Newt { self.write_noun(stack, work); } - /** Send %work %bail, failed to run event. */ + /** Send %work %bail, failed to run event. + * + * lud = list of goof + */ pub fn work_bail(&mut self, stack: &mut NockStack, lud: Noun) { let work = T(stack, &[D(tas!(b"work")), D(tas!(b"bail")), lud]); self.write_noun(stack, work); diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index efcb487..2798d6e 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -178,7 +178,6 @@ pub fn serf() -> io::Result<()> { newt.work_swap(stack, current_event_num, current_mug as u64, crud, fec); } Err(goof_crud) => { - // lud = (list goof) let lud = T(stack, &[goof_crud, goof, D(0)]); newt.work_bail(stack, lud); } @@ -207,7 +206,7 @@ pub fn slam( interpret(stack, &mut Some(newt), sub, fol) } -/** Run slam, then process stack trace to tang */ +/** Run slam, process stack trace to tang if error */ pub fn soft( stack: &mut NockStack, newt: &mut Newt, From 95083f7b78a4b8f76100c3682eb67582ef7cd65a Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Tue, 19 Sep 2023 20:47:21 -0600 Subject: [PATCH 04/19] WIP 09/19 --- rust/ares/src/interpreter.rs | 105 +++++++++++++++++++++++++---------- 1 file changed, 77 insertions(+), 28 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 262cd3a..69460c4 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -227,7 +227,8 @@ enum NockWork { #[derive(Debug)] pub enum NockErr { Blocked(Noun), - Error(Noun), + DeterministicError(Noun), + NonDeterministicError(Noun), } impl From for () { @@ -887,43 +888,91 @@ fn match_hint_pre_hint( tag: Atom, hint: Noun, body: Noun, -) -> Option { +// ) -> Result, JetErr> { +// possible cases: +// 1. Deterministic error, no trace +// jet - nock mismatch +// need to add a mean hint, then fail +// 2. Deterministic error, trace +// Deterministic error in jet or nock +// 3. Nondeterministic error, trace +// Nondeterministic error in jet or nock +// +// +// 4. Nondeterministic error, no trace +// ??? does this exist? +// +) -> Result, NockErr> { // XX: handle IndirectAtom tags match tag.direct()?.data() { // %sham hints are scaffolding until we have a real jet dashboard tas!(b"sham") => { - let jet_formula = hint.cell()?; + let jet_formula = hint.as_cell()?; // XX: what is the head here? let jet_name = jet_formula.tail(); - let jet = jets::get_jet(jet_name)?; - if let Ok(mut jet_res) = jet(stack, newt, subject) { - // if in test mode, check that the jet returns the same result as the raw nock - if jets::get_jet_test_mode(jet_name) { - // Throw away trace because we'll regenerate it later, and this is in test mode - // so it's okay if it runs twice - interpret(stack, newt, subject, body) - .ok() - .map(|mut nock_res| { - if unsafe { !unifying_equality(stack, &mut nock_res, &mut jet_res) } { - eprintln!( - "\rJet {} failed, raw: {}, jetted: {}", - jet_name, nock_res, jet_res - ); - None - } else { - Some(jet_res) + if let Some(jet) = jets::get_jet(jet_name) { + match jet(stack, newt, subject) { + Ok(mut jet_res) => { + // if in test mode, check that the jet returns the same result as the raw nock + if jets::get_jet_test_mode(jet_name) { + // Throw away trace because we'll regenerate it later, and this is in test mode + // so it's okay if it runs twice + match interpret(stack, newt, subject, body) { + Ok(mut nock_res) => { + if unsafe { !unifying_equality(stack, &mut nock_res, &mut jet_res) } { + eprintln!( + "\rJet {} failed, raw: {}, jetted: {}", + jet_name, nock_res, jet_res + ); + Err(Deterministic) + } else { + Ok(Some(jet_res)) + } + } + Err(NockErr::DeterministicError(trace)) => { + + } } - }) - .unwrap() + + + interpret(stack, newt, subject, body) + .ok() + .map(|mut nock_res| { + if unsafe { !unifying_equality(stack, &mut nock_res, &mut jet_res) } { + eprintln!( + "\rJet {} failed, raw: {}, jetted: {}", + jet_name, nock_res, jet_res + ); + None + } else { + Some(jet_res) + } + }) + .unwrap() + + + + } else { + Ok(Some(jet_res)) + } + } + } + + + if let Ok(mut jet_res) = jet(stack, newt, subject) { + } else { - Some(jet_res) + // Print jet errors and punt to Nock + eprintln!("\rJet {} failed: ", jet_name); + None } } else { - // Print jet errors and punt to Nock - eprintln!("\rJet {} failed: ", jet_name); - None + Ok(None) } + + + } tas!(b"memo") => { let mut key = Cell::new(stack, subject, body).as_noun(); @@ -942,7 +991,7 @@ fn match_hint_pre_nock( _hint: Option, _body: Noun, res: Option, -) -> Option { +) -> Result, JetErr> { // XX: assert Some(res) <=> Some(hint) // XX: handle IndirectAtom tags @@ -1028,7 +1077,7 @@ fn match_hint_post_nock( _hint: Option, body: Noun, res: Noun, -) -> Option { +) -> Result, JetErr> { // XX: handle IndirectAtom tags match tag.direct()?.data() { tas!(b"memo") => { From 0072e09a8521205066ac3a6943c4e9e7f80e26d6 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Thu, 21 Sep 2023 00:04:20 -0600 Subject: [PATCH 05/19] Forward non-deterministic errors to the senior frame --- rust/ares/src/interpreter.rs | 347 ++++++++++++++++++----------------- rust/ares/src/jets.rs | 2 +- rust/ares/src/jets/form.rs | 4 +- rust/ares/src/jets/nock.rs | 36 ++-- rust/ares/src/jets/text.rs | 2 +- rust/ares/src/newt.rs | 18 +- rust/ares/src/serf.rs | 35 ++-- 7 files changed, 235 insertions(+), 209 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 69460c4..7f8c9f8 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -1,10 +1,12 @@ use crate::hamt::Hamt; use crate::jets; use crate::jets::nock::util::mook; +use crate::jets::JetErr; use crate::mem::unifying_equality; use crate::mem::NockStack; use crate::newt::Newt; -use crate::noun::{Atom, Cell, IndirectAtom, Noun, Slots, D, T}; +use crate::noun; +use crate::noun::{tape, Atom, Cell, IndirectAtom, Noun, Slots, D, T}; use ares_macros::tas; use assert_no_alloc::assert_no_alloc; use bitvec::prelude::{BitSlice, Lsb0}; @@ -226,14 +228,33 @@ enum NockWork { #[derive(Debug)] pub enum NockErr { + Deterministic, + NonDeterministic, +} + +#[derive(Debug)] +pub enum Tone { Blocked(Noun), - DeterministicError(Noun), - NonDeterministicError(Noun), + Error(NockErr, Noun), } impl From for () { - fn from(_: NockErr) -> Self { - () + fn from(_: NockErr) -> Self {} +} + +impl From for NockErr { + fn from(_: noun::Error) -> Self { + NockErr::Deterministic + } +} + +impl From for NockErr { + fn from(e: JetErr) -> Self { + match e { + JetErr::Deterministic => NockErr::Deterministic, + JetErr::NonDeterministic => NockErr::NonDeterministic, + JetErr::Punt => panic!("unhandled JetErr::Punt"), + } } } @@ -243,9 +264,8 @@ pub fn interpret( newt: &mut Option<&mut Newt>, // For printing slogs; if None, print to stdout mut subject: Noun, formula: Noun, -) -> Result { +) -> Result { let mut res: Noun = D(0); - let mut trace: Noun; let mut cache = Hamt::::new(); // XX: Should this come after initial frame_push()? let virtual_frame = stack.get_frame_pointer(); @@ -254,7 +274,6 @@ pub fn interpret( unsafe { *stack.push() = NockWork::Done; }; - push_formula(stack, formula, true)?; // DO NOT REMOVE THIS ASSERTION // // If you need to allocate for debugging, wrap the debugging code in @@ -266,7 +285,8 @@ pub fn interpret( // ``` // // (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use) - let tone = assert_no_alloc(|| unsafe { + let nock = assert_no_alloc(|| unsafe { + push_formula(stack, formula, true)?; loop { let work: NockWork = *stack.top(); match work { @@ -303,7 +323,7 @@ pub fn interpret( res = noun; stack.pop::(); } else { - break Err(NockErr::Error(D(1))); + break Err(NockErr::Deterministic); } } NockWork::Work1(once) => { @@ -364,7 +384,7 @@ pub fn interpret( stack.pop::(); } else { // Cannot increment (Nock 4) a cell - break Err(NockErr::Error(D(2))); + break Err(NockErr::Deterministic); } } }, @@ -405,11 +425,11 @@ pub fn interpret( push_formula(stack, cond.once, cond.tail)?; } else { // Test branch of Nock 6 must return 0 or 1 - break Err(NockErr::Error(D(3))); + break Err(NockErr::Deterministic); } } else { // Test branch of Nock 6 must return a direct atom - break Err(NockErr::Error(D(4))); + break Err(NockErr::Deterministic); } } }, @@ -484,7 +504,7 @@ pub fn interpret( } } else { // Axis into core must be atom - break Err(NockErr::Error(D(5))); + break Err(NockErr::Deterministic); } } Todo9::RestoreSubject => { @@ -513,20 +533,25 @@ pub fn interpret( } NockWork::Work11D(mut dint) => match dint.todo { Todo11D::ComputeHint => { - if let Some(found) = match_hint_pre_hint( + match match_hint_pre_hint( stack, newt, &cache, subject, dint.tag, dint.hint, dint.body, ) { - res = found; - stack.pop::(); - } else { - dint.todo = Todo11D::ComputeResult; - *stack.top() = NockWork::Work11D(dint); - push_formula(stack, dint.hint, false)?; + Ok(Some(found)) => { + res = found; + stack.pop::(); + } + Ok(None) => { + dint.todo = Todo11D::ComputeResult; + *stack.top() = NockWork::Work11D(dint); + push_formula(stack, dint.hint, false)?; + } + Err(err) => { + break Err(err); + } } } Todo11D::ComputeResult => { - dint.todo = Todo11D::Done; - if let Some(found) = match_hint_pre_nock( + match match_hint_pre_nock( stack, newt, subject, @@ -535,12 +560,18 @@ pub fn interpret( dint.body, Some(res), ) { - res = found; - stack.pop::(); - } else { - dint.todo = Todo11D::Done; - *stack.top() = NockWork::Work11D(dint); - push_formula(stack, dint.body, false)?; + Ok(Some(found)) => { + res = found; + stack.pop::(); + } + Ok(None) => { + dint.todo = Todo11D::Done; + *stack.top() = NockWork::Work11D(dint); + push_formula(stack, dint.body, false)?; + } + Err(err) => { + break Err(err); + } } } Todo11D::Done => { @@ -560,16 +591,21 @@ pub fn interpret( }, NockWork::Work11S(mut sint) => match sint.todo { Todo11S::ComputeResult => { - sint.todo = Todo11S::Done; - if let Some(found) = match_hint_pre_nock( + match match_hint_pre_nock( stack, newt, subject, sint.tag, None, sint.body, None, ) { - res = found; - stack.pop::(); - } else { - sint.todo = Todo11S::Done; - *stack.top() = NockWork::Work11S(sint); - push_formula(stack, sint.body, false)?; + Ok(Some(found)) => { + res = found; + stack.pop::(); + } + Ok(None) => { + sint.todo = Todo11S::Done; + *stack.top() = NockWork::Work11S(sint); + push_formula(stack, sint.body, false)?; + } + Err(err) => { + break Err(err); + } } } Todo11S::Done => { @@ -585,13 +621,9 @@ pub fn interpret( } }); - match tone { + match nock { Ok(res) => Ok(res), - Err(_err) => { - trace = stack.get_mean_stack(); - exit_early(stack, virtual_frame, &mut trace, &mut cache); - Err(NockErr::Error(trace)) - } + Err(err) => Err(exit_early(stack, &mut cache, virtual_frame, err)), } } @@ -615,7 +647,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(NockErr::Error(D(1))); + return Err(NockErr::Deterministic); } } 1 => { @@ -633,7 +665,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), }); } else { // Argument to Nock 2 must be cell - return Err(NockErr::Error(D(21))); + return Err(NockErr::Deterministic); }; } 3 => { @@ -657,7 +689,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), }); } else { // Argument to Nock 5 must be cell - return Err(NockErr::Error(D(51))); + return Err(NockErr::Deterministic); }; } 6 => { @@ -672,11 +704,11 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), }); } else { // Argument tail to Nock 6 must be cell - return Err(NockErr::Error(D(62))); + return Err(NockErr::Deterministic); }; } else { // Argument to Nock 6 must be cell - return Err(NockErr::Error(D(61))); + return Err(NockErr::Deterministic); } } 7 => { @@ -689,7 +721,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), }); } else { // Argument to Nock 7 must be cell - return Err(NockErr::Error(D(71))); + return Err(NockErr::Deterministic); }; } 8 => { @@ -702,7 +734,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), }); } else { // Argument to Nock 8 must be cell - return Err(NockErr::Error(D(81))); + return Err(NockErr::Deterministic); }; } 9 => { @@ -716,11 +748,11 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), }); } else { // Axis for Nock 9 must be an atom - return Err(NockErr::Error(D(92))); + return Err(NockErr::Deterministic); } } else { // Argument to Nock 9 must be cell - return Err(NockErr::Error(D(91))); + return Err(NockErr::Deterministic); }; } 10 => { @@ -735,15 +767,15 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), }); } else { // Axis for Nock 10 must be an atom - return Err(NockErr::Error(D(103))); + return Err(NockErr::Deterministic); } } else { // Heah of argument to Nock 10 must be a cell - return Err(NockErr::Error(D(102))); + return Err(NockErr::Deterministic); }; } else { // Argument to Nock 10 must be a cell - return Err(NockErr::Error(D(101))); + return Err(NockErr::Deterministic); }; } 11 => { @@ -766,29 +798,29 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), }); } else { // Hint tag must be an atom - return Err(NockErr::Error(D(112))); + return Err(NockErr::Deterministic); } } }; } else { // Argument for Nock 11 must be cell - return Err(NockErr::Error(D(111))); + return Err(NockErr::Deterministic); }; } _ => { // Invalid formula opcode - return Err(NockErr::Error(D(0))); + return Err(NockErr::Deterministic); } } } else { // Formula opcode must be direct atom - return Err(NockErr::Error(D(0))); + return Err(NockErr::Deterministic); } } } } else { // Bad formula: atoms are not formulas - return Err(NockErr::Error(D(0))); + return Err(NockErr::Deterministic); } } Ok(()) @@ -796,17 +828,19 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), pub fn exit_early( stack: &mut NockStack, - virtual_frame: *const u64, - trace: &mut Noun, cache: &mut Hamt, -) { + virtual_frame: *const u64, + error: NockErr, +) -> Tone { + let mut trace = stack.get_mean_stack(); unsafe { while stack.get_frame_pointer() != virtual_frame { - stack.preserve(trace); + stack.preserve(&mut trace); stack.preserve(cache); stack.frame_pop(); } - } + }; + Tone::Error(error, trace) } fn edit( @@ -888,23 +922,9 @@ fn match_hint_pre_hint( tag: Atom, hint: Noun, body: Noun, -// ) -> Result, JetErr> { -// possible cases: -// 1. Deterministic error, no trace -// jet - nock mismatch -// need to add a mean hint, then fail -// 2. Deterministic error, trace -// Deterministic error in jet or nock -// 3. Nondeterministic error, trace -// Nondeterministic error in jet or nock -// -// -// 4. Nondeterministic error, no trace -// ??? does this exist? -// ) -> Result, NockErr> { // XX: handle IndirectAtom tags - match tag.direct()?.data() { + match tag.as_direct()?.data() { // %sham hints are scaffolding until we have a real jet dashboard tas!(b"sham") => { let jet_formula = hint.as_cell()?; @@ -914,71 +934,69 @@ fn match_hint_pre_hint( if let Some(jet) = jets::get_jet(jet_name) { match jet(stack, newt, subject) { Ok(mut jet_res) => { + // XX: simplify this by moving jet test mode into the 11 code in interpret, or into its own function? // if in test mode, check that the jet returns the same result as the raw nock if jets::get_jet_test_mode(jet_name) { - // Throw away trace because we'll regenerate it later, and this is in test mode - // so it's okay if it runs twice + // XX: we throw away trace, which might matter for non-deterministic errors + // maybe mook and slog it? match interpret(stack, newt, subject, body) { Ok(mut nock_res) => { - if unsafe { !unifying_equality(stack, &mut nock_res, &mut jet_res) } { + if unsafe { + !unifying_equality(stack, &mut nock_res, &mut jet_res) + } { + // XX: need string interpolation without allocation, then delete eprintln + // let tape = tape(stack, "jet mismatch in {}, raw: {}, jetted: {}", jet_name, nock_res, jet_res); eprintln!( - "\rJet {} failed, raw: {}, jetted: {}", + "\rjet {} failed, raw: {:?}, jetted: {}", jet_name, nock_res, jet_res ); - Err(Deterministic) + let tape = tape(stack, "jet mismatch"); + let mean = T(stack, &[D(tas!(b"mean")), tape]); + stack.trace_push(mean); + Err(NockErr::Deterministic) } else { - Ok(Some(jet_res)) + Ok(Some(nock_res)) } } - Err(NockErr::DeterministicError(trace)) => { - + Err(Tone::Error(err, _)) => { + // XX: need string interpolation without allocation, then delete eprintln + // let tape = tape(stack, "jet mismatch in {}, raw: {}, jetted: {}", jet_name, err, jet_res); + eprintln!( + "\rjet {} failed, raw: {:?}, jetted: {}", + jet_name, err, jet_res + ); + let tape = tape(stack, "jet mismatch"); + let mean = T(stack, &[D(tas!(b"mean")), tape]); + stack.trace_push(mean); + Err(err) + } + Err(Tone::Blocked(_)) => { + panic!("jet test mode: no scry handling") } } - - - interpret(stack, newt, subject, body) - .ok() - .map(|mut nock_res| { - if unsafe { !unifying_equality(stack, &mut nock_res, &mut jet_res) } { - eprintln!( - "\rJet {} failed, raw: {}, jetted: {}", - jet_name, nock_res, jet_res - ); - None - } else { - Some(jet_res) - } - }) - .unwrap() - - - } else { Ok(Some(jet_res)) } } - } - - - if let Ok(mut jet_res) = jet(stack, newt, subject) { - - } else { - // Print jet errors and punt to Nock - eprintln!("\rJet {} failed: ", jet_name); - None + Err(JetErr::Punt) => Ok(None), + Err(err) => { + // XX: need string interpolation without allocation + // let tape = tape(stack, "{} jet error in {}", err, jet_name); + let tape = tape(stack, "jet error"); + let mean = T(stack, &[D(tas!(b"mean")), tape]); + stack.trace_push(mean); + Err(err.into()) + } } } else { Ok(None) } - - - } tas!(b"memo") => { let mut key = Cell::new(stack, subject, body).as_noun(); - cache.lookup(stack, &mut key) + Ok(cache.lookup(stack, &mut key)) } - _ => None, + _ => Ok(None), } } @@ -991,41 +1009,27 @@ fn match_hint_pre_nock( _hint: Option, _body: Noun, res: Option, -) -> Result, JetErr> { +) -> Result, NockErr> { // XX: assert Some(res) <=> Some(hint) // XX: handle IndirectAtom tags - match tag.direct()?.data() { + match tag.as_direct()?.data() { tas!(b"slog") => { - let slog_cell = res?.cell()?; - let pri = slog_cell.head().direct()?.data(); + let slog_cell = res.ok_or(NockErr::Deterministic)?.as_cell()?; + let pri = slog_cell.head().as_direct()?.data(); let tank = slog_cell.tail(); if let Some(not) = newt { not.slog(stack, pri, tank); } else { eprintln!("raw slog: {} {}", pri, tank); } + Ok(None) } tas!(b"hand") | tas!(b"hunk") | tas!(b"lose") | tas!(b"mean") | tas!(b"spot") => { - let trace = Cell::new(stack, tag.as_noun(), res?).as_noun(); + let trace = T(stack, &[tag.as_noun(), res.ok_or(NockErr::Deterministic)?]); stack.trace_push(trace); + Ok(None) } - // - // u3_serf_writ -> u3_serf_work -> _serf_work -> _serf_poke -> u3m_soft -> u3dc -> u3v_do -> u3v_wish -> +wish in Arvo - // | - // V - // mook - // - // No +wish in toy Arvo; missing +slap and a ton of parsing functions needed by +ream - // - // u3t_slog = print on thing directly - // u3t_slog_trace = print stack trace = - convert tone to toon - // - presume toon is [%2 tang] - // - print each tank in tang one at at time using u3t_slog - // u3t_slog_hela = print entire stack trace = - weld stacks from all roads together - // - call u3t_slog_trace on combined stack - // u3t_slog_nara = print home road stack trace = call u3t_slog_trace on home road stack - // tas!(b"hela") => { // XX: should this be virtualized? // pretty sure we should be bailing on error @@ -1033,39 +1037,44 @@ fn match_hint_pre_nock( let stak = stack.get_mean_stack(); let tone = Cell::new(stack, D(2), stak); - if let Ok(toon) = mook(stack, newt, tone, true) { - if unsafe { !toon.head().raw_equals(D(2)) } { - // Print jet error and punt to Nock - eprintln!("\r%hela failed: toon not %2"); - return None; - } - - let mut list = toon.tail(); - loop { - if unsafe { list.raw_equals(D(0)) } { - break; + match mook(stack, newt, tone, true) { + Ok(toon) => { + if unsafe { !toon.head().raw_equals(D(2)) } { + let tape = tape(stack, "%hela failed: toon not %2"); + let mean = T(stack, &[D(tas!(b"mean")), tape]); + stack.trace_push(mean); + return Err(NockErr::Deterministic); } - let cell = list.as_cell().unwrap(); - if let Some(not) = newt { - // XX: %hela priority is configurable, but I'm not sure how - not.slog(stack, 0, cell.head()); - } else { - eprintln!("raw slog: {} {}", 0, cell.head()); + let mut list = toon.tail(); + loop { + if unsafe { list.raw_equals(D(0)) } { + break; + } + + let cell = list.as_cell().unwrap(); + if let Some(not) = newt { + // XX: %hela priority is configurable, but I'm not sure how + not.slog(stack, 0, cell.head()); + } else { + eprintln!("raw slog: {} {}", 0, cell.head()); + } + + list = cell.tail(); } - list = cell.tail(); + Ok(None) + } + Err(err) => { + let tape = tape(stack, "%hela failed: mook error"); + let mean = T(stack, &[D(tas!(b"mean")), tape]); + stack.trace_push(mean); + Err(err.into()) } - } else { - // Print jet errors and punt to Nock - eprintln!("\r%hela failed: mook error"); - return None; } } - _ => {} + _ => Ok(None), } - - None } /** Match static and dynamic hints after the nock formula is evaluated */ @@ -1077,7 +1086,7 @@ fn match_hint_post_nock( _hint: Option, body: Noun, res: Noun, -) -> Result, JetErr> { +) -> Option { // XX: handle IndirectAtom tags match tag.direct()?.data() { tas!(b"memo") => { @@ -1085,7 +1094,7 @@ fn match_hint_post_nock( *cache = cache.insert(stack, &mut key, res); } tas!(b"hand") | tas!(b"hunk") | tas!(b"lose") | tas!(b"mean") | tas!(b"spot") => { - // In the future, we should only do this if 11 is not in tail position + // XX: we should only do this if 11 is not in tail position stack.trace_pop(); } _ => {} diff --git a/rust/ares/src/jets.rs b/rust/ares/src/jets.rs index fccb087..0a185d5 100644 --- a/rust/ares/src/jets.rs +++ b/rust/ares/src/jets.rs @@ -172,7 +172,7 @@ pub mod util { let step = cell.tail().as_direct()?.data() as usize; Ok((bloq, step)) } else { - bloq(a).map(|x| (x, 1 as usize)) + bloq(a).map(|x| (x, 1_usize)) } } diff --git a/rust/ares/src/jets/form.rs b/rust/ares/src/jets/form.rs index bdbc907..494e15e 100644 --- a/rust/ares/src/jets/form.rs +++ b/rust/ares/src/jets/form.rs @@ -29,13 +29,13 @@ pub mod util { ) -> jets::Result { match aura.data() { tas!(b"ud") => { - if let None = atom.as_bitslice().first_one() { + if atom.as_bitslice().first_one().is_none() { return Ok(T(stack, &[D(b'0' as u64), D(0)])); } let mut root = D(0); let mut lent = 0; - if let Some(_) = atom.direct() { + if atom.direct().is_some() { let mut n = atom.as_direct()?.data(); while n != 0 { diff --git a/rust/ares/src/jets/nock.rs b/rust/ares/src/jets/nock.rs index a94f60f..4650b98 100644 --- a/rust/ares/src/jets/nock.rs +++ b/rust/ares/src/jets/nock.rs @@ -1,8 +1,9 @@ /** Virtualization jets */ -use crate::interpreter::{interpret, NockErr}; +use crate::interpreter::{interpret, NockErr, Tone}; use crate::jets; use crate::jets::util::slot; +use crate::jets::JetErr; use crate::mem::NockStack; use crate::newt::Newt; use crate::noun::{Noun, D, T}; @@ -22,12 +23,14 @@ pub fn jet_mink( let v_formula = slot(arg, 5)?; let _scry = slot(arg, 3)?; - // XX: NonDeterministic errors need to bail here, too match interpret(stack, newt, v_subject, v_formula) { Ok(res) => Ok(T(stack, &[D(0), res])), Err(err) => match err { - NockErr::Blocked(block) => Ok(T(stack, &[D(1), block])), - NockErr::Error(error) => Ok(T(stack, &[D(2), error])), + Tone::Blocked(block) => Ok(T(stack, &[D(1), block])), + Tone::Error(err, trace) => match err { + NockErr::Deterministic => Ok(T(stack, &[D(2), trace])), + NockErr::NonDeterministic => Err(JetErr::NonDeterministic), + }, }, } } @@ -58,15 +61,20 @@ pub mod util { let tag = tone.head().as_direct()?; let original_list = tone.tail(); - if tag.data() < 2 { - return Ok(tone); - } else if tag.data() > 2 { - return Err(JetErr::Deterministic); - } + // if tag.data() < 2 { + // return Ok(tone); + // } else if tag.data() > 2 { + // return Err(JetErr::Deterministic); + // } + match tag.data() { + x if x < 2 => return Ok(tone), + x if x > 2 => return Err(JetErr::Deterministic), + _ => {} + }; if unsafe { original_list.raw_equals(D(0)) } { return Ok(tone); - } else if let Some(_) = original_list.atom() { + } else if original_list.atom().is_some() { return Err(JetErr::Deterministic); } @@ -135,7 +143,7 @@ pub mod util { let mut list = end_col.as_cell()?; loop { - if let Some(_) = list.tail().atom() { + if list.tail().atom().is_some() { break; } list = list.tail().as_cell()?; @@ -146,7 +154,7 @@ pub mod util { list = end_lin.as_cell()?; loop { - if let Some(_) = list.tail().atom() { + if list.tail().atom().is_some() { break; } list = list.tail().as_cell()?; @@ -157,7 +165,7 @@ pub mod util { list = str_col.as_cell()?; loop { - if let Some(_) = list.tail().atom() { + if list.tail().atom().is_some() { break; } list = list.tail().as_cell()?; @@ -171,7 +179,7 @@ pub mod util { list = str_lin.as_cell()?; loop { - if let Some(_) = list.tail().atom() { + if list.tail().atom().is_some() { break; } list = list.tail().as_cell()?; diff --git a/rust/ares/src/jets/text.rs b/rust/ares/src/jets/text.rs index 87692bd..486c87e 100644 --- a/rust/ares/src/jets/text.rs +++ b/rust/ares/src/jets/text.rs @@ -22,7 +22,7 @@ pub mod util { let mut list = tape; loop { if let Some(atom) = list.atom() { - if let None = atom.as_bitslice().first_one() { + if atom.as_bitslice().first_one().is_none() { break; } else { return Err(JetErr::Deterministic); diff --git a/rust/ares/src/newt.rs b/rust/ares/src/newt.rs index 2b073f7..1b996a9 100644 --- a/rust/ares/src/newt.rs +++ b/rust/ares/src/newt.rs @@ -107,11 +107,11 @@ impl Newt { ); }, }; - self.output.write_all(&buf).unwrap(); + self.output.write_all(buf).unwrap(); } /** Send %ripe, the first event. - * + * * eve = event number * mug = mug of Arvo after above event */ @@ -135,7 +135,7 @@ impl Newt { } /** Send %slog, pretty-printed debug output. - * + * * pri = debug priority * tank = output as tank */ @@ -157,7 +157,7 @@ impl Newt { } /** Send %peek %bail, unsuccessfully scried. - * + * * dud = goof */ pub fn peek_bail(&mut self, stack: &mut NockStack, dud: Noun) { @@ -166,7 +166,7 @@ impl Newt { } /** Send %play %done, successfully replayed events. - * + * * mug = mug of Arvo after full replay */ pub fn play_done(&mut self, stack: &mut NockStack, mug: u64) { @@ -175,7 +175,7 @@ impl Newt { } /** Send %play %bail, failed to replay events. - * + * * eve = last good event number * mug = mug of Arvo after above event * dud = goof when trying next event @@ -189,7 +189,7 @@ impl Newt { } /** Send %work %done, successfully ran event. - * + * * eve = new event number * mug = mug of Arvo after above event * fec = list of effects @@ -203,7 +203,7 @@ impl Newt { } /** Send %work %swap, successfully replaced failed event. - * + * * eve = new event number * mug = mug of Arvo after above event * job = event performed instead of the one given to serf by king @@ -218,7 +218,7 @@ impl Newt { } /** Send %work %bail, failed to run event. - * + * * lud = list of goof */ pub fn work_bail(&mut self, stack: &mut NockStack, lud: Noun) { diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 2798d6e..dc76449 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -1,4 +1,4 @@ -use crate::interpreter::{interpret, NockErr, inc}; +use crate::interpreter::{inc, interpret, Tone}; use crate::jets::nock::util::mook; use crate::jets::text::util::lent; use crate::mem::NockStack; @@ -82,7 +82,6 @@ pub fn serf() -> io::Result<()> { let eve = slot(writ, 7)?; let sub = T(stack, &[D(0), D(3)]); let lyf = T(stack, &[D(2), sub, D(0), D(2)]); - // XX: TODO match interpret(stack, &mut Some(newt), eve, lyf) { Ok(gat) => { arvo = slot(gat, 7) @@ -91,7 +90,7 @@ pub fn serf() -> io::Result<()> { lent(eve).expect("serf: play: boot event number failure") as u64; current_mug = mug_u32(stack, arvo); } - Err(NockErr::Error(trace)) => { + Err(Tone::Error(_, trace)) => { let tone = Cell::new(stack, D(2), trace); let tang = mook(stack, &mut Some(newt), tone, false) .expect("serf: play: +mook crashed on bail") @@ -99,7 +98,7 @@ pub fn serf() -> io::Result<()> { let goof = T(stack, &[D(tas!(b"exit")), tang]); newt.play_bail(stack, 0, 0, goof); } - Err(NockErr::Blocked(_)) => { + Err(Tone::Blocked(_)) => { panic!("play: blocked err handling unimplemented") } } @@ -118,7 +117,7 @@ pub fn serf() -> io::Result<()> { current_mug = mug_u32(stack, arvo); current_event_num += 1; } - Err(NockErr::Error(trace)) => { + Err(Tone::Error(_, trace)) => { let tone = Cell::new(stack, D(2), trace); let tang = mook(stack, &mut Some(newt), tone, false) .expect("serf: play: +mook crashed on bail") @@ -126,7 +125,7 @@ pub fn serf() -> io::Result<()> { let goof = T(stack, &[D(tas!(b"exit")), tang]); newt.play_bail(stack, current_event_num, current_mug as u64, goof); } - Err(NockErr::Blocked(_)) => { + Err(Tone::Blocked(_)) => { panic!("play: blocked err handling unimplemented") } } @@ -161,13 +160,18 @@ pub fn serf() -> io::Result<()> { // // crud = [+(now) [%$ %arvo ~] [%crud goof ovo]] let job_cell = job.as_cell().expect("serf: work: job not a cell"); - let now = inc(stack, job_cell.head().as_atom().expect("serf: work: now not atom")).as_noun(); + let now = inc( + stack, + job_cell.head().as_atom().expect("serf: work: now not atom"), + ) + .as_noun(); let wire = T(stack, &[D(0), D(tas!(b"arvo")), D(0)]); let crud = T(stack, &[now, wire, D(tas!(b"crud")), goof, job_cell.tail()]); match soft(stack, newt, arvo, POKE_AXIS, crud) { Ok(res) => { - let cell = res.as_cell().expect("serf: work: crud +slam returned atom"); + let cell = + res.as_cell().expect("serf: work: crud +slam returned atom"); let fec = cell.head(); arvo = cell.tail(); snap.save(stack, &mut arvo); @@ -175,7 +179,13 @@ pub fn serf() -> io::Result<()> { current_mug = mug_u32(stack, arvo); current_event_num += 1; - newt.work_swap(stack, current_event_num, current_mug as u64, crud, fec); + newt.work_swap( + stack, + current_event_num, + current_mug as u64, + crud, + fec, + ); } Err(goof_crud) => { let lud = T(stack, &[goof_crud, goof, D(0)]); @@ -198,7 +208,7 @@ pub fn slam( core: Noun, axis: u64, ovo: Noun, -) -> Result { +) -> Result { let pul = T(stack, &[D(9), D(axis), D(0), D(2)]); let sam = T(stack, &[D(6), D(0), D(7)]); let fol = T(stack, &[D(8), pul, D(9), D(2), D(10), sam, D(0), D(2)]); @@ -216,7 +226,7 @@ pub fn soft( ) -> Result { match slam(stack, newt, core, axis, ovo) { Ok(res) => Ok(res), - Err(NockErr::Error(trace)) => { + Err(Tone::Error(_, trace)) => { let tone = Cell::new(stack, D(2), trace); let tang = mook(stack, &mut Some(newt), tone, false) .expect("serf: soft: +mook crashed on bail") @@ -226,11 +236,10 @@ pub fn soft( let goof = T(stack, &[D(tas!(b"exit")), tang]); Err(goof) } - Err(NockErr::Blocked(_)) => panic!("soft: blocked err handling unimplemented"), + Err(Tone::Blocked(_)) => panic!("soft: blocked err handling unimplemented"), } } - fn slot(noun: Noun, axis: u64) -> io::Result { noun.slot(axis) .map_err(|_e| io::Error::new(io::ErrorKind::InvalidInput, "Bad axis")) From ae674c1372adbd42919d7df55e082f7c9ed1679d Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Mon, 25 Sep 2023 00:08:51 -0600 Subject: [PATCH 06/19] Add SIGINT handler --- hoon/scaffolding/toddler.hoon | 26 +++++-- rust/ares/Cargo.lock | 21 +++++ rust/ares/Cargo.toml | 24 +++--- rust/ares/src/interpreter.rs | 141 +++++++++++++++++++++------------- rust/ares/src/jets/nock.rs | 23 ++++-- rust/ares/src/lib.rs | 2 + rust/ares/src/serf.rs | 31 ++++++-- 7 files changed, 185 insertions(+), 83 deletions(-) diff --git a/hoon/scaffolding/toddler.hoon b/hoon/scaffolding/toddler.hoon index 79be6f9..9eb8d31 100644 --- a/hoon/scaffolding/toddler.hoon +++ b/hoon/scaffolding/toddler.hoon @@ -35,7 +35,7 @@ :: %mean hint ~_ [%leaf "I am a %mean hint via ~_ from +wack"] :: %hela hint - :: ~> %hela + ~> %hela :: %memo hint ~+ ?~ m +(n) @@ -47,13 +47,23 @@ !. |= [m=@ud n=@ud] :: %hela hint - :: ~> %hela + ~> %hela :: %memo hint ~+ ?~ m +(n) ?~ n (wack (dec m) 1) (wack (dec m) $(n (dec n))) + ++ slow + |= x=@ud + !: + (slow-help x 0) + ++ slow-help + |= [x=@ud y=@ud] + !. + ?: .= x y + x + $(y .+(y)) -- => :: |% @@ -65,19 +75,23 @@ ^- ^ :: ?. ?=(?(%crud %wack %wyrd) p.card.ovo) - ~> %slog.[0 leaf+(scow %ud (wack 1 1))] + ~> %slog.[0 leaf+(scow %ud (slow (bex 23)))] [~ ..poke] :: =/ buz ~> %mean.'pith: bad wasp' ;;(wasp card.ovo) ?+ -.buz - ~> %slog.[0 leaf+"default $wasp"] + ~> %slog.[0 leaf+(scow %ud (wack 1 1))] [~ ..poke] :: %crud - ~> %slog.[0 goof.buz] - [~ ..poke] + =/ tang tang.goof.buz + |- + ?~ tang + [~ ..poke] + ~> %slog.[0 -.tang] + $(tang +.tang) == -- :: diff --git a/rust/ares/Cargo.lock b/rust/ares/Cargo.lock index fb69b32..a99638f 100644 --- a/rust/ares/Cargo.lock +++ b/rust/ares/Cargo.lock @@ -20,11 +20,13 @@ dependencies = [ "either", "ibig", "intmap", + "lazy_static", "libc", "memmap", "murmur3", "num-derive", "num-traits", + "signal-hook", "static_assertions", ] @@ -569,6 +571,25 @@ dependencies = [ "serde", ] +[[package]] +name = "signal-hook" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +dependencies = [ + "libc", +] + [[package]] name = "static_assertions" version = "1.1.0" diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 88bd51a..23f7368 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -11,18 +11,20 @@ edition = "2018" [dependencies] ares_macros = { path = "../ares_macros" } -bitvec = "1.0.0" -either = "1.9.0" -libc = "0.2.126" -murmur3 = { git = "https://github.com/tloncorp/murmur3", rev = "7878a0f" } -memmap = "0.7.0" -intmap = "1.1.0" -num-traits = "0.2" -num-derive = "0.3" -criterion = "0.4" -static_assertions = "1.1.0" -ibig = { path = "../ibig-rs" } assert_no_alloc = "1.1.2" +bitvec = "1.0.0" +criterion = "0.4" +either = "1.9.0" +ibig = { path = "../ibig-rs" } +intmap = "1.1.0" +lazy_static = "1.4.0" +libc = "0.2.126" +memmap = "0.7.0" +murmur3 = { git = "https://github.com/tloncorp/murmur3", rev = "7878a0f" } +num-derive = "0.3" +num-traits = "0.2" +signal-hook = "0.3" +static_assertions = "1.1.0" [build-dependencies] cc = "1.0.79" diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 7f8c9f8..984ece0 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -7,10 +7,13 @@ use crate::mem::NockStack; use crate::newt::Newt; use crate::noun; use crate::noun::{tape, Atom, Cell, IndirectAtom, Noun, Slots, D, T}; +use crate::serf::TERMINATOR; use ares_macros::tas; use assert_no_alloc::assert_no_alloc; use bitvec::prelude::{BitSlice, Lsb0}; use either::Either::*; +use std::sync::atomic::Ordering; +use std::sync::Arc; crate::gdb!(); @@ -265,10 +268,12 @@ pub fn interpret( mut subject: Noun, formula: Noun, ) -> Result { + let terminator = Arc::clone(&TERMINATOR); let mut res: Noun = D(0); let mut cache = Hamt::::new(); // XX: Should this come after initial frame_push()? let virtual_frame = stack.get_frame_pointer(); + let virtual_trace = stack.get_mean_stack(); stack.frame_push(0); unsafe { @@ -323,6 +328,7 @@ pub fn interpret( res = noun; stack.pop::(); } else { + // Axis invalid for input Noun break Err(NockErr::Deterministic); } } @@ -330,37 +336,43 @@ pub fn interpret( res = once.noun; stack.pop::(); } - NockWork::Work2(mut vale) => match vale.todo { - Todo2::ComputeSubject => { - vale.todo = Todo2::ComputeFormula; - *stack.top() = NockWork::Work2(vale); - push_formula(stack, vale.subject, false)?; + NockWork::Work2(mut vale) => { + if (*terminator).load(Ordering::Relaxed) { + break Err(NockErr::NonDeterministic); } - Todo2::ComputeFormula => { - vale.todo = Todo2::ComputeResult; - vale.subject = res; - *stack.top() = NockWork::Work2(vale); - push_formula(stack, vale.formula, false)?; - } - Todo2::ComputeResult => { - if vale.tail { - stack.pop::(); - subject = vale.subject; - push_formula(stack, res, true)?; - } else { - vale.todo = Todo2::RestoreSubject; - std::mem::swap(&mut vale.subject, &mut subject); + + match vale.todo { + Todo2::ComputeSubject => { + vale.todo = Todo2::ComputeFormula; *stack.top() = NockWork::Work2(vale); - stack.frame_push(0); - *stack.push() = NockWork::Ret; - push_formula(stack, res, true)?; + push_formula(stack, vale.subject, false)?; + } + Todo2::ComputeFormula => { + vale.todo = Todo2::ComputeResult; + vale.subject = res; + *stack.top() = NockWork::Work2(vale); + push_formula(stack, vale.formula, false)?; + } + Todo2::ComputeResult => { + if vale.tail { + stack.pop::(); + subject = vale.subject; + push_formula(stack, res, true)?; + } else { + vale.todo = Todo2::RestoreSubject; + std::mem::swap(&mut vale.subject, &mut subject); + *stack.top() = NockWork::Work2(vale); + stack.frame_push(0); + *stack.push() = NockWork::Ret; + push_formula(stack, res, true)?; + } + } + Todo2::RestoreSubject => { + subject = vale.subject; + stack.pop::(); } } - Todo2::RestoreSubject => { - subject = vale.subject; - stack.pop::(); - } - }, + } NockWork::Work3(mut thee) => match thee.todo { Todo3::ComputeChild => { thee.todo = Todo3::ComputeType; @@ -481,37 +493,43 @@ pub fn interpret( stack.pop::(); } }, - NockWork::Work9(mut kale) => match kale.todo { - Todo9::ComputeCore => { - kale.todo = Todo9::ComputeResult; - *stack.top() = NockWork::Work9(kale); - push_formula(stack, kale.core, false)?; + NockWork::Work9(mut kale) => { + if (*terminator).load(Ordering::Relaxed) { + break Err(NockErr::NonDeterministic); } - Todo9::ComputeResult => { - if let Ok(formula) = res.slot_atom(kale.axis) { - if kale.tail { - stack.pop::(); - subject = res; - push_formula(stack, formula, true)?; + + match kale.todo { + Todo9::ComputeCore => { + kale.todo = Todo9::ComputeResult; + *stack.top() = NockWork::Work9(kale); + push_formula(stack, kale.core, false)?; + } + Todo9::ComputeResult => { + if let Ok(formula) = res.slot_atom(kale.axis) { + if kale.tail { + stack.pop::(); + subject = res; + push_formula(stack, formula, true)?; + } else { + kale.todo = Todo9::RestoreSubject; + kale.core = subject; + *stack.top() = NockWork::Work9(kale); + subject = res; + stack.frame_push(0); + *stack.push() = NockWork::Ret; + push_formula(stack, formula, true)?; + } } else { - kale.todo = Todo9::RestoreSubject; - kale.core = subject; - *stack.top() = NockWork::Work9(kale); - subject = res; - stack.frame_push(0); - *stack.push() = NockWork::Ret; - push_formula(stack, formula, true)?; + // Axis into core must be atom + break Err(NockErr::Deterministic); } - } else { - // Axis into core must be atom - break Err(NockErr::Deterministic); + } + Todo9::RestoreSubject => { + subject = kale.core; + stack.pop::(); } } - Todo9::RestoreSubject => { - subject = kale.core; - stack.pop::(); - } - }, + } NockWork::Work10(mut diet) => { match diet.todo { Todo10::ComputeTree => { @@ -623,7 +641,13 @@ pub fn interpret( match nock { Ok(res) => Ok(res), - Err(err) => Err(exit_early(stack, &mut cache, virtual_frame, err)), + Err(err) => Err(exit_early( + stack, + &mut cache, + virtual_frame, + virtual_trace, + err, + )), } } @@ -830,6 +854,7 @@ pub fn exit_early( stack: &mut NockStack, cache: &mut Hamt, virtual_frame: *const u64, + virtual_trace: Noun, error: NockErr, ) -> Tone { let mut trace = stack.get_mean_stack(); @@ -839,6 +864,9 @@ pub fn exit_early( stack.preserve(cache); stack.frame_pop(); } + while !stack.get_mean_stack().raw_equals(virtual_trace) { + stack.trace_pop(); + } }; Tone::Error(error, trace) } @@ -1026,6 +1054,11 @@ fn match_hint_pre_nock( Ok(None) } 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(NockErr::NonDeterministic); + } + let trace = T(stack, &[tag.as_noun(), res.ok_or(NockErr::Deterministic)?]); stack.trace_push(trace); Ok(None) diff --git a/rust/ares/src/jets/nock.rs b/rust/ares/src/jets/nock.rs index 4650b98..322b7e7 100644 --- a/rust/ares/src/jets/nock.rs +++ b/rust/ares/src/jets/nock.rs @@ -10,7 +10,7 @@ use crate::noun::{Noun, D, T}; crate::gdb!(); -// XX: interpret should accept optional scry function and potentially produce blocked +// XX: interpret should accept optional scry function and potentially produce blocked pub fn jet_mink( stack: &mut NockStack, newt: &mut Option<&mut Newt>, @@ -23,6 +23,7 @@ pub fn jet_mink( let v_formula = slot(arg, 5)?; let _scry = slot(arg, 3)?; + // XX: no partial traces; all of our traces go down to the "home road" match interpret(stack, newt, v_subject, v_formula) { Ok(res) => Ok(T(stack, &[D(0), res])), Err(err) => match err { @@ -61,11 +62,6 @@ pub mod util { let tag = tone.head().as_direct()?; let original_list = tone.tail(); - // if tag.data() < 2 { - // return Ok(tone); - // } else if tag.data() > 2 { - // return Err(JetErr::Deterministic); - // } match tag.data() { x if x < 2 => return Ok(tone), x if x > 2 => return Err(JetErr::Deterministic), @@ -260,6 +256,21 @@ pub mod util { mod tests { use super::*; use crate::jets::util::test::{assert_jet, init_stack}; + use crate::serf::TERMINATOR; + use std::sync::Arc; + + #[test] + fn init() { + // This needs to be done because TERMINATOR is lazy allocated, and if you don't + // do it before you call the unit tests it'll get allocated on the Rust heap + // inside an assert_no_alloc block. + // + // Also Rust has no primitive for pre-test setup / post-test teardown, so we + // do it in a test that we rely on being called before any other in this file, + // since we're already using single-threaded test mode to avoid race conditions + // (because Rust doesn't support test order dependencies either). + let _ = Arc::clone(&TERMINATOR); + } #[test] fn test_mink_success() { diff --git a/rust/ares/src/lib.rs b/rust/ares/src/lib.rs index 5ae5ba2..23e67e1 100644 --- a/rust/ares/src/lib.rs +++ b/rust/ares/src/lib.rs @@ -1,5 +1,7 @@ extern crate num_derive; #[macro_use] +extern crate lazy_static; +#[macro_use] extern crate static_assertions; pub mod interpreter; pub mod jets; diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index dc76449..92dfe68 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -7,12 +7,14 @@ use crate::newt::Newt; use crate::noun::{Cell, Noun, Slots, D, T}; use crate::snapshot::{self, Snapshot}; use ares_macros::tas; +use signal_hook; +use signal_hook::consts::SIGINT; use std::fs::create_dir_all; use std::io; use std::path::PathBuf; use std::result::Result; -use std::thread::sleep; -use std::time; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; crate::gdb!(); @@ -23,12 +25,20 @@ const POKE_AXIS: u64 = 23; #[allow(dead_code)] const WISH_AXIS: u64 = 10; +// Necessary because Arc::new is not const +lazy_static! { + pub static ref TERMINATOR: Arc = Arc::new(AtomicBool::new(false)); +} + /** * This is suitable for talking to the king process. To test, change the arg_c[0] line in * u3_lord_init in vere to point at this binary and start vere like normal. */ pub fn serf() -> io::Result<()> { - sleep(time::Duration::from_secs(0)); + // Register SIGTERM signal hook to set flag first time, shutdown second time + signal_hook::flag::register_conditional_shutdown(SIGINT, 1, Arc::clone(&TERMINATOR))?; + signal_hook::flag::register(SIGINT, Arc::clone(&TERMINATOR))?; + let snap_path_string = std::env::args() .nth(2) .ok_or(io::Error::new(io::ErrorKind::Other, "no pier path"))?; @@ -157,7 +167,9 @@ pub fn serf() -> io::Result<()> { // TODO: on decryption failure in aes_siv, should bail as fast as // possible, without rendering stack trace or injecting crud event. See // c3__evil in vere. - // + + clear_interrupt(); + // crud = [+(now) [%$ %arvo ~] [%crud goof ovo]] let job_cell = job.as_cell().expect("serf: work: job not a cell"); let now = inc( @@ -197,6 +209,8 @@ pub fn serf() -> io::Result<()> { } _ => panic!("got message with unknown tag {}", tag), }; + + clear_interrupt(); } Ok(()) @@ -231,8 +245,9 @@ pub fn soft( let tang = mook(stack, &mut Some(newt), tone, false) .expect("serf: soft: +mook crashed on bail") .tail(); - // TODO: shouldn't always be exit, look at the comment by u3m_bail to see how vere - // handles this + // XX: noun::Tone or noun::NockErr should use a bail enum system similar to u3m_bail motes; + // might be able to replace NockErr with mote and map determinism to individual motes; + // for, always set to %exit let goof = T(stack, &[D(tas!(b"exit")), tang]); Err(goof) } @@ -244,3 +259,7 @@ fn slot(noun: Noun, axis: u64) -> io::Result { noun.slot(axis) .map_err(|_e| io::Error::new(io::ErrorKind::InvalidInput, "Bad axis")) } + +fn clear_interrupt() { + (*TERMINATOR).store(false, Ordering::Relaxed); +} From 19cdaaa40cd37c4fa18f81346d7fe59a4a214f39 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 4 Oct 2023 13:36:06 -0600 Subject: [PATCH 07/19] disable check_acyclic macro --- rust/ares/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 9bfc978..42999d5 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -42,4 +42,3 @@ opt-level = 3 opt-level = 3 [features] -check_acyclic=[] From 65bd2f19a491d42de4f01f162d86bb2b53707019 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 4 Oct 2023 13:36:32 -0600 Subject: [PATCH 08/19] increase size of Ares serf --- rust/ares/src/serf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 92dfe68..ddff7e7 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -50,7 +50,7 @@ pub fn serf() -> io::Result<()> { // let snap = &mut snapshot::pma::Pma::new(snap_path); let snap = &mut snapshot::double_jam::DoubleJam::new(snap_path); - let stack = &mut NockStack::new(96 << 10 << 10, 0); + let stack = &mut NockStack::new(256 << 10 << 10, 0); let newt = &mut Newt::new(); let (_epoch, loaded_event_num, mut arvo) = snap.load(stack).unwrap_or((0, 0, D(0))); From 6d414e994af67aabdb4328bc260369f130670f78 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 4 Oct 2023 13:37:18 -0600 Subject: [PATCH 09/19] add check for null pointers in frame_pop --- rust/ares/src/mem.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 1ae7802..7ef216c 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -528,6 +528,16 @@ impl NockStack { self.stack_pointer = prev_stack_ptr; self.alloc_pointer = prev_alloc_ptr; + if self.frame_pointer.is_null() + || self.stack_pointer.is_null() + || self.alloc_pointer.is_null() + { + panic!( + "serf: frame_pop: null NockStack pointer f={:p} s={:p} a={:p}", + self.frame_pointer, self.stack_pointer, self.alloc_pointer + ); + } + self.pc = false; } @@ -738,7 +748,6 @@ pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Nou ) == 0 { let (_senior, junior) = senior_pointer_first(stack, x_as_ptr, y_as_ptr); - // unify if x_as_ptr == junior { *x = *y; } else { From d9bd4d7716d494a46236bbf4b9d4184b750defcf Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Wed, 4 Oct 2023 13:38:16 -0600 Subject: [PATCH 10/19] clean up toddler pill --- hoon/scaffolding/toddler.hoon | 24 ++++-------------------- rust/ares/src/hamt.rs | 4 ++-- rust/ares/src/interpreter.rs | 16 ---------------- 3 files changed, 6 insertions(+), 38 deletions(-) diff --git a/hoon/scaffolding/toddler.hoon b/hoon/scaffolding/toddler.hoon index 9eb8d31..577c9c7 100644 --- a/hoon/scaffolding/toddler.hoon +++ b/hoon/scaffolding/toddler.hoon @@ -28,32 +28,16 @@ +$ wynn (list weft) :: mutually recursive Ackermann functions :: test turning %spot hints on/off - ++ wack + ++ ack :: re-enable %spot hints !: |= [m=@ud n=@ud] - :: %mean hint - ~_ [%leaf "I am a %mean hint via ~_ from +wack"] - :: %hela hint - ~> %hela :: %memo hint ~+ ?~ m +(n) ?~ n - (tack (dec m) 1) - (tack (dec m) $(n (dec n))) - ++ tack - :: disable %spot hints - !. - |= [m=@ud n=@ud] - :: %hela hint - ~> %hela - :: %memo hint - ~+ - ?~ m +(n) - ?~ n - (wack (dec m) 1) - (wack (dec m) $(n (dec n))) + (ack (dec m) 1) + (ack (dec m) $(n (dec n))) ++ slow |= x=@ud !: @@ -82,7 +66,7 @@ ~> %mean.'pith: bad wasp' ;;(wasp card.ovo) ?+ -.buz - ~> %slog.[0 leaf+(scow %ud (wack 1 1))] + ~> %slog.[0 leaf+(scow %ud (ack 2 1))] [~ ..poke] :: %crud diff --git a/rust/ares/src/hamt.rs b/rust/ares/src/hamt.rs index 9087cf7..2f2fb28 100644 --- a/rust/ares/src/hamt.rs +++ b/rust/ares/src/hamt.rs @@ -104,7 +104,7 @@ impl MutHamt { mug >>= 5; match (*stem).entry(chunk) { None => { - let new_leaf_buffer = stack.struct_alloc(1); + let new_leaf_buffer = stack.struct_alloc::<(Noun, T)>(1); *new_leaf_buffer = (*n, t); (*stem).bitmap |= chunk_to_bit(chunk); (*stem).typemap &= !chunk_to_bit(chunk); @@ -480,7 +480,7 @@ impl Preserve for Hamt { }; *(stem.buffer.add(idx) as *mut Entry) = Entry { stem: new_stem }; assert!(traversal_depth <= 5); // will increment - traversal_stack[traversal_depth - 1].unwrap().1 = position + 1; + traversal_stack[traversal_depth - 1] = Some((stem, position + 1)); traversal_stack[traversal_depth] = Some((new_stem, 0)); traversal_depth += 1; continue 'preserve; diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 8389dfe..7f66ea0 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -1112,22 +1112,6 @@ fn match_hint_pre_nock( mean_push(stack, noun); Ok(None) } - // - // u3_serf_writ -> u3_serf_work -> _serf_work -> _serf_poke -> u3m_soft -> u3dc -> u3v_do -> u3v_wish -> +wish in Arvo - // | - // V - // mook - // - // No +wish in toy Arvo; missing +slap and a ton of parsing functions needed by +ream - // - // u3t_slog = print on thing directly - // u3t_slog_trace = print stack trace = - convert tone to toon - // - presume toon is [%2 tang] - // - print each tank in tang one at at time using u3t_slog - // u3t_slog_hela = print entire stack trace = - weld stacks from all roads together - // - call u3t_slog_trace on combined stack - // u3t_slog_nara = print home road stack trace = call u3t_slog_trace on home road stack - // tas!(b"hela") => { // XX: should this be virtualized? // pretty sure we should be bailing on error From df67782978d94c379195c5e985e57700fb684405 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Thu, 5 Oct 2023 12:54:52 -0600 Subject: [PATCH 11/19] Update rust/ares/src/serf.rs Accepting typo feedback from Ed Co-authored-by: Edward Amsden --- rust/ares/src/serf.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index ddff7e7..1a089f9 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -35,7 +35,7 @@ lazy_static! { * u3_lord_init in vere to point at this binary and start vere like normal. */ pub fn serf() -> io::Result<()> { - // Register SIGTERM signal hook to set flag first time, shutdown second time + // Register SIGINT signal hook to set flag first time, shutdown second time signal_hook::flag::register_conditional_shutdown(SIGINT, 1, Arc::clone(&TERMINATOR))?; signal_hook::flag::register(SIGINT, Arc::clone(&TERMINATOR))?; From 2ad506de2ca100973b6ed7e266fe0423a9d941d0 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Thu, 5 Oct 2023 13:02:14 -0600 Subject: [PATCH 12/19] wrap allocation in panic with permit_alloc --- rust/ares/src/mem.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 7ef216c..5bc2c35 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -1,6 +1,7 @@ use crate::assert_acyclic; use crate::noun::{Atom, Cell, CellMemory, IndirectAtom, Noun, NounAllocator}; use crate::snapshot::pma::{pma_in_arena, pma_malloc_w}; +use assert_no_alloc::permit_alloc; use either::Either::{self, Left, Right}; use ibig::Stack; use libc::{c_void, memcmp}; @@ -532,10 +533,12 @@ impl NockStack { || self.stack_pointer.is_null() || self.alloc_pointer.is_null() { - panic!( - "serf: frame_pop: null NockStack pointer f={:p} s={:p} a={:p}", - self.frame_pointer, self.stack_pointer, self.alloc_pointer - ); + permit_alloc(|| { + panic!( + "serf: frame_pop: null NockStack pointer f={:p} s={:p} a={:p}", + self.frame_pointer, self.stack_pointer, self.alloc_pointer + ); + }); } self.pc = false; From 6bbd114d78928927a962e8d4efd13b0b17ca504c Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Thu, 5 Oct 2023 13:02:27 -0600 Subject: [PATCH 13/19] add style convention comment to Cargo.toml --- rust/ares/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index 42999d5..f842319 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -9,6 +9,7 @@ edition = "2018" # [patch.crates-io] # ibig = { path = "../ibig-rs" } +# Please keep these alphabetized [dependencies] ares_macros = { path = "../ares_macros" } assert_no_alloc = "1.1.2" From b21828ecc5132c3f087e28e17ce6c1fc4ef0582c Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Fri, 6 Oct 2023 18:26:37 -0600 Subject: [PATCH 14/19] add a context object to simplify passing function arguments --- rust/ares/src/interpreter.rs | 642 ++++++++++++++------------- rust/ares/src/jets.rs | 23 +- rust/ares/src/jets/bits.rs | 124 ++---- rust/ares/src/jets/form.rs | 9 +- rust/ares/src/jets/hash.rs | 9 +- rust/ares/src/jets/math.rs | 133 ++---- rust/ares/src/jets/nock.rs | 99 ++--- rust/ares/src/jets/text.rs | 7 +- rust/ares/src/jets/tree.rs | 18 +- rust/ares/src/main.rs | 14 +- rust/ares/src/serf.rs | 444 +++++++++++------- rust/ares/src/snapshot/double_jam.rs | 1 + 12 files changed, 774 insertions(+), 749 deletions(-) diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 7f66ea0..993e4a8 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -1,14 +1,11 @@ use crate::hamt::Hamt; -use crate::jets; -use crate::jets::nock::util::mook; use crate::jets::JetErr; use crate::mem::unifying_equality; use crate::mem::NockStack; use crate::newt::Newt; use crate::noun; -use crate::noun::{tape, Atom, Cell, IndirectAtom, Noun, Slots, D, T}; +use crate::noun::{Atom, Cell, IndirectAtom, Noun, Slots, D, T}; use crate::serf::TERMINATOR; -use ares_macros::tas; use assert_no_alloc::assert_no_alloc; use bitvec::prelude::{BitSlice, Lsb0}; use either::Either::*; @@ -231,10 +228,13 @@ enum NockWork { Work11S(Nock11S), } -#[derive(Debug)] -pub enum NockErr { - Deterministic, - NonDeterministic, +pub struct Context<'a, 'b, 'c> { + pub stack: &'a mut NockStack, + // For printing slogs; if None, print to stdout; Option slated to be removed + pub newt: Option<&'b mut Newt>, + // Per-event cache; option to share cache with virtualized events + pub cache: &'c mut Hamt, + // XX: persistent memo cache } #[derive(Debug)] @@ -243,6 +243,12 @@ pub enum Tone { Error(NockErr, Noun), } +#[derive(Debug)] +pub enum NockErr { + Deterministic, + NonDeterministic, +} + impl From for () { fn from(_: NockErr) -> Self {} } @@ -264,23 +270,17 @@ impl From for NockErr { } /** Interpret nock */ -pub fn interpret( - stack: &mut NockStack, - newt: &mut Option<&mut Newt>, // For printing slogs; if None, print to stdout - mut subject: Noun, - formula: Noun, -) -> Result { +pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Result { let terminator = Arc::clone(&TERMINATOR); + let virtual_frame: *const u64 = context.stack.get_frame_pointer(); let mut res: Noun = D(0); - let mut cache = Hamt::::new(); - let virtual_frame = stack.get_frame_pointer(); // Setup stack for Nock computation unsafe { - stack.frame_push(1); + context.stack.frame_push(1); // Bottom of mean stack - *(stack.local_noun_pointer(0)) = D(0); - *stack.push() = NockWork::Done; + *(context.stack.local_noun_pointer(0)) = D(0); + *context.stack.push() = NockWork::Done; }; // DO NOT REMOVE THIS ASSERTION @@ -295,43 +295,43 @@ pub fn interpret( // // (See https://docs.rs/assert_no_alloc/latest/assert_no_alloc/#advanced-use) let nock = assert_no_alloc(|| unsafe { - push_formula(stack, formula, true)?; + push_formula(context.stack, formula, true)?; loop { - let work: NockWork = *stack.top(); + let work: NockWork = *context.stack.top(); match work { NockWork::Done => { - stack.preserve(&mut cache); - stack.preserve(&mut res); - stack.frame_pop(); + context.stack.preserve(context.cache); + context.stack.preserve(&mut res); + context.stack.frame_pop(); break Ok(res); } NockWork::Ret => { - stack.preserve(&mut cache); - stack.preserve(&mut res); - stack.frame_pop(); + context.stack.preserve(context.cache); + context.stack.preserve(&mut res); + context.stack.frame_pop(); } NockWork::WorkCons(mut cons) => match cons.todo { TodoCons::ComputeHead => { cons.todo = TodoCons::ComputeTail; - *stack.top() = NockWork::WorkCons(cons); - push_formula(stack, cons.head, false)?; + *context.stack.top() = NockWork::WorkCons(cons); + push_formula(context.stack, cons.head, false)?; } TodoCons::ComputeTail => { cons.todo = TodoCons::Cons; cons.head = res; - *stack.top() = NockWork::WorkCons(cons); - push_formula(stack, cons.tail, false)?; + *context.stack.top() = NockWork::WorkCons(cons); + push_formula(context.stack, cons.tail, false)?; } TodoCons::Cons => { - res = T(stack, &[cons.head, res]); - stack.pop::(); + res = T(context.stack, &[cons.head, res]); + context.stack.pop::(); } }, NockWork::Work0(zero) => { if let Ok(noun) = subject.slot_atom(zero.axis) { res = noun; - stack.pop::(); + context.stack.pop::(); } else { // Axis invalid for input Noun break Err(NockErr::Deterministic); @@ -339,7 +339,7 @@ pub fn interpret( } NockWork::Work1(once) => { res = once.noun; - stack.pop::(); + context.stack.pop::(); } NockWork::Work2(mut vale) => { if (*terminator).load(Ordering::Relaxed) { @@ -349,56 +349,56 @@ pub fn interpret( match vale.todo { Todo2::ComputeSubject => { vale.todo = Todo2::ComputeFormula; - *stack.top() = NockWork::Work2(vale); - push_formula(stack, vale.subject, false)?; + *context.stack.top() = NockWork::Work2(vale); + push_formula(context.stack, vale.subject, false)?; } Todo2::ComputeFormula => { vale.todo = Todo2::ComputeResult; vale.subject = res; - *stack.top() = NockWork::Work2(vale); - push_formula(stack, vale.formula, false)?; + *context.stack.top() = NockWork::Work2(vale); + push_formula(context.stack, vale.formula, false)?; } Todo2::ComputeResult => { if vale.tail { - stack.pop::(); + context.stack.pop::(); subject = vale.subject; - push_formula(stack, res, true)?; + push_formula(context.stack, res, true)?; } else { vale.todo = Todo2::RestoreSubject; std::mem::swap(&mut vale.subject, &mut subject); - *stack.top() = NockWork::Work2(vale); - mean_frame_push(stack, 0); - *stack.push() = NockWork::Ret; - push_formula(stack, res, true)?; + *context.stack.top() = NockWork::Work2(vale); + mean_frame_push(context.stack, 0); + *context.stack.push() = NockWork::Ret; + push_formula(context.stack, res, true)?; } } Todo2::RestoreSubject => { subject = vale.subject; - stack.pop::(); + context.stack.pop::(); } } } NockWork::Work3(mut thee) => match thee.todo { Todo3::ComputeChild => { thee.todo = Todo3::ComputeType; - *stack.top() = NockWork::Work3(thee); - push_formula(stack, thee.child, false)?; + *context.stack.top() = NockWork::Work3(thee); + push_formula(context.stack, thee.child, false)?; } Todo3::ComputeType => { res = if res.is_cell() { D(0) } else { D(1) }; - stack.pop::(); + context.stack.pop::(); } }, NockWork::Work4(mut four) => match four.todo { Todo4::ComputeChild => { four.todo = Todo4::Increment; - *stack.top() = NockWork::Work4(four); - push_formula(stack, four.child, false)?; + *context.stack.top() = NockWork::Work4(four); + push_formula(context.stack, four.child, false)?; } Todo4::Increment => { if let Ok(atom) = res.as_atom() { - res = inc(stack, atom).as_noun(); - stack.pop::(); + res = inc(context.stack, atom).as_noun(); + context.stack.pop::(); } else { // Cannot increment (Nock 4) a cell break Err(NockErr::Deterministic); @@ -408,38 +408,38 @@ pub fn interpret( NockWork::Work5(mut five) => match five.todo { Todo5::ComputeLeftChild => { five.todo = Todo5::ComputeRightChild; - *stack.top() = NockWork::Work5(five); - push_formula(stack, five.left, false)?; + *context.stack.top() = NockWork::Work5(five); + push_formula(context.stack, five.left, false)?; } Todo5::ComputeRightChild => { five.todo = Todo5::TestEquals; five.left = res; - *stack.top() = NockWork::Work5(five); - push_formula(stack, five.right, false)?; + *context.stack.top() = NockWork::Work5(five); + push_formula(context.stack, five.right, false)?; } Todo5::TestEquals => { let saved_value_ptr = &mut five.left; - res = if unifying_equality(stack, &mut res, saved_value_ptr) { + res = if unifying_equality(context.stack, &mut res, saved_value_ptr) { D(0) } else { D(1) }; - stack.pop::(); + context.stack.pop::(); } }, NockWork::Work6(mut cond) => match cond.todo { Todo6::ComputeTest => { cond.todo = Todo6::ComputeBranch; - *stack.top() = NockWork::Work6(cond); - push_formula(stack, cond.test, false)?; + *context.stack.top() = NockWork::Work6(cond); + push_formula(context.stack, cond.test, false)?; } Todo6::ComputeBranch => { - stack.pop::(); + context.stack.pop::(); if let Left(direct) = res.as_either_direct_allocated() { if direct.data() == 0 { - push_formula(stack, cond.zero, cond.tail)?; + push_formula(context.stack, cond.zero, cond.tail)?; } else if direct.data() == 1 { - push_formula(stack, cond.once, cond.tail)?; + push_formula(context.stack, cond.once, cond.tail)?; } else { // Test branch of Nock 6 must return 0 or 1 break Err(NockErr::Deterministic); @@ -453,49 +453,49 @@ pub fn interpret( NockWork::Work7(mut pose) => match pose.todo { Todo7::ComputeSubject => { pose.todo = Todo7::ComputeResult; - *stack.top() = NockWork::Work7(pose); - push_formula(stack, pose.subject, false)?; + *context.stack.top() = NockWork::Work7(pose); + push_formula(context.stack, pose.subject, false)?; } Todo7::ComputeResult => { if pose.tail { - stack.pop::(); + context.stack.pop::(); subject = res; - push_formula(stack, pose.formula, true)?; + push_formula(context.stack, pose.formula, true)?; } else { pose.todo = Todo7::RestoreSubject; pose.subject = subject; - *stack.top() = NockWork::Work7(pose); + *context.stack.top() = NockWork::Work7(pose); subject = res; - push_formula(stack, pose.formula, false)?; + push_formula(context.stack, pose.formula, false)?; } } Todo7::RestoreSubject => { subject = pose.subject; - stack.pop::(); + context.stack.pop::(); } }, NockWork::Work8(mut pins) => match pins.todo { Todo8::ComputeSubject => { pins.todo = Todo8::ComputeResult; - *stack.top() = NockWork::Work8(pins); - push_formula(stack, pins.pin, false)?; + *context.stack.top() = NockWork::Work8(pins); + push_formula(context.stack, pins.pin, false)?; } Todo8::ComputeResult => { if pins.tail { - subject = T(stack, &[res, subject]); - stack.pop::(); - push_formula(stack, pins.formula, true)?; + subject = T(context.stack, &[res, subject]); + context.stack.pop::(); + push_formula(context.stack, pins.formula, true)?; } else { pins.todo = Todo8::RestoreSubject; pins.pin = subject; - *stack.top() = NockWork::Work8(pins); - subject = T(stack, &[res, subject]); - push_formula(stack, pins.formula, false)?; + *context.stack.top() = NockWork::Work8(pins); + subject = T(context.stack, &[res, subject]); + push_formula(context.stack, pins.formula, false)?; } } Todo8::RestoreSubject => { subject = pins.pin; - stack.pop::(); + context.stack.pop::(); } }, NockWork::Work9(mut kale) => { @@ -506,23 +506,23 @@ pub fn interpret( match kale.todo { Todo9::ComputeCore => { kale.todo = Todo9::ComputeResult; - *stack.top() = NockWork::Work9(kale); - push_formula(stack, kale.core, false)?; + *context.stack.top() = NockWork::Work9(kale); + push_formula(context.stack, kale.core, false)?; } Todo9::ComputeResult => { if let Ok(formula) = res.slot_atom(kale.axis) { if kale.tail { - stack.pop::(); + context.stack.pop::(); subject = res; - push_formula(stack, formula, true)?; + push_formula(context.stack, formula, true)?; } else { kale.todo = Todo9::RestoreSubject; kale.core = subject; - *stack.top() = NockWork::Work9(kale); + *context.stack.top() = NockWork::Work9(kale); subject = res; - mean_frame_push(stack, 0); - *stack.push() = NockWork::Ret; - push_formula(stack, formula, true)?; + mean_frame_push(context.stack, 0); + *context.stack.push() = NockWork::Ret; + push_formula(context.stack, formula, true)?; } } else { // Axis into core must be atom @@ -531,7 +531,7 @@ pub fn interpret( } Todo9::RestoreSubject => { subject = kale.core; - stack.pop::(); + context.stack.pop::(); } } } @@ -539,34 +539,33 @@ pub fn interpret( match diet.todo { Todo10::ComputeTree => { diet.todo = Todo10::ComputePatch; // should we compute patch then tree? - *stack.top() = NockWork::Work10(diet); - push_formula(stack, diet.tree, false)?; + *context.stack.top() = NockWork::Work10(diet); + push_formula(context.stack, diet.tree, false)?; } Todo10::ComputePatch => { diet.todo = Todo10::Edit; diet.tree = res; - *stack.top() = NockWork::Work10(diet); - push_formula(stack, diet.patch, false)?; + *context.stack.top() = NockWork::Work10(diet); + push_formula(context.stack, diet.patch, false)?; } Todo10::Edit => { - res = edit(stack, diet.axis.as_bitslice(), res, diet.tree); - stack.pop::(); + res = edit(context.stack, diet.axis.as_bitslice(), res, diet.tree); + context.stack.pop::(); } } } NockWork::Work11D(mut dint) => match dint.todo { Todo11D::ComputeHint => { - match match_hint_pre_hint( - stack, newt, &cache, subject, dint.tag, dint.hint, dint.body, - ) { + match hint::match_pre_hint(context, subject, dint.tag, dint.hint, dint.body) + { Ok(Some(found)) => { res = found; - stack.pop::(); + context.stack.pop::(); } Ok(None) => { dint.todo = Todo11D::ComputeResult; - *stack.top() = NockWork::Work11D(dint); - push_formula(stack, dint.hint, false)?; + *context.stack.top() = NockWork::Work11D(dint); + push_formula(context.stack, dint.hint, false)?; } Err(err) => { break Err(err); @@ -574,27 +573,25 @@ pub fn interpret( } } Todo11D::ComputeResult => { - match match_hint_pre_nock( - stack, - newt, + match hint::match_pre_nock( + context, subject, dint.tag, - Some(dint.hint), + Some((dint.hint, res)), dint.body, - Some(res), ) { Ok(Some(found)) => { res = found; - stack.pop::(); + context.stack.pop::(); } Ok(None) => { dint.todo = Todo11D::Done; if dint.tail { - stack.pop::(); + context.stack.pop::(); } else { - *stack.top() = NockWork::Work11D(dint); + *context.stack.top() = NockWork::Work11D(dint); } - push_formula(stack, dint.body, dint.tail)?; + push_formula(context.stack, dint.body, dint.tail)?; } Err(err) => { break Err(err); @@ -602,37 +599,29 @@ pub fn interpret( } } Todo11D::Done => { - if let Some(found) = match_hint_post_nock( - stack, - &mut cache, - subject, - dint.tag, - Some(dint.hint), - dint.body, - res, - ) { + if let Some(found) = + hint::match_post_nock(context, subject, dint.tag, dint.body, res) + { res = found; } - stack.pop::(); + context.stack.pop::(); } }, NockWork::Work11S(mut sint) => match sint.todo { Todo11S::ComputeResult => { - match match_hint_pre_nock( - stack, newt, subject, sint.tag, None, sint.body, None, - ) { + match hint::match_pre_nock(context, subject, sint.tag, None, sint.body) { Ok(Some(found)) => { res = found; - stack.pop::(); + context.stack.pop::(); } Ok(None) => { sint.todo = Todo11S::Done; if sint.tail { - stack.pop::(); + context.stack.pop::(); } else { - *stack.top() = NockWork::Work11S(sint); + *context.stack.top() = NockWork::Work11S(sint); } - push_formula(stack, sint.body, sint.tail)?; + push_formula(context.stack, sint.body, sint.tail)?; } Err(err) => { break Err(err); @@ -640,12 +629,12 @@ pub fn interpret( } } Todo11S::Done => { - if let Some(found) = match_hint_post_nock( - stack, &mut cache, subject, sint.tag, None, sint.body, res, - ) { + if let Some(found) = + hint::match_post_nock(context, subject, sint.tag, sint.body, res) + { res = found; } - stack.pop::(); + context.stack.pop::(); } }, }; @@ -654,7 +643,7 @@ pub fn interpret( match nock { Ok(res) => Ok(res), - Err(err) => Err(exit_early(stack, &mut cache, virtual_frame, err)), + Err(err) => Err(exit_early(context.stack, context.cache, virtual_frame, err)), } } @@ -817,7 +806,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), todo: Todo11S::ComputeResult, tag: tag_atom, body: arg_cell.tail(), - tail: tail && is_hint_tail(tag_atom), + tail: tail && hint::is_tail(tag_atom), }); } Right(hint_cell) => { @@ -827,7 +816,7 @@ fn push_formula(stack: &mut NockStack, formula: Noun, tail: bool) -> Result<(), tag: tag_atom, hint: hint_cell.tail(), body: arg_cell.tail(), - tail: tail && is_hint_tail(tag_atom), + tail: tail && hint::is_tail(tag_atom), }); } else { // Hint tag must be an atom @@ -977,209 +966,228 @@ pub fn inc(stack: &mut NockStack, atom: Atom) -> Atom { } } -fn is_hint_tail(tag: Atom) -> bool { - // XX: handle IndirectAtom tags - match tag.direct() { - #[allow(clippy::match_like_matches_macro)] - Some(dtag) => match dtag.data() { - tas!(b"fast") => false, - tas!(b"memo") => false, - _ => true, - }, - None => true, +mod hint { + use super::*; + use crate::jets; + use crate::jets::nock::util::mook; + use crate::mem::unifying_equality; + use crate::noun::{tape, Atom, Cell, Noun, D, T}; + use crate::serf::TERMINATOR; + use ares_macros::tas; + use std::sync::atomic::Ordering; + use std::sync::Arc; + + pub fn is_tail(tag: Atom) -> bool { + // XX: handle IndirectAtom tags + match tag.direct() { + #[allow(clippy::match_like_matches_macro)] + Some(dtag) => match dtag.data() { + tas!(b"fast") => false, + tas!(b"memo") => false, + _ => true, + }, + None => true, + } } -} -/** Match dynamic hints before the hint formula is evaluated */ -fn match_hint_pre_hint( - stack: &mut NockStack, - newt: &mut Option<&mut Newt>, - cache: &Hamt, - subject: Noun, - tag: Atom, - hint: Noun, - body: Noun, -) -> Result, NockErr> { - // XX: handle IndirectAtom tags - match tag.as_direct()?.data() { - // %sham hints are scaffolding until we have a real jet dashboard - tas!(b"sham") => { - let jet_formula = hint.as_cell()?; - // XX: what is the head here? - let jet_name = jet_formula.tail(); + /** Match dynamic hints before the hint formula is evaluated */ + pub fn match_pre_hint( + context: &mut Context, + subject: Noun, + tag: Atom, + hint: Noun, + body: Noun, + ) -> Result, NockErr> { + // XX: handle IndirectAtom tags + match tag.as_direct()?.data() { + // %sham hints are scaffolding until we have a real jet dashboard + tas!(b"sham") => { + let jet_formula = hint.as_cell()?; + // XX: what is the head here? + let jet_name = jet_formula.tail(); - if let Some(jet) = jets::get_jet(jet_name) { - match jet(stack, newt, subject) { - Ok(mut jet_res) => { - // XX: simplify this by moving jet test mode into the 11 code in interpret, or into its own function? - // if in test mode, check that the jet returns the same result as the raw nock - if jets::get_jet_test_mode(jet_name) { - // XX: we throw away trace, which might matter for non-deterministic errors - // maybe mook and slog it? - match interpret(stack, newt, subject, body) { - Ok(mut nock_res) => { - if unsafe { - !unifying_equality(stack, &mut nock_res, &mut jet_res) - } { + if let Some(jet) = jets::get_jet(jet_name) { + match jet(context, subject) { + Ok(mut jet_res) => { + // XX: simplify this by moving jet test mode into the 11 code in interpret, or into its own function? + // if in test mode, check that the jet returns the same result as the raw nock + if jets::get_jet_test_mode(jet_name) { + // XX: we throw away trace, which might matter for non-deterministic errors + // maybe mook and slog it? + match interpret(context, subject, body) { + Ok(mut nock_res) => { + let stack = &mut context.stack; + if unsafe { + !unifying_equality(stack, &mut nock_res, &mut jet_res) + } { + // XX: need string interpolation without allocation, then delete eprintln + // let tape = tape(stack, "jet mismatch in {}, raw: {}, jetted: {}", jet_name, nock_res, jet_res); + eprintln!( + "\rjet {} failed, raw: {:?}, jetted: {}", + jet_name, nock_res, jet_res + ); + let tape = tape(*stack, "jet mismatch"); + let mean = T(*stack, &[D(tas!(b"mean")), tape]); + mean_push(stack, mean); + Err(NockErr::Deterministic) + } else { + Ok(Some(nock_res)) + } + } + Err(Tone::Error(err, _)) => { + let stack = &mut context.stack; // XX: need string interpolation without allocation, then delete eprintln - // let tape = tape(stack, "jet mismatch in {}, raw: {}, jetted: {}", jet_name, nock_res, jet_res); + // let tape = tape(stack, "jet mismatch in {}, raw: {}, jetted: {}", jet_name, err, jet_res); eprintln!( "\rjet {} failed, raw: {:?}, jetted: {}", - jet_name, nock_res, jet_res + jet_name, err, jet_res ); - let tape = tape(stack, "jet mismatch"); - let mean = T(stack, &[D(tas!(b"mean")), tape]); + let tape = tape(*stack, "jet mismatch"); + let mean = T(*stack, &[D(tas!(b"mean")), tape]); mean_push(stack, mean); - Err(NockErr::Deterministic) - } else { - Ok(Some(nock_res)) + Err(err) + } + Err(Tone::Blocked(_)) => { + panic!("jet test mode: no scry handling") } } - Err(Tone::Error(err, _)) => { - // XX: need string interpolation without allocation, then delete eprintln - // let tape = tape(stack, "jet mismatch in {}, raw: {}, jetted: {}", jet_name, err, jet_res); - eprintln!( - "\rjet {} failed, raw: {:?}, jetted: {}", - jet_name, err, jet_res - ); - let tape = tape(stack, "jet mismatch"); - let mean = T(stack, &[D(tas!(b"mean")), tape]); - mean_push(stack, mean); - Err(err) - } - Err(Tone::Blocked(_)) => { - panic!("jet test mode: no scry handling") - } + } else { + Ok(Some(jet_res)) } - } else { - Ok(Some(jet_res)) + } + Err(JetErr::Punt) => Ok(None), + Err(err) => { + let stack = &mut context.stack; + // XX: need string interpolation without allocation + // let tape = tape(stack, "{} jet error in {}", err, jet_name); + let tape = tape(*stack, "jet error"); + let mean = T(*stack, &[D(tas!(b"mean")), tape]); + mean_push(stack, mean); + Err(err.into()) } } - Err(JetErr::Punt) => Ok(None), + } else { + Ok(None) + } + } + tas!(b"memo") => { + let stack = &mut context.stack; + let mut key = Cell::new(*stack, subject, body).as_noun(); + Ok(context.cache.lookup(stack, &mut key)) + } + _ => Ok(None), + } + } + + /** Match static and dynamic hints before the nock formula is evaluated */ + pub fn match_pre_nock( + context: &mut Context, + _subject: Noun, + tag: Atom, + hint: Option<(Noun, Noun)>, + _body: Noun, + ) -> Result, NockErr> { + // XX: handle IndirectAtom tags + match tag.as_direct()?.data() { + tas!(b"slog") => { + let (_, hint_res) = hint.ok_or(NockErr::Deterministic)?; + let slog_cell = hint_res.as_cell()?; + let pri = slog_cell.head().as_direct()?.data(); + let tank = slog_cell.tail(); + if context.newt.is_none() { + eprintln!("raw slog: {} {}", pri, tank); + } else { + context + .newt + .as_mut() + .unwrap() + .slog(context.stack, pri, tank); + } + Ok(None) + } + 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(NockErr::NonDeterministic); + } + + let stack = &mut context.stack; + let (_, hint_res) = hint.ok_or(NockErr::Deterministic)?; + let noun = T(*stack, &[tag.as_noun(), hint_res]); + 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, NockErr> + let mean = unsafe { *(context.stack.local_noun_pointer(0)) }; + let tone = Cell::new(context.stack, D(2), mean); + + match mook(context, tone, true) { + Ok(toon) => { + let stack = &mut context.stack; + if unsafe { !toon.head().raw_equals(D(2)) } { + let tape = tape(*stack, "%hela failed: toon not %2"); + let mean = T(*stack, &[D(tas!(b"mean")), tape]); + mean_push(stack, mean); + return Err(NockErr::Deterministic); + } + + let mut list = toon.tail(); + loop { + if unsafe { list.raw_equals(D(0)) } { + break; + } + + let cell = list.as_cell().unwrap(); + if context.newt.is_none() { + eprintln!("raw slog: {} {}", 0, cell.head()); + } else { + context.newt.as_mut().unwrap().slog(stack, 0, cell.head()); + } + + list = cell.tail(); + } + + Ok(None) + } Err(err) => { - // XX: need string interpolation without allocation - // let tape = tape(stack, "{} jet error in {}", err, jet_name); - let tape = tape(stack, "jet error"); - let mean = T(stack, &[D(tas!(b"mean")), tape]); + let stack = &mut context.stack; + let tape = tape(*stack, "%hela failed: mook error"); + let mean = T(*stack, &[D(tas!(b"mean")), tape]); mean_push(stack, mean); Err(err.into()) } } - } else { - Ok(None) } + _ => Ok(None), } - tas!(b"memo") => { - let mut key = Cell::new(stack, subject, body).as_noun(); - Ok(cache.lookup(stack, &mut key)) - } - _ => Ok(None), - } -} - -/** Match static and dynamic hints before the nock formula is evaluated */ -fn match_hint_pre_nock( - stack: &mut NockStack, - newt: &mut Option<&mut Newt>, - _subject: Noun, - tag: Atom, - _hint: Option, - _body: Noun, - res: Option, -) -> Result, NockErr> { - // XX: assert Some(res) <=> Some(hint) - - // XX: handle IndirectAtom tags - match tag.as_direct()?.data() { - tas!(b"slog") => { - let slog_cell = res.ok_or(NockErr::Deterministic)?.as_cell()?; - let pri = slog_cell.head().as_direct()?.data(); - let tank = slog_cell.tail(); - if let Some(not) = newt { - not.slog(stack, pri, tank); - } else { - eprintln!("raw slog: {} {}", pri, tank); - } - Ok(None) - } - 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(NockErr::NonDeterministic); - } - - let noun = T(stack, &[tag.as_noun(), res.ok_or(NockErr::Deterministic)?]); - 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, NockErr> - let stak = unsafe { *(stack.local_noun_pointer(0)) }; - let tone = Cell::new(stack, D(2), stak); - - match mook(stack, newt, tone, true) { - Ok(toon) => { - if unsafe { !toon.head().raw_equals(D(2)) } { - let tape = tape(stack, "%hela failed: toon not %2"); - let mean = T(stack, &[D(tas!(b"mean")), tape]); - mean_push(stack, mean); - return Err(NockErr::Deterministic); - } - - let mut list = toon.tail(); - loop { - if unsafe { list.raw_equals(D(0)) } { - break; - } - - let cell = list.as_cell().unwrap(); - if let Some(not) = newt { - // XX: %hela priority is configurable, but I'm not sure how - not.slog(stack, 0, cell.head()); - } else { - eprintln!("raw slog: {} {}", 0, cell.head()); - } - - list = cell.tail(); - } - - Ok(None) - } - Err(err) => { - let tape = tape(stack, "%hela failed: mook error"); - let mean = T(stack, &[D(tas!(b"mean")), tape]); - mean_push(stack, mean); - Err(err.into()) - } - } - } - _ => Ok(None), - } -} - -/** Match static and dynamic hints after the nock formula is evaluated */ -fn match_hint_post_nock( - stack: &mut NockStack, - cache: &mut Hamt, - subject: Noun, - tag: Atom, - _hint: Option, - body: Noun, - res: Noun, -) -> Option { - // XX: handle IndirectAtom tags - match tag.direct()?.data() { - tas!(b"memo") => { - let mut key = Cell::new(stack, subject, body).as_noun(); - *cache = cache.insert(stack, &mut key, res); - } - tas!(b"hand") | tas!(b"hunk") | tas!(b"lose") | tas!(b"mean") | tas!(b"spot") => { - mean_pop(stack); - } - _ => {} } - None + /** Match static and dynamic hints after the nock formula is evaluated */ + pub fn match_post_nock( + context: &mut Context, + subject: Noun, + tag: Atom, + body: Noun, + res: Noun, + ) -> Option { + let stack = &mut context.stack; + let cache = &mut context.cache; + + // XX: handle IndirectAtom tags + match tag.direct()?.data() { + tas!(b"memo") => { + let mut key = Cell::new(*stack, subject, body).as_noun(); + **cache = (*cache).insert(stack, &mut key, res); + } + tas!(b"hand") | tas!(b"hunk") | tas!(b"lose") | tas!(b"mean") | tas!(b"spot") => { + mean_pop(stack); + } + _ => {} + } + + None + } } diff --git a/rust/ares/src/jets.rs b/rust/ares/src/jets.rs index 0a185d5..56e6081 100644 --- a/rust/ares/src/jets.rs +++ b/rust/ares/src/jets.rs @@ -6,6 +6,7 @@ pub mod nock; pub mod text; pub mod tree; +use crate::interpreter::Context; use crate::jets::bits::*; use crate::jets::form::*; use crate::jets::hash::*; @@ -14,7 +15,6 @@ use crate::jets::nock::*; use crate::jets::text::*; use crate::jets::tree::*; use crate::mem::NockStack; -use crate::newt::Newt; use crate::noun::{self, Noun, Slots}; use ares_macros::tas; use std::cmp; @@ -23,7 +23,7 @@ crate::gdb!(); /// Return Err if the computation crashed or should punt to Nock pub type Result = std::result::Result; -pub type Jet = fn(&mut NockStack, &mut Option<&mut Newt>, Noun) -> Result; +pub type Jet = fn(&mut Context, Noun) -> Result; /** * Only return a deterministic error if the Nock would have deterministically @@ -288,6 +288,7 @@ pub mod util { pub mod test { use super::*; + use crate::hamt::Hamt; use crate::mem::{unifying_equality, NockStack}; use crate::noun::{Atom, Noun, D, T}; use assert_no_alloc::assert_no_alloc; @@ -308,8 +309,13 @@ pub mod util { } pub fn assert_jet(stack: &mut NockStack, jet: Jet, sam: Noun, res: Noun) { - let sam = T(stack, &[D(0), sam, D(0)]); - let jet_res = assert_no_alloc(|| jet(stack, &mut None, sam).unwrap()); + let mut context = Context { + stack, + newt: None, + cache: &mut Hamt::::new(), + }; + let sam = T(context.stack, &[D(0), sam, D(0)]); + let jet_res = assert_no_alloc(|| jet(&mut context, sam).unwrap()); assert_noun_eq(stack, jet_res, res); } @@ -324,8 +330,13 @@ pub mod util { } pub fn assert_jet_err(stack: &mut NockStack, jet: Jet, sam: Noun, err: JetErr) { - let sam = T(stack, &[D(0), sam, D(0)]); - let jet_res = jet(stack, &mut None, sam); + let mut context = Context { + stack, + newt: None, + cache: &mut Hamt::::new(), + }; + let sam = T(context.stack, &[D(0), sam, D(0)]); + let jet_res = jet(&mut context, sam); assert!( jet_res.is_err(), "with sample: {}, expected err: {:?}, got: {:?}", diff --git a/rust/ares/src/jets/bits.rs b/rust/ares/src/jets/bits.rs index 57c4588..a12be17 100644 --- a/rust/ares/src/jets/bits.rs +++ b/rust/ares/src/jets/bits.rs @@ -1,10 +1,9 @@ /** Bit arithmetic & logic jets */ -use crate::jets; +use crate::interpreter::Context; use crate::jets::util::*; use crate::jets::JetErr::*; -use crate::mem::NockStack; -use crate::newt::Newt; +use crate::jets::Result; use crate::noun::{DirectAtom, IndirectAtom, Noun, D}; use std::cmp; @@ -14,20 +13,12 @@ crate::gdb!(); * Bit arithmetic */ -pub fn jet_bex( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_bex(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?.as_direct()?.data() as usize; - Ok(bex(stack, arg).as_noun()) + Ok(bex(context.stack, arg).as_noun()) } -pub fn jet_can( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_can(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let bloq = bloq(slot(arg, 2)?)?; let original_list = slot(arg, 3)?; @@ -52,7 +43,7 @@ pub fn jet_can( } else { unsafe { let (mut new_indirect, new_slice) = - IndirectAtom::new_raw_mut_bitslice(stack, bite_to_word(bloq, len)?); + IndirectAtom::new_raw_mut_bitslice(context.stack, bite_to_word(bloq, len)?); let mut pos = 0; let mut list = original_list; loop { @@ -74,11 +65,7 @@ pub fn jet_can( } } -pub fn jet_cat( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_cat(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let bloq = bloq(slot(arg, 2)?)?; let a = slot(arg, 6)?.as_atom()?; @@ -91,7 +78,8 @@ pub fn jet_cat( Ok(a.as_noun()) } else { unsafe { - let (mut new_indirect, new_slice) = IndirectAtom::new_raw_mut_bitslice(stack, new_len); + let (mut new_indirect, new_slice) = + IndirectAtom::new_raw_mut_bitslice(context.stack, new_len); chop(bloq, 0, len_a, 0, new_slice, a.as_bitslice())?; chop(bloq, 0, len_b, len_a, new_slice, b.as_bitslice())?; Ok(new_indirect.normalize_as_atom().as_noun()) @@ -99,11 +87,7 @@ pub fn jet_cat( } } -pub fn jet_cut( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_cut(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let bloq = bloq(slot(arg, 2)?)?; let start = slot(arg, 12)?.as_direct()?.data() as usize; @@ -116,18 +100,14 @@ pub fn jet_cut( let new_indirect = unsafe { let (mut new_indirect, new_slice) = - IndirectAtom::new_raw_mut_bitslice(stack, bite_to_word(bloq, run)?); + IndirectAtom::new_raw_mut_bitslice(context.stack, bite_to_word(bloq, run)?); chop(bloq, start, run, 0, new_slice, atom.as_bitslice())?; new_indirect.normalize_as_atom() }; Ok(new_indirect.as_noun()) } -pub fn jet_end( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_end(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let (bloq, step) = bite(slot(arg, 2)?)?; let a = slot(arg, 3)?.as_atom()?; @@ -139,18 +119,14 @@ pub fn jet_end( } else { unsafe { let (mut new_indirect, new_slice) = - IndirectAtom::new_raw_mut_bitslice(stack, bite_to_word(bloq, step)?); + IndirectAtom::new_raw_mut_bitslice(context.stack, bite_to_word(bloq, step)?); chop(bloq, 0, step, 0, new_slice, a.as_bitslice())?; Ok(new_indirect.normalize_as_atom().as_noun()) } } } -pub fn jet_lsh( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_lsh(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let (bloq, step) = bite(slot(arg, 2)?)?; let a = slot(arg, 3)?.as_atom()?; @@ -162,17 +138,13 @@ pub fn jet_lsh( let new_size = bits_to_word(checked_add(a.bit_size(), checked_left_shift(bloq, step)?)?)?; unsafe { - let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(stack, new_size); + let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(context.stack, new_size); chop(bloq, 0, len, step, dest, a.as_bitslice())?; Ok(atom.normalize_as_atom().as_noun()) } } -pub fn jet_met( - _stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_met(_context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let bloq = bloq(slot(arg, 2)?)?; let a = slot(arg, 3)?.as_atom()?; @@ -180,11 +152,7 @@ pub fn jet_met( Ok(D(met(bloq, a) as u64)) } -pub fn jet_rap( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_rap(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let bloq = bloq(slot(arg, 2)?)?; let original_list = slot(arg, 3)?; @@ -207,7 +175,7 @@ pub fn jet_rap( } else { unsafe { let (mut new_indirect, new_slice) = - IndirectAtom::new_raw_mut_bitslice(stack, bite_to_word(bloq, len)?); + IndirectAtom::new_raw_mut_bitslice(context.stack, bite_to_word(bloq, len)?); let mut pos = 0; let mut list = original_list; @@ -230,11 +198,7 @@ pub fn jet_rap( } } -pub fn jet_rep( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_rep(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let (bloq, step) = bite(slot(arg, 2)?)?; let original_list = slot(arg, 3)?; @@ -257,7 +221,7 @@ pub fn jet_rep( } else { unsafe { let (mut new_indirect, new_slice) = - IndirectAtom::new_raw_mut_bitslice(stack, bite_to_word(bloq, len)?); + IndirectAtom::new_raw_mut_bitslice(context.stack, bite_to_word(bloq, len)?); let mut pos = 0; let mut list = original_list; loop { @@ -277,11 +241,7 @@ pub fn jet_rep( } } -pub fn jet_rev( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_rev(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let boz = slot(arg, 2)?.as_atom()?.as_direct()?.data(); @@ -301,7 +261,7 @@ pub fn jet_rev( let mut output = if dat.is_direct() && bits < 64 { unsafe { DirectAtom::new_unchecked(0).as_atom() } } else { - unsafe { IndirectAtom::new_raw(stack, ((bits + 7) / 8) as usize, &0).as_atom() } + unsafe { IndirectAtom::new_raw(context.stack, ((bits + 7) / 8) as usize, &0).as_atom() } }; let src = dat.as_bitslice(); @@ -317,22 +277,14 @@ pub fn jet_rev( Ok(unsafe { output.normalize() }.as_noun()) } -pub fn jet_rip( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_rip(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let (bloq, step) = bite(slot(arg, 2)?)?; let atom = slot(arg, 3)?.as_atom()?; - rip(stack, bloq, step, atom) + rip(context.stack, bloq, step, atom) } -pub fn jet_rsh( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_rsh(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let (bloq, step) = bite(slot(arg, 2)?)?; let a = slot(arg, 3)?.as_atom()?; @@ -344,7 +296,7 @@ pub fn jet_rsh( let new_size = bits_to_word(checked_sub(a.bit_size(), checked_left_shift(bloq, step)?)?)?; unsafe { - let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(stack, new_size); + let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(context.stack, new_size); chop(bloq, step, len - step, 0, dest, a.as_bitslice())?; Ok(atom.normalize_as_atom().as_noun()) } @@ -354,23 +306,15 @@ pub fn jet_rsh( * Bit logic */ -pub fn jet_con( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_con(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; - Ok(con(stack, a, b).as_noun()) + Ok(con(context.stack, a, b).as_noun()) } -pub fn jet_dis( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_dis(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; @@ -378,7 +322,7 @@ pub fn jet_dis( let new_size = cmp::max(a.size(), b.size()); unsafe { - let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(stack, new_size); + let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(context.stack, new_size); let a_bit = a.as_bitslice(); dest[..a_bit.len()].copy_from_bitslice(a_bit); *dest &= b.as_bitslice(); @@ -386,11 +330,7 @@ pub fn jet_dis( } } -pub fn jet_mix( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_mix(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; @@ -398,7 +338,7 @@ pub fn jet_mix( let new_size = cmp::max(a.size(), b.size()); unsafe { - let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(stack, new_size); + let (mut atom, dest) = IndirectAtom::new_raw_mut_bitslice(context.stack, new_size); let a_bit = a.as_bitslice(); dest[..a_bit.len()].copy_from_bitslice(a_bit); *dest ^= b.as_bitslice(); diff --git a/rust/ares/src/jets/form.rs b/rust/ares/src/jets/form.rs index 494e15e..8e819ae 100644 --- a/rust/ares/src/jets/form.rs +++ b/rust/ares/src/jets/form.rs @@ -1,17 +1,16 @@ -use crate::jets::util::slot; /** Formatting jets */ +use crate::interpreter::Context; +use crate::jets::util::slot; use crate::jets::Result; -use crate::mem::NockStack; -use crate::newt::Newt; use crate::noun::Noun; crate::gdb!(); -pub fn jet_scow(stack: &mut NockStack, _newt: &mut Option<&mut Newt>, subject: Noun) -> Result { +pub fn jet_scow(context: &mut Context, subject: Noun) -> Result { let aura = slot(subject, 12)?.as_direct()?; let atom = slot(subject, 13)?.as_atom()?; - util::scow(stack, aura, atom) + util::scow(context.stack, aura, atom) } pub mod util { diff --git a/rust/ares/src/jets/hash.rs b/rust/ares/src/jets/hash.rs index a58262b..f622319 100644 --- a/rust/ares/src/jets/hash.rs +++ b/rust/ares/src/jets/hash.rs @@ -1,17 +1,16 @@ -use crate::jets::util::*; /** Hash jets */ +use crate::interpreter::Context; +use crate::jets::util::*; use crate::jets::Result; -use crate::mem::NockStack; use crate::mug::mug; -use crate::newt::Newt; use crate::noun::Noun; crate::gdb!(); -pub fn jet_mug(stack: &mut NockStack, _newt: &mut Option<&mut Newt>, subject: Noun) -> Result { +pub fn jet_mug(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; - Ok(mug(stack, arg).as_noun()) + Ok(mug(context.stack, arg).as_noun()) } #[cfg(test)] diff --git a/rust/ares/src/jets/math.rs b/rust/ares/src/jets/math.rs index 3a36353..934da47 100644 --- a/rust/ares/src/jets/math.rs +++ b/rust/ares/src/jets/math.rs @@ -12,11 +12,10 @@ * 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::jets; +use crate::interpreter::Context; use crate::jets::util::*; use crate::jets::JetErr::*; -use crate::mem::NockStack; -use crate::newt::Newt; +use crate::jets::Result; use crate::noun::{Atom, DirectAtom, IndirectAtom, Noun, D, DIRECT_MAX, NO, T, YES}; use either::{Left, Right}; use ibig::ops::DivRem; @@ -24,30 +23,23 @@ use ibig::UBig; crate::gdb!(); -pub fn jet_add( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_add(context: &mut Context, subject: Noun) -> Result { + let stack = &mut context.stack; let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) { - Ok(Atom::new(stack, a.data() + b.data()).as_noun()) + Ok(Atom::new(*stack, a.data() + b.data()).as_noun()) } else { - let a_big = a.as_ubig(stack); - let b_big = b.as_ubig(stack); - let res = UBig::add_stack(stack, a_big, b_big); - Ok(Atom::from_ubig(stack, &res).as_noun()) + let a_big = a.as_ubig(*stack); + let b_big = b.as_ubig(*stack); + let res = UBig::add_stack(*stack, a_big, b_big); + Ok(Atom::from_ubig(*stack, &res).as_noun()) } } -pub fn jet_dec( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_dec(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; if let Ok(atom) = arg.as_atom() { match atom.as_either() { @@ -65,8 +57,9 @@ pub fn jet_dec( panic!("Decrementing 0 stored as an indirect atom"); } Some(first_one) => { - let (mut new_indirect, new_slice) = - unsafe { IndirectAtom::new_raw_mut_bitslice(stack, indirect.size()) }; + let (mut new_indirect, new_slice) = unsafe { + IndirectAtom::new_raw_mut_bitslice(context.stack, indirect.size()) + }; if first_one > 0 { new_slice[..first_one].fill(true); } @@ -84,11 +77,8 @@ pub fn jet_dec( } } -pub fn jet_div( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_div(context: &mut Context, subject: Noun) -> Result { + let stack = &mut context.stack; let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; @@ -98,18 +88,15 @@ pub fn jet_div( } 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 { - let a_big = a.as_ubig(stack); - let b_big = b.as_ubig(stack); - let res = UBig::div_stack(stack, a_big, b_big); - Ok(Atom::from_ubig(stack, &res).as_noun()) + let a_big = a.as_ubig(*stack); + let b_big = b.as_ubig(*stack); + let res = UBig::div_stack(*stack, a_big, b_big); + Ok(Atom::from_ubig(*stack, &res).as_noun()) } } -pub fn jet_dvr( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_dvr(context: &mut Context, subject: Noun) -> Result { + let stack = &mut context.stack; let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; @@ -126,22 +113,18 @@ pub fn jet_dvr( ) } } else { - let (div, rem) = a.as_ubig(stack).div_rem(b.as_ubig(stack)); + let (div, rem) = a.as_ubig(*stack).div_rem(b.as_ubig(*stack)); ( - Atom::from_ubig(stack, &div).as_noun(), - Atom::from_ubig(stack, &rem).as_noun(), + Atom::from_ubig(*stack, &div).as_noun(), + Atom::from_ubig(*stack, &rem).as_noun(), ) }; - Ok(T(stack, &[div, rem])) + Ok(T(*stack, &[div, rem])) } } -pub fn jet_gte( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_gte(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; @@ -156,18 +139,14 @@ pub fn jet_gte( YES } else if a.bit_size() < b.bit_size() { NO - } else if a.as_ubig(stack) >= b.as_ubig(stack) { + } else if a.as_ubig(context.stack) >= b.as_ubig(context.stack) { YES } else { NO }) } -pub fn jet_gth( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_gth(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; @@ -182,18 +161,14 @@ pub fn jet_gth( YES } else if a.bit_size() < b.bit_size() { NO - } else if a.as_ubig(stack) > b.as_ubig(stack) { + } else if a.as_ubig(context.stack) > b.as_ubig(context.stack) { YES } else { NO }) } -pub fn jet_lte( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_lte(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; @@ -208,18 +183,14 @@ pub fn jet_lte( YES } else if a.bit_size() > b.bit_size() { NO - } else if a.as_ubig(stack) <= b.as_ubig(stack) { + } else if a.as_ubig(context.stack) <= b.as_ubig(context.stack) { YES } else { NO }) } -pub fn jet_lth( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_lth(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; @@ -234,18 +205,15 @@ pub fn jet_lth( YES } else if a.bit_size() > b.bit_size() { NO - } else if a.as_ubig(stack) < b.as_ubig(stack) { + } else if a.as_ubig(context.stack) < b.as_ubig(context.stack) { YES } else { NO }) } -pub fn jet_mod( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_mod(context: &mut Context, subject: Noun) -> Result { + let stack = &mut context.stack; let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; @@ -255,16 +223,13 @@ pub fn jet_mod( } 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 { - let res = a.as_ubig(stack) % b.as_ubig(stack); - Ok(Atom::from_ubig(stack, &res).as_noun()) + let res = a.as_ubig(*stack) % b.as_ubig(*stack); + Ok(Atom::from_ubig(*stack, &res).as_noun()) } } -pub fn jet_mul( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_mul(context: &mut Context, subject: Noun) -> Result { + let stack = &mut context.stack; let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; @@ -272,11 +237,11 @@ pub fn jet_mul( if let (Ok(a), Ok(b)) = (a.as_direct(), b.as_direct()) { let res = a.data() as u128 * b.data() as u128; if res < DIRECT_MAX as u128 { - Ok(Atom::new(stack, res as u64).as_noun()) + Ok(Atom::new(*stack, res as u64).as_noun()) } else { Ok(unsafe { IndirectAtom::new_raw_bytes( - stack, + *stack, if res < u64::MAX as u128 { 8 } else { 16 }, &res as *const u128 as *const u8, ) @@ -284,23 +249,19 @@ pub fn jet_mul( .as_noun()) } } else { - let a_big = a.as_ubig(stack); - let b_big = b.as_ubig(stack); - let res = UBig::mul_stack(stack, a_big, b_big); - Ok(Atom::from_ubig(stack, &res).as_noun()) + let a_big = a.as_ubig(*stack); + let b_big = b.as_ubig(*stack); + let res = UBig::mul_stack(*stack, a_big, b_big); + Ok(Atom::from_ubig(*stack, &res).as_noun()) } } -pub fn jet_sub( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_sub(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let a = slot(arg, 2)?.as_atom()?; let b = slot(arg, 3)?.as_atom()?; - Ok(sub(stack, a, b)?.as_noun()) + Ok(sub(context.stack, a, b)?.as_noun()) } #[cfg(test)] diff --git a/rust/ares/src/jets/nock.rs b/rust/ares/src/jets/nock.rs index 0ab6711..ed22fc8 100644 --- a/rust/ares/src/jets/nock.rs +++ b/rust/ares/src/jets/nock.rs @@ -1,48 +1,30 @@ /** Virtualization jets */ -use crate::interpreter::{interpret, NockErr, Tone}; -use crate::jets; +use crate::interpreter::Context; use crate::jets::util::slot; -use crate::jets::JetErr; -use crate::mem::NockStack; -use crate::newt::Newt; -use crate::noun::{Noun, D, T}; +use crate::jets::Result; +use crate::noun::Noun; crate::gdb!(); // XX: interpret should accept optional scry function and potentially produce blocked -pub fn jet_mink( - stack: &mut NockStack, - newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_mink(context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; // mink sample = [nock scry_namespace] // = [[subject formula] scry_namespace] let v_subject = slot(arg, 4)?; let v_formula = slot(arg, 5)?; let _scry = slot(arg, 3)?; - - // XX: no partial traces; all of our traces go down to the "home road" - match interpret(stack, newt, v_subject, v_formula) { - Ok(res) => Ok(T(stack, &[D(0), res])), - Err(err) => match err { - Tone::Blocked(block) => Ok(T(stack, &[D(1), block])), - Tone::Error(err, trace) => match err { - NockErr::Deterministic => Ok(T(stack, &[D(2), trace])), - NockErr::NonDeterministic => Err(JetErr::NonDeterministic), - }, - }, - } + util::mink(context, v_subject, v_formula) } pub mod util { + use crate::interpreter::{interpret, Context, NockErr, Tone}; use crate::jets; use crate::jets::form::util::scow; use crate::jets::util::rip; - use crate::jets::{jet_mink, JetErr}; + use crate::jets::JetErr; use crate::mem::NockStack; - use crate::newt::Newt; use crate::noun::{tape, Cell, Noun, D, T}; use ares_macros::tas; use std::result; @@ -50,15 +32,24 @@ pub mod util { const LEAF: Noun = D(tas!(b"leaf")); const ROSE: Noun = D(tas!(b"rose")); + pub fn mink(context: &mut Context, subject: Noun, formula: Noun) -> jets::Result { + // XX: no partial traces; all of our traces go down to the "home road" + match interpret(context, subject, formula) { + Ok(res) => Ok(T(context.stack, &[D(0), res])), + Err(err) => match err { + Tone::Blocked(block) => Ok(T(context.stack, &[D(1), block])), + Tone::Error(err, trace) => match err { + NockErr::Deterministic => Ok(T(context.stack, &[D(2), trace])), + NockErr::NonDeterministic => Err(JetErr::NonDeterministic), + }, + }, + } + } + /** Consume $tone, produce $toon */ // XX: should write a jet_mook wrapper for this function - pub fn mook( - stack: &mut NockStack, - newt: &mut Option<&mut Newt>, - tone: Cell, - flop: bool, - ) -> result::Result { + pub fn mook(context: &mut Context, tone: Cell, flop: bool) -> result::Result { let tag = tone.head().as_direct()?; let original_list = tone.tail(); @@ -80,7 +71,7 @@ pub mod util { let mut res = D(0); let mut list = original_list; // Unused if flopping - let (mut new_cell, mut new_memory) = Cell::new_raw_mut(stack); + let (mut new_cell, mut new_memory) = Cell::new_raw_mut(context.stack); let mut memory = new_memory; // loop guaranteed to run at least once @@ -90,7 +81,7 @@ pub mod util { } else if !flop && res.raw_equals(D(0)) { res = new_cell.as_noun(); } else if !flop { - (new_cell, new_memory) = Cell::new_raw_mut(stack); + (new_cell, new_memory) = Cell::new_raw_mut(context.stack); (*memory).tail = new_cell.as_noun(); memory = new_memory } @@ -103,16 +94,13 @@ pub mod util { let tank: Noun = match tag.data() { tas!(b"mean") => { if let Ok(atom) = dat.as_atom() { - let tape = rip(stack, 3, 1, atom)?; - T(stack, &[LEAF, tape]) + let tape = rip(context.stack, 3, 1, atom)?; + T(context.stack, &[LEAF, tape]) } else { - let virt = T(stack, &[dat, dat.as_cell()?.head()]); - let load = T(stack, &[virt, D(0)]); - let subj = T(stack, &[D(0), load, D(0)]); - let tone = jet_mink(stack, newt, subj)?.as_cell()?; + let tone = mink(context, dat, dat.as_cell()?.head())?.as_cell()?; if !tone.head().raw_equals(D(0)) { - let tape = tape(stack, "####"); - T(stack, &[LEAF, tape]) + let tape = tape(context.stack, "####"); + T(context.stack, &[LEAF, tape]) } else { // XX: need to check that this is actually a tank // return leaf+"mean.mook" if not @@ -121,13 +109,14 @@ pub mod util { } } tas!(b"spot") => { + let stack = &mut context.stack; let spot = dat.as_cell()?; let pint = spot.tail().as_cell()?; let pstr = pint.head().as_cell()?; let pend = pint.tail().as_cell()?; - let colo = T(stack, &[D(b':' as u64), D(0)]); - let trel = T(stack, &[colo, D(0), D(0)]); + let colo = T(*stack, &[D(b':' as u64), D(0)]); + let trel = T(*stack, &[colo, D(0), D(0)]); let smyt = smyt(stack, spot.head())?; @@ -145,7 +134,7 @@ pub mod util { list = list.tail().as_cell()?; } // "{end_col}]>" - let p4 = T(stack, &[D(b']' as u64), D(b'>' as u64), D(0)]); + let p4 = T(*stack, &[D(b']' as u64), D(b'>' as u64), D(0)]); (*list.tail_as_mut()) = p4; list = end_lin.as_cell()?; @@ -156,7 +145,7 @@ pub mod util { list = list.tail().as_cell()?; } // "{end_lin} {end_col}]>" - let p3 = T(stack, &[D(b' ' as u64), end_col]); + let p3 = T(*stack, &[D(b' ' as u64), end_col]); (*list.tail_as_mut()) = p3; list = str_col.as_cell()?; @@ -168,7 +157,7 @@ pub mod util { } // "{str_col}].[{end_lin} {end_col}]>" let p2 = T( - stack, + *stack, &[D(b']' as u64), D(b'.' as u64), D(b'[' as u64), end_lin], ); (*list.tail_as_mut()) = p2; @@ -181,19 +170,19 @@ pub mod util { list = list.tail().as_cell()?; } // "{str_lin} {str_col}].[{end_lin} {end_col}]>" - let p1 = T(stack, &[D(b' ' as u64), str_col]); + let p1 = T(*stack, &[D(b' ' as u64), str_col]); (*list.tail_as_mut()) = p1; // "<[{str_lin} {str_col}].[{end_lin} {end_col}]>" - let tape = T(stack, &[D(b'<' as u64), D(b'[' as u64), str_lin]); - let finn = T(stack, &[LEAF, tape]); + let tape = T(*stack, &[D(b'<' as u64), D(b'[' as u64), str_lin]); + let finn = T(*stack, &[LEAF, tape]); - T(stack, &[ROSE, trel, smyt, finn, D(0)]) + T(*stack, &[ROSE, trel, smyt, finn, D(0)]) } _ => { - let tape = rip(stack, 3, 1, tag.as_atom())?; + let tape = rip(context.stack, 3, 1, tag.as_atom())?; T( - stack, + context.stack, &[ D(tas!(b"m")), D(tas!(b"o")), @@ -210,7 +199,7 @@ pub mod util { }; if flop { - res = T(stack, &[tank, res]); + res = T(context.stack, &[tank, res]); } else { (*memory).head = tank; } @@ -221,7 +210,7 @@ pub mod util { (*memory).tail = D(0); } - let toon = Cell::new(stack, D(2), res); + let toon = Cell::new(context.stack, D(2), res); Ok(toon) } } @@ -256,6 +245,8 @@ pub mod util { mod tests { use super::*; use crate::jets::util::test::{assert_jet, init_stack}; + use crate::mem::NockStack; + use crate::noun::{D, T}; use crate::serf::TERMINATOR; use std::sync::Arc; diff --git a/rust/ares/src/jets/text.rs b/rust/ares/src/jets/text.rs index 486c87e..c26fdf7 100644 --- a/rust/ares/src/jets/text.rs +++ b/rust/ares/src/jets/text.rs @@ -1,14 +1,13 @@ -use crate::jets::util::slot; /** Text processing jets */ +use crate::interpreter::Context; +use crate::jets::util::slot; use crate::jets::Result; -use crate::mem::NockStack; -use crate::newt::Newt; use crate::noun::{Noun, D}; crate::gdb!(); -pub fn jet_lent(_stack: &mut NockStack, _newt: &mut Option<&mut Newt>, subject: Noun) -> Result { +pub fn jet_lent(_context: &mut Context, subject: Noun) -> Result { let tape = slot(subject, 6)?; util::lent(tape).map(|x| D(x as u64)) } diff --git a/rust/ares/src/jets/tree.rs b/rust/ares/src/jets/tree.rs index 9e71263..891e793 100644 --- a/rust/ares/src/jets/tree.rs +++ b/rust/ares/src/jets/tree.rs @@ -1,19 +1,14 @@ /** Tree jets */ -use crate::jets; +use crate::interpreter::Context; use crate::jets::util::*; use crate::jets::JetErr::*; -use crate::mem::NockStack; -use crate::newt::Newt; +use crate::jets::Result; use crate::noun::{Noun, D}; crate::gdb!(); -pub fn jet_cap( - _stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_cap(_context: &mut Context, subject: Noun) -> Result { let arg = slot(subject, 6)?; let tom = arg.as_atom()?; let met = met(0, tom); @@ -29,11 +24,8 @@ pub fn jet_cap( } } -pub fn jet_mas( - stack: &mut NockStack, - _newt: &mut Option<&mut Newt>, - subject: Noun, -) -> jets::Result { +pub fn jet_mas(context: &mut Context, subject: Noun) -> Result { + let stack = &mut context.stack; let arg = slot(subject, 6)?; let tom = arg.as_atom()?; let met = met(0, tom); diff --git a/rust/ares/src/main.rs b/rust/ares/src/main.rs index 3fafd59..75afefb 100644 --- a/rust/ares/src/main.rs +++ b/rust/ares/src/main.rs @@ -1,6 +1,7 @@ -use ares::interpreter::interpret; +use ares::hamt::Hamt; +use ares::interpreter::{interpret, Context}; use ares::mem::NockStack; -use ares::noun::IndirectAtom; +use ares::noun::{IndirectAtom, Noun}; use ares::serf::serf; use ares::serialization::{cue, jam}; use memmap::Mmap; @@ -61,8 +62,13 @@ fn main() -> io::Result<()> { let input_cell = input .as_cell() .expect("Input must be jam of subject/formula pair"); - let result = interpret(&mut stack, &mut None, input_cell.head(), input_cell.tail()) - .expect("nock failed"); + let mut context = Context { + stack: &mut stack, + newt: None, + cache: &mut Hamt::::new(), + }; + let result = + interpret(&mut context, input_cell.head(), input_cell.tail()).expect("nock failed"); if let Ok(atom) = result.as_atom() { println!("Result: {}", atom); } diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index 1a089f9..2442890 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -1,3 +1,5 @@ +use crate::hamt::Hamt; +use crate::interpreter; use crate::interpreter::{inc, interpret, Tone}; use crate::jets::nock::util::mook; use crate::jets::text::util::lent; @@ -5,7 +7,8 @@ use crate::mem::NockStack; use crate::mug::mug_u32; use crate::newt::Newt; use crate::noun::{Cell, Noun, Slots, D, T}; -use crate::snapshot::{self, Snapshot}; +use crate::snapshot::double_jam::DoubleJam; +use crate::snapshot::Snapshot; use ares_macros::tas; use signal_hook; use signal_hook::consts::SIGINT; @@ -18,6 +21,138 @@ use std::sync::Arc; crate::gdb!(); +struct Context { + epoch: u64, + event_num: u64, + snapshot: DoubleJam, + arvo: Noun, + mug: u32, + stack: NockStack, + newt: Newt, + cache: Hamt, + // XX: persistent memo cache +} + +impl Context { + pub fn new(snap_path: &PathBuf) -> Self { + // TODO: switch to Pma when ready + // let snap = &mut snapshot::pma::Pma::new(snap_path); + let mut snapshot = DoubleJam::new(snap_path); + let mut stack = NockStack::new(256 << 10 << 10, 0); + let newt = Newt::new(); + let cache = Hamt::::new(); + + let (epoch, event_num, arvo) = snapshot.load(&mut stack).unwrap_or((0, 0, D(0))); + let mug = mug_u32(&mut stack, arvo); + + Context { + epoch, + event_num, + snapshot, + arvo, + mug, + stack, + newt, + cache, + } + } + + // + // Getters + // + + pub fn epoch(&self) -> u64 { + self.epoch + } + + pub fn event_num(&self) -> u64 { + self.event_num + } + + pub fn arvo(&self) -> Noun { + self.arvo + } + + pub fn stack_as_mut(&mut self) -> &mut NockStack { + &mut self.stack + } + + pub fn for_interpreter(&mut self) -> interpreter::Context { + self.cache = Hamt::::new(); + + interpreter::Context { + stack: &mut self.stack, + newt: Some(&mut self.newt), + cache: &mut self.cache, + } + } + + // + // Setters + // + + pub fn event_update(&mut self, new_event_num: u64, new_arvo: Noun) { + // XX: assert event numbers are continuous + self.arvo = new_arvo; + self.event_num = new_event_num; + self.snapshot.save(&mut self.stack, &mut self.arvo); + self.mug = mug_u32(&mut self.stack, self.arvo); + } + + // + // Snapshot functions + // + + pub fn sync(&mut self) { + self.snapshot + .sync(&mut self.stack, self.epoch, self.event_num); + } + + // + // Newt functions + // + + pub fn next(&mut self) -> Option { + self.newt.next(&mut self.stack) + } + + pub fn ripe(&mut self) { + self.newt + .ripe(&mut self.stack, self.event_num, self.mug as u64); + } + + pub fn live(&mut self) { + self.newt.live(&mut self.stack); + } + + pub fn peek_done(&mut self, dat: Noun) { + self.newt.peek_done(&mut self.stack, dat); + } + + pub fn play_done(&mut self) { + self.newt.play_done(&mut self.stack, self.mug as u64); + } + + pub fn play_bail(&mut self, dud: Noun) { + self.newt + .play_bail(&mut self.stack, self.event_num, self.mug as u64, dud); + } + + pub fn work_done(&mut self, fec: Noun) { + self.newt + .work_done(&mut self.stack, self.event_num, self.mug as u64, fec); + } + + pub fn work_swap(&mut self, job: Noun, fec: Noun) { + self.newt + .work_swap(&mut self.stack, self.event_num, self.mug as u64, job, fec); + } + + pub fn work_bail(&mut self, lud: Noun) { + self.newt.work_bail(&mut self.stack, lud); + } +} + #[allow(dead_code)] const LOAD_AXIS: u64 = 4; const PEEK_AXIS: u64 = 22; @@ -46,22 +181,14 @@ pub fn serf() -> io::Result<()> { snap_path.push(".urb"); snap_path.push("chk"); create_dir_all(&snap_path)?; - // TODO: switch to Pma when ready - // let snap = &mut snapshot::pma::Pma::new(snap_path); - let snap = &mut snapshot::double_jam::DoubleJam::new(snap_path); - let stack = &mut NockStack::new(256 << 10 << 10, 0); - let newt = &mut Newt::new(); - - let (_epoch, loaded_event_num, mut arvo) = snap.load(stack).unwrap_or((0, 0, D(0))); - let mut current_event_num = loaded_event_num; - let loaded_mug = mug_u32(stack, arvo); - let mut current_mug = loaded_mug; - - newt.ripe(stack, current_event_num, loaded_mug as u64); + let mut context = Context::new(&snap_path); + context.ripe(); // Can't use for loop because it borrows newt - while let Some(writ) = newt.next(stack) { + while let Some(writ) = context.next() { + // XX: probably want to bookend this logic frame_push / frame_pop + // preserve jet state and persistent cache, lose everything else let tag = slot(writ, 2)?.as_direct().unwrap(); match tag.data() { tas!(b"live") => { @@ -72,140 +199,33 @@ pub fn serf() -> io::Result<()> { tas!(b"save") => { // XX what is eve for? eprintln!("save"); - snap.sync(stack, 0, current_event_num); + context.sync(); } tas!(b"meld") => eprintln!("meld"), tas!(b"pack") => eprintln!("pack"), _ => eprintln!("unknown live"), } - newt.live(stack); + context.live(); } tas!(b"peek") => { let sam = slot(writ, 7)?; - let res = slam(stack, newt, arvo, PEEK_AXIS, sam) - .expect("peek error handling unimplemented"); - newt.peek_done(stack, res); + let res = + slam(&mut context, PEEK_AXIS, sam).expect("peek error handling unimplemented"); + context.peek_done(res); } tas!(b"play") => { - // apply lifecycle to first batch - if current_event_num == 0 { - let eve = slot(writ, 7)?; - let sub = T(stack, &[D(0), D(3)]); - let lyf = T(stack, &[D(2), sub, D(0), D(2)]); - match interpret(stack, &mut Some(newt), eve, lyf) { - Ok(gat) => { - arvo = slot(gat, 7) - .expect("serf: play: lifecycle didn't return initial Arvo"); - current_event_num = - lent(eve).expect("serf: play: boot event number failure") as u64; - current_mug = mug_u32(stack, arvo); - } - Err(Tone::Error(_, trace)) => { - let tone = Cell::new(stack, D(2), trace); - let tang = mook(stack, &mut Some(newt), tone, false) - .expect("serf: play: +mook crashed on bail") - .tail(); - let goof = T(stack, &[D(tas!(b"exit")), tang]); - newt.play_bail(stack, 0, 0, goof); - } - Err(Tone::Blocked(_)) => { - panic!("play: blocked err handling unimplemented") - } - } + let lit = slot(writ, 7)?; + if context.epoch() == 0 && context.event_num() == 0 { + // apply lifecycle to first batch + play_life(&mut context, lit); } else { - // do we need to assert something here? - // current_event_num = slot(writ, 6)?.as_direct().unwrap().data(); - - let mut lit = slot(writ, 7)?; - while let Ok(cell) = lit.as_cell() { - let ovo = cell.head(); - - match slam(stack, newt, arvo, POKE_AXIS, ovo) { - Ok(res) => { - let cell = res.as_cell().expect("serf: play: +slam returned atom"); - arvo = cell.tail(); - current_mug = mug_u32(stack, arvo); - current_event_num += 1; - } - Err(Tone::Error(_, trace)) => { - let tone = Cell::new(stack, D(2), trace); - let tang = mook(stack, &mut Some(newt), tone, false) - .expect("serf: play: +mook crashed on bail") - .tail(); - let goof = T(stack, &[D(tas!(b"exit")), tang]); - newt.play_bail(stack, current_event_num, current_mug as u64, goof); - } - Err(Tone::Blocked(_)) => { - panic!("play: blocked err handling unimplemented") - } - } - - lit = cell.tail(); - } + play_list(&mut context, lit); }; - - snap.save(stack, &mut arvo); - newt.play_done(stack, current_mug as u64); } tas!(b"work") => { // XX: what is in slot 6? it's mil_w in Vere Serf - // TODO: assert event numbers are continuous let job = slot(writ, 7)?; - match soft(stack, newt, arvo, POKE_AXIS, job) { - Ok(res) => { - let cell = res.as_cell().expect("serf: work: +slam returned atom"); - let fec = cell.head(); - arvo = cell.tail(); - snap.save(stack, &mut arvo); - - current_mug = mug_u32(stack, arvo); - current_event_num += 1; - - newt.work_done(stack, current_event_num, current_mug as u64, fec); - } - Err(goof) => { - // TODO: on decryption failure in aes_siv, should bail as fast as - // possible, without rendering stack trace or injecting crud event. See - // c3__evil in vere. - - clear_interrupt(); - - // crud = [+(now) [%$ %arvo ~] [%crud goof ovo]] - let job_cell = job.as_cell().expect("serf: work: job not a cell"); - let now = inc( - stack, - job_cell.head().as_atom().expect("serf: work: now not atom"), - ) - .as_noun(); - let wire = T(stack, &[D(0), D(tas!(b"arvo")), D(0)]); - let crud = T(stack, &[now, wire, D(tas!(b"crud")), goof, job_cell.tail()]); - - match soft(stack, newt, arvo, POKE_AXIS, crud) { - Ok(res) => { - let cell = - res.as_cell().expect("serf: work: crud +slam returned atom"); - let fec = cell.head(); - arvo = cell.tail(); - snap.save(stack, &mut arvo); - - current_mug = mug_u32(stack, arvo); - current_event_num += 1; - - newt.work_swap( - stack, - current_event_num, - current_mug as u64, - crud, - fec, - ); - } - Err(goof_crud) => { - let lud = T(stack, &[goof_crud, goof, D(0)]); - newt.work_bail(stack, lud); - } - } - } - } + work(&mut context, job); } _ => panic!("got message with unknown tag {}", tag), }; @@ -216,45 +236,143 @@ pub fn serf() -> io::Result<()> { Ok(()) } -pub fn slam( - stack: &mut NockStack, - newt: &mut Newt, - core: Noun, - axis: u64, - ovo: Noun, -) -> Result { - let pul = T(stack, &[D(9), D(axis), D(0), D(2)]); - let sam = T(stack, &[D(6), D(0), D(7)]); - let fol = T(stack, &[D(8), pul, D(9), D(2), D(10), sam, D(0), D(2)]); - let sub = T(stack, &[core, ovo]); - interpret(stack, &mut Some(newt), sub, fol) +fn burn(context: &mut Context, subject: Noun, formula: Noun) -> Result { + let burn_context = &mut context.for_interpreter(); + interpret(burn_context, subject, formula) +} + +fn slam(context: &mut Context, axis: u64, ovo: Noun) -> Result { + let arvo = context.arvo(); + let pul = T(context.stack_as_mut(), &[D(9), D(axis), D(0), D(2)]); + let sam = T(context.stack_as_mut(), &[D(6), D(0), D(7)]); + let fol = T( + context.stack_as_mut(), + &[D(8), pul, D(9), D(2), D(10), sam, D(0), D(2)], + ); + let sub = T(context.stack_as_mut(), &[arvo, ovo]); + burn(context, sub, fol) +} + +fn goof(context: &mut Context, trace: Noun) -> Noun { + let tone = Cell::new(context.stack_as_mut(), D(2), trace); + let mook_context = &mut context.for_interpreter(); + + let tang = mook(mook_context, tone, false) + .expect("serf: goof: +mook crashed on bail") + .tail(); + // XX: noun::Tone or noun::NockErr should use a bail enum system similar to u3m_bail motes; + // might be able to replace NockErr with mote and map determinism to individual motes; + // for, always set to %exit + T(mook_context.stack, &[D(tas!(b"exit")), tang]) } /** Run slam, process stack trace to tang if error */ -pub fn soft( - stack: &mut NockStack, - newt: &mut Newt, - core: Noun, - axis: u64, - ovo: Noun, -) -> Result { - match slam(stack, newt, core, axis, ovo) { +fn soft(context: &mut Context, ovo: Noun) -> Result { + match slam(context, POKE_AXIS, ovo) { Ok(res) => Ok(res), - Err(Tone::Error(_, trace)) => { - let tone = Cell::new(stack, D(2), trace); - let tang = mook(stack, &mut Some(newt), tone, false) - .expect("serf: soft: +mook crashed on bail") - .tail(); - // XX: noun::Tone or noun::NockErr should use a bail enum system similar to u3m_bail motes; - // might be able to replace NockErr with mote and map determinism to individual motes; - // for, always set to %exit - let goof = T(stack, &[D(tas!(b"exit")), tang]); - Err(goof) - } + Err(Tone::Error(_, trace)) => Err(goof(context, trace)), Err(Tone::Blocked(_)) => panic!("soft: blocked err handling unimplemented"), } } +fn play_life(context: &mut Context, eve: Noun) { + let sub = T(context.stack_as_mut(), &[D(0), D(3)]); + let lyf = T(context.stack_as_mut(), &[D(2), sub, D(0), D(2)]); + match burn(context, eve, lyf) { + Ok(gat) => { + let eved = lent(eve).expect("serf: play: boot event number failure") as u64; + let arvo = slot(gat, 7).expect("serf: play: lifecycle didn't return initial Arvo"); + + context.event_update(eved, arvo); + context.play_done(); + } + Err(Tone::Error(_, trace)) => { + let goof = goof(context, trace); + context.play_bail(goof); + } + Err(Tone::Blocked(_)) => { + panic!("play: blocked err handling unimplemented") + } + } +} + +fn play_list(context: &mut Context, mut lit: Noun) { + let mut eve = context.event_num(); + while let Ok(cell) = lit.as_cell() { + let ovo = cell.head(); + match soft(context, ovo) { + Ok(res) => { + let arvo = res + .as_cell() + .expect("serf: work: +slam returned atom") + .tail(); + eve += 1; + + context.event_update(eve, arvo); + } + Err(goof) => { + return context.play_bail(goof); + } + } + lit = cell.tail(); + } + context.play_done(); +} + +fn work(context: &mut Context, job: Noun) { + match soft(context, job) { + Ok(res) => { + let cell = res.as_cell().expect("serf: work: +slam returned atom"); + let fec = cell.head(); + let eve = context.event_num(); + + context.event_update(eve + 1, cell.tail()); + context.work_done(fec); + } + Err(goof) => { + work_swap(context, job, goof); + } + } +} + +fn work_swap(context: &mut Context, job: Noun, goof: Noun) { + // TODO: on decryption failure in aes_siv, should bail as fast as + // possible, without rendering stack trace or injecting crud event. See + // c3__evil in vere. + + clear_interrupt(); + + // crud = [+(now) [%$ %arvo ~] [%crud goof ovo]] + let job_cell = job.as_cell().expect("serf: work: job not a cell"); + let job_now = job_cell.head().as_atom().expect("serf: work: now not atom"); + let now = inc(context.stack_as_mut(), job_now).as_noun(); + let wire = T(context.stack_as_mut(), &[D(0), D(tas!(b"arvo")), D(0)]); + let crud = T( + context.stack_as_mut(), + &[now, wire, D(tas!(b"crud")), goof, job_cell.tail()], + ); + + match soft(context, crud) { + Ok(res) => { + let cell = res.as_cell().expect("serf: work: crud +slam returned atom"); + let fec = cell.head(); + let eve = context.event_num(); + + context.event_update(eve + 1, cell.tail()); + context.work_swap(crud, fec); + } + Err(goof_crud) => { + work_bail(context, &[goof_crud, goof]); + } + } +} + +fn work_bail(context: &mut Context, goofs: &[Noun]) { + let lest = T(context.stack_as_mut(), goofs); + let lud = T(context.stack_as_mut(), &[lest, D(0)]); + context.work_bail(lud); +} + fn slot(noun: Noun, axis: u64) -> io::Result { noun.slot(axis) .map_err(|_e| io::Error::new(io::ErrorKind::InvalidInput, "Bad axis")) diff --git a/rust/ares/src/snapshot/double_jam.rs b/rust/ares/src/snapshot/double_jam.rs index 2ae13c9..e021d2e 100644 --- a/rust/ares/src/snapshot/double_jam.rs +++ b/rust/ares/src/snapshot/double_jam.rs @@ -110,6 +110,7 @@ impl Snapshot for DoubleJam { } fn save(&mut self, _stack: &mut NockStack, noun: &mut Noun) { + // XX: I don't think this needs to be mut self.noun = *noun; } From 760e20e844f5bbb14f56773279c5af4a10e61aad Mon Sep 17 00:00:00 2001 From: Matthew LeVan Date: Tue, 10 Oct 2023 13:45:11 -0400 Subject: [PATCH 15/19] noun: fix `DirectAtom::as_bytes()` --- rust/ares/src/noun.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/ares/src/noun.rs b/rust/ares/src/noun.rs index 4404fd9..451f14d 100644 --- a/rust/ares/src/noun.rs +++ b/rust/ares/src/noun.rs @@ -188,7 +188,7 @@ impl DirectAtom { } pub fn as_bytes(&self) -> &[u8] { - let bytes: &[u8; 8] = unsafe { std::mem::transmute(self.0) }; + let bytes: &[u8; 8] = unsafe { std::mem::transmute(&self.0) }; &bytes[..] } } From cf6427ebd955d575754f15ab92fbe260afe96e88 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Tue, 10 Oct 2023 11:26:53 -0600 Subject: [PATCH 16/19] Add debug assertions from NockStack investigation --- rust/ares/Cargo.toml | 5 ++ rust/ares/src/interpreter.rs | 44 +++++++++++++++ rust/ares/src/mem.rs | 105 ++++++++++++++++++++++++++++++++++- rust/ares/src/mug.rs | 11 ++++ rust/ares/src/noun.rs | 48 +++++++++++++++- 5 files changed, 211 insertions(+), 2 deletions(-) diff --git a/rust/ares/Cargo.toml b/rust/ares/Cargo.toml index f842319..8988f91 100644 --- a/rust/ares/Cargo.toml +++ b/rust/ares/Cargo.toml @@ -42,4 +42,9 @@ opt-level = 3 [profile.dev.package."*"] opt-level = 3 +# run with e.g. 'cargo build --features check_forwarding,check_acyclic' [features] +check_all = [ "check_acyclic", "check_forwarding", "check_junior" ] +check_acyclic = [] +check_forwarding = [] +check_junior = [] diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index 993e4a8..aa70d36 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -1,3 +1,6 @@ +use crate::assert_acyclic; +use crate::assert_no_forwarding_pointers; +use crate::assert_no_junior_pointers; use crate::hamt::Hamt; use crate::jets::JetErr; use crate::mem::unifying_equality; @@ -269,9 +272,17 @@ impl From for NockErr { } } +#[allow(unused_variables)] +fn debug_assertions(stack: &mut NockStack, noun: Noun) { + assert_acyclic!(noun); + assert_no_forwarding_pointers!(noun); + assert_no_junior_pointers!(stack, noun); +} + /** Interpret nock */ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Result { let terminator = Arc::clone(&TERMINATOR); + let orig_subject = subject; // for debugging let virtual_frame: *const u64 = context.stack.get_frame_pointer(); let mut res: Noun = D(0); @@ -301,15 +312,30 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res let work: NockWork = *context.stack.top(); match work { NockWork::Done => { + debug_assertions(context.stack, orig_subject); + debug_assertions(context.stack, subject); + debug_assertions(context.stack, res); + context.stack.preserve(context.cache); context.stack.preserve(&mut res); context.stack.frame_pop(); + + debug_assertions(context.stack, orig_subject); + debug_assertions(context.stack, res); + break Ok(res); } NockWork::Ret => { + debug_assertions(context.stack, orig_subject); + debug_assertions(context.stack, subject); + debug_assertions(context.stack, res); + context.stack.preserve(context.cache); context.stack.preserve(&mut res); context.stack.frame_pop(); + + debug_assertions(context.stack, orig_subject); + debug_assertions(context.stack, res); } NockWork::WorkCons(mut cons) => match cons.todo { TodoCons::ComputeHead => { @@ -367,6 +393,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res vale.todo = Todo2::RestoreSubject; std::mem::swap(&mut vale.subject, &mut subject); *context.stack.top() = NockWork::Work2(vale); + + debug_assertions(context.stack, orig_subject); + debug_assertions(context.stack, subject); + debug_assertions(context.stack, res); + mean_frame_push(context.stack, 0); *context.stack.push() = NockWork::Ret; push_formula(context.stack, res, true)?; @@ -375,6 +406,10 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res Todo2::RestoreSubject => { subject = vale.subject; context.stack.pop::(); + + debug_assertions(context.stack, orig_subject); + debug_assertions(context.stack, subject); + debug_assertions(context.stack, res); } } } @@ -519,6 +554,11 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res kale.todo = Todo9::RestoreSubject; kale.core = subject; *context.stack.top() = NockWork::Work9(kale); + + debug_assertions(context.stack, orig_subject); + debug_assertions(context.stack, subject); + debug_assertions(context.stack, res); + subject = res; mean_frame_push(context.stack, 0); *context.stack.push() = NockWork::Ret; @@ -532,6 +572,10 @@ pub fn interpret(context: &mut Context, mut subject: Noun, formula: Noun) -> Res Todo9::RestoreSubject => { subject = kale.core; context.stack.pop::(); + + debug_assertions(context.stack, orig_subject); + debug_assertions(context.stack, subject); + debug_assertions(context.stack, res); } } } diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 5bc2c35..dfcc79b 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -1,4 +1,6 @@ use crate::assert_acyclic; +use crate::assert_no_forwarding_pointers; +use crate::assert_no_junior_pointers; use crate::noun::{Atom, Cell, CellMemory, IndirectAtom, Noun, NounAllocator}; use crate::snapshot::pma::{pma_in_arena, pma_malloc_w}; use assert_no_alloc::permit_alloc; @@ -356,6 +358,10 @@ impl NockStack { } unsafe fn copy(&mut self, noun: &mut Noun) { + assert_acyclic!(*noun); + assert_no_forwarding_pointers!(*noun); + assert_no_junior_pointers!(self, *noun); + self.pre_copy(); assert!(self.stack_is_empty()); let noun_ptr = noun as *mut Noun; @@ -443,7 +449,10 @@ impl NockStack { } } // Set saved previous allocation pointer its new value after this allocation + assert_acyclic!(*noun); + assert_no_forwarding_pointers!(*noun); + assert_no_junior_pointers!(self, *noun); } pub unsafe fn copy_pma(&mut self, noun: &mut Noun) { @@ -517,7 +526,6 @@ impl NockStack { }, } } - assert_acyclic!(*noun); } pub unsafe fn frame_pop(&mut self) { @@ -679,6 +687,88 @@ impl NockStack { unsafe { self.stack_pointer == self.alloc_pointer.add(RESERVED) } } } + + pub fn no_junior_pointers(&self, noun: Noun) -> bool { + unsafe { + if let Ok(c) = noun.as_cell() { + let mut fp: *mut u64; + let mut sp = self.stack_pointer; + let mut ap = self.alloc_pointer; + let mut pfp = *(self.prev_frame_pointer_pointer()) as *mut u64; + let mut psp = *(self.prev_stack_pointer_pointer()) as *mut u64; + let mut pap = *(self.prev_alloc_pointer_pointer()) as *mut u64; + + let mut dbg_stack = Vec::new(); + + // Detemine range + let (rlo, rhi) = loop { + if psp.is_null() { + psp = ((self.start as u64) + ((self.size << 3) as u64)) as *mut u64; + } + let (lo, hi) = if sp < ap { (ap, psp) } else { (psp, ap) }; + let ptr = c.to_raw_pointer() as *mut u64; + if ptr >= lo && ptr < hi { + break if sp < ap { (sp, ap) } else { (ap, sp) }; + } else { + fp = pfp; + sp = psp; + ap = pap; + if sp < ap { + pfp = *(fp.sub(FRAME + 1)) as *mut u64; + psp = *(fp.sub(STACK + 1)) as *mut u64; + pap = *(fp.sub(ALLOC + 1)) as *mut u64; + } else { + pfp = *(fp.add(FRAME)) as *mut u64; + psp = *(fp.add(STACK)) as *mut u64; + pap = *(fp.add(ALLOC)) as *mut u64; + } + } + }; + + dbg_stack.push(c.head()); + dbg_stack.push(c.tail()); + while let Some(n) = dbg_stack.pop() { + if let Ok(a) = n.as_allocated() { + let ptr = a.to_raw_pointer(); + if ptr >= rlo && ptr < rhi { + eprintln!( + "\rserf: Noun {:x} has Noun {:x} in junior of range {:p}-{:p}", + (noun.raw << 3), + (n.raw << 3), + rlo, + rhi + ); + return false; + } + if let Some(c) = a.cell() { + dbg_stack.push(c.tail()); + dbg_stack.push(c.head()); + } + } + } + + true + } else { + true + } + } + } +} + +#[cfg(feature = "check_junior")] +#[macro_export] +macro_rules! assert_no_junior_pointers { + ( $x:expr, $y:expr ) => { + assert_no_alloc::permit_alloc(|| { + assert!($x.no_junior_pointers($y)); + }) + }; +} + +#[cfg(not(feature = "check_junior"))] +#[macro_export] +macro_rules! assert_no_junior_pointers { + ( $x:expr, $y:expr ) => {}; } pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Noun) -> bool { @@ -705,6 +795,13 @@ pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Nou * senior noun, *never vice versa*, to avoid introducing references from more senior frames * into more junior frames, which would result in incorrect operation of the copier. */ + assert_acyclic!(*a); + assert_acyclic!(*b); + assert_no_forwarding_pointers!(*a); + assert_no_forwarding_pointers!(*b); + assert_no_junior_pointers!(stack, *a); + assert_no_junior_pointers!(stack, *b); + // If the nouns are already word-equal we have nothing to do if (*a).raw_equals(*b) { return true; @@ -800,8 +897,14 @@ pub unsafe fn unifying_equality(stack: &mut NockStack, a: *mut Noun, b: *mut Nou } } stack.frame_pop(); + assert_acyclic!(*a); assert_acyclic!(*b); + assert_no_forwarding_pointers!(*a); + assert_no_forwarding_pointers!(*b); + assert_no_junior_pointers!(stack, *a); + assert_no_junior_pointers!(stack, *b); + (*a).raw_equals(*b) } diff --git a/rust/ares/src/mug.rs b/rust/ares/src/mug.rs index 68d9b5c..7e5f0af 100644 --- a/rust/ares/src/mug.rs +++ b/rust/ares/src/mug.rs @@ -1,4 +1,6 @@ use crate::assert_acyclic; +use crate::assert_no_forwarding_pointers; +use crate::assert_no_junior_pointers; use crate::mem::*; use crate::noun::{Allocated, Atom, DirectAtom, Noun}; use either::Either::*; @@ -119,7 +121,11 @@ pub fn mug_u32(stack: &mut NockStack, noun: Noun) -> u32 { if let Some(mug) = get_mug(noun) { return mug; } + assert_acyclic!(noun); + assert_no_forwarding_pointers!(noun); + assert_no_junior_pointers!(stack, noun); + stack.frame_push(0); unsafe { *(stack.push()) = noun; @@ -171,6 +177,11 @@ pub fn mug_u32(stack: &mut NockStack, noun: Noun) -> u32 { unsafe { stack.frame_pop(); } + + assert_acyclic!(noun); + assert_no_forwarding_pointers!(noun); + assert_no_junior_pointers!(stack, noun); + get_mug(noun).expect("Noun should have a mug once it is mugged.") } diff --git a/rust/ares/src/noun.rs b/rust/ares/src/noun.rs index 451f14d..232d391 100644 --- a/rust/ares/src/noun.rs +++ b/rust/ares/src/noun.rs @@ -44,7 +44,9 @@ pub const NO: Noun = D(1); #[macro_export] macro_rules! assert_acyclic { ( $x:expr ) => { - assert!(crate::noun::acyclic_noun($x)); + assert_no_alloc::permit_alloc(|| { + assert!(crate::noun::acyclic_noun($x)); + }) }; } @@ -82,6 +84,42 @@ fn acyclic_noun_go(noun: Noun, seen: &mut IntMap<()>) -> bool { } } +#[cfg(feature = "check_forwarding")] +#[macro_export] +macro_rules! assert_no_forwarding_pointers { + ( $x:expr ) => { + assert_no_alloc::permit_alloc(|| { + assert!(crate::noun::no_forwarding_pointers($x)); + }) + }; +} + +#[cfg(not(feature = "check_forwarding"))] +#[macro_export] +macro_rules! assert_no_forwarding_pointers { + ( $x:expr ) => {}; +} + +pub fn no_forwarding_pointers(noun: Noun) -> bool { + let mut dbg_stack = Vec::new(); + dbg_stack.push(noun); + + while !dbg_stack.is_empty() { + if let Some(noun) = dbg_stack.pop() { + if unsafe { noun.raw & FORWARDING_MASK == FORWARDING_TAG } { + return false; + } else if let Ok(cell) = noun.as_cell() { + dbg_stack.push(cell.tail()); + dbg_stack.push(cell.head()); + } + } else { + break; + } + } + + true +} + /** Test if a noun is a direct atom. */ fn is_direct_atom(noun: u64) -> bool { noun & DIRECT_MASK == DIRECT_TAG @@ -835,6 +873,14 @@ impl Allocated { } } + pub fn cell(&self) -> Option { + if self.is_cell() { + unsafe { Some(self.cell) } + } else { + None + } + } + pub fn as_noun(&self) -> Noun { Noun { allocated: *self } } From c32b8654d120e3d9723e49387f0764fd7bedef20 Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Tue, 10 Oct 2023 21:54:51 -0600 Subject: [PATCH 17/19] fix linter complaints --- rust/ares/src/mem.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index dfcc79b..5a10803 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -694,9 +694,9 @@ impl NockStack { let mut fp: *mut u64; let mut sp = self.stack_pointer; let mut ap = self.alloc_pointer; - let mut pfp = *(self.prev_frame_pointer_pointer()) as *mut u64; - let mut psp = *(self.prev_stack_pointer_pointer()) as *mut u64; - let mut pap = *(self.prev_alloc_pointer_pointer()) as *mut u64; + let mut pfp = *(self.prev_frame_pointer_pointer()); + let mut psp = *(self.prev_stack_pointer_pointer()); + let mut pap = *(self.prev_alloc_pointer_pointer()); let mut dbg_stack = Vec::new(); From 823c95abf8346cd556516955affbcf7e08722a9b Mon Sep 17 00:00:00 2001 From: Alex Shelkovnykov Date: Tue, 10 Oct 2023 21:55:47 -0600 Subject: [PATCH 18/19] enable all NockStack validation checks for CI --- .github/workflows/ares-shared.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ares-shared.yml b/.github/workflows/ares-shared.yml index 7c00c57..ac35f4b 100644 --- a/.github/workflows/ares-shared.yml +++ b/.github/workflows/ares-shared.yml @@ -36,7 +36,7 @@ jobs: -A clippy::missing_safety_doc - name: Build - run: cargo build --release --verbose + run: cargo build --release --verbose --features check_all - name: Run tests run: cargo test --verbose -- --test-threads=1 From 6c82f61edc4334ec94b90ce72b0c0d4f3cdb0d58 Mon Sep 17 00:00:00 2001 From: Jonathan Paprocki Date: Fri, 13 Oct 2023 10:57:42 -0400 Subject: [PATCH 19/19] mem: add reset() for NockStack --- rust/ares/src/mem.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 5a10803..c8c80c1 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -88,6 +88,19 @@ impl NockStack { } } + /** Resets the NockStack. */ + pub fn reset(&mut self, top_slots: usize) { + self.frame_pointer = unsafe { self.start.add(RESERVED + top_slots) } as *mut u64; + self.stack_pointer = self.frame_pointer; + self.alloc_pointer = unsafe { self.start.add(self.size) } as *mut u64; + self.pc = false; + unsafe { + *self.frame_pointer.sub(FRAME + 1) = ptr::null::() as u64; // "frame pointer" from "previous" frame + *self.frame_pointer.sub(STACK + 1) = ptr::null::() as u64; // "stack pointer" from "previous" frame + *self.frame_pointer.sub(ALLOC + 1) = ptr::null::() as u64; // "alloc pointer" from "previous" frame + }; + } + /** Current frame pointer of this NockStack */ pub fn get_frame_pointer(&self) -> *const u64 { self.frame_pointer