@@ -8,6 +8,7 @@ use nock::{
88 noun::{cell, Atom, Noun},
99};
1010use spinoff::Spinner;
11+use std::cell::LazyCell;
1112use std::{
1213 fs::File,
1314 io::{stdin, stdout, Read, Write},
@@ -16,7 +17,7 @@ use std::{
1617 rc::Rc,
1718};
1819
19−const URBIT: &[u8] = include_bytes!("../res/urbit.jam");
20+static GENERATE_URBIT: fn() -> Rc<Noun> = || cue_bytes(include_bytes!("../res/urbit.jam"));
2021
2122#[derive(Parser)]
2223#[command(version)]
@@ -65,34 +66,77 @@ enum RunCommand {
6566}
6667
6768fn main() -> Result<(), std::io::Error> {
69+ let urbit = LazyCell::new(GENERATE_URBIT);
70+ let mut ctx = generate_interpreter_context();
71+
6872 let cli = Cli::parse();
6973
7074 match cli.command {
7175 NockCommand::Run { command } => match command {
72− RunCommand::Interact { gate } => interact(gate)?,
76+ RunCommand::Interact { gate: gate_file } => {
77+ let mut spinner = new_spinner(format!("Loading {gate_file:#?}"));
78+
79+ let (_typ, nok) = read_nock_or_compile(&mut spinner, &mut ctx, &urbit, &gate_file)?;
80+
81+ spinner.success(&format!("Loaded {gate_file:#?}"));
82+
83+ let source = get_stdin().unwrap();
84+
85+ let mut spinner = new_spinner(String::from("Slamming gate..."));
86+ let slam = match slam(&mut ctx, &nok, &source) {
87+ Ok(slam) => slam,
88+ Err(tanks) => {
89+ spinner.fail("Gate execution failed");
90+
91+ let mut spinner = new_spinner(String::from("Building trace"));
92+ let trace = wash(&mut ctx, &urbit, tanks);
93+ spinner.success("Trace built");
94+ println!("{trace}");
95+ exit(1);
96+ }
97+ };
98+ let Some(target) = slam.as_bytes() else {
99+ spinner.fail("The gate did not produce a valid atom");
100+ exit(1);
101+ };
102+ spinner.success("Gate slammed successfully");
103+
104+ stdout().write_all(&target)?;
105+ }
73106 },
74107 NockCommand::Compile { root, output } => {
75− compile(root, output).unwrap();
108+ let mut spinner = new_spinner(format!("Compiling {root:#?}"));
109+
110+ let nock = compile_to_nock(&mut ctx, &urbit, &mut spinner, root)?;
111+
112+ spinner.update_text(format!("Writing output to {output:#?}"));
113+
114+ let mut output_file = File::create(output.clone())?;
115+ File::write_all(&mut output_file, &jam_to_bytes(&nock)).unwrap();
116+
117+ spinner.success(&*format!("Compiled {output:#?} successfully"));
118+
119+ ()
76120 }
77121 NockCommand::HashGate { gate } => {
78− let gate = read_nock(&gate)?;
122+ let (_type, gate) = read_nock(&gate)?;
79123 let (hash, _sample) = gate.hash_gate();
80124
81125 println!("{hash}");
82126 }
83127 NockCommand::HashDoubleGate { gate } => {
84− let gate = read_nock(&gate)?;
128+ let (_type, gate) = read_nock(&gate)?;
85129 let (hash, _sample_1, _sample_2) = gate.hash_double_gate();
86130
87131 println!("{hash}");
88132 }
89133 NockCommand::Eval { nock } => {
90− let mut ctx = generate_interpreter_context();
134+ let mut spinner = new_spinner(format!("Loading {nock:#?}"));
91135
92− let nock = read_nock_or_compile(&mut ctx, nock)?;
136+ let (_typ, nok) = read_nock_or_compile(&mut spinner, &mut ctx, &urbit, &nock)?;
93137
94138 let sig = ctx.nouns.sig.clone();
95− let result = tar(&mut ctx, sig, &nock).unwrap();
139+ let result = tar(&mut ctx, sig, &nok).unwrap();
96140
97141 println!("{result}");
98142 }
@@ -101,42 +145,13 @@ fn main() -> Result<(), std::io::Error> {
101145 Ok(())
102146}
103147
104−fn interact(gate_file: PathBuf) -> Result<(), std::io::Error> {
105− let urbit = cue_bytes(URBIT);
106− let mut spinner = new_spinner(format!("Loading {gate_file:#?}"));
107− let binding = read_nock(&gate_file).unwrap();
108− let Some((_gate_type, gate)) = binding.as_cell() else {
109− spinner.fail(&format!("Failed to load {gate_file:#?}"));
110− exit(1);
111− };
112− spinner.success(&format!("Loaded {gate_file:#?}"));
113−
114− let source = get_stdin().unwrap();
115−
116− let mut spinner = new_spinner(String::from("Slamming gate..."));
117− let slam = match slam(&mut generate_interpreter_context(), &gate, &source) {
118− Ok(slam) => slam,
119− Err(tanks) => {
120− spinner.fail("Gate execution failed");
121− let mut ctx = generate_interpreter_context();
122− let trace = wash(&mut ctx, &urbit, tanks);
123− println!("{trace}");
124− exit(1);
125− }
126− };
127− let Some(target) = slam.as_bytes() else {
128− spinner.fail("The gate did not produce a valid atom");
129− exit(1);
130− };
131− spinner.success("Gate slammed successfully");
132−
133− stdout().write_all(&target)?;
134−
135− Ok(())
136−}
148+type Vase = (Rc<Noun>, Rc<Noun>);
137149
138−fn read_nock(nock_file: &PathBuf) -> Result<Rc<Noun>, std::io::Error> {
139− Ok(cue_bytes(&read_file(nock_file)?))
150+/// Reads a `vase` from a `.nock` file.
151+fn read_nock(nock_file: &PathBuf) -> Result<Vase, std::io::Error> {
152+ let nock = cue_bytes(&read_file(nock_file)?);
153+ let (typ, nok) = nock.as_cell().unwrap();
154+ Ok((typ.clone(), nok.clone()))
140155}
141156
142157fn read_file(nock_file: &PathBuf) -> Result<Vec<u8>, std::io::Error> {
@@ -156,29 +171,12 @@ fn new_spinner(txt: String) -> Spinner {
156171 Spinner::new_with_stream(spinoff::spinners::Dots, txt, None, spinoff::Streams::Stderr)
157172}
158173
159−fn compile(root: PathBuf, output: PathBuf) -> Result<(), std::io::Error> {
160− let mut spinner = new_spinner(format!("Loading Hoon compiler"));
161−
162− let mut ctx = generate_interpreter_context();
163−
164− let nock = compile_to_nock(&mut ctx, &mut spinner, root)?;
165−
166− spinner.update_text(format!("Writing output to {output:#?}"));
167−
168− let mut output_file = File::create(output.clone())?;
169− File::write_all(&mut output_file, &jam_to_bytes(&nock)).unwrap();
170−
171− spinner.success(&*format!("Compiled {output:#?} successfully"));
172−
173− Ok(())
174−}
175−
176174fn compile_to_nock(
177175 ctx: &mut InterpreterContext,
176+ urbit: &LazyCell<Rc<Noun>>,
178177 spinner: &mut Spinner,
179178 root: PathBuf,
180179) -> Result<Rc<Noun>, std::io::Error> {
181− let urbit = cue_bytes(URBIT);
182180 let hoon = slam_pulled_gate(
183181 ctx,
184182 &urbit,
@@ -223,14 +221,15 @@ fn compile_to_nock(
223221}
224222
225223fn read_nock_or_compile(
224+ spinner: &mut Spinner,
226225 ctx: &mut InterpreterContext,
227− path: PathBuf,
228−) -> Result<Rc<Noun>, std::io::Error> {
226+ urbit: &LazyCell<Rc<Noun>>,
227+ path: &PathBuf,
228+) -> Result<Vase, std::io::Error> {
229229 if path.extension().and_then(|x| x.to_str()) == Some("hoon") {
230− let mut spinner = new_spinner(String::new());
231− let nock = compile_to_nock(ctx, &mut spinner, path.clone())?;
232− spinner.success(&*format!("Compiled {path:#?} successfully"));
233− Ok(nock)
230+ let nock = compile_to_nock(ctx, urbit, spinner, path.clone())?;
231+ let (typ, nok) = nock.as_cell().unwrap();
232+ Ok((typ.clone(), nok.clone()))
234233 } else {
235234 read_nock(&path)
236235 }
@@ -239,7 +238,6 @@ fn read_nock_or_compile(
239238fn wash(ctx: &mut InterpreterContext, urbit: &Rc<Noun>, tanks: Tanks) -> String {
240239 let mut target = String::new();
241240 for (subj, q) in tanks.iter() {
242− eprintln!("washing {q}");
243241 let gate = tar(ctx, subj.clone(), q).unwrap();
244242 let tank = eval_pulled_gate(ctx, gate).unwrap();
245243 let mut str = slam_pulled_gate(