Merge branch 'status' into msl/ordering

This commit is contained in:
Matthew LeVan 2023-10-13 12:05:23 -04:00 committed by GitHub
commit d52ac68a1f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 1313 additions and 768 deletions

View File

@ -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

View File

@ -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 $-(* *)

View File

@ -8,39 +8,46 @@
!=
=>
|%
++ 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
++ 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
(ack (dec m) 1)
(ack (dec m) $(n (dec n)))
++ slow
|= x=@ud
!:
(slow-help x 0)
++ slow-help
|= [x=@ud y=@ud]
!.
|= [m=@ud n=@ud]
:: %hela hint
~> %hela
:: %memo hint
~+
?~ m +(n)
?~ n
(wack (dec m) 1)
(wack (dec m) $(n (dec n)))
?: .= x y
x
$(y .+(y))
-- =>
::
|%
@ -50,8 +57,26 @@
++ 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 (slow (bex 23)))]
[~ ..poke]
::
=/ buz
~> %mean.'pith: bad wasp'
;;(wasp card.ovo)
?+ -.buz
~> %slog.[0 leaf+(scow %ud (ack 2 1))]
[~ ..poke]
::
%crud
=/ tang tang.goof.buz
|-
?~ tang
[~ ..poke]
~> %slog.[0 -.tang]
$(tang +.tang)
==
--
::
|= [now=@da ovo=ovum]

21
rust/ares/Cargo.lock generated
View File

@ -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"

View File

@ -9,22 +9,25 @@ edition = "2018"
# [patch.crates-io]
# ibig = { path = "../ibig-rs" }
# Please keep these alphabetized
[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"
# use this when debugging requires allocation (e.g. eprintln)
# assert_no_alloc = {version="1.1.2", features=["warn_debug"]}
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"
@ -39,5 +42,9 @@ opt-level = 3
[profile.dev.package."*"]
opt-level = 3
# run with e.g. 'cargo build --features check_forwarding,check_acyclic'
[features]
check_acyclic=[]
check_all = [ "check_acyclic", "check_forwarding", "check_junior" ]
check_acyclic = []
check_forwarding = []
check_junior = []

View File

@ -104,7 +104,7 @@ impl<T: Copy> MutHamt<T> {
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<T: Copy + Preserve> Preserve for Hamt<T> {
};
*(stem.buffer.add(idx) as *mut Entry<T>) = 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;

File diff suppressed because it is too large Load Diff

View File

@ -7,6 +7,7 @@ pub mod sort;
pub mod text;
pub mod tree;
use crate::interpreter::Context;
use crate::jets::bits::*;
use crate::jets::form::*;
use crate::jets::hash::*;
@ -16,7 +17,6 @@ use crate::jets::sort::*;
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;
@ -25,7 +25,7 @@ crate::gdb!();
/// Return Err if the computation crashed or should punt to Nock
pub type Result = std::result::Result<Noun, JetErr>;
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
@ -294,6 +294,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;
@ -314,8 +315,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::<Noun>::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);
}
@ -330,8 +336,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::<Noun>::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: {:?}",

View File

@ -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();

View File

@ -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 {

View File

@ -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)]

View File

@ -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,7 +183,7 @@ 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
@ -227,11 +202,8 @@ pub fn jet_lth(
Ok(util::lth(a, b))
}
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()?;
@ -241,16 +213,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()?;
@ -258,11 +227,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,
)
@ -270,23 +239,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())
}
pub mod util {

View File

@ -1,44 +1,30 @@
/** Virtualization jets
*/
use crate::interpreter::{interpret, NockErr};
use crate::jets;
use crate::interpreter::Context;
use crate::jets::util::slot;
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 {
// XX: interpret should accept optional scry function and potentially produce blocked
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: 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])),
},
}
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;
@ -46,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<Cell, JetErr> {
pub fn mook(context: &mut Context, tone: Cell, flop: bool) -> result::Result<Cell, JetErr> {
let tag = tone.head().as_direct()?;
let original_list = tone.tail();
@ -76,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
@ -86,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
}
@ -99,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
@ -117,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())?;
@ -141,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()?;
@ -152,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()?;
@ -164,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;
@ -177,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")),
@ -206,7 +199,7 @@ pub mod util {
};
if flop {
res = T(stack, &[tank, res]);
res = T(context.stack, &[tank, res]);
} else {
(*memory).head = tank;
}
@ -217,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)
}
}
@ -252,6 +245,23 @@ 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;
#[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() {

View File

@ -1,5 +1,7 @@
/** Text processing jets
*/
use crate::interpreter::Context;
use crate::jets::util::slot;
use crate::jets::Result;
use crate::jets::util::slot;
use crate::mem::NockStack;
@ -8,7 +10,7 @@ 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))
}

View File

@ -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);

View File

@ -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;

View File

@ -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::<Noun>::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);
}

View File

@ -1,6 +1,9 @@
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;
use either::Either::{self, Left, Right};
use ibig::Stack;
use libc::{c_void, memcmp};
@ -85,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::<u64>() as u64; // "frame pointer" from "previous" frame
*self.frame_pointer.sub(STACK + 1) = ptr::null::<u64>() as u64; // "stack pointer" from "previous" frame
*self.frame_pointer.sub(ALLOC + 1) = ptr::null::<u64>() as u64; // "alloc pointer" from "previous" frame
};
}
/** Current frame pointer of this NockStack */
pub fn get_frame_pointer(&self) -> *const u64 {
self.frame_pointer
@ -355,6 +371,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;
@ -442,7 +462,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) {
@ -516,7 +539,6 @@ impl NockStack {
},
}
}
assert_acyclic!(*noun);
}
pub unsafe fn frame_pop(&mut self) {
@ -528,6 +550,18 @@ 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()
{
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;
}
@ -666,6 +700,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());
let mut psp = *(self.prev_stack_pointer_pointer());
let mut pap = *(self.prev_alloc_pointer_pointer());
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 {
@ -692,6 +808,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;
@ -738,7 +861,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 {
@ -788,8 +910,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)
}

View File

@ -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.")
}

View File

@ -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);

View File

@ -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
@ -188,7 +226,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[..]
}
}
@ -835,6 +873,14 @@ impl Allocated {
}
}
pub fn cell(&self) -> Option<Cell> {
if self.is_cell() {
unsafe { Some(self.cell) }
} else {
None
}
}
pub fn as_noun(&self) -> Noun {
Noun { allocated: *self }
}

View File

@ -1,21 +1,158 @@
use crate::interpreter::{interpret, NockErr};
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;
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;
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!();
struct Context {
epoch: u64,
event_num: u64,
snapshot: DoubleJam,
arvo: Noun,
mug: u32,
stack: NockStack,
newt: Newt,
cache: Hamt<Noun>,
// 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::<Noun>::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::<Noun>::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<Noun> {
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;
@ -23,12 +160,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<AtomicBool> = 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 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))?;
let snap_path_string = std::env::args()
.nth(2)
.ok_or(io::Error::new(io::ErrorKind::Other, "no pier path"))?;
@ -36,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(96 << 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") => {
@ -62,135 +199,185 @@ 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)]);
// XX: TODO
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(NockErr::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(NockErr::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(NockErr::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(NockErr::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
let job = slot(writ, 7)?;
match slam(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(NockErr::Error(trace)) => {
// XX: Our Arvo can't currently handle %crud, so just bail
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]);
// lud = (list goof)
let lud = T(stack, &[goof, D(0)]);
newt.work_bail(stack, lud);
}
Err(NockErr::Blocked(_)) => {
panic!("play: blocked err handling unimplemented")
}
}
work(&mut context, job);
}
_ => panic!("got message with unknown tag {}", tag),
};
clear_interrupt();
}
Ok(())
}
pub fn slam(
stack: &mut NockStack,
newt: &mut Newt,
core: Noun,
axis: u64,
ovo: Noun,
) -> Result<Noun, NockErr> {
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<Noun, Tone> {
let burn_context = &mut context.for_interpreter();
interpret(burn_context, subject, formula)
}
fn slam(context: &mut Context, axis: u64, ovo: Noun) -> Result<Noun, Tone> {
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 */
fn soft(context: &mut Context, ovo: Noun) -> Result<Noun, Noun> {
match slam(context, POKE_AXIS, ovo) {
Ok(res) => Ok(res),
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> {
noun.slot(axis)
.map_err(|_e| io::Error::new(io::ErrorKind::InvalidInput, "Bad axis"))
}
fn clear_interrupt() {
(*TERMINATOR).store(false, Ordering::Relaxed);
}

View File

@ -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;
}