- Tested with Mycology.
- History is using delta's to preserve memory. - Input in debug mode. - Scroll code in debug mode. - Wrap output in debug mode. - Special error types. - Imlement FungeSpace as a combination of Strings and a HashMap. - Option for befunge 93, 97, 98 -> 97 runs soup.bf.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,3 +10,4 @@ Cargo.lock
|
|||||||
**/*.rs.bk
|
**/*.rs.bk
|
||||||
|
|
||||||
/.idea
|
/.idea
|
||||||
|
/Mycology/
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rusty_funge"
|
name = "rusty_funge"
|
||||||
version = "2022.12.4"
|
version = "2022.12.5"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
@@ -11,4 +11,8 @@ chrono = "0.4.23"
|
|||||||
cursive = { version = "0.20.0", features = ["termion-backend"] }
|
cursive = { version = "0.20.0", features = ["termion-backend"] }
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
num = "0.4.0"
|
num = "0.4.0"
|
||||||
anyhow = "1.0.66"
|
anyhow = "1.0.68"
|
||||||
|
thiserror = "1.0.38"
|
||||||
|
strum = "0.24.1"
|
||||||
|
strum_macros = "0.24.3"
|
||||||
|
regex = "1.7.0"
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
# Rusty Funge
|
# Rusty Funge
|
||||||
[Befunge](https://en.wikipedia.org/wiki/Befunge) interpreter and debugger for Befunge 93/98: [Funges](https://github.com/catseye/Funge-98/blob/master/doc/funge98.markdown#Whatis).
|
[Befunge](https://en.wikipedia.org/wiki/Befunge) interpreter and debugger for Befunge 93/98: [Funges](https://github.com/catseye/Funge-98/blob/master/doc/funge98.markdown#Whatis).
|
||||||
|
Rusty Funge passes all [Mycology](https://github.com/Deewiant/Mycology) tests.
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
Make sure Rust [Cargo](https://doc.rust-lang.org/cargo/) is installed, then:
|
Make sure Rust [Cargo](https://doc.rust-lang.org/cargo/) is installed, then:
|
||||||
|
|||||||
11
examples/mandelbrot.bf
Executable file
11
examples/mandelbrot.bf
Executable file
@@ -0,0 +1,11 @@
|
|||||||
|
#!/usr/bin/env rusty_funge
|
||||||
|
0>:00p58*`#@_0>:01p78vv$$<
|
||||||
|
@^+1g00,+55_v# !`\+*9<>4v$
|
||||||
|
@v30p20"?~^"< ^+1g10,+*8<$
|
||||||
|
@>p0\>\::*::882**02g*0v >^
|
||||||
|
`*:*" d":+*:-*"[Z"+g3 < |<
|
||||||
|
v-*"[Z"+g30*g20**288\--\<#
|
||||||
|
>2**5#>8*:*/00g"P"*58*:*v^
|
||||||
|
v*288 p20/**288:+*"[Z"+-<:
|
||||||
|
>*%03 p58*:*/01g"3"* v>::^
|
||||||
|
\_^#!:-1\+-*2*:*85<^
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
#!/usr/bin/env rusty_funge
|
#!/usr/bin/env -S rusty_funge -B97
|
||||||
060p070 p'O80v
|
060p070 p'O80v
|
||||||
pb2*90p4$4> $4$>v>
|
pb2*90p4$4> $4$>v>
|
||||||
v4$>4$>4$>4$># ARGH>!
|
v4$>4$>4$>4$># ARGH>!
|
||||||
|
|||||||
349
src/debug.rs
349
src/debug.rs
@@ -1,26 +1,109 @@
|
|||||||
use std::fmt::{Display, Formatter};
|
use std::cmp::{min, max};
|
||||||
|
use std::collections::HashMap;
|
||||||
use std::sync::{Arc, Mutex};
|
use std::sync::{Arc, Mutex};
|
||||||
use std::thread::{spawn, sleep};
|
use std::thread::{spawn, sleep};
|
||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
use anyhow::{Error, Result};
|
||||||
use cursive::{Cursive, CursiveExt, Printer, Vec2};
|
use cursive::{Cursive, CursiveExt, Printer, Vec2};
|
||||||
use cursive::view::View;
|
use cursive::view::View;
|
||||||
use cursive::theme::{BorderStyle, ColorStyle, Palette, Theme};
|
use cursive::theme::{BorderStyle, ColorStyle, Palette, Theme};
|
||||||
use cursive::event::{Event, EventResult, Key};
|
use cursive::event::{Event, EventResult, Key};
|
||||||
use rusty_funge::{Int, Funge, join, ord, IO};
|
use cursive::views::{Dialog, EditView};
|
||||||
use anyhow::Result;
|
use rusty_funge::{Int, Funge, join, ord, IO, cast_int, chr, Rect, IP};
|
||||||
|
|
||||||
|
|
||||||
// TODO: Use anyhow::Result
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum FungeError<I: Int> {
|
struct FungeDelta<I: Int> {
|
||||||
Funge(Funge<I>),
|
code: HashMap<Vec<isize>, I>,
|
||||||
Error(String)
|
ips: Vec<IP<I>>,
|
||||||
|
output: usize,
|
||||||
|
input: Vec<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Int> FungeDelta<I> {
|
||||||
|
fn new(code: HashMap<Vec<isize>, I>, ips: Vec<IP<I>>, output: usize, input: Vec<String>) -> Self {
|
||||||
|
Self { code, ips, output, input }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct FungeHist<I: Int> {
|
||||||
|
maxlen: usize,
|
||||||
|
history: Vec<FungeDelta<I>>,
|
||||||
|
last: Option<Funge<I>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<I: Int> FungeHist<I> {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self { maxlen: 16348, history: Vec::new(), last: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.history.len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push(&mut self, old: &Funge<I>, new: &Result<Funge<I>>) {
|
||||||
|
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<I>>) -> Funge<I> {
|
||||||
|
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<I: Int> {
|
struct FungeDebug<I: Int> {
|
||||||
funge: Option<FungeError<I>>,
|
funge: Option<Result<Funge<I>>>,
|
||||||
history: Vec<Funge<I>>,
|
history: FungeHist<I>,
|
||||||
interval: f64,
|
interval: f64,
|
||||||
running: bool,
|
running: bool,
|
||||||
stop_op: Option<I>
|
stop_op: Option<I>
|
||||||
@@ -29,8 +112,8 @@ struct FungeDebug<I: Int> {
|
|||||||
impl<I: Int> FungeDebug<I> {
|
impl<I: Int> FungeDebug<I> {
|
||||||
fn new(funge: Funge<I>) -> Self {
|
fn new(funge: Funge<I>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
funge: Some(FungeError::Funge(funge)),
|
funge: Some(Ok(funge)),
|
||||||
history: Vec::new(),
|
history: FungeHist::new(),
|
||||||
interval: 0.05,
|
interval: 0.05,
|
||||||
running: false,
|
running: false,
|
||||||
stop_op: None
|
stop_op: None
|
||||||
@@ -38,50 +121,41 @@ impl<I: Int> FungeDebug<I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn step_back(&mut self) {
|
fn step_back(&mut self) {
|
||||||
match self.history.pop() {
|
|
||||||
Some(funge) => {
|
|
||||||
self.running = false;
|
self.running = false;
|
||||||
self.funge = Some(FungeError::Funge(funge));
|
if let Some(new) = self.funge.take() {
|
||||||
}
|
self.funge = Some(Ok(self.history.pop(new)));
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_terminated(&self) -> bool {
|
|
||||||
match &self.funge {
|
|
||||||
Some(FungeError::Funge(f)) if !f.terminated => false,
|
|
||||||
_ => true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step(&mut self) {
|
fn step(&mut self) {
|
||||||
match self.funge.to_owned() {
|
self.funge = match self.funge.take() {
|
||||||
Some(FungeError::Funge(funge)) if !funge.terminated => {
|
Some(Ok(funge)) => {
|
||||||
self.history.push(funge.clone());
|
let old = funge.clone();
|
||||||
if self.history.len() > 16384 {
|
let new = funge.step();
|
||||||
self.history.remove(0);
|
self.history.push(&old, &new);
|
||||||
|
Some(new)
|
||||||
}
|
}
|
||||||
match funge.step() {
|
funge => funge
|
||||||
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<I: Int> Display for FungeDebug<I> {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
fn input_dialog() -> Result<String> {
|
||||||
match self.funge.as_ref().expect("No funge found") {
|
let mut app = Cursive::new();
|
||||||
FungeError::Funge(funge) => write!(f, "{}", funge),
|
app.add_layer(Dialog::new().title("Funge is asking for input").content(EditView::new()));
|
||||||
FungeError::Error(e) => write!(f, "{}", e)
|
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::<Dialog>() {
|
||||||
|
if let Some(edit) = dialog.get_content().downcast_ref::<EditView>() {
|
||||||
|
return Ok(edit.get_content().as_ref().to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Err(Error::msg("Input went wrong!"))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
pub(crate) struct FungeView<I: Int> {
|
pub(crate) struct FungeView<I: Int> {
|
||||||
@@ -95,7 +169,7 @@ impl<I: Int> FungeView<I> {
|
|||||||
.with_store(input)
|
.with_store(input)
|
||||||
.with_input(|store| {
|
.with_input(|store| {
|
||||||
Ok(match store.pop() {
|
Ok(match store.pop() {
|
||||||
None => String::from("5"), // TODO: cursive input
|
None => input_dialog()?,
|
||||||
Some(s) => s
|
Some(s) => s
|
||||||
})
|
})
|
||||||
})).with_output(IO::new()
|
})).with_output(IO::new()
|
||||||
@@ -106,29 +180,23 @@ impl<I: Int> FungeView<I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn step_back(&mut self) {
|
fn step_back(&mut self) {
|
||||||
match self.funge.lock() {
|
if let Ok(mut funge) = self.funge.lock() {
|
||||||
Ok(mut f) => f.step_back(),
|
funge.step_back()
|
||||||
Err(_) => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step(&mut self) {
|
fn step(&mut self) {
|
||||||
match self.funge.lock() {
|
if let Ok(mut funge) = self.funge.lock() {
|
||||||
Ok(mut f) => f.step(),
|
funge.step();
|
||||||
Err(_) => {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn step_n(&mut self, n: usize) {
|
pub fn step_n(&mut self, n: usize) {
|
||||||
match self.funge.lock() {
|
if let Ok(mut funge) = self.funge.lock() {
|
||||||
Ok(mut funge) => {
|
|
||||||
for _ in 0..n {
|
for _ in 0..n {
|
||||||
funge.step();
|
funge.step();
|
||||||
if funge.is_terminated() { break }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_mutex(&self) -> Self {
|
fn new_mutex(&self) -> Self {
|
||||||
@@ -138,28 +206,34 @@ impl<I: Int> FungeView<I> {
|
|||||||
fn is_running(&self) -> bool {
|
fn is_running(&self) -> bool {
|
||||||
match self.funge.lock() {
|
match self.funge.lock() {
|
||||||
Ok(mut funge) => {
|
Ok(mut funge) => {
|
||||||
if funge.is_terminated() | !funge.running { return false }
|
let running = if !funge.running {
|
||||||
if let Some(op) = funge.stop_op {
|
false
|
||||||
|
} else {
|
||||||
match funge.funge.as_ref() {
|
match funge.funge.as_ref() {
|
||||||
Some(FungeError::Funge(f)) => {
|
Some(Ok(f)) => {
|
||||||
|
if let Some(op) = funge.stop_op {
|
||||||
|
let mut running = true;
|
||||||
for pos in f.ips_pos() {
|
for pos in f.ips_pos() {
|
||||||
if f.code[&pos] == op {
|
if f.code[&pos] == op {
|
||||||
funge.stop_op = None;
|
funge.stop_op = None;
|
||||||
funge.running = false;
|
running = false;
|
||||||
return false
|
break
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Some(FungeError::Error(_)) => {
|
|
||||||
funge.running = false;
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
None => return false
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
running
|
||||||
|
} else {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
Err(_) => false
|
}
|
||||||
|
_ => false
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if !running {
|
||||||
|
funge.running = false
|
||||||
|
}
|
||||||
|
running
|
||||||
|
}
|
||||||
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,70 +250,131 @@ impl<I: Int> FungeView<I> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self) {
|
fn run(&self) {
|
||||||
let mut funge_mutex = self.new_mutex();
|
let mut funge = self.new_mutex();
|
||||||
{ funge_mutex.funge.lock().unwrap().running = true; }
|
{ funge.funge.lock().unwrap().running = true; }
|
||||||
spawn(move || {
|
spawn(move || {
|
||||||
loop {
|
loop {
|
||||||
let instant = Instant::now();
|
let instant = Instant::now();
|
||||||
funge_mutex.step();
|
funge.step();
|
||||||
let duration = Duration::from_micros(match funge_mutex.funge.lock() {
|
let duration = Duration::from_micros(match funge.funge.lock() {
|
||||||
Ok(f) => (f.interval * 1e6) as u64,
|
Ok(f) => (f.interval * 1e6) as u64,
|
||||||
Err(_) => 100000
|
Err(_) => 100000
|
||||||
});
|
});
|
||||||
if !funge_mutex.is_running() {
|
if !funge.is_running() {
|
||||||
funge_mutex.funge.lock().unwrap().running = false;
|
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
let sleep_time = duration - instant.elapsed();
|
let elapsed = instant.elapsed();
|
||||||
if sleep_time.as_secs_f64() > 0f64 { sleep(duration) }
|
if duration > elapsed {
|
||||||
|
sleep(duration - elapsed)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn debug(self, interval: Option<f64>) {
|
pub(crate) fn debug(self, interval: Option<f64>) {
|
||||||
let mut app = Cursive::new();
|
let mut app = Cursive::new();
|
||||||
match interval {
|
if let Some(interval) = interval {
|
||||||
None => {}
|
|
||||||
Some(interval) => {
|
|
||||||
{ self.funge.lock().unwrap().interval = interval; }
|
{ self.funge.lock().unwrap().interval = interval; }
|
||||||
self.toggle_run();
|
self.toggle_run();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
app.add_layer(self);
|
app.add_layer(self);
|
||||||
app.add_global_callback(Key::Esc, |app| app.quit());
|
app.add_global_callback(Key::Esc, |app| app.quit());
|
||||||
app.set_autorefresh(true);
|
app.set_autorefresh(true);
|
||||||
app.set_theme(Theme { shadow: false, borders: BorderStyle::None, palette: Palette::default() });
|
app.set_theme(Theme { shadow: false, borders: BorderStyle::None, palette: Palette::default() });
|
||||||
app.run();
|
app.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn wrap(string: String, width: usize) -> Vec<String> {
|
||||||
|
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<I: Int> View for FungeView<I> {
|
impl<I: Int> View for FungeView<I> {
|
||||||
fn draw(&self, printer: &Printer) {
|
fn draw(&self, printer: &Printer) {
|
||||||
match self.funge.lock().as_ref() {
|
if let Ok(funge_mutex) = self.funge.lock().as_ref() {
|
||||||
Ok(funge_mutex) => {
|
|
||||||
let hist_len = funge_mutex.history.len();
|
let hist_len = funge_mutex.history.len();
|
||||||
let running = funge_mutex.running;
|
let running = funge_mutex.running;
|
||||||
match funge_mutex.funge.as_ref() {
|
match funge_mutex.funge.as_ref() {
|
||||||
Some(FungeError::Funge(funge)) => {
|
Some(Ok(funge)) => {
|
||||||
let text = format!("{}", funge);
|
let cheight = (printer.size.y / 2) as isize;
|
||||||
let lines: Vec<&str> = text.lines().collect();
|
let cwidth = printer.size.x as isize;
|
||||||
for (i, line) in lines.iter().enumerate() {
|
let fheight = funge.extent.height();
|
||||||
printer.print((0, i), line);
|
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::<isize>() / (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::<isize>() / (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().iter() {
|
for pos in funge.ips_pos() {
|
||||||
if (pos[0] >= 0) & (pos[1] >= 0) {
|
if (left <= pos[0]) & (pos[0] < right) & (top <= pos[1]) & (pos[1] < bottom) {
|
||||||
let x = pos[0] as usize;
|
let c = match cast_int::<u8, _>(funge.code[&pos]) {
|
||||||
let y = pos[1] as usize + 1;
|
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.with_color(ColorStyle::highlight(),
|
||||||
|printer| {
|
|printer| {
|
||||||
match lines[y].chars().nth(x) {
|
printer.print(((pos[0] - left) as usize, (pos[1] - top) as usize), &c.to_string());
|
||||||
None => {}
|
|
||||||
Some(l) => printer.print((x, y), &*l.to_string())
|
|
||||||
}
|
}
|
||||||
});
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let n = lines.len() + 1;
|
|
||||||
|
let mut n = (bottom - top) as usize;
|
||||||
|
let offset: Vec<Vec<isize>> = 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"];
|
let mut text = vec!["esc: quit"];
|
||||||
if hist_len > 0 {
|
if hist_len > 0 {
|
||||||
@@ -250,23 +385,20 @@ impl<I: Int> View for FungeView<I> {
|
|||||||
} else {
|
} else {
|
||||||
text.push("space: run")
|
text.push("space: run")
|
||||||
}
|
}
|
||||||
if !funge.terminated {
|
text.push("enter: step");
|
||||||
text.push("enter: step")
|
|
||||||
}
|
|
||||||
let interval = format!("interval: {} up/down arrow", funge_mutex.interval);
|
let interval = format!("interval: {} up/down arrow", funge_mutex.interval);
|
||||||
text.push(&*interval);
|
text.push(&*interval);
|
||||||
printer.print((0, n + 1), &*join(&text, ", "));
|
printer.print((0, printer.size.y - 1), &*join(&text, ", "));
|
||||||
}
|
}
|
||||||
Some(FungeError::Error(e)) => {
|
Some(Err(e)) => {
|
||||||
printer.print((0, 0), "Error occured:");
|
printer.print((0, 0), "Error occured:");
|
||||||
printer.print((0, 1), &*format!("{}", e));
|
printer.print((0, 1), &*format!("{}", e));
|
||||||
|
printer.print((0, 2), &*format!("running: {}", running));
|
||||||
printer.print((0, 3), "esc: quit, backspace: back");
|
printer.print((0, 3), "esc: quit, backspace: back");
|
||||||
}
|
}
|
||||||
None => {}
|
None => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn required_size(&mut self, constraint: Vec2) -> Vec2 {
|
fn required_size(&mut self, constraint: Vec2) -> Vec2 {
|
||||||
@@ -292,8 +424,8 @@ impl<I: Int> View for FungeView<I> {
|
|||||||
let lock = self.funge.lock();
|
let lock = self.funge.lock();
|
||||||
let mut funge = lock.unwrap();
|
let mut funge = lock.unwrap();
|
||||||
let interval = funge.interval / 2.0;
|
let interval = funge.interval / 2.0;
|
||||||
if interval < 0.01 {
|
if interval < 0.001 {
|
||||||
funge.interval = 0.01;
|
funge.interval = 0.001;
|
||||||
} else {
|
} else {
|
||||||
funge.interval = interval;
|
funge.interval = interval;
|
||||||
}
|
}
|
||||||
@@ -304,11 +436,10 @@ impl<I: Int> View for FungeView<I> {
|
|||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
Event::Char(c) => {
|
Event::Char(c) => {
|
||||||
match ord(c) {
|
if let Ok(op) = ord(c) {
|
||||||
Ok(i) => self.funge.lock().unwrap().stop_op = Some(i),
|
self.funge.lock().unwrap().stop_op = Some(op);
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
self.run();
|
self.run();
|
||||||
|
}
|
||||||
EventResult::Consumed(None)
|
EventResult::Consumed(None)
|
||||||
}
|
}
|
||||||
_ => EventResult::Ignored
|
_ => EventResult::Ignored
|
||||||
|
|||||||
968
src/lib.rs
968
src/lib.rs
File diff suppressed because it is too large
Load Diff
10
src/main.rs
10
src/main.rs
@@ -1,9 +1,9 @@
|
|||||||
mod debug;
|
mod debug;
|
||||||
|
|
||||||
|
use anyhow::Result;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use rusty_funge::Funge;
|
use rusty_funge::Funge;
|
||||||
use debug::FungeView;
|
use debug::FungeView;
|
||||||
use anyhow::Result;
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
@@ -18,6 +18,8 @@ struct Args {
|
|||||||
bits: Option<u8>,
|
bits: Option<u8>,
|
||||||
#[arg(help = "skip steps", short, long)]
|
#[arg(help = "skip steps", short, long)]
|
||||||
steps: Option<usize>,
|
steps: Option<usize>,
|
||||||
|
#[arg(help = "befunge version (93, 97, 98)", short = 'B', long)]
|
||||||
|
befunge: Option<String>,
|
||||||
#[arg(id = "arguments to the funge (& or ~)")]
|
#[arg(id = "arguments to the funge (& or ~)")]
|
||||||
arguments: Vec<String>,
|
arguments: Vec<String>,
|
||||||
}
|
}
|
||||||
@@ -26,6 +28,9 @@ struct Args {
|
|||||||
macro_rules! run {
|
macro_rules! run {
|
||||||
($a:expr, $i:ty) => {
|
($a:expr, $i:ty) => {
|
||||||
let mut funge = Funge::<$i>::from_file(&$a.input)?;
|
let mut funge = Funge::<$i>::from_file(&$a.input)?;
|
||||||
|
if let Some(s) = $a.befunge {
|
||||||
|
funge = funge.with_version(format!("B{}", s))?;
|
||||||
|
}
|
||||||
match $a.debug {
|
match $a.debug {
|
||||||
Some(interval) => {
|
Some(interval) => {
|
||||||
let mut funge = FungeView::new(funge, $a.arguments)?;
|
let mut funge = FungeView::new(funge, $a.arguments)?;
|
||||||
@@ -35,8 +40,7 @@ macro_rules! run {
|
|||||||
funge.debug(interval);
|
funge.debug(interval);
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
funge = funge.with_arguments($a.arguments).run()?;
|
std::process::exit(funge.with_arguments($a.arguments).run()?);
|
||||||
std::process::exit(funge.return_code);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user