mirror of
https://github.com/urbit/ares.git
synced 2024-12-22 21:01:29 +03:00
snag and roll jets (#269)
* snag * roll and call site caching logic * site: propagate errors * interpreter: use flog for string interpolation * yak shaving, reorder jets alphabetically * jets: flip sample in snag tests
This commit is contained in:
parent
74fad40bdc
commit
299292a876
@ -12,7 +12,7 @@ sword_guard = { path = "../sword_guard" }
|
||||
sword_crypto = { path = "../sword_crypto" }
|
||||
sword_macros = { path = "../sword_macros" }
|
||||
sword_pma = { path = "../sword_pma" }
|
||||
assert_no_alloc = { path = "../assert_no_alloc" }
|
||||
assert_no_alloc = { path = "../assert_no_alloc", features=["warn_debug"]}
|
||||
murmur3 = { path = "../murmur3" }
|
||||
bitvec = "1.0.0"
|
||||
criterion = "0.4"
|
||||
|
@ -1563,9 +1563,7 @@ mod hint {
|
||||
slogger.slog(stack, 0, cell.head());
|
||||
list = cell.tail();
|
||||
} else {
|
||||
let stack = &mut context.stack;
|
||||
let tape = tape(stack, "serf: %hela: list ends without ~");
|
||||
slog_leaf(stack, slogger, tape);
|
||||
flog!(context, "serf: %hela: list ends without ~");
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1609,37 +1607,36 @@ mod hint {
|
||||
tas!(b"fast") => {
|
||||
if !cfg!(feature = "sham_hints") {
|
||||
if let Some(clue) = hint {
|
||||
let cold_res: cold::Result = {
|
||||
let chum = clue.slot(2).ok()?;
|
||||
|
||||
let mut parent = clue.slot(6).ok()?;
|
||||
loop {
|
||||
if let Ok(parent_cell) = parent.as_cell() {
|
||||
if unsafe { parent_cell.head().raw_equals(D(11)) } {
|
||||
match parent.slot(7) {
|
||||
Ok(noun) => {
|
||||
parent = noun;
|
||||
}
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
let chum = clue.slot(2).ok()?;
|
||||
let mut parent = clue.slot(6).ok()?;
|
||||
loop {
|
||||
if let Ok(parent_cell) = parent.as_cell() {
|
||||
if unsafe { parent_cell.head().raw_equals(D(11)) } {
|
||||
match parent.slot(7) {
|
||||
Ok(noun) => {
|
||||
parent = noun;
|
||||
}
|
||||
Err(_) => {
|
||||
return None;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
let parent_formula_op = parent.slot(2).ok()?.atom()?.direct()?;
|
||||
let parent_formula_ax = parent.slot(3).ok()?.atom()?;
|
||||
}
|
||||
let parent_formula_op = parent.slot(2).ok()?.atom()?.direct()?;
|
||||
let parent_formula_ax = parent.slot(3).ok()?.atom()?;
|
||||
|
||||
let cold_res: cold::Result = {
|
||||
if parent_formula_op.data() == 1 {
|
||||
if parent_formula_ax.direct()?.data() == 0 {
|
||||
cold.register(stack, res, parent_formula_ax, chum)
|
||||
} else {
|
||||
// XX: Need better message in slog; need better slogging tools
|
||||
// format!("invalid root parent axis: {} {}", chum, parent_formula_ax)
|
||||
// XX: flog! is ideal, but it runs afoul of the borrow checker
|
||||
// flog!(context, "invalid root parent formula: {} {}", chum, parent);
|
||||
let tape = tape(
|
||||
stack,
|
||||
"serf: cold: register: invalid root parent axis",
|
||||
@ -1655,25 +1652,15 @@ mod hint {
|
||||
match cold_res {
|
||||
Ok(true) => context.warm = Warm::init(stack, cold, hot),
|
||||
Err(cold::Error::NoParent) => {
|
||||
// XX: Need better message in slog; need better slogging tools
|
||||
// format!("could not find parent battery at given axis: {} {}", chum, parent_formula_ax)
|
||||
let tape = tape(
|
||||
stack,
|
||||
"serf: cold: register: could not find parent battery at given axis",
|
||||
);
|
||||
slog_leaf(stack, slogger, tape);
|
||||
flog!(context, "serf: cold: register: could not match parent battery at given axis: {} {}", chum, parent_formula_ax);
|
||||
}
|
||||
Err(cold::Error::BadNock) => {
|
||||
// XX: Need better message in slog; need better slogging tools
|
||||
// format!("bad clue formula: {}", clue)
|
||||
let tape = tape(stack, "serf: cold: register: bad clue formula");
|
||||
slog_leaf(stack, slogger, tape);
|
||||
flog!(context, "serf: cold: register: bad clue formula: {}", clue);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
} else {
|
||||
let tape = tape(stack, "serf: cold: register: no clue for %fast");
|
||||
slog_leaf(stack, slogger, tape);
|
||||
flog!(context, "serf: cold: register: no clue for %fast");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -94,6 +94,16 @@ pub const URBIT_HOT_STATE: &[HotEntry] = &[
|
||||
1,
|
||||
jet_lent,
|
||||
),
|
||||
(
|
||||
&[K_139, Left(b"one"), Left(b"two"), Left(b"roll")],
|
||||
1,
|
||||
jet_roll,
|
||||
),
|
||||
(
|
||||
&[K_139, Left(b"one"), Left(b"two"), Left(b"snag")],
|
||||
1,
|
||||
jet_snag,
|
||||
),
|
||||
(
|
||||
&[K_139, Left(b"one"), Left(b"two"), Left(b"snip")],
|
||||
1,
|
||||
|
@ -1,11 +1,10 @@
|
||||
/** Text processing jets
|
||||
*/
|
||||
use crate::interpreter::{interpret, Context};
|
||||
use crate::interpreter::Context;
|
||||
use crate::jets::util::{slot, BAIL_FAIL};
|
||||
use crate::jets::Result;
|
||||
use crate::noun::{Cell, Noun, D, T};
|
||||
use bitvec::order::Lsb0;
|
||||
use bitvec::slice::BitSlice;
|
||||
use crate::site::{site_slam, Site};
|
||||
|
||||
crate::gdb!();
|
||||
|
||||
@ -19,11 +18,71 @@ pub fn jet_lent(_context: &mut Context, subject: Noun) -> Result {
|
||||
util::lent(list).map(|x| D(x as u64))
|
||||
}
|
||||
|
||||
pub fn jet_roll(context: &mut Context, subject: Noun) -> Result {
|
||||
let sample = slot(subject, 6)?;
|
||||
let mut list = slot(sample, 2)?;
|
||||
let mut gate = slot(sample, 3)?;
|
||||
let mut prod = slot(gate, 13)?;
|
||||
|
||||
let site = Site::new(context, &mut gate);
|
||||
loop {
|
||||
if let Ok(list_cell) = list.as_cell() {
|
||||
list = list_cell.tail();
|
||||
let sam = T(&mut context.stack, &[list_cell.head(), prod]);
|
||||
prod = site_slam(context, &site, sam)?;
|
||||
} else {
|
||||
if unsafe { !list.raw_equals(D(0)) } {
|
||||
return Err(BAIL_FAIL);
|
||||
}
|
||||
return Ok(prod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jet_snag(_context: &mut Context, subject: Noun) -> Result {
|
||||
let sam = slot(subject, 6)?;
|
||||
let index = slot(sam, 2)?;
|
||||
let list = slot(sam, 3)?;
|
||||
|
||||
util::snag(list, index)
|
||||
}
|
||||
|
||||
pub fn jet_snip(context: &mut Context, subject: Noun) -> Result {
|
||||
let list = slot(subject, 6)?;
|
||||
util::snip(&mut context.stack, list)
|
||||
}
|
||||
|
||||
pub fn jet_turn(context: &mut Context, subject: Noun) -> Result {
|
||||
let sample = slot(subject, 6)?;
|
||||
let mut list = slot(sample, 2)?;
|
||||
let mut gate = slot(sample, 3)?;
|
||||
let mut res = D(0);
|
||||
let mut dest: *mut Noun = &mut res; // Mutable pointer because we cannot guarantee initialized
|
||||
|
||||
// Since the gate doesn't change, we can do a single jet check and use that through the whole
|
||||
// loop
|
||||
let site = Site::new(context, &mut gate);
|
||||
loop {
|
||||
if let Ok(list_cell) = list.as_cell() {
|
||||
list = list_cell.tail();
|
||||
unsafe {
|
||||
let (new_cell, new_mem) = Cell::new_raw_mut(&mut context.stack);
|
||||
(*new_mem).head = site_slam(context, &site, list_cell.head())?;
|
||||
*dest = new_cell.as_noun();
|
||||
dest = &mut (*new_mem).tail;
|
||||
}
|
||||
} else {
|
||||
if unsafe { !list.raw_equals(D(0)) } {
|
||||
return Err(BAIL_FAIL);
|
||||
}
|
||||
unsafe {
|
||||
*dest = D(0);
|
||||
};
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn jet_zing(context: &mut Context, subject: Noun) -> Result {
|
||||
let list = slot(subject, 6)?;
|
||||
let stack = &mut context.stack;
|
||||
@ -31,97 +90,6 @@ pub fn jet_zing(context: &mut Context, subject: Noun) -> Result {
|
||||
util::zing(stack, list)
|
||||
}
|
||||
|
||||
pub fn jet_turn(context: &mut Context, subject: Noun) -> Result {
|
||||
let sample = slot(subject, 6)?;
|
||||
let mut list = slot(sample, 2)?;
|
||||
let mut gate = slot(sample, 3)?;
|
||||
let mut gate_battery = slot(gate, 2)?;
|
||||
let gate_context = slot(gate, 7)?;
|
||||
let mut res = D(0);
|
||||
let mut dest: *mut Noun = &mut res; // Mutable pointer because we cannot guarantee initialized
|
||||
|
||||
// Since the gate doesn't change, we can do a single jet check and use that through the whole
|
||||
// loop
|
||||
if let Some((jet, _path)) = context
|
||||
.warm
|
||||
.find_jet(&mut context.stack, &mut gate, &mut gate_battery)
|
||||
.filter(|(_jet, mut path)| {
|
||||
// check that 7 is a prefix of the parent battery axis,
|
||||
// to ensure that the sample (axis 6) is not part of the jet match.
|
||||
//
|
||||
// XX TODO this check is pessimized since there could be multiple ways to match the
|
||||
// jet and we only actually match one of them, but we check all of them and run
|
||||
// unjetted if any have an axis outside 7.
|
||||
let axis_7_bits: &BitSlice<u64, Lsb0> = BitSlice::from_element(&7u64);
|
||||
let batteries_list = context.cold.find(&mut context.stack, &mut path);
|
||||
let mut ret = true;
|
||||
for mut batteries in batteries_list {
|
||||
if let Some((_battery, parent_axis)) = batteries.next() {
|
||||
let parent_axis_prefix_bits = &parent_axis.as_bitslice()[0..3];
|
||||
if parent_axis_prefix_bits == axis_7_bits {
|
||||
continue;
|
||||
} else {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret
|
||||
})
|
||||
{
|
||||
loop {
|
||||
if let Ok(list_cell) = list.as_cell() {
|
||||
list = list_cell.tail();
|
||||
let element_subject = T(
|
||||
&mut context.stack,
|
||||
&[gate_battery, list_cell.head(), gate_context],
|
||||
);
|
||||
unsafe {
|
||||
let (new_cell, new_mem) = Cell::new_raw_mut(&mut context.stack);
|
||||
(*new_mem).head = jet(context, element_subject)?;
|
||||
*dest = new_cell.as_noun();
|
||||
dest = &mut (*new_mem).tail;
|
||||
}
|
||||
} else {
|
||||
if unsafe { !list.raw_equals(D(0)) } {
|
||||
return Err(BAIL_FAIL);
|
||||
}
|
||||
unsafe {
|
||||
*dest = D(0);
|
||||
};
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
loop {
|
||||
if let Ok(list_cell) = list.as_cell() {
|
||||
list = list_cell.tail();
|
||||
let element_subject = T(
|
||||
&mut context.stack,
|
||||
&[gate_battery, list_cell.head(), gate_context],
|
||||
);
|
||||
unsafe {
|
||||
let (new_cell, new_mem) = Cell::new_raw_mut(&mut context.stack);
|
||||
(*new_mem).head = interpret(context, element_subject, gate_battery)?;
|
||||
*dest = new_cell.as_noun();
|
||||
dest = &mut (*new_mem).tail;
|
||||
}
|
||||
} else {
|
||||
if unsafe { !list.raw_equals(D(0)) } {
|
||||
return Err(BAIL_FAIL);
|
||||
}
|
||||
unsafe {
|
||||
*dest = D(0);
|
||||
};
|
||||
return Ok(res);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub mod util {
|
||||
use crate::jets::util::BAIL_EXIT;
|
||||
use crate::jets::{JetErr, Result};
|
||||
@ -165,6 +133,22 @@ pub mod util {
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
pub fn snag(tape: Noun, index: Noun) -> Result {
|
||||
let mut list = tape;
|
||||
let mut idx = index.as_atom()?.as_u64()? as usize;
|
||||
loop {
|
||||
if unsafe { list.raw_equals(D(0)) } {
|
||||
return Err(BAIL_EXIT);
|
||||
}
|
||||
let cell = list.as_cell()?;
|
||||
if idx == 0 {
|
||||
return Ok(cell.head());
|
||||
}
|
||||
idx -= 1;
|
||||
list = cell.tail();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snip(stack: &mut NockStack, tape: Noun) -> Result {
|
||||
let mut ret = D(0);
|
||||
let mut dest = &mut ret as *mut Noun;
|
||||
@ -283,6 +267,24 @@ mod tests {
|
||||
assert_jet_err(c, jet_lent, sam, BAIL_EXIT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_snag() {
|
||||
let c = &mut init_context();
|
||||
let list1 = T(&mut c.stack, &[D(1), D(2), D(3), D(0)]);
|
||||
let sam = T(&mut c.stack, &[D(1), list1]);
|
||||
assert_jet(c, jet_snag, sam, D(2));
|
||||
|
||||
let list2 = T(&mut c.stack, &[D(1), D(0)]);
|
||||
let sam = T(&mut c.stack, &[D(0), list2]);
|
||||
assert_jet(c, jet_snag, sam, D(1));
|
||||
|
||||
let sam = T(&mut c.stack, &[D(3), list1]);
|
||||
assert_jet_err(c, jet_snag, sam, BAIL_EXIT);
|
||||
|
||||
let sam = T(&mut c.stack, &[D(0), D(0)]);
|
||||
assert_jet_err(c, jet_snag, sam, BAIL_EXIT);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_snip() {
|
||||
let c = &mut init_context();
|
||||
|
@ -13,6 +13,7 @@ pub mod mug;
|
||||
pub mod newt;
|
||||
pub mod noun;
|
||||
pub mod serf;
|
||||
pub mod site;
|
||||
//pub mod bytecode;
|
||||
pub mod persist;
|
||||
pub mod serialization;
|
||||
|
73
rust/sword/src/site.rs
Normal file
73
rust/sword/src/site.rs
Normal file
@ -0,0 +1,73 @@
|
||||
/** Call site of a kick (Nock 9), used to cache call targets. */
|
||||
use bitvec::order::Lsb0;
|
||||
use bitvec::slice::BitSlice;
|
||||
|
||||
use crate::interpreter::{interpret, Context};
|
||||
use crate::jets::util::slot;
|
||||
use crate::jets::{Jet, JetErr};
|
||||
use crate::noun::{Noun, D, T};
|
||||
|
||||
/// Return Err if the computation crashed or should punt to Nock
|
||||
pub type Result = std::result::Result<Noun, JetErr>;
|
||||
|
||||
pub struct Site {
|
||||
pub battery: Noun, // battery
|
||||
pub context: Noun, // context
|
||||
pub jet: Option<Jet>, // jet driver
|
||||
pub path: Noun, // label
|
||||
}
|
||||
|
||||
impl Site {
|
||||
/// Prepare a locally cached gate to call repeatedly.
|
||||
pub fn new(ctx: &mut Context, core: &mut Noun) -> Site {
|
||||
let mut battery = slot(*core, 2).unwrap();
|
||||
let context = slot(*core, 7).unwrap();
|
||||
|
||||
let warm_result = ctx
|
||||
.warm
|
||||
.find_jet(&mut ctx.stack, core, &mut battery)
|
||||
.filter(|(_jet, mut path)| {
|
||||
// check that 7 is a prefix of the parent battery axis,
|
||||
// to ensure that the sample (axis 6) is not part of the jet match.
|
||||
//
|
||||
// XX TODO this check is pessimized since there could be multiple ways to match the
|
||||
// jet and we only actually match one of them, but we check all of them and run
|
||||
// unjetted if any have an axis outside 7.
|
||||
let axis_7_bits: &BitSlice<u64, Lsb0> = BitSlice::from_element(&7u64);
|
||||
let batteries_list = ctx.cold.find(&mut ctx.stack, &mut path);
|
||||
let mut ret = true;
|
||||
for mut batteries in batteries_list {
|
||||
if let Some((_battery, parent_axis)) = batteries.next() {
|
||||
let parent_axis_prefix_bits = &parent_axis.as_bitslice()[0..3];
|
||||
if parent_axis_prefix_bits == axis_7_bits {
|
||||
continue;
|
||||
} else {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ret = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ret
|
||||
});
|
||||
Site {
|
||||
battery,
|
||||
context,
|
||||
jet: warm_result.map(|(jet, _)| jet),
|
||||
path: warm_result.map(|(_, path)| path).unwrap_or(D(0)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Slam a cached call site.
|
||||
pub fn site_slam(ctx: &mut Context, site: &Site, sample: Noun) -> Result {
|
||||
let subject = T(&mut ctx.stack, &[site.battery, sample, site.context]);
|
||||
if site.jet.is_some() {
|
||||
let jet = site.jet.unwrap();
|
||||
jet(ctx, subject)
|
||||
} else {
|
||||
Ok(interpret(ctx, subject, site.battery)?)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user