From 6e40a1ebee4a75eade2ae2bba53fd6c11e9a27a2 Mon Sep 17 00:00:00 2001 From: Edward Amsden Date: Wed, 10 Apr 2024 20:48:49 -0500 Subject: [PATCH] codegen: progress re-implementing rust interpreter --- hoon/codegen/lib/line.hoon | 2 +- hoon/codegen/sur/gene.hoon | 10 +- rust/ares/src/codegen.rs | 423 +++++++++++++++++++++++++++++++++++ rust/ares/src/interpreter.rs | 3 +- rust/ares/src/jets.rs | 4 +- rust/ares/src/jets/seam.rs | 25 ++- rust/ares/src/lib.rs | 1 + rust/ares/src/mem.rs | 41 +++- rust/ares/src/noun.rs | 12 +- rust/ares/src/serf.rs | 3 +- 10 files changed, 504 insertions(+), 20 deletions(-) create mode 100644 rust/ares/src/codegen.rs diff --git a/hoon/codegen/lib/line.hoon b/hoon/codegen/lib/line.hoon index 36f76d2..4fe4eb1 100644 --- a/hoon/codegen/lib/line.hoon +++ b/hoon/codegen/lib/line.hoon @@ -1265,7 +1265,7 @@ hill -- :: codegen interface -:- %1 +=+ %1 |% :: :: core reference diff --git a/hoon/codegen/sur/gene.hoon b/hoon/codegen/sur/gene.hoon index 2439fb7..566af83 100644 --- a/hoon/codegen/sur/gene.hoon +++ b/hoon/codegen/sur/gene.hoon @@ -202,11 +202,11 @@ :: :: long: starting label for direct calls axis 2 :: want: input registers for direct calls axis 6 -:: walt: input starting registers LR axis 30 -:: wish: starting label for indirect calls axis 62 -:: sire: input register for indirect calls axis 126 -:: will: code table for arm axis 254 -:: sans: next SSA register axis 255 +:: walt: input starting registers LR axis 14 +:: wish: starting label for indirect calls axis 30 +:: sire: input register for indirect calls axis 62 +:: will: code table for arm axis 126 +:: sans: next SSA register axis 127 +$ pile $: long=bile want=need diff --git a/rust/ares/src/codegen.rs b/rust/ares/src/codegen.rs new file mode 100644 index 0000000..56c830b --- /dev/null +++ b/rust/ares/src/codegen.rs @@ -0,0 +1,423 @@ +use crate::interpreter::{inc, interpret, Context, Error, Result, BAIL_EXIT}; +use crate::jets::seam::util::get_by; +use crate::jets::util::slot; +use crate::mem::NockStack; +use crate::noun::{DirectAtom, Noun, D, NOUN_NONE, T}; +use ares_macros::tas; +use std::mem::size_of; +use std::ptr::write_bytes; +use std::slice::{from_raw_parts, from_raw_parts_mut}; + +#[derive(Copy, Clone)] +struct Frame { + /// Slow stack as a list + slow: Noun, + /// Mean stack as a list + mean: Noun, + /// Code table for current arm + pile: Noun, + /// Continuation label when returning to this frame + cont: Noun, + /// Result register when returning to this frame + salt: usize, + /// number of locals + vars: usize, +} + +impl Frame { + fn init(&mut self, vars: usize, prev: Option<&Frame>) { + *self = Frame { + slow: prev.map_or(D(0), |f| f.slow), + mean: prev.map_or(D(0), |f| f.mean), + pile: NOUN_NONE, + cont: NOUN_NONE, + salt: usize::MAX, + vars, + }; + unsafe { write_bytes((self as *mut Frame).add(1) as *mut u64, 0, vars) }; + } + + unsafe fn current<'a>(stack: &NockStack) -> &'a Self { + &(*(stack.get_frame_base() as *const Frame)) + } + + unsafe fn current_mut<'a>(stack: &NockStack) -> &'a mut Self { + &mut (*(stack.get_frame_base() as *mut Frame)) + } + + fn vars<'a>(&self) -> &'a [Noun] { + unsafe { from_raw_parts((self as *const Frame).add(1) as *const Noun, self.vars) } + } + + fn vars_mut<'a>(&mut self) -> &'a mut [Noun] { + unsafe { from_raw_parts_mut((self as *mut Frame).add(1) as *mut Noun, self.vars) } + } + + fn mean_push(&mut self, stack: &mut NockStack, entry: Noun) { + self.mean = T(stack, &[entry, self.mean]); + } + + fn mean_pop(&mut self) { + self.mean = self + .mean + .as_cell() + .expect("Cannot pop empty mean stack") + .tail(); + } + + fn slow_push(&mut self, stack: &mut NockStack, entry: Noun) { + self.slow = T(stack, &[entry, self.slow]); + } + + fn slow_pop(&mut self) { + self.slow = self + .slow + .as_cell() + .expect("Cannot pop empty slow stack") + .tail(); + } +} + +assert_eq_align!(Frame, u64, usize); +assert_eq_size!(u64, usize); +const FRAME_WORD_SIZE: usize = size_of::() / size_of::(); + +fn push_interpreter_frame(stack: &mut NockStack, pile: Noun) { + let vars = pile_sans(pile); + let prev = unsafe { Frame::current(stack) }; + stack.frame_push(FRAME_WORD_SIZE + vars); + let frame = unsafe { Frame::current_mut(stack) }; + frame.init(vars, Some(prev)); + frame.pile = pile; +} + +fn push_outer_frame(stack: &mut NockStack, pile: Noun) { + let vars = pile_sans(pile); + stack.frame_push(FRAME_WORD_SIZE + vars); + let frame = unsafe { Frame::current_mut(stack) }; + frame.init(vars, None); + frame.pile = pile; +} + +const PEEK_AXIS: u64 = 4; +const POKE_AXIS: u64 = 46; + +fn slam_line(context: &mut Context, arm_axis: u64, sample: Noun) -> Noun { + let axis_noun = DirectAtom::new_panic(arm_axis).as_noun(); + let subject = T(&mut context.stack, &[sample, context.line]); + let sample_patch = T(&mut context.stack, &[D(6), D(0), D(2)]); + let arm_kick_form = T(&mut context.stack, &[D(9), axis_noun, D(0), D(3)]); + let gate_slam_form = T( + &mut context.stack, + &[D(9), D(2), D(10), sample_patch, arm_kick_form], + ); + interpret(context, subject, gate_slam_form).expect("Crash in codegen") +} + +fn cg_peek(context: &mut Context, subject: Noun, formula: Noun) -> Option { + assert!(!context.line.is_none()); + let sample = T(&mut context.stack, &[subject, formula]); + let peek_result = slam_line(context, PEEK_AXIS, sample); + if unsafe { peek_result.raw_equals(D(0)) } { + None + } else { + let unit_cell = peek_result.as_cell().expect("Peek should return unit"); + Some(unit_cell.tail()) + } +} + +fn cg_poke(context: &mut Context, slow: Noun, subject: Noun, formula: Noun) { + assert!(!context.line.is_none()); + let sample = T( + &mut context.stack, + &[D(tas!(b"comp")), slow, subject, formula], + ); + let result = slam_line(context, POKE_AXIS, sample); + let new_line = slot(result, 7).expect("Poke should return triple"); + context.line = new_line; +} + +/// Get the $pile for an arm, possibly updating the line core +fn cg_indirect( + context: &mut Context, + hill: &mut Noun, + slow: Noun, + subject: Noun, + formula: Noun, +) -> Noun { + let bell_hill = if let Some(res) = cg_peek(context, subject, formula) { + res + } else { + cg_poke(context, slow, subject, formula); + cg_peek(context, subject, formula).expect("Codegen peek should return value after poke.") + }; + let bell_hill_cell = bell_hill + .as_cell() + .expect("Codegen successful peek should return pair"); + get_by( + &mut context.stack, + &mut bell_hill_cell.tail(), + &mut bell_hill_cell.head(), + ) + .expect("Codegen bell lookup should succeed.") + .expect("Codegen peek bell should be in hill") +} + +pub fn cg_interpret(context: &mut Context, slow: Noun, subject: Noun, formula: Noun) -> Result { + let mut hill = NOUN_NONE; + let outer_pile = cg_indirect(context, &mut hill, slow, subject, formula); + let virtual_frame = context.stack.get_frame_pointer(); + push_outer_frame(&mut context.stack, outer_pile); + let mut wish = pile_wish(outer_pile); + let (mut body, mut bend) = get_blob(context, outer_pile, &mut wish); + let sire = pile_sire(outer_pile); + (unsafe { Frame::current_mut(&context.stack).vars_mut() })[sire] = subject; + let inner_res = 'interpret: loop { + let frame = unsafe { Frame::current_mut(&context.stack) }; + if let Ok(body_cell) = body.as_cell() { + body = body_cell.tail(); + let inst_cell = body_cell + .head() + .as_cell() + .expect("Codegen instruction should be a cell"); + let inst_tag = inst_cell + .head() + .as_atom() + .expect("Codegen instruction tag should be atom") + .as_u64() + .expect("codegen instruction tag should convert to u64"); + match inst_tag { + tas!(b"imm") => { + let imm_cell = inst_cell.tail().as_cell().unwrap(); + let imm_n = imm_cell.head(); + let imm_d = imm_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize; + frame.vars_mut()[imm_d] = imm_n; + } + tas!(b"mov") => { + let mov_cell = inst_cell.tail().as_cell().unwrap(); + let mov_s = mov_cell.head().as_atom().unwrap().as_u64().unwrap() as usize; + let mov_d = mov_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize; + frame.vars_mut()[mov_d] = frame.vars()[mov_s]; + } + tas!(b"inc") => { + let inc_cell = inst_cell.tail().as_cell().unwrap(); + let inc_s = inc_cell.head().as_atom().unwrap().as_u64().unwrap() as usize; + let inc_d = inc_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize; + if let Ok(s_atom) = frame.vars()[inc_s].as_atom() { + frame.vars_mut()[inc_d] = inc(&mut context.stack, s_atom).as_noun(); + } else { + break BAIL_EXIT; + } + } + tas!(b"con") => { + let con_cell = inst_cell.tail().as_cell().unwrap(); + let con_h = con_cell.head().as_atom().unwrap().as_u64().unwrap() as usize; + let con_tell = con_cell.tail().as_cell().unwrap(); + let con_t = con_tell.head().as_atom().unwrap().as_u64().unwrap() as usize; + let con_d = con_tell.tail().as_atom().unwrap().as_u64().unwrap() as usize; + frame.vars_mut()[con_d] = T( + &mut context.stack, + &[frame.vars()[con_h], frame.vars()[con_t]], + ); + } + tas!(b"hed") => { + let hed_cell = inst_cell.tail().as_cell().unwrap(); + let hed_s = hed_cell.head().as_atom().unwrap().as_u64().unwrap() as usize; + let hed_d = hed_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize; + let s_noun = frame.vars()[hed_s]; + if s_noun.is_none() { + frame.vars_mut()[hed_d] = NOUN_NONE; + } else if let Ok(s_cell) = frame.vars()[hed_s].as_cell() { + frame.vars_mut()[hed_d] = s_cell.head(); + } else { + frame.vars_mut()[hed_d] = NOUN_NONE; + } + } + tas!(b"tal") => { + let tal_cell = inst_cell.tail().as_cell().unwrap(); + let tal_s = tal_cell.head().as_atom().unwrap().as_u64().unwrap() as usize; + let tal_d = tal_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize; + let s_noun = frame.vars()[tal_s]; + if s_noun.is_none() { + frame.vars_mut()[tal_d] = NOUN_NONE; + } else if let Ok(s_cell) = frame.vars()[tal_s].as_cell() { + frame.vars_mut()[tal_d] = s_cell.tail(); + } else { + frame.vars_mut()[tal_d] = NOUN_NONE; + } + } + tas!(b"men") => { + let men_cell = inst_cell.tail().as_cell().unwrap(); + let men_l = men_cell.head(); + assert!(men_l.is_atom()); + let men_s = men_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize; + let men_entry = T(&mut context.stack, &[men_l, frame.vars()[men_s]]); + frame.mean = T(&mut context.stack, &[men_entry, frame.mean]) + } + tas!(b"man") => { + frame.mean = frame.mean.as_cell().unwrap().tail(); + } + tas!(b"slo") => { + let slo_s = inst_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize; + let slo_tag = frame.vars()[slo_s]; + assert!(slo_tag.is_atom()); + frame.slow = T(&mut context.stack, &[slo_tag, frame.slow]); + } + tas!(b"sld") => { + frame.slow = frame.slow.as_cell().unwrap().tail(); + todo!("sld") + } + tas!(b"hit") => { + // XX TODO implement + } + tas!(b"slg") => { + let slg_s = inst_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize; + context + .newt + .slog(&mut context.stack, 0, frame.vars()[slg_s]); + } + tas!(b"mew") => { + // XX TODO implement + } + tas!(b"tim") => { + // XX TODO implement + } + tas!(b"tom") => { + // XX TODO implement + } + tas!(b"mem") => { + // XX TODO implement + } + tas!(b"poi") => { + let poi_p = inst_cell.tail().as_atom().unwrap().as_u64().unwrap() as usize; + frame.vars_mut()[poi_p] = NOUN_NONE; + } + tas!(b"ipb") => { + let mut ipb_p = inst_cell.tail(); + 'ipb: loop { + if unsafe { ipb_p.raw_equals(D(0)) } { + break 'ipb; + } else { + let p_cell = ipb_p.as_cell().unwrap(); + ipb_p = p_cell.tail(); + let p_i = p_cell.head().as_atom().unwrap().as_u64().unwrap() as usize; + if frame.vars()[p_i].is_none() { + break 'interpret BAIL_EXIT; + } + } + } + } + _ => { + panic!("Codegen instruction unsupported"); + } + } + } else { + let inst_cell = bend + .as_cell() + .expect("Codegen instruction should be a cell"); + let inst_tag = inst_cell + .head() + .as_atom() + .expect("Codegen instruction tag should be atom") + .as_u64() + .expect("codegen instruction tag should convert to u64"); + match inst_tag { + tas!(b"clq") => { + todo!("clq") + } + tas!(b"eqq") => { + todo!("eqq") + } + tas!(b"brn") => { + todo!("brn") + } + tas!(b"hop") => { + todo!("hop") + } + tas!(b"hip") => { + todo!("hip") + } + tas!(b"lnk") => { + todo!("lnk") + } + tas!(b"cal") => { + todo!("cal") + } + tas!(b"caf") => { + todo!("caf") + } + tas!(b"lnt") => { + todo!("lnt") + } + tas!(b"jmp") => { + todo!("jmp") + } + tas!(b"jmf") => { + todo!("jmf") + } + tas!(b"spy") => { + todo!("spy") + } + tas!(b"mer") => { + todo!("mer") + } + tas!(b"don") => { + todo!("don") + } + tas!(b"bom") => { + todo!("bom") + } + _ => { + panic!("Codegen instruction unsupported"); + } + } + } + }; + match inner_res { + Ok(res) => inner_res, + Err(err) => exit(context, err), + } +} + +/// Crash with an +fn exit(context: &mut Context, err: Error) -> Result { + todo!("exit") +} + +fn pile_sans(pile: Noun) -> usize { + (slot(pile, 127) + .expect("Codegen pile should have sans face") + .as_atom() + .expect("Codegen sans should be atom") + .as_u64() + .expect("Codegen sans too big")) as usize +} + +fn pile_wish(pile: Noun) -> Noun { + slot(pile, 30).expect("Codegen pile should have wish face") +} + +fn pile_sire(pile: Noun) -> usize { + (slot(pile, 62) + .expect("Codegen pile should have sire face") + .as_atom() + .expect("Codegen sire should be atom") + .as_u64() + .expect("Codegen sire too big")) as usize +} + +fn pile_will(pile: Noun) -> Noun { + slot(pile, 126).expect("Codegen pile should have will face") +} + +fn get_blob(context: &mut Context, pile: Noun, bile: &mut Noun) -> (Noun, Noun) { + let mut will = pile_will(pile); + let blob_with_biff = get_by(&mut context.stack, &mut will, bile) + .expect("Codegen bile lookup successful") + .expect("Codegen will has bile"); + let blob_cell = slot(blob_with_biff, 3) + .expect("Codegen blob has tail") + .as_cell() + .expect("Codegen blob tail should be cell"); + (blob_cell.head(), blob_cell.tail()) +} diff --git a/rust/ares/src/interpreter.rs b/rust/ares/src/interpreter.rs index c250d88..2d64efe 100644 --- a/rust/ares/src/interpreter.rs +++ b/rust/ares/src/interpreter.rs @@ -268,6 +268,7 @@ pub struct Context { pub cold: Cold, pub warm: Warm, pub hot: Hot, + pub line: Noun, pub cache: Hamt, pub scry_stack: Noun, pub trace_info: Option, @@ -360,7 +361,7 @@ impl From for Error { pub type Result = result::Result; -const BAIL_EXIT: Result = Err(Error::Deterministic(Mote::Exit, D(0))); +pub const BAIL_EXIT: Result = Err(Error::Deterministic(Mote::Exit, D(0))); const BAIL_FAIL: Result = Err(Error::NonDeterministic(Mote::Fail, D(0))); const BAIL_INTR: Result = Err(Error::NonDeterministic(Mote::Intr, D(0))); diff --git a/rust/ares/src/jets.rs b/rust/ares/src/jets.rs index cda3403..70cae28 100644 --- a/rust/ares/src/jets.rs +++ b/rust/ares/src/jets.rs @@ -310,7 +310,7 @@ pub mod util { use super::*; use crate::hamt::Hamt; use crate::mem::NockStack; - use crate::noun::{Atom, Noun, D, T}; + use crate::noun::{Atom, Noun, D, NOUN_NONE, T}; use crate::unifying_equality::unifying_equality; use assert_no_alloc::assert_no_alloc; use ibig::UBig; @@ -322,6 +322,7 @@ pub mod util { let warm = Warm::new(&mut stack); let hot = Hot::init(&mut stack, URBIT_HOT_STATE); let cache = Hamt::::new(&mut stack); + let line = NOUN_NONE; Context { stack, @@ -330,6 +331,7 @@ pub mod util { warm, hot, cache, + line, scry_stack: D(0), trace_info: None, } diff --git a/rust/ares/src/jets/seam.rs b/rust/ares/src/jets/seam.rs index 0e4ba5f..c778649 100644 --- a/rust/ares/src/jets/seam.rs +++ b/rust/ares/src/jets/seam.rs @@ -5,7 +5,6 @@ // use crate::jets::util::*; // use crate::jets::Result; // use crate::noun::{IndirectAtom, Noun, D}; - use self::util::*; crate::gdb!(); @@ -13,14 +12,14 @@ crate::gdb!(); // XX TODO actual jets pub mod util { - use crate::mug::mug_u32; - use crate::unifying_equality::unifying_equality; - use crate::mem::NockStack; use crate::jets::math::util::lth_b; use crate::jets::util::slot; - use crate::noun::{Noun, D}; - use either::Either::*; use crate::jets::JetErr; + use crate::mem::NockStack; + use crate::mug::mug_u32; + use crate::noun::{Noun, D}; + use crate::unifying_equality::unifying_equality; + use either::Either::*; pub fn dor_b(stack: &mut NockStack, a: &mut Noun, b: &mut Noun) -> bool { let mut ap = a as *mut Noun; @@ -38,10 +37,14 @@ pub mod util { } else { break true; } - }, + } Right(a_cell) => { if let Ok(b_cell) = (*bp).as_cell() { - if unifying_equality(stack, a_cell.head_as_mut(), b_cell.head_as_mut()) { + if unifying_equality( + stack, + a_cell.head_as_mut(), + b_cell.head_as_mut(), + ) { ap = a_cell.tail_as_mut(); bp = b_cell.tail_as_mut(); continue; @@ -70,7 +73,11 @@ pub mod util { } } - pub fn get_by(stack: &mut NockStack, a: &mut Noun, b: &mut Noun) -> Result, JetErr> { + pub fn get_by( + stack: &mut NockStack, + a: &mut Noun, + b: &mut Noun, + ) -> Result, JetErr> { let mut ap = a as *mut Noun; let bp = b as *mut Noun; unsafe { diff --git a/rust/ares/src/lib.rs b/rust/ares/src/lib.rs index e7c37ec..d4908f4 100644 --- a/rust/ares/src/lib.rs +++ b/rust/ares/src/lib.rs @@ -3,6 +3,7 @@ extern crate num_derive; extern crate lazy_static; #[macro_use] extern crate static_assertions; +pub mod codegen; pub mod flog; pub mod guard; pub mod hamt; diff --git a/rust/ares/src/mem.rs b/rust/ares/src/mem.rs index 6852139..e9ea1b1 100644 --- a/rust/ares/src/mem.rs +++ b/rust/ares/src/mem.rs @@ -9,7 +9,7 @@ use memmap::MmapMut; use std::alloc::Layout; use std::mem; use std::ptr; -use std::ptr::copy_nonoverlapping; +use std::ptr::{copy, copy_nonoverlapping}; crate::gdb!(); @@ -146,6 +146,45 @@ impl NockStack { self.frame_pointer } + pub fn get_frame_base(&self) -> *mut u64 { + if self.is_west() { + unsafe { *(self.prev_alloc_pointer_pointer()) } + } else { + unsafe { self.frame_pointer.add(RESERVED) } + } + } + + pub unsafe fn resize_frame(&mut self, new_size: usize) { + // lightweight stack must be empty + assert!(self.stack_pointer == self.frame_pointer); + let raw_new_size = (new_size + RESERVED) as isize; + if self.is_west() { + let current_size = self + .frame_pointer + .offset_from(*(self.prev_alloc_pointer_pointer())); + assert!(current_size >= RESERVED as isize); + let offset = raw_new_size - current_size; + let new_frame_pointer = self.frame_pointer.offset(offset); + copy( + self.frame_pointer.sub(RESERVED), + new_frame_pointer.sub(RESERVED), + RESERVED, + ); + self.frame_pointer = new_frame_pointer; + self.stack_pointer = new_frame_pointer; + } else { + let current_size = + (*(self.prev_alloc_pointer_pointer())).offset_from(self.frame_pointer); + assert!(current_size >= RESERVED as isize); + let offset = current_size - raw_new_size; + let new_frame_pointer = self.frame_pointer.offset(offset); + let copy_size = current_size.min(raw_new_size); + copy(self.frame_pointer, new_frame_pointer, RESERVED); + self.frame_pointer = new_frame_pointer; + self.stack_pointer = new_frame_pointer; + } + } + /** Current stack pointer of this NockStack */ pub fn get_stack_pointer(&self) -> *const u64 { self.stack_pointer diff --git a/rust/ares/src/noun.rs b/rust/ares/src/noun.rs index 3ce5f80..0943493 100644 --- a/rust/ares/src/noun.rs +++ b/rust/ares/src/noun.rs @@ -31,6 +31,9 @@ const CELL_TAG: u64 = u64::MAX & INDIRECT_MASK; /** Tag mask for a cell. */ const CELL_MASK: u64 = !(u64::MAX >> 3); +const NONE_BITS: u64 = !(u64::MAX >> 3); +pub const NOUN_NONE: Noun = Noun { raw: NONE_BITS }; + /* A note on forwarding pointers: * * Forwarding pointers are only used temporarily during copies between NockStack frames and between @@ -155,6 +158,10 @@ fn is_cell(noun: u64) -> bool { noun & CELL_MASK == CELL_TAG } +fn is_none(noun: u64) -> bool { + noun == NONE_BITS +} + /** A noun-related error. */ #[derive(Debug, PartialEq)] pub enum Error { @@ -1023,14 +1030,16 @@ pub union Noun { impl Noun { pub fn is_none(self) -> bool { - unsafe { self.raw == u64::MAX } + unsafe { is_none(self.raw) } } pub fn is_direct(&self) -> bool { + assert!(!self.is_none()); unsafe { is_direct_atom(self.raw) } } pub fn is_indirect(&self) -> bool { + assert!(!self.is_none()); unsafe { is_indirect_atom(self.raw) } } @@ -1043,6 +1052,7 @@ impl Noun { } pub fn is_cell(&self) -> bool { + assert!(!self.is_none()); unsafe { is_cell(self.raw) } } diff --git a/rust/ares/src/serf.rs b/rust/ares/src/serf.rs index be84067..54ee68b 100644 --- a/rust/ares/src/serf.rs +++ b/rust/ares/src/serf.rs @@ -8,7 +8,7 @@ use crate::jets::warm::Warm; use crate::mem::NockStack; use crate::mug::*; use crate::newt::Newt; -use crate::noun::{Atom, Cell, DirectAtom, Noun, Slots, D, T}; +use crate::noun::{Atom, Cell, DirectAtom, Noun, Slots, D, NOUN_NONE, T}; use crate::persist::pma_meta_set; use crate::persist::{pma_meta_get, pma_open, pma_sync, Persist}; use crate::trace::*; @@ -172,6 +172,7 @@ impl Context { cache, scry_stack: D(0), trace_info, + line: NOUN_NONE, }; Context {