@@ -8,6 +8,7 @@ use nock::{
8 noun::{cell, Atom, Noun},
8 noun::{cell, Atom, Noun},
10use spinoff::Spinner;
10use spinoff::Spinner;
11+use std::cell::LazyCell;
13 io::{stdin, stdout, Read, Write},
14 io::{stdin, stdout, Read, Write},
@@ -16,7 +17,7 @@ use std::{
19−const URBIT: &[u8] = include_bytes!("../res/urbit.jam");
20+static GENERATE_URBIT: fn() -> Rc<Noun> = || cue_bytes(include_bytes!("../res/urbit.jam"));
21#[derive(Parser)]
22#[derive(Parser)]
22#[command(version)]
23#[command(version)]
@@ -65,34 +66,77 @@ enum RunCommand {
67fn main() -> Result<(), std::io::Error> {
68fn main() -> Result<(), std::io::Error> {
69+ let urbit = LazyCell::new(GENERATE_URBIT);
70+ let mut ctx = generate_interpreter_context();
68 let cli = Cli::parse();
72 let cli = Cli::parse();
70 match cli.command {
74 match cli.command {
71 NockCommand::Run { command } => match command {
75 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:#?}"));
79+ let (_typ, nok) = read_nock_or_compile(&mut spinner, &mut ctx, &urbit, &gate_file)?;
81+ spinner.success(&format!("Loaded {gate_file:#?}"));
83+ let source = get_stdin().unwrap();
85+ let mut spinner = new_spinner(String::from("Slamming gate..."));
86+ let slam = match slam(&mut ctx, &nok, &source) {
89+ spinner.fail("Gate execution failed");
91+ let mut spinner = new_spinner(String::from("Building trace"));
92+ let trace = wash(&mut ctx, &urbit, tanks);
93+ spinner.success("Trace built");
98+ let Some(target) = slam.as_bytes() else {
99+ spinner.fail("The gate did not produce a valid atom");
102+ spinner.success("Gate slammed successfully");
104+ stdout().write_all(&target)?;
74 NockCommand::Compile { root, output } => {
107 NockCommand::Compile { root, output } => {
75− compile(root, output).unwrap();
108+ let mut spinner = new_spinner(format!("Compiling {root:#?}"));
110+ let nock = compile_to_nock(&mut ctx, &urbit, &mut spinner, root)?;
112+ spinner.update_text(format!("Writing output to {output:#?}"));
114+ let mut output_file = File::create(output.clone())?;
115+ File::write_all(&mut output_file, &jam_to_bytes(&nock)).unwrap();
117+ spinner.success(&*format!("Compiled {output:#?} successfully"));
77 NockCommand::HashGate { gate } => {
121 NockCommand::HashGate { gate } => {
78− let gate = read_nock(&gate)?;
122+ let (_type, gate) = read_nock(&gate)?;
79 let (hash, _sample) = gate.hash_gate();
123 let (hash, _sample) = gate.hash_gate();
81 println!("{hash}");
125 println!("{hash}");
83 NockCommand::HashDoubleGate { gate } => {
127 NockCommand::HashDoubleGate { gate } => {
84− let gate = read_nock(&gate)?;
128+ let (_type, gate) = read_nock(&gate)?;
85 let (hash, _sample_1, _sample_2) = gate.hash_double_gate();
129 let (hash, _sample_1, _sample_2) = gate.hash_double_gate();
87 println!("{hash}");
131 println!("{hash}");
89 NockCommand::Eval { nock } => {
133 NockCommand::Eval { nock } => {
90− let mut ctx = generate_interpreter_context();
134+ let mut spinner = new_spinner(format!("Loading {nock:#?}"));
92− let nock = read_nock_or_compile(&mut ctx, nock)?;
136+ let (_typ, nok) = read_nock_or_compile(&mut spinner, &mut ctx, &urbit, &nock)?;
94 let sig = ctx.nouns.sig.clone();
138 let sig = ctx.nouns.sig.clone();
95− let result = tar(&mut ctx, sig, &nock).unwrap();
139+ let result = tar(&mut ctx, sig, &nok).unwrap();
97 println!("{result}");
141 println!("{result}");
@@ -101,42 +145,13 @@ fn main() -> Result<(), std::io::Error> {
104−fn interact(gate_file: PathBuf) -> Result<(), std::io::Error> {
148+type Vase = (Rc<Noun>, Rc<Noun>);
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:#?}"));
112− spinner.success(&format!("Loaded {gate_file:#?}"));
114− let source = get_stdin().unwrap();
116− let mut spinner = new_spinner(String::from("Slamming gate..."));
117− let slam = match slam(&mut generate_interpreter_context(), &gate, &source) {
120− spinner.fail("Gate execution failed");
121− let mut ctx = generate_interpreter_context();
122− let trace = wash(&mut ctx, &urbit, tanks);
123− println!("{trace}");
127− let Some(target) = slam.as_bytes() else {
128− spinner.fail("The gate did not produce a valid atom");
131− spinner.success("Gate slammed successfully");
133− stdout().write_all(&target)?;
150+/// Reads a `vase` from a `.nock` file.
138−fn read_nock(nock_file: &PathBuf) -> Result<Rc<Noun>, std::io::Error> {
151+fn read_nock(nock_file: &PathBuf) -> Result<Vase, std::io::Error> {
139− Ok(cue_bytes(&read_file(nock_file)?))
152+ let nock = cue_bytes(&read_file(nock_file)?);
153+ let (typ, nok) = nock.as_cell().unwrap();
154+ Ok((typ.clone(), nok.clone()))
142fn read_file(nock_file: &PathBuf) -> Result<Vec<u8>, std::io::Error> {
157fn read_file(nock_file: &PathBuf) -> Result<Vec<u8>, std::io::Error> {
@@ -156,29 +171,12 @@ fn new_spinner(txt: String) -> Spinner {
156 Spinner::new_with_stream(spinoff::spinners::Dots, txt, None, spinoff::Streams::Stderr)
171 Spinner::new_with_stream(spinoff::spinners::Dots, txt, None, spinoff::Streams::Stderr)
159−fn compile(root: PathBuf, output: PathBuf) -> Result<(), std::io::Error> {
160− let mut spinner = new_spinner(format!("Loading Hoon compiler"));
162− let mut ctx = generate_interpreter_context();
164− let nock = compile_to_nock(&mut ctx, &mut spinner, root)?;
166− spinner.update_text(format!("Writing output to {output:#?}"));
168− let mut output_file = File::create(output.clone())?;
169− File::write_all(&mut output_file, &jam_to_bytes(&nock)).unwrap();
171− spinner.success(&*format!("Compiled {output:#?} successfully"));
176fn compile_to_nock(
174fn compile_to_nock(
177 ctx: &mut InterpreterContext,
175 ctx: &mut InterpreterContext,
176+ urbit: &LazyCell<Rc<Noun>>,
178 spinner: &mut Spinner,
177 spinner: &mut Spinner,
179 root: PathBuf,
178 root: PathBuf,
180) -> Result<Rc<Noun>, std::io::Error> {
179) -> Result<Rc<Noun>, std::io::Error> {
181− let urbit = cue_bytes(URBIT);
182 let hoon = slam_pulled_gate(
180 let hoon = slam_pulled_gate(
@@ -223,14 +221,15 @@ fn compile_to_nock(
225fn read_nock_or_compile(
223fn read_nock_or_compile(
224+ spinner: &mut Spinner,
226 ctx: &mut InterpreterContext,
225 ctx: &mut InterpreterContext,
226+ urbit: &LazyCell<Rc<Noun>>,
227− path: PathBuf,
227+ path: &PathBuf,
228−) -> Result<Rc<Noun>, std::io::Error> {
228+) -> Result<Vase, std::io::Error> {
229 if path.extension().and_then(|x| x.to_str()) == Some("hoon") {
229 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())?;
230+ let nock = compile_to_nock(ctx, urbit, spinner, path.clone())?;
232− spinner.success(&*format!("Compiled {path:#?} successfully"));
231+ let (typ, nok) = nock.as_cell().unwrap();
233− Ok(nock)
232+ Ok((typ.clone(), nok.clone()))
235 read_nock(&path)
234 read_nock(&path)
@@ -239,7 +238,6 @@ fn read_nock_or_compile(
239fn wash(ctx: &mut InterpreterContext, urbit: &Rc<Noun>, tanks: Tanks) -> String {
238fn wash(ctx: &mut InterpreterContext, urbit: &Rc<Noun>, tanks: Tanks) -> String {
240 let mut target = String::new();
239 let mut target = String::new();
241 for (subj, q) in tanks.iter() {
240 for (subj, q) in tanks.iter() {
242− eprintln!("washing {q}");
243 let gate = tar(ctx, subj.clone(), q).unwrap();
241 let gate = tar(ctx, subj.clone(), q).unwrap();
244 let tank = eval_pulled_gate(ctx, gate).unwrap();
242 let tank = eval_pulled_gate(ctx, gate).unwrap();
245 let mut str = slam_pulled_gate(
243 let mut str = slam_pulled_gate(