mirror of
https://github.com/urbit/ares.git
synced 2024-12-24 13:55:23 +03:00
Merge branch 'status' into msl/ordering
This commit is contained in:
commit
d52ac68a1f
2
.github/workflows/ares-shared.yml
vendored
2
.github/workflows/ares-shared.yml
vendored
@ -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
|
||||
|
@ -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 $-(* *)
|
||||
|
@ -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
21
rust/ares/Cargo.lock
generated
@ -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"
|
||||
|
@ -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 = []
|
||||
|
@ -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
@ -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: {:?}",
|
||||
|
@ -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();
|
||||
|
@ -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 {
|
||||
|
@ -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)]
|
||||
|
@ -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 {
|
||||
|
@ -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() {
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
@ -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.")
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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 }
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user