- Use anyhow to handle errors.
- Debug speed control, pause/run, step back, run until. - IO is a struct instead of traits. - Handprint now is wprusty. - Return code upon q instruction. - Option to quit when not implemented.
This commit is contained in:
@@ -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
|
||||
@@ -11,3 +11,4 @@ chrono = "0.4.23"
|
||||
cursive = { version = "0.20.0", features = ["termion-backend"] }
|
||||
rand = "0.8.5"
|
||||
num = "0.4.0"
|
||||
anyhow = "1.0.66"
|
||||
321
src/debug.rs
321
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<I: Int> {
|
||||
funge: Option<Funge<I>>
|
||||
// TODO: Use anyhow::Result
|
||||
#[derive(Clone)]
|
||||
enum FungeError<I: Int> {
|
||||
Funge(Funge<I>),
|
||||
Error(String)
|
||||
}
|
||||
|
||||
impl<I: Int> FungeMutex<I> {
|
||||
|
||||
struct FungeDebug<I: Int> {
|
||||
funge: Option<FungeError<I>>,
|
||||
history: Vec<Funge<I>>,
|
||||
interval: f64,
|
||||
running: bool,
|
||||
stop_op: Option<I>
|
||||
}
|
||||
|
||||
impl<I: Int> FungeDebug<I> {
|
||||
fn new(funge: Funge<I>) -> Self {
|
||||
Self { funge: Some(funge) }
|
||||
}
|
||||
|
||||
fn step(&mut self) -> Result<bool, Box<dyn Error>> {
|
||||
let mut funge = self.funge.to_owned().ok_or("No funge found")?;
|
||||
if !funge.terminated {
|
||||
funge = funge.step()?;
|
||||
}
|
||||
let terminated = funge.terminated;
|
||||
self.funge = Some(funge);
|
||||
Ok(terminated)
|
||||
}
|
||||
|
||||
fn funge_ref(&self) -> Option<&Funge<I>> {
|
||||
self.funge.as_ref()
|
||||
Self {
|
||||
funge: Some(FungeError::Funge(funge)),
|
||||
history: Vec::new(),
|
||||
interval: 0.05,
|
||||
running: false,
|
||||
stop_op: None
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Int> Display for FungeMutex<I> {
|
||||
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<I: Int> Display for FungeDebug<I> {
|
||||
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<I: Int> {
|
||||
funge: Arc<Mutex<FungeMutex<I>>>
|
||||
funge: Arc<Mutex<FungeDebug<I>>>
|
||||
}
|
||||
|
||||
impl<I: Int> FungeView<I> {
|
||||
pub (crate) fn new(funge: Funge<I>) -> Result<Self, Box<dyn Error>> {
|
||||
Ok(FungeView { funge: Arc::new(Mutex::new(FungeMutex::new(funge.with_output()?))) } )
|
||||
pub (crate) fn new(funge: Funge<I>, input: Vec<String>) -> Result<Self> {
|
||||
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<bool, Box<dyn Error>> {
|
||||
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<f64>) -> Result<(), Box<dyn Error>> {
|
||||
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<f64>) {
|
||||
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<I: Int> View for FungeView<I> {
|
||||
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)
|
||||
};
|
||||
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 ips_pos.iter() {
|
||||
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| printer.print((x, y), &*lines[y].chars().nth(x).unwrap().to_string()));
|
||||
printer.with_color(ColorStyle::highlight(),
|
||||
|printer| {
|
||||
match lines[y].chars().nth(x) {
|
||||
None => {}
|
||||
Some(l) => printer.print((x, y), &*l.to_string())
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
let mut bottom = String::from("Press 'q' to quit");
|
||||
if terminated {
|
||||
bottom.push_str(".");
|
||||
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 {
|
||||
bottom.push_str(", any other key to continue.");
|
||||
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(_) => {}
|
||||
}
|
||||
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::<Vec<usize>>().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
|
||||
|
||||
230
src/lib.rs
230
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<I: Integer + NumCast + FromStr + Hash + Clone + Copy + Sync + Send + Display + 'static> Int for I {}
|
||||
|
||||
|
||||
fn join<T: ToString>(v: &Vec<T>, s: &str) -> String {
|
||||
pub fn join<T: ToString>(v: &Vec<T>, 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<T: ToString>(v: &Vec<T>, s: &str) -> String {
|
||||
string
|
||||
}
|
||||
|
||||
fn cast_int<I: NumCast, J: NumCast>(j: J) -> Result<I, Box<dyn Error>> {
|
||||
Ok(I::from(j).ok_or("Could not convert from primitive")?)
|
||||
fn cast_int<I: NumCast, J: NumCast>(j: J) -> Result<I> {
|
||||
Ok(I::from(j).ok_or(Error::msg("Could not convert from primitive"))?)
|
||||
}
|
||||
|
||||
fn cast_vec_int<I: NumCast, J: NumCast>(j: Vec<J>) -> Result<Vec<I>, Box<dyn Error>> {
|
||||
fn cast_vec_int<I: NumCast, J: NumCast>(j: Vec<J>) -> Result<Vec<I>> {
|
||||
let mut i = Vec::<I>::new();
|
||||
for n in j {
|
||||
i.push(cast_int(n)?);
|
||||
@@ -40,12 +41,11 @@ fn cast_vec_int<I: NumCast, J: NumCast>(j: Vec<J>) -> Result<Vec<I>, Box<dyn Err
|
||||
Ok(i)
|
||||
}
|
||||
|
||||
fn ord<I: NumCast>(c: char) -> Result<I, Box<dyn Error>>
|
||||
{
|
||||
pub fn ord<I: NumCast>(c: char) -> Result<I> {
|
||||
Ok(cast_int::<_, u32>(c.try_into()?)?)
|
||||
}
|
||||
|
||||
fn chr<I: NumCast>(i: I) -> Result<char, Box<dyn Error>> {
|
||||
fn chr<I: NumCast>(i: I) -> Result<char> {
|
||||
Ok(cast_int::<u32, _>(i)?.try_into()?)
|
||||
}
|
||||
|
||||
@@ -61,51 +61,64 @@ fn sub<I: Sub + Copy>(a: &Vec<I>, b: &Vec<I>) -> Vec<I> where
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
enum InputEnum {
|
||||
StdIn,
|
||||
Vector(Vec<String>)
|
||||
pub struct IO {
|
||||
store: Vec<String>,
|
||||
input: fn(&mut Vec<String>) -> Result<String>,
|
||||
output: fn(&mut Vec<String>, String) -> Result<()>
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Input {
|
||||
source: InputEnum
|
||||
}
|
||||
|
||||
impl Input {
|
||||
fn get(&mut self) -> Result<String, Box<dyn Error>> {
|
||||
Ok(match self.source {
|
||||
InputEnum::StdIn => {
|
||||
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
|
||||
}
|
||||
InputEnum::Vector(ref mut v) => v.pop().ok_or("No more input!")?
|
||||
Some(s) => s
|
||||
})
|
||||
},
|
||||
output: |_, s| {
|
||||
print!("{}", s);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
enum OutputEnum {
|
||||
StdOut,
|
||||
Vector(Vec<String>)
|
||||
pub fn with_store(mut self, mut store: Vec<String>) -> Self {
|
||||
store.reverse();
|
||||
self.store = store;
|
||||
self
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Output {
|
||||
sink: OutputEnum
|
||||
pub fn with_input(mut self, fun: fn(&mut Vec<String>) -> Result<String>) -> Self {
|
||||
self.input = fun;
|
||||
self
|
||||
}
|
||||
|
||||
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_output(mut self, fun: fn(&mut Vec<String>, String) -> Result<()>) -> Self {
|
||||
self.output = fun;
|
||||
self
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Result<String> {
|
||||
(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<I: Int> {
|
||||
stack: Vec<I>
|
||||
@@ -229,11 +242,11 @@ impl<I: Int> Display for StackStack<I> {
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
struct IP<I: Int> {
|
||||
pub struct IP<I: Int> { // TODO, getter fns in funge instead of pubs
|
||||
id: usize,
|
||||
position: Vec<isize>,
|
||||
delta: Vec<isize>,
|
||||
offset: Vec<isize>,
|
||||
pub offset: Vec<isize>,
|
||||
string: bool,
|
||||
stack: StackStack<I>,
|
||||
fingerprint_ops: HashMap<I, ()>,
|
||||
@@ -241,7 +254,7 @@ struct IP<I: Int> {
|
||||
|
||||
|
||||
impl<I: Int> IP<I> {
|
||||
fn new(funge: &Funge<I>) -> Result<Self, Box<dyn Error>> {
|
||||
fn new(funge: &Funge<I>) -> Result<Self> {
|
||||
let mut new = IP {
|
||||
id: funge.ips.len(),
|
||||
position: vec![0, 0],
|
||||
@@ -285,7 +298,7 @@ impl<I: Int> IP<I> {
|
||||
self.delta = vec![self.delta[1], -self.delta[0]];
|
||||
}
|
||||
|
||||
fn advance(&mut self, funge: &Funge<I>) -> Result<(), Box<dyn Error>> {
|
||||
fn advance(&mut self, funge: &Funge<I>) -> Result<()> {
|
||||
let space: I = cast_int(32)?;
|
||||
let semicolon: I = cast_int(59)?;
|
||||
Ok(if self.string {
|
||||
@@ -322,7 +335,7 @@ impl<I: Int> IP<I> {
|
||||
self.position = self.next_pos(funge);
|
||||
}
|
||||
|
||||
fn skip(mut self, funge: Funge<I>) -> Result<(Funge<I>, Option<Vec<Self>>), Box<dyn Error>> {
|
||||
fn skip(mut self, funge: Funge<I>) -> Result<(Funge<I>, Option<Vec<Self>>)> {
|
||||
self.movep(&funge);
|
||||
if let Ok(32 | 59) = cast_int(self.op(&funge)) {
|
||||
self.advance(&funge)?;
|
||||
@@ -349,7 +362,7 @@ impl<I: Int> IP<I> {
|
||||
pos
|
||||
}
|
||||
|
||||
fn read_string(&mut self) -> Result<String, Box<dyn Error>> {
|
||||
fn read_string(&mut self) -> Result<String> {
|
||||
let mut string = String::new();
|
||||
loop {
|
||||
let f = self.stack.pop();
|
||||
@@ -361,7 +374,7 @@ impl<I: Int> IP<I> {
|
||||
}
|
||||
}
|
||||
|
||||
fn get_info(&self, funge: &Funge<I>, n: I) -> Result<Vec<I>, Box<dyn Error>> {
|
||||
fn get_info(&self, funge: &Funge<I>, n: I) -> Result<Vec<I>> {
|
||||
let time = Local::now();
|
||||
match n.to_usize() {
|
||||
Some(n @ 1..=20) => {
|
||||
@@ -370,7 +383,7 @@ impl<I: Int> IP<I> {
|
||||
2 => vec![cast_int(8 * std::mem::size_of::<I>())?],
|
||||
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::<isize>(c)?;
|
||||
}
|
||||
vec![cast_int(f)?]
|
||||
@@ -410,8 +423,8 @@ impl<I: Int> IP<I> {
|
||||
r.push(I::zero());
|
||||
let file = &args[0];
|
||||
let path = Path::new(&file);
|
||||
let j: Vec<I> = path.file_name().ok_or("No file name.")?
|
||||
.to_str().ok_or("Cannot convert String.")?
|
||||
let j: Vec<I> = 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<I: Int> IP<I> {
|
||||
}
|
||||
})
|
||||
}
|
||||
_ => {
|
||||
// TODO: return Error
|
||||
println!("{}", "Stack size overflow");
|
||||
Ok(Vec::new())
|
||||
}
|
||||
_ => Err(Error::msg("Stack size overflow"))
|
||||
}
|
||||
}
|
||||
|
||||
fn not_implemented(&mut self, funge: &Funge<I>) {
|
||||
// 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<I>) -> Option<Self> {
|
||||
match funge.on_error {
|
||||
OnError::Reverse => {
|
||||
self.reverse();
|
||||
Some(self)
|
||||
}
|
||||
OnError::Quit => None
|
||||
}
|
||||
}
|
||||
|
||||
fn step(mut self, mut funge: Funge<I>, k: bool) -> Result<(Funge<I>, Option<Vec<Self>>), Box<dyn Error>> {
|
||||
fn step(mut self, mut funge: Funge<I>, k: bool) -> Result<(Funge<I>, Option<Vec<Self>>)> {
|
||||
let mut new_ips = Vec::new();
|
||||
let op = self.op(&funge);
|
||||
let op8 = op.to_u8();
|
||||
@@ -558,8 +571,8 @@ impl<I: Int> IP<I> {
|
||||
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<I: Int> IP<I> {
|
||||
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<char> = 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<I: Int> IP<I> {
|
||||
}
|
||||
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<I: Int> IP<I> {
|
||||
} 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<String> = Vec::new();
|
||||
for line in text {
|
||||
let a = format!("{}{}", line, join(&vec![" "; width - line.len()], ""));
|
||||
@@ -739,7 +752,10 @@ impl<I: Int> IP<I> {
|
||||
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<I: Int> IP<I> {
|
||||
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<I: Int> IP<I> {
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
struct DefaultHashMap<K: Eq + Hash, V: Clone> {
|
||||
pub struct DefaultHashMap<K: Eq + Hash, V: Clone> {
|
||||
hashmap: HashMap<K, V>,
|
||||
default: V
|
||||
}
|
||||
@@ -854,30 +876,42 @@ impl<K: Eq + Hash, V: Clone> Index<&K> for DefaultHashMap<K, V> {
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
enum OnError {
|
||||
Reverse,
|
||||
#[allow(dead_code)]
|
||||
Quit,
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Funge<I: Int> {
|
||||
extent: Vec<isize>,
|
||||
code: DefaultHashMap<Vec<isize>, I>,
|
||||
steps: isize,
|
||||
ips: Vec<IP<I>>,
|
||||
inputs: Input,
|
||||
output: Output,
|
||||
pub terminated: bool
|
||||
pub extent: Vec<isize>,
|
||||
pub code: DefaultHashMap<Vec<isize>, I>,
|
||||
pub steps: isize,
|
||||
pub ips: Vec<IP<I>>,
|
||||
pub input: IO,
|
||||
pub output: IO,
|
||||
pub terminated: bool,
|
||||
pub return_code: i32,
|
||||
on_error: OnError
|
||||
}
|
||||
|
||||
impl<I: Int> Funge<I> {
|
||||
pub fn new<T: ToString>(code: T) -> Result<Self, Box<dyn Error>> {
|
||||
pub fn new<T: ToString>(code: T) -> Result<Self> {
|
||||
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<String> = 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<I: Int> Funge<I> {
|
||||
Ok(new)
|
||||
}
|
||||
|
||||
pub fn from_file(file: &String) -> Result<Self, Box<dyn Error>> {
|
||||
pub fn from_file(file: &String) -> Result<Self> {
|
||||
Ok(Self::new(fs::read_to_string(file)?)?)
|
||||
}
|
||||
|
||||
pub fn with_inputs(mut self, inputs: Vec<String>) -> Result<Self, Box<dyn Error>> {
|
||||
self.inputs = Input { source: InputEnum::Vector(inputs) };
|
||||
Ok(self)
|
||||
pub fn with_arguments(mut self, args: Vec<String>) -> Self {
|
||||
self.input = IO::new().with_store(args);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn with_output(mut self) -> Result<Self, Box<dyn Error>> {
|
||||
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<String>, x0: isize, y0: isize) -> Result<(), Box<dyn Error>> {
|
||||
fn insert_code(&mut self, code: Vec<String>, 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<I: Int> Funge<I> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(mut self) -> Result<Self, Box<dyn Error>>{
|
||||
pub fn run(mut self) -> Result<Self>{
|
||||
while !self.terminated {
|
||||
self = self.step()?;
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
|
||||
pub fn step(mut self) -> Result<Self, Box<dyn Error>> {
|
||||
pub fn step(mut self) -> Result<Self> {
|
||||
if !self.terminated {
|
||||
self.ips.reverse();
|
||||
let mut new_ips = Vec::new();
|
||||
@@ -998,13 +1037,12 @@ impl<I: Int> Funge<I> {
|
||||
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());
|
||||
|
||||
22
src/main.rs
22
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<Option<f64>>,
|
||||
#[arg(help = "number of bits in cell and funge values", short, long)]
|
||||
bits: Option<u8>,
|
||||
#[arg(help = "skip steps", short, long)]
|
||||
steps: Option<usize>,
|
||||
#[arg(id = "arguments to the funge (& or ~)")]
|
||||
arguments: Vec<String>,
|
||||
}
|
||||
@@ -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<dyn Error>> {
|
||||
fn main() -> Result<()> {
|
||||
let args = Args::parse();
|
||||
if let None = args.bits {
|
||||
run!(args, isize);
|
||||
|
||||
Reference in New Issue
Block a user