use cyber_hemera as hemera;
use rune_ast::Noun;
pub fn path_atom(s: &str) -> u64 {
let digest = hemera::hash(s.as_bytes());
u64::from_le_bytes(digest.as_bytes()[..8].try_into().unwrap())
}
pub fn world(entries: &[(&str, Noun)]) -> Noun {
let mut w = Noun::Atom(0);
for (path, val) in entries.iter().rev() {
let entry = Noun::cell(Noun::Atom(path_atom(path)), val.clone());
w = Noun::cell(entry, w);
}
w
}
pub struct Subject {
pub self_neuron: Noun,
pub now: Noun,
pub here: Noun,
pub caps: Noun,
pub code: Noun,
pub libs: Noun,
pub mem: Noun,
pub world: Noun,
}
impl Subject {
pub fn to_noun(&self) -> Noun {
let inner7 = Noun::cell(self.mem.clone(), self.world.clone());
let inner6 = Noun::cell(self.libs.clone(), inner7);
let inner5 = Noun::cell(self.code.clone(), inner6);
let inner4 = Noun::cell(self.caps.clone(), inner5);
let inner3 = Noun::cell(self.here.clone(), inner4);
let inner2 = Noun::cell(self.now.clone(), inner3);
Noun::cell(self.self_neuron.clone(), inner2)
}
pub fn minimal() -> Self {
Subject {
self_neuron: Noun::Atom(0),
now: Noun::Atom(0),
here: Noun::Atom(0),
caps: Noun::Atom(0),
code: Noun::Atom(0),
libs: Noun::Atom(0),
mem: Noun::Atom(0),
world: Noun::Atom(0),
}
}
pub fn standalone() -> Self {
Subject {
self_neuron: Noun::Atom(path_atom("@self")),
now: Noun::Atom(0),
here: Noun::Atom(path_atom("~/")),
caps: Noun::Atom(0),
code: Noun::Atom(0),
libs: Noun::Atom(0),
mem: Noun::Atom(0),
world: Noun::Atom(0),
}
}
}
pub mod axis {
pub const SELF: u64 = 2;
pub const NOW: u64 = 6;
pub const HERE: u64 = 14;
pub const CAPS: u64 = 30;
pub const CODE: u64 = 62;
pub const LIBS: u64 = 126;
pub const MEM: u64 = 254;
pub const WORLD: u64 = 255;
pub fn subject_slot_axis(n: usize) -> u64 {
if n < 7 { (1u64 << (n + 2)) - 2 } else { 255 }
}
}
#[cfg(test)]
mod tests {
use super::*;
use rune_interp::axis as nox_axis;
fn a(n: u64) -> Noun { Noun::Atom(n) }
#[test]
fn axis_constants_are_correct() {
let subj = Subject {
self_neuron: a(10),
now: a(11),
here: a(12),
caps: a(13),
code: a(14),
libs: a(15),
mem: a(16),
world: a(17),
};
let noun = subj.to_noun();
let get = |ax: u64| nox_axis(&noun, ax).expect("axis failed");
assert_eq!(get(axis::SELF), a(10), "SELF axis {}", axis::SELF);
assert_eq!(get(axis::NOW), a(11), "NOW axis {}", axis::NOW);
assert_eq!(get(axis::HERE), a(12), "HERE axis {}", axis::HERE);
assert_eq!(get(axis::CAPS), a(13), "CAPS axis {}", axis::CAPS);
assert_eq!(get(axis::CODE), a(14), "CODE axis {}", axis::CODE);
assert_eq!(get(axis::LIBS), a(15), "LIBS axis {}", axis::LIBS);
assert_eq!(get(axis::MEM), a(16), "MEM axis {}", axis::MEM);
assert_eq!(get(axis::WORLD), a(17), "WORLD axis {}", axis::WORLD);
}
#[test]
fn path_atom_is_stable_and_distinct() {
assert_eq!(path_atom("cyber/truth"), path_atom("cyber/truth"));
assert_ne!(path_atom("cyber/truth"), path_atom("cyber/rune"));
assert_ne!(path_atom("cyber/truth"), 0);
}
#[test]
fn world_keys_are_path_atoms() {
let w = world(&[("cyber/truth", a(42))]);
let expected = Noun::cell(
Noun::cell(a(path_atom("cyber/truth")), a(42)),
a(0),
);
assert_eq!(w, expected);
assert_eq!(world(&[]), a(0));
}
#[test]
fn standalone_self_resolves_nonzero() {
let noun = Subject::standalone().to_noun();
let self_neuron = nox_axis(&noun, axis::SELF).expect("axis");
assert_ne!(self_neuron, a(0));
}
#[test]
fn subject_slot_axis_matches_constants() {
assert_eq!(axis::subject_slot_axis(0), axis::SELF);
assert_eq!(axis::subject_slot_axis(1), axis::NOW);
assert_eq!(axis::subject_slot_axis(2), axis::HERE);
assert_eq!(axis::subject_slot_axis(3), axis::CAPS);
assert_eq!(axis::subject_slot_axis(4), axis::CODE);
assert_eq!(axis::subject_slot_axis(5), axis::LIBS);
assert_eq!(axis::subject_slot_axis(6), axis::MEM);
assert_eq!(axis::subject_slot_axis(7), axis::WORLD);
}
}