diff --git a/Cargo.toml b/Cargo.toml index 3ac595b..8a3c8d1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rusty_funge" -version = "2022.12.3" +version = "2022.12.4" edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -10,4 +10,5 @@ clap = { version = "4.0.29", features = ["derive"] } chrono = "0.4.23" cursive = { version = "0.20.0", features = ["termion-backend"] } rand = "0.8.5" -num = "0.4.0" \ No newline at end of file +num = "0.4.0" +anyhow = "1.0.66" \ No newline at end of file diff --git a/src/debug.rs b/src/debug.rs index b99909a..6b26848 100644 --- a/src/debug.rs +++ b/src/debug.rs @@ -1,133 +1,314 @@ -use std::cmp::{min, max}; -use std::{error::Error, time::Duration}; use std::fmt::{Display, Formatter}; use std::sync::{Arc, Mutex}; -use std::thread::{sleep, spawn}; +use std::thread::{spawn, sleep}; +use std::time::{Duration, Instant}; use cursive::{Cursive, CursiveExt, Printer, Vec2}; -use cursive::{theme::ColorStyle, view::View}; -use cursive::event::{Event, EventResult}; -use rusty_funge::{Int, Funge}; +use cursive::view::View; +use cursive::theme::{BorderStyle, ColorStyle, Palette, Theme}; +use cursive::event::{Event, EventResult, Key}; +use rusty_funge::{Int, Funge, join, ord, IO}; +use anyhow::Result; -struct FungeMutex { - funge: Option> +// TODO: Use anyhow::Result +#[derive(Clone)] +enum FungeError { + Funge(Funge), + Error(String) } -impl FungeMutex { + +struct FungeDebug { + funge: Option>, + history: Vec>, + interval: f64, + running: bool, + stop_op: Option +} + +impl FungeDebug { fn new(funge: Funge) -> Self { - Self { funge: Some(funge) } - } - - fn step(&mut self) -> Result> { - let mut funge = self.funge.to_owned().ok_or("No funge found")?; - if !funge.terminated { - funge = funge.step()?; + Self { + funge: Some(FungeError::Funge(funge)), + history: Vec::new(), + interval: 0.05, + running: false, + stop_op: None } - let terminated = funge.terminated; - self.funge = Some(funge); - Ok(terminated) } - fn funge_ref(&self) -> Option<&Funge> { - self.funge.as_ref() + fn step_back(&mut self) { + match self.history.pop() { + Some(funge) => { + self.running = false; + self.funge = Some(FungeError::Funge(funge)); + } + None => {} + } + } + + fn is_terminated(&self) -> bool { + match &self.funge { + Some(FungeError::Funge(f)) if !f.terminated => false, + _ => true + } + } + + fn step(&mut self) { + match self.funge.to_owned() { + Some(FungeError::Funge(funge)) if !funge.terminated => { + self.history.push(funge.clone()); + if self.history.len() > 16384 { + self.history.remove(0); + } + match funge.step() { + Ok(f) => self.funge = Some(FungeError::Funge(f)), + Err(e) => { + self.funge = Some(FungeError::Error(e.to_string())) + } + } + } + Some(f) => self.funge = Some(f), + None => {} + } } } -impl Display for FungeMutex { +impl Display for FungeDebug { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.funge.as_ref().expect("No funge found")) + match self.funge.as_ref().expect("No funge found") { + FungeError::Funge(funge) => write!(f, "{}", funge), + FungeError::Error(e) => write!(f, "{}", e) + } } } pub(crate) struct FungeView { - funge: Arc>> + funge: Arc>> } impl FungeView { - pub (crate) fn new(funge: Funge) -> Result> { - Ok(FungeView { funge: Arc::new(Mutex::new(FungeMutex::new(funge.with_output()?))) } ) + pub (crate) fn new(funge: Funge, input: Vec) -> Result { + Ok(FungeView { funge: Arc::new(Mutex::new(FungeDebug::new( + funge.with_input(IO::new() + .with_store(input) + .with_input(|store| { + Ok(match store.pop() { + None => String::from("5"), // TODO: cursive input + Some(s) => s + }) + })).with_output(IO::new() + .with_output(|store, s| { + Ok(store.push(s)) + }))))) + }) } - fn step(&mut self) -> Result> { - self.funge.lock().unwrap().step() + fn step_back(&mut self) { + match self.funge.lock() { + Ok(mut f) => f.step_back(), + Err(_) => {} + } } - fn get_mutex(&self) -> Self { + fn step(&mut self) { + match self.funge.lock() { + Ok(mut f) => f.step(), + Err(_) => {} + } + } + + pub fn step_n(&mut self, n: usize) { + match self.funge.lock() { + Ok(mut funge) => { + for _ in 0..n { + funge.step(); + if funge.is_terminated() { break } + } + } + Err(_) => {} + } + } + + fn new_mutex(&self) -> Self { Self { funge: Arc::clone(&self.funge) } } - pub(crate) fn debug(self, interval: Option) -> Result<(), Box> { + fn is_running(&self) -> bool { + match self.funge.lock() { + Ok(mut funge) => { + if funge.is_terminated() | !funge.running { return false } + if let Some(op) = funge.stop_op { + match funge.funge.as_ref() { + Some(FungeError::Funge(f)) => { + for pos in f.ips_pos() { + if f.code[&pos] == op { + funge.stop_op = None; + funge.running = false; + return false + } + } + } + Some(FungeError::Error(_)) => { + funge.running = false; + return false + } + None => return false + } + } + true + } + Err(_) => false + } + } + + fn toggle_run(&self) { + let running = { self.funge.lock().unwrap().running }; + match running { + true => self.pause(), + false => self.run() + } + } + + fn pause(&self) { + self.funge.lock().unwrap().running = false; + } + + fn run(&self) { + let mut funge_mutex = self.new_mutex(); + { funge_mutex.funge.lock().unwrap().running = true; } + spawn(move || { + loop { + let instant = Instant::now(); + funge_mutex.step(); + let duration = Duration::from_micros(match funge_mutex.funge.lock() { + Ok(f) => (f.interval * 1e6) as u64, + Err(_) => 100000 + }); + if !funge_mutex.is_running() { + funge_mutex.funge.lock().unwrap().running = false; + break + } + let sleep_time = duration - instant.elapsed(); + if sleep_time.as_secs_f64() > 0f64 { sleep(duration) } + } + }); + } + + pub(crate) fn debug(self, interval: Option) { let mut app = Cursive::new(); match interval { None => {} Some(interval) => { - let duration = Duration::from_micros((interval * 1e6) as u64); - let mut funge_mutex = self.get_mutex(); - app.set_fps(max((1f64 / interval) as u32, 50)); - spawn(move || { - loop { - sleep(duration); - match funge_mutex.step() { - Ok(terminated) => if terminated { break }, - Err(_) => break - } - } - }); + { self.funge.lock().unwrap().interval = interval; } + self.toggle_run(); } } app.add_layer(self); - app.add_global_callback('q', |app| app.quit()); + app.add_global_callback(Key::Esc, |app| app.quit()); + app.set_autorefresh(true); + app.set_theme(Theme { shadow: false, borders: BorderStyle::None, palette: Palette::default() }); app.run(); - Ok(()) } } impl View for FungeView { fn draw(&self, printer: &Printer) { - let (text, ips_pos, terminated) = { - let lock = self.funge.lock(); - let funge = lock.as_ref().unwrap().funge_ref().unwrap(); - (format!("{}", funge), funge.ips_pos(), funge.terminated) - }; - let lines: Vec<&str> = text.lines().collect(); - for (i, line) in lines.iter().enumerate() { - printer.print((0, i), line); - } - for pos in ips_pos.iter() { - if (pos[0] >= 0) & (pos[1] >= 0) { - let x = pos[0] as usize; - let y = pos[1] as usize + 1; - printer.with_color(ColorStyle::highlight(), |printer| printer.print((x, y), &*lines[y].chars().nth(x).unwrap().to_string())); + match self.funge.lock().as_ref() { + Ok(funge_mutex) => { + let hist_len = funge_mutex.history.len(); + let running = funge_mutex.running; + match funge_mutex.funge.as_ref() { + Some(FungeError::Funge(funge)) => { + let text = format!("{}", funge); + let lines: Vec<&str> = text.lines().collect(); + for (i, line) in lines.iter().enumerate() { + printer.print((0, i), line); + } + for pos in funge.ips_pos().iter() { + if (pos[0] >= 0) & (pos[1] >= 0) { + let x = pos[0] as usize; + let y = pos[1] as usize + 1; + printer.with_color(ColorStyle::highlight(), + |printer| { + match lines[y].chars().nth(x) { + None => {} + Some(l) => printer.print((x, y), &*l.to_string()) + } + }); + } + } + let n = lines.len() + 1; + + let mut text = vec!["esc: quit"]; + if hist_len > 0 { + text.push("backspace: back"); + } + if running { + text.push("space: pause") + } else { + text.push("space: run") + } + if !funge.terminated { + text.push("enter: step") + } + let interval = format!("interval: {} up/down arrow", funge_mutex.interval); + text.push(&*interval); + printer.print((0, n + 1), &*join(&text, ", ")); + } + Some(FungeError::Error(e)) => { + printer.print((0, 0), "Error occured:"); + printer.print((0, 1), &*format!("{}", e)); + printer.print((0, 3), "esc: quit, backspace: back"); + } + None => {} + } } + Err(_) => {} } - let mut bottom = String::from("Press 'q' to quit"); - if terminated { - bottom.push_str("."); - } else { - bottom.push_str(", any other key to continue."); - } - printer.print((0, lines.len() + 1), &*bottom); } fn required_size(&mut self, constraint: Vec2) -> Vec2 { - let text = format!("{}", self.funge.lock().as_ref().unwrap()); - let x = match text.lines().map(|line| line.len()).collect::>().iter().max() { - None => 0, - Some(x) => *x - }; - Vec2::new(min(max(80, x), constraint.x), min(max(25, text.lines().count() + 2), constraint.y)) + constraint } fn on_event(&mut self, event: Event) -> EventResult { match event { - Event::Char('q') => EventResult::Ignored, - Event::Char(_) => { - self.step().ok(); + Event::Key(Key::Esc) => EventResult::Ignored, + Event::Key(Key::Backspace) => { + self.step_back(); EventResult::Consumed(None) } - Event::Key(_) => { - self.step().ok(); + Event::Char(' ') => { + self.toggle_run(); + EventResult::Consumed(None) + } + Event::Key(Key::Enter) => { + self.step(); + EventResult::Consumed(None) + } + Event::Key(Key::Up) => { + let lock = self.funge.lock(); + let mut funge = lock.unwrap(); + let interval = funge.interval / 2.0; + if interval < 0.01 { + funge.interval = 0.01; + } else { + funge.interval = interval; + } + EventResult::Consumed(None) + } + Event::Key(Key::Down) => { + self.funge.lock().unwrap().interval *= 2.0; + EventResult::Consumed(None) + } + Event::Char(c) => { + match ord(c) { + Ok(i) => self.funge.lock().unwrap().stop_op = Some(i), + Err(_) => {} + } + self.run(); EventResult::Consumed(None) } _ => EventResult::Ignored diff --git a/src/lib.rs b/src/lib.rs index d182c40..41f1080 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,11 @@ use std::collections::HashMap; use std::{env, fs, fmt, fmt::{Debug, Display, Formatter}}; use std::ops::{Add, Deref, DerefMut, Index, IndexMut, Sub}; -use std::{error::Error, hash::Hash, path::Path, str::FromStr, io::stdin}; +use std::{hash::Hash, path::Path, str::FromStr, io::stdin}; use chrono::{offset::Local, {Datelike, Timelike}}; use rand::Rng; use num::{Integer, NumCast}; +use anyhow::{Error, Result}; const VERSION: &str = env!("CARGO_PKG_VERSION"); @@ -14,7 +15,7 @@ pub trait Int: Integer + NumCast + FromStr + Hash + Clone + Copy + Sync + Send + impl Int for I {} -fn join(v: &Vec, s: &str) -> String { +pub fn join(v: &Vec, s: &str) -> String { let mut string = String::new(); if v.len() > 1 { for i in 0..v.len() - 1 { @@ -28,11 +29,11 @@ fn join(v: &Vec, s: &str) -> String { string } -fn cast_int(j: J) -> Result> { - Ok(I::from(j).ok_or("Could not convert from primitive")?) +fn cast_int(j: J) -> Result { + Ok(I::from(j).ok_or(Error::msg("Could not convert from primitive"))?) } -fn cast_vec_int(j: Vec) -> Result, Box> { +fn cast_vec_int(j: Vec) -> Result> { let mut i = Vec::::new(); for n in j { i.push(cast_int(n)?); @@ -40,12 +41,11 @@ fn cast_vec_int(j: Vec) -> Result, Box(c: char) -> Result> -{ +pub fn ord(c: char) -> Result { Ok(cast_int::<_, u32>(c.try_into()?)?) } -fn chr(i: I) -> Result> { +fn chr(i: I) -> Result { Ok(cast_int::(i)?.try_into()?) } @@ -61,51 +61,64 @@ fn sub(a: &Vec, b: &Vec) -> Vec where #[derive(Clone)] -enum InputEnum { - StdIn, - Vector(Vec) +pub struct IO { + store: Vec, + input: fn(&mut Vec) -> Result, + output: fn(&mut Vec, String) -> Result<()> } -#[derive(Clone)] -struct Input { - source: InputEnum -} - -impl Input { - fn get(&mut self) -> Result> { - Ok(match self.source { - InputEnum::StdIn => { - let mut s = String::new(); - stdin().read_line(&mut s)?; - s +impl IO { + pub fn new() -> Self { + Self { + store: Vec::new(), + input: |store| { + Ok(match store.pop() { + None => { + let mut s = String::new(); + stdin().read_line(&mut s)?; + s + } + Some(s) => s + }) + }, + output: |_, s| { + print!("{}", s); + Ok(()) } - InputEnum::Vector(ref mut v) => v.pop().ok_or("No more input!")? - }) - } -} - - -#[derive(Clone)] -enum OutputEnum { - StdOut, - Vector(Vec) -} - -#[derive(Clone)] -struct Output { - sink: OutputEnum -} - -impl Output { - fn print(&mut self, string: String) { - match self.sink { - OutputEnum::StdOut => print!("{}", string), - OutputEnum::Vector(ref mut v) => v.push(string) } } + + pub fn with_store(mut self, mut store: Vec) -> Self { + store.reverse(); + self.store = store; + self + } + + pub fn with_input(mut self, fun: fn(&mut Vec) -> Result) -> Self { + self.input = fun; + self + } + + pub fn with_output(mut self, fun: fn(&mut Vec, String) -> Result<()>) -> Self { + self.output = fun; + self + } + + fn pop(&mut self) -> Result { + (self.input)(&mut self.store) + } + + fn push(&mut self, s: String) -> Result<()> { + (self.output)(&mut self.store, s) + } + + pub fn get(&self) -> String { + join(&self.store, "") + } } + #[derive(Clone)] struct Stack { stack: Vec @@ -229,11 +242,11 @@ impl Display for StackStack { #[derive(Clone)] -struct IP { +pub struct IP { // TODO, getter fns in funge instead of pubs id: usize, position: Vec, delta: Vec, - offset: Vec, + pub offset: Vec, string: bool, stack: StackStack, fingerprint_ops: HashMap, @@ -241,7 +254,7 @@ struct IP { impl IP { - fn new(funge: &Funge) -> Result> { + fn new(funge: &Funge) -> Result { let mut new = IP { id: funge.ips.len(), position: vec![0, 0], @@ -285,7 +298,7 @@ impl IP { self.delta = vec![self.delta[1], -self.delta[0]]; } - fn advance(&mut self, funge: &Funge) -> Result<(), Box> { + fn advance(&mut self, funge: &Funge) -> Result<()> { let space: I = cast_int(32)?; let semicolon: I = cast_int(59)?; Ok(if self.string { @@ -322,7 +335,7 @@ impl IP { self.position = self.next_pos(funge); } - fn skip(mut self, funge: Funge) -> Result<(Funge, Option>), Box> { + fn skip(mut self, funge: Funge) -> Result<(Funge, Option>)> { self.movep(&funge); if let Ok(32 | 59) = cast_int(self.op(&funge)) { self.advance(&funge)?; @@ -349,7 +362,7 @@ impl IP { pos } - fn read_string(&mut self) -> Result> { + fn read_string(&mut self) -> Result { let mut string = String::new(); loop { let f = self.stack.pop(); @@ -361,7 +374,7 @@ impl IP { } } - fn get_info(&self, funge: &Funge, n: I) -> Result, Box> { + fn get_info(&self, funge: &Funge, n: I) -> Result> { let time = Local::now(); match n.to_usize() { Some(n @ 1..=20) => { @@ -370,7 +383,7 @@ impl IP { 2 => vec![cast_int(8 * std::mem::size_of::())?], 3 => { let mut f = 0; - for (i, c) in "wprustyfunge".chars().enumerate() { + for (i, c) in "wprusty".chars().enumerate() { f += (256 as isize).pow(i as u32) * ord::(c)?; } vec![cast_int(f)?] @@ -410,8 +423,8 @@ impl IP { r.push(I::zero()); let file = &args[0]; let path = Path::new(&file); - let j: Vec = path.file_name().ok_or("No file name.")? - .to_str().ok_or("Cannot convert String.")? + let j: Vec = path.file_name().ok_or(Error::msg("No file name."))? + .to_str().ok_or(Error::msg("Cannot convert String."))? .chars().map(|i| ord(i).expect("")).collect(); r.extend(j); r.push(I::zero()); @@ -445,21 +458,21 @@ impl IP { } }) } - _ => { - // TODO: return Error - println!("{}", "Stack size overflow"); - Ok(Vec::new()) - } + _ => Err(Error::msg("Stack size overflow")) } } - fn not_implemented(&mut self, funge: &Funge) { - // TODO: reverse or quit option - println!("operator {} at {} not implemented", self.op(funge), join(&self.position, ", ")); - self.reverse() + fn not_implemented(mut self, funge: &Funge) -> Option { + match funge.on_error { + OnError::Reverse => { + self.reverse(); + Some(self) + } + OnError::Quit => None + } } - fn step(mut self, mut funge: Funge, k: bool) -> Result<(Funge, Option>), Box> { + fn step(mut self, mut funge: Funge, k: bool) -> Result<(Funge, Option>)> { let mut new_ips = Vec::new(); let op = self.op(&funge); let op8 = op.to_u8(); @@ -558,8 +571,8 @@ impl IP { self.stack.push(b); } 36 => { self.stack.pop(); } // $ - 46 => funge.output.print(format!("{} ", self.stack.pop())), // . - 44 => funge.output.print(format!("{}", chr(self.stack.pop())?)), // , + 46 => funge.output.push(format!("{} ", self.stack.pop()))?, + 44 => funge.output.push(format!("{}", chr(self.stack.pop())?))?, // , 35 => { // # self.movep(&funge); return self.skip(funge) @@ -576,18 +589,18 @@ impl IP { self.stack.push(*&funge.code[&vec![x + self.offset[0], y + self.offset[1]]]); } 38 => { // & - let s = funge.inputs.get()?; + let s = funge.input.pop()?; let i: Vec = s.chars() .skip_while(|i| !i.is_digit(10)) .take_while(|i| i.is_digit(10)).collect(); match join(&i, "").parse() { Ok(n) => self.stack.push(n), - _ => println!("Cannot convert input to number.") // TODO: Error + _ => Err(Error::msg("Cannot convert input to number."))? } } 126 => { // ~ - let s = funge.inputs.get()?; - self.stack.push(ord(s.chars().nth(0).ok_or("No valid input.")?)?); + let s = funge.input.pop()?; + self.stack.push(ord(s.chars().nth(0).ok_or(Error::msg("No valid input."))?)?); } 64 => { return Ok((funge, Some(Vec::new()))); } // @ 32 => { // space @@ -649,7 +662,7 @@ impl IP { } 40 => { // ( no fingerprints are implemented // self.read_fingerprint(); - // self.fingerprint_ops[] = self.reverse; + // self.fingerprint_ops[] = self.Reverse; self.reverse(); } 41 => { // ) @@ -670,7 +683,7 @@ impl IP { } else { let text: Vec<&str> = text.lines().collect(); let height = text.len(); - let width = text.iter().map(|i| i.len()).min().ok_or("Cannot calculate width.")?; + let width = text.iter().map(|i| i.len()).min().ok_or(Error::msg("Cannot calculate width."))?; let mut code: Vec = Vec::new(); for line in text { let a = format!("{}{}", line, join(&vec![" "; width - line.len()], "")); @@ -739,7 +752,10 @@ impl IP { let text = join(&text, "\n"); fs::write(file, text)?; } - 113 => { return Ok((funge, None)) } // q + 113 => { + funge.return_code = cast_int(self.stack.pop())?; + return Ok((funge, None)) + } // q 114 => self.reverse(), // r 115 => { // s self.movep(&funge); @@ -804,10 +820,16 @@ impl IP { 122 => { } // z 48..=57 => self.stack.push(self.op(&funge) - cast_int(48)?), // 0123456789 97..=102 => self.stack.push(self.op(&funge) - cast_int(87)?), // abcdef - _ => self.not_implemented(&funge) + _ => self = match self.not_implemented(&funge) { + Some(ip) => ip, + None => return Ok((funge, None)) + } } } else { - self.not_implemented(&funge); + self = match self.not_implemented(&funge) { + Some(ip) => ip, + None => return Ok((funge, None)) + } } if !k { self.advance(&funge)?; @@ -820,7 +842,7 @@ impl IP { #[derive(Clone)] -struct DefaultHashMap { +pub struct DefaultHashMap { hashmap: HashMap, default: V } @@ -854,30 +876,42 @@ impl Index<&K> for DefaultHashMap { } +#[derive(Clone)] +enum OnError { + Reverse, + #[allow(dead_code)] + Quit, +} + + #[derive(Clone)] pub struct Funge { - extent: Vec, - code: DefaultHashMap, I>, - steps: isize, - ips: Vec>, - inputs: Input, - output: Output, - pub terminated: bool + pub extent: Vec, + pub code: DefaultHashMap, I>, + pub steps: isize, + pub ips: Vec>, + pub input: IO, + pub output: IO, + pub terminated: bool, + pub return_code: i32, + on_error: OnError } impl Funge { - pub fn new(code: T) -> Result> { + pub fn new(code: T) -> Result { let mut new = Self { extent: vec![0; 4], code: DefaultHashMap::new(cast_int(32)?), steps: 0, ips: Vec::new(), - inputs: Input { source: InputEnum::StdIn }, - output: Output { sink: OutputEnum::StdOut }, - terminated: false + input: IO::new(), + output: IO::new(), + terminated: false, + return_code: 0, + on_error: OnError::Reverse }; let mut code: Vec = code.to_string().lines().map(|i| String::from(i)).collect(); - let exe = env::current_exe()?.file_name().ok_or("No exe name")?.to_str().unwrap().to_string(); + let exe = env::current_exe()?.file_name().ok_or(Error::msg("No exe name"))?.to_str().unwrap().to_string(); if code[0].starts_with(&*format!(r"#!/usr/bin/env {}", exe)) | code[0].starts_with(&*format!(r"#!/usr/bin/env -S {}", exe)) { code.remove(0); } @@ -886,25 +920,30 @@ impl Funge { Ok(new) } - pub fn from_file(file: &String) -> Result> { + pub fn from_file(file: &String) -> Result { Ok(Self::new(fs::read_to_string(file)?)?) } - pub fn with_inputs(mut self, inputs: Vec) -> Result> { - self.inputs = Input { source: InputEnum::Vector(inputs) }; - Ok(self) + pub fn with_arguments(mut self, args: Vec) -> Self { + self.input = IO::new().with_store(args); + self } - pub fn with_output(mut self) -> Result> { - self.output = Output { sink: OutputEnum::Vector(Vec::new()) }; - Ok(self) + pub fn with_input(mut self, input: IO) -> Self { + self.input = input; + self + } + + pub fn with_output(mut self, output: IO) -> Self { + self.output = output; + self } fn insert(&mut self, i: I, x: isize, y: isize) { self.code.insert(vec![x, y], i); } - fn insert_code(&mut self, code: Vec, x0: isize, y0: isize) -> Result<(), Box> { + fn insert_code(&mut self, code: Vec, x0: isize, y0: isize) -> Result<()> { for (y, line) in code.iter().enumerate() { for (x, char) in line.chars().enumerate() { let x1: isize = x.try_into()?; @@ -926,14 +965,14 @@ impl Funge { Ok(()) } - pub fn run(mut self) -> Result>{ + pub fn run(mut self) -> Result{ while !self.terminated { self = self.step()?; } Ok(self) } - pub fn step(mut self) -> Result> { + pub fn step(mut self) -> Result { if !self.terminated { self.ips.reverse(); let mut new_ips = Vec::new(); @@ -998,13 +1037,12 @@ impl Funge { string.push_str("\n\nstacks:\n"); string.push_str(&join(&(&self.ips).iter().map(|ip| ip.stack.to_string()).collect(), "\n")); - match &self.output.sink { - OutputEnum::StdOut => { }, - OutputEnum::Vector(v) => { - string.push_str("\n\nOutput:\n"); - string.push_str(&join(&v, "")); - } - }; + string.push_str("\n\nIP offset:\n"); + string.push_str(&*join(&self.ips.iter().map(|ip| join(&ip.offset, ", ")).collect(), "; ")); + + + string.push_str("\n\nOutput\n"); + string.push_str(&self.output.get()); string.push_str("\n\nsteps:\n"); string.push_str(&self.steps.to_string()); diff --git a/src/main.rs b/src/main.rs index b2d139b..eb39b8f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,9 @@ mod debug; -use std::error::Error; use clap::Parser; use rusty_funge::Funge; use debug::FungeView; +use anyhow::Result; #[derive(Parser)] @@ -16,6 +16,8 @@ struct Args { debug: Option>, #[arg(help = "number of bits in cell and funge values", short, long)] bits: Option, + #[arg(help = "skip steps", short, long)] + steps: Option, #[arg(id = "arguments to the funge (& or ~)")] arguments: Vec, } @@ -24,18 +26,24 @@ struct Args { macro_rules! run { ($a:expr, $i:ty) => { let mut funge = Funge::<$i>::from_file(&$a.input)?; - if $a.arguments.len() > 0 { - funge = funge.with_inputs($a.arguments)?; - } match $a.debug { - Some(interval) => FungeView::new(funge)?.debug(interval).unwrap(), - None => { funge.run()?; } + Some(interval) => { + let mut funge = FungeView::new(funge, $a.arguments)?; + if let Some(s) = $a.steps { + funge.step_n(s); + } + funge.debug(interval); + } + None => { + funge = funge.with_arguments($a.arguments).run()?; + std::process::exit(funge.return_code); + } } } } -fn main() -> Result<(), Box> { +fn main() -> Result<()> { let args = Args::parse(); if let None = args.bits { run!(args, isize);