use std::cmp::{min, max}; use std::collections::HashMap; use std::sync::{Arc, Mutex}; use std::thread::{spawn, sleep}; use std::time::{Duration, Instant}; use anyhow::{Error, Result}; use cursive::{Cursive, CursiveExt, Printer, Vec2}; use cursive::view::View; use cursive::theme::{BorderStyle, ColorStyle, Palette, Theme}; use cursive::event::{Event, EventResult, Key}; use cursive::views::{Dialog, EditView}; use rusty_funge::{Int, Funge, join, ord, IO, cast_int, chr, Rect, IP}; #[derive(Clone)] struct FungeDelta { code: HashMap, I>, ips: Vec>, output: usize, input: Vec } impl FungeDelta { fn new(code: HashMap, I>, ips: Vec>, output: usize, input: Vec) -> Self { Self { code, ips, output, input } } } #[derive(Clone)] struct FungeHist { maxlen: usize, history: Vec>, last: Option> } impl FungeHist { fn new() -> Self { Self { maxlen: 16348, history: Vec::new(), last: None } } fn len(&self) -> usize { self.history.len() } fn push(&mut self, old: &Funge, new: &Result>) { if let Ok(new) = new { let mut code = HashMap::new(); for (y, (line_old, line_new)) in old.code.orig_code.iter().zip(new.code.orig_code.iter()).enumerate() { if line_old != line_new { for x in 0..min(line_old.len(), line_new.len()) { if line_old[x] != line_new[x] { code.insert(vec![x as isize, y as isize], line_old[x]); } } for x in min(line_old.len(), line_new.len())..max(line_old.len(), line_new.len()) { code.insert(vec![x as isize, y as isize], line_old[x]); } } } for (pos, op) in &new.code.new_code { if *op != old.code[pos] { code.insert(pos.to_owned(), *op); } } let ips = old.ips.clone(); let output = new.output.len() - old.output.len(); let input = old.input.store.to_owned().into_iter().rev().take(old.input.len() - new.input.len()).rev().collect(); self.history.push(FungeDelta::new(code, ips, output, input)); if self.len() > self.maxlen { self.history.remove(0); } } else { self.last = Some(old.clone()); } } fn pop(&mut self, funge: Result>) -> Funge { match funge { Ok(mut funge) => { match self.history.pop() { Some(delta) => { for (pos, op) in delta.code { funge.code.insert(pos, op); } funge.ips = delta.ips; for _ in 0..delta.output { funge.output.store.pop(); } funge.input.store.extend(delta.input); funge.steps -= 1; funge } None => funge } } _ => self.last.take().expect("There should be a funge here.") } } } struct FungeDebug { funge: Option>>, history: FungeHist, interval: f64, running: bool, stop_op: Option } impl FungeDebug { fn new(funge: Funge) -> Self { Self { funge: Some(Ok(funge)), history: FungeHist::new(), interval: 0.05, running: false, stop_op: None } } fn step_back(&mut self) { self.running = false; if let Some(new) = self.funge.take() { self.funge = Some(Ok(self.history.pop(new))); } } fn step(&mut self) { self.funge = match self.funge.take() { Some(Ok(funge)) => { let old = funge.clone(); let new = funge.step(); self.history.push(&old, &new); Some(new) } funge => funge } } } fn input_dialog() -> Result { let mut app = Cursive::new(); app.add_layer(Dialog::new().title("Funge is asking for input").content(EditView::new())); app.add_global_callback(Key::Enter, |app| app.quit()); app.set_theme(Theme { shadow: false, borders: BorderStyle::None, palette: Palette::default() }); app.run(); if let Some(view) = app.pop_layer() { if let Ok(dialog) = view.downcast::() { if let Some(edit) = dialog.get_content().downcast_ref::() { return Ok(edit.get_content().as_ref().to_string()) } } } Err(Error::msg("Input went wrong!")) } pub(crate) struct FungeView { funge: Arc>> } impl FungeView { 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 => input_dialog()?, Some(s) => s }) })).with_output(IO::new() .with_output(|store, s| { Ok(store.push(s)) }))))) }) } fn step_back(&mut self) { if let Ok(mut funge) = self.funge.lock() { funge.step_back() } } fn step(&mut self) { if let Ok(mut funge) = self.funge.lock() { funge.step(); } } pub fn step_n(&mut self, n: usize) { if let Ok(mut funge) = self.funge.lock() { for _ in 0..n { funge.step(); } } } fn new_mutex(&self) -> Self { Self { funge: Arc::clone(&self.funge) } } fn is_running(&self) -> bool { match self.funge.lock() { Ok(mut funge) => { let running = if !funge.running { false } else { match funge.funge.as_ref() { Some(Ok(f)) => { if let Some(op) = funge.stop_op { let mut running = true; for pos in f.ips_pos() { if f.code[&pos] == op { funge.stop_op = None; running = false; break } } running } else { true } } _ => false } }; if !running { funge.running = false } running } _ => 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 = self.new_mutex(); { funge.funge.lock().unwrap().running = true; } spawn(move || { loop { let instant = Instant::now(); funge.step(); let duration = Duration::from_micros(match funge.funge.lock() { Ok(f) => (f.interval * 1e6) as u64, Err(_) => 100000 }); if !funge.is_running() { break } let elapsed = instant.elapsed(); if duration > elapsed { sleep(duration - elapsed) } } }); } pub(crate) fn debug(self, interval: Option) { let mut app = Cursive::new(); if let Some(interval) = interval { { self.funge.lock().unwrap().interval = interval; } self.toggle_run(); } app.add_layer(self); 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(); } fn wrap(string: String, width: usize) -> Vec { let mut lines = Vec::new(); let mut _a: &str = ""; for mut line in string.lines() { while line.len() > width { (_a, line) = line.split_at(width); lines.push(_a.to_string()); } if line.len() > 0 { lines.push(line.to_string()); } } lines } } impl View for FungeView { fn draw(&self, printer: &Printer) { if let Ok(funge_mutex) = self.funge.lock().as_ref() { let hist_len = funge_mutex.history.len(); let running = funge_mutex.running; match funge_mutex.funge.as_ref() { Some(Ok(funge)) => { let cheight = (printer.size.y / 2) as isize; let cwidth = printer.size.x as isize; let fheight = funge.extent.height(); let fwidth = funge.extent.width(); let (top, bottom) = if cheight >= fheight { (funge.extent.top, funge.extent.bottom) } else { let y = funge.ips_pos().iter().map(|i| i[1]).sum::() / (funge.ips.len() as isize); let top = max(y - &cheight / 2, funge.extent.top); (top, top + cheight) }; let (left, right) = if cwidth >= fwidth { (funge.extent.left, funge.extent.right) } else { let x = funge.ips_pos().iter().map(|i| i[0]).sum::() / (funge.ips.len() as isize); let left = max(x - &cwidth / 2, funge.extent.left); (left, left + cwidth) }; for (n, line) in funge.code.get_string(Rect::new(left, right, top, bottom)).iter().enumerate() { printer.print((0, n), line); } for pos in funge.ips_pos() { if (left <= pos[0]) & (pos[0] < right) & (top <= pos[1]) & (pos[1] < bottom) { let c = match cast_int::(funge.code[&pos]) { Ok(n @ 32..=126) | Ok(n @ 161..=255) => n, _ => 164 }; let c = chr(c).expect("c can only be valid u8 for char"); printer.with_color(ColorStyle::highlight(), |printer| { printer.print(((pos[0] - left) as usize, (pos[1] - top) as usize), &c.to_string()); } ) } } let mut n = (bottom - top) as usize; let offset: Vec> = funge.ips.iter().map(|ip| ip.offset.clone()).collect(); printer.print((0, n + 1), &format!("top-left: {}, {}, ip pos: {:?}, offset: {:?}", top, left, funge.ips_pos(), offset)); let cwidth = cwidth as usize; let mut stack = Self::wrap(funge.get_stack_string(), cwidth); let mut output = Self::wrap(funge.output.get(), cwidth); if printer.size.y >= n + 9 { stack = stack.into_iter().rev().take(printer.size.y / 5).rev().collect(); if printer.size.y >= stack.len() + n + 9 { output = output.into_iter().rev().take(printer.size.y - stack.len() - n - 9).rev().collect(); } else { output = Vec::new(); } } else { stack = Vec::new(); output = Vec::new(); } printer.print((0, n + 3), &format!("stacks:")); n += 4; for line in stack { printer.print((0, n), &*line); n += 1; } printer.print((0, n + 1), "output:"); n += 2; for line in output { printer.print((0, n), &*line); n += 1; } printer.print((0, n + 1), &format!("steps: {}", funge.steps)); 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") } text.push("enter: step"); let interval = format!("interval: {} up/down arrow", funge_mutex.interval); text.push(&*interval); printer.print((0, printer.size.y - 1), &*join(&text, ", ")); } Some(Err(e)) => { printer.print((0, 0), "Error occured:"); printer.print((0, 1), &*format!("{}", e)); printer.print((0, 2), &*format!("running: {}", running)); printer.print((0, 3), "esc: quit, backspace: back"); } None => {} } } } fn required_size(&mut self, constraint: Vec2) -> Vec2 { constraint } fn on_event(&mut self, event: Event) -> EventResult { match event { Event::Key(Key::Esc) => EventResult::Ignored, Event::Key(Key::Backspace) => { self.step_back(); EventResult::Consumed(None) } 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.001 { funge.interval = 0.001; } else { funge.interval = interval; } EventResult::Consumed(None) } Event::Key(Key::Down) => { self.funge.lock().unwrap().interval *= 2.0; EventResult::Consumed(None) } Event::Char(c) => { if let Ok(op) = ord(c) { self.funge.lock().unwrap().stop_op = Some(op); self.run(); } EventResult::Consumed(None) } _ => EventResult::Ignored } } }