diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..26d3352
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml
diff --git a/.idea/befunge.iml b/.idea/befunge.iml
new file mode 100644
index 0000000..b47e22d
--- /dev/null
+++ b/.idea/befunge.iml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..af4ac55
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
new file mode 100644
index 0000000..35eb1dd
--- /dev/null
+++ b/.idea/vcs.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..4c3e086
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "befunge"
+version = "2022.12.1"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+clap = { version = "4.0.29", features = ["derive"] }
+chrono = "0.4.23"
+cursive = { version = "0.20.0", features = ["termion-backend"] }
+rand = "0.8.5"
diff --git a/examples/99.bf b/examples/99.bf
new file mode 100755
index 0000000..153f7c7
--- /dev/null
+++ b/examples/99.bf
@@ -0,0 +1,12 @@
+#!/usr/bin/env befunge
+992+*: v: <
+v" ".:<_091+".reeb fo selttob erom on ,llaw eht no reeb fo selttob erom oN">:v
+,v"Go to the store and buy some more, 99 bottles of beer on the wall."+910<^,_v
+ >:#,_@>:101-*+v >$0" ,llaw eht no reeb fo ">:#,_$:.1v ^ $<
+>0\ >> ^ v_0"elttob">:#,_$\:!| >091+".reeb fo ">:#,_$ v
+ >0"selttob"^ >1-!| >" ",\v
+>$2\ ^\2<," ".< >091+:".llaw eht no reeb fo "v
+^_v > ^ |:< v!:<"Take one down and pass it around, "0< v!:<
+ !,v ^ ^:-1$_, ^ v$_>,^
+>^ ^ <
+^:<"no more "0< > ^
\ No newline at end of file
diff --git a/examples/cat_v.bf b/examples/cat_v.bf
new file mode 100755
index 0000000..fb2c8da
--- /dev/null
+++ b/examples/cat_v.bf
@@ -0,0 +1,4 @@
+#!/usr/bin/env befunge
+ w4110'vin10
+">#,:#<_@
\ No newline at end of file
diff --git a/examples/concurrent.bf b/examples/concurrent.bf
new file mode 100755
index 0000000..c2b5959
--- /dev/null
+++ b/examples/concurrent.bf
@@ -0,0 +1,3 @@
+#!/usr/bin/env befunge
+ v
+@000_1t111@
\ No newline at end of file
diff --git a/examples/dna.bf b/examples/dna.bf
new file mode 100755
index 0000000..60d2cca
--- /dev/null
+++ b/examples/dna.bf
@@ -0,0 +1,9 @@
+#!/usr/bin/env befunge
+7^DN>vA
+v_#v? v
+7^<""""
+3 ACGT
+90!""""
+4*:>>>v
++8^-1,<
+> ,+,@)
\ No newline at end of file
diff --git a/examples/ex1.bf b/examples/ex1.bf
new file mode 100755
index 0000000..c5568e2
--- /dev/null
+++ b/examples/ex1.bf
@@ -0,0 +1,3 @@
+ v <
+>?"/",^
+ >"\",^
diff --git a/examples/factorial.bf b/examples/factorial.bf
new file mode 100755
index 0000000..dc2ad20
--- /dev/null
+++ b/examples/factorial.bf
@@ -0,0 +1,8 @@
+" ?tupni",v
+v.:&,,,,,,<
+>" = !",,,v
+v1: <\0< ,<
+> -:|
+>* v$
+|:\<<
+>$.25*,@
\ No newline at end of file
diff --git a/examples/factorial0.bf b/examples/factorial0.bf
new file mode 100755
index 0000000..c5d1b17
--- /dev/null
+++ b/examples/factorial0.bf
@@ -0,0 +1,10 @@
+#!/usr/bin/env befunge
+v
+&
+>>:1v
+ |:-<
+ $
+>v
+ \
+*:
+^_$.55+,@
\ No newline at end of file
diff --git a/examples/factorial_eso.bf b/examples/factorial_eso.bf
new file mode 100755
index 0000000..35c4c04
--- /dev/null
+++ b/examples/factorial_eso.bf
@@ -0,0 +1,3 @@
+#!/usr/bin/env befunge
+&>:1-:v v *_$.@
+ ^ _$>\:^
\ No newline at end of file
diff --git a/examples/factorial_heap.bf b/examples/factorial_heap.bf
new file mode 100755
index 0000000..c85b5d4
--- /dev/null
+++ b/examples/factorial_heap.bf
@@ -0,0 +1,3 @@
+#!/usr/bin/env befunge
+&:>00p1-::v
+ ^ *g00 _g.25*,@
diff --git a/examples/fibonacci.bf b/examples/fibonacci.bf
new file mode 100755
index 0000000..6ddfb0d
--- /dev/null
+++ b/examples/fibonacci.bf
@@ -0,0 +1,2 @@
+#!/usr/bin/env befunge
+j1\:b0p+:.' 1
\ No newline at end of file
diff --git a/examples/guess.bf b/examples/guess.bf
new file mode 100755
index 0000000..9cd5771
--- /dev/null
+++ b/examples/guess.bf
@@ -0,0 +1,25 @@
+#!/usr/bin/env befunge
+vv < <
+ 2
+ ^ v<
+ v1>3v4
+ ^ ^
+> >?> ?>5^
+ v v
+ v9>7v6
+ v v<
+ 8
+ > > ^
+ vv < <
+ 2
+ ^ v<
+ v1>3v4
+ ^ ^
+ > >?> ?>5^
+ v v v ,*25 <<
+ v9>7v6 ,,
+ v v< ""
+ 8 ><
+ > > ^ ""v
+ >*: >0"!rebmun tupnI">:#,_$25*,:&:99p`|^< _0"!niw uoY">:#,_$25*,@
+ ^ < >:99g01-*+^
\ No newline at end of file
diff --git a/examples/guess2.bf b/examples/guess2.bf
new file mode 100755
index 0000000..9f138a2
--- /dev/null
+++ b/examples/guess2.bf
@@ -0,0 +1,16 @@
+#!/usr/bin/env befunge
+v>>> > v>>> > v
+ 012 3 012 3
+ ^?^ ^?^
+>>?#v?4>>?#v?4v
+ v?v v?v
+ 98765 98765
+ >>>>> ^>>>>> v
+v 0 + * + : 5 <
+>"!sseuG">:#,_v
+0v_v#:-&:,+:5$<
+, v>0"!niw uoY"
++0>:#,_$5:+,@
+:>`0\"!"\v
+ v"small"_"gib"
+^>" ooT">:#,_$5
\ No newline at end of file
diff --git a/examples/hello_b98.bf b/examples/hello_b98.bf
new file mode 100755
index 0000000..621d2ab
--- /dev/null
+++ b/examples/hello_b98.bf
@@ -0,0 +1,2 @@
+#!/usr/bin/env befunge
+<>:#,_# @#"Hello, World!"
\ No newline at end of file
diff --git a/examples/hello_world.bf b/examples/hello_world.bf
new file mode 100755
index 0000000..7ccf6d1
--- /dev/null
+++ b/examples/hello_world.bf
@@ -0,0 +1,5 @@
+> v
+v ,,,,,"Hello"<
+>48*, v
+v,,,,,,"World!"<
+>25*,@
\ No newline at end of file
diff --git a/examples/hello_world2.bf b/examples/hello_world2.bf
new file mode 100755
index 0000000..2c3185c
--- /dev/null
+++ b/examples/hello_world2.bf
@@ -0,0 +1,3 @@
+ >25*"!dlrow ,olleH":v
+ v:,_@
+ > ^
diff --git a/examples/mill.bf b/examples/mill.bf
new file mode 100755
index 0000000..d7d9b4a
--- /dev/null
+++ b/examples/mill.bf
@@ -0,0 +1,9 @@
+#!/usr/bin/env befunge
+ ] v
+ >v
+v?t1?
+ 1 t
+ t 1
+ ?1t?v
+v< <
+>29*y.@
\ No newline at end of file
diff --git a/examples/multiplier.bf b/examples/multiplier.bf
new file mode 100755
index 0000000..61112ca
--- /dev/null
+++ b/examples/multiplier.bf
@@ -0,0 +1 @@
+&&*.25*,@
\ No newline at end of file
diff --git a/examples/pi.bf b/examples/pi.bf
new file mode 100755
index 0000000..f7c90ba
--- /dev/null
+++ b/examples/pi.bf
@@ -0,0 +1,4 @@
+#!/usr/bin/env befunge
+"^a&EPm=kY}t/qYC+i9wHye$m N@~x+"v
+"|DsY<"-"z6n<[Yo2x|UP5VD:">:#v_@>
+-:19+/"0"+,19+%"0"+, ^ >39*
\ No newline at end of file
diff --git a/examples/quine1.bf b/examples/quine1.bf
new file mode 100755
index 0000000..724c8b1
--- /dev/null
+++ b/examples/quine1.bf
@@ -0,0 +1,2 @@
+#!/usr/bin/env befunge
+01->1# +# :# 0# g# ,# :# 5# 8# *# 4# +# -# _@
\ No newline at end of file
diff --git a/examples/random.bf b/examples/random.bf
new file mode 100755
index 0000000..4ea14af
--- /dev/null
+++ b/examples/random.bf
@@ -0,0 +1,8 @@
+ v>>>>>v
+ 12345
+ ^?^
+ > ? ?^
+ v?v
+ 6789
+ >>>> v
+ ^ .<
diff --git a/examples/random_n.bf b/examples/random_n.bf
new file mode 100755
index 0000000..bab7643
--- /dev/null
+++ b/examples/random_n.bf
@@ -0,0 +1,6 @@
+#!/usr/bin/env befunge
+& :v>00g2/.@
+v00_^#!`/2g00:<
+>0p:1>>:10p` !|
+>+00p^?<*2g01:<
+^ g00:<
\ No newline at end of file
diff --git a/examples/sieve.bf b/examples/sieve.bf
new file mode 100755
index 0000000..30d8810
--- /dev/null
+++ b/examples/sieve.bf
@@ -0,0 +1,5 @@
+#!/usr/bin/env befunge
+2>:3g" "-!v\ g30 <
+ |!`"O":+1_:.:03p>03g+:"O"`|
+ @ ^ p3\" ":<
+2 234567890123456789012345678901234567890123456789012345678901234567890123456789
\ No newline at end of file
diff --git a/examples/soup.bf b/examples/soup.bf
new file mode 100755
index 0000000..abfe598
--- /dev/null
+++ b/examples/soup.bf
@@ -0,0 +1,23 @@
+#!/usr/bin/env befunge
+ 060p070 p'O80v
+ pb2*90p4$4> $4$>v>
+ v4$>4$>4$>4$># ARGH>!
+ <{[BEFUNGE_97]}> FUNGE!
+ ##:-:## #####* 4$*>4$ >060p> 60g80g -!#v_ 60g1+ 60p60v
+ #vOOGAH **>4$>^!!eg nufeB^ $4$4$4 $470g>90g-! #@_^Befunge!! 123456 123456 VvVv!#!>Weird! >0ggv*
+ ^$4$4p07+1g07 ,a<$4< <$4$4< <$4$4< <$4$4< <<#<*-=-=-=-=-* -=-=v*
+ ::48*-#v_>,4$> 4$4$4 $4$4$ 4$4$4$ 4$4$4$ 4$^*!* XXXXXX XXX>
+ BOINK>$60g1-7 0g+d2* %'A+,1 $1$1$1 $1$1$1 $>^<$ HAR!!! 8888
+ Befunge_is such_a pretty langua ge,_is n't_i t?_It_ 8888
+ looks_so much_l ike_li ne_noi se_and it's_ STILL_ ā88ā
+Turing- Complet e!_Cam ouflag e_your code!! Confu se_the
+hell_out of_every one_re ading_ your_co de._Oh, AND_y ou.:-) ,o88o.
+ Once_this_thing_i s_code d,_rea ding_it_back_ver ges_on the_imp 888888
+ ossible._Obfusc ate_the_obfus cated!_Befunge_ debuggers_are__ 888888
+ your_friends! By:_Alexios Chouchou las... X-X-X-X-X-X-X! 888888
+ -=*##*=- \*****/ 9797* -=97=- !@-*= ***** ā"88Pā
+ *!@-*
+ =*!@-
+ -=*!@
+ @-=*!
\ No newline at end of file
diff --git a/examples/test.bf b/examples/test.bf
new file mode 100755
index 0000000..5c61223
--- /dev/null
+++ b/examples/test.bf
@@ -0,0 +1,2 @@
+#!/usr/bin/env befunge
+45*1-y>>#,:#<_$:w@
\ No newline at end of file
diff --git a/examples/v b/examples/v
new file mode 100755
index 0000000..6769dd6
--- /dev/null
+++ b/examples/v
@@ -0,0 +1 @@
+Hello world!
\ No newline at end of file
diff --git a/src/debug.rs b/src/debug.rs
new file mode 100644
index 0000000..4b9499b
--- /dev/null
+++ b/src/debug.rs
@@ -0,0 +1,139 @@
+use std::cmp::{min, max};
+use std::error::Error;
+use std::fmt::{Display, Formatter};
+use std::sync::{Arc, Mutex};
+use std::thread::{sleep, spawn};
+use std::time::Duration;
+use cursive::{Cursive, CursiveExt, Vec2};
+use cursive::view::View;
+use cursive::Printer;
+use cursive::event::{Event, EventResult};
+use cursive::theme::ColorStyle;
+use crate::Funge;
+
+
+struct FungeMutex {
+ funge: Option
+}
+
+impl FungeMutex {
+ 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()?;
+ }
+ let terminated = funge.terminated;
+ self.funge = Some(funge);
+ Ok(terminated)
+ }
+
+ fn funge_ref(&self) -> Option<&Funge> {
+ self.funge.as_ref()
+ }
+}
+
+impl Display for FungeMutex {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.funge.as_ref().expect("No funge found"))
+ }
+}
+
+
+pub(crate) struct FungeView {
+ funge: Arc>
+}
+
+impl FungeView {
+ pub (crate) fn new(funge: Funge) -> Result> {
+ Ok(FungeView { funge: Arc::new(Mutex::new(FungeMutex::new(funge.with_output()?))) } )
+ }
+
+ fn step(&mut self) -> Result> {
+ self.funge.lock().unwrap().step()
+ }
+
+ fn get_mutex(&self) -> Self {
+ Self { funge: Arc::clone(&self.funge) }
+ }
+
+ pub(crate) fn debug(self, interval: Option) -> Result<(), Box> {
+ 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
+ }
+ }
+ });
+ }
+ }
+ app.add_layer(self);
+ app.add_global_callback('q', |app| app.quit());
+ 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()));
+ }
+ }
+ 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))
+ }
+
+ fn on_event(&mut self, event: Event) -> EventResult {
+ match event {
+ Event::Char('q') => EventResult::Ignored,
+ Event::Char(_) => {
+ self.step().ok();
+ EventResult::Consumed(None)
+ }
+ Event::Key(_) => {
+ self.step().ok();
+ EventResult::Consumed(None)
+ }
+ _ => EventResult::Ignored
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..3c730bd
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,943 @@
+use std::collections::HashMap;
+use std::{fmt, fs};
+use std::fmt::{Debug, Display, Formatter};
+use std::io::stdin;
+use std::ops::{Index, IndexMut};
+use std::error::Error;
+use std::path::Path;
+use chrono::{Datelike, Timelike};
+use rand::Rng;
+use chrono::offset::Local;
+
+
+const VERSION: &str = env!("CARGO_PKG_VERSION");
+
+
+fn join(v: &Vec, s: &str) -> String {
+ let mut string = String::new();
+ if v.len() > 1 {
+ for i in 0..v.len() - 1 {
+ string.push_str(&v[i].to_string());
+ string.push_str(&s);
+ }
+ }
+ if v.len() > 0 {
+ string.push_str(&v[v.len() - 1].to_string());
+ }
+ string
+}
+
+fn ord(c: char) -> Result> {
+ Ok(u32::try_from(c)?.try_into()?)
+}
+
+fn chr(i: isize) -> Result> {
+ Ok(u32::try_from(i)?.try_into()?)
+}
+
+fn add(a: &Vec, b: &Vec) -> Vec {
+ a.iter().zip(b.iter()).map(|(&a, &b)| a + b).collect()
+}
+
+fn sub(a: &Vec, b: &Vec) -> Vec {
+ a.iter().zip(b.iter()).map(|(&a, &b)| a - b).collect()
+}
+
+
+#[derive(Clone)]
+enum InputEnum {
+ StdIn,
+ Vector(Vec)
+}
+
+#[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
+ }
+ 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 => println!("{}", string),
+ OutputEnum::Vector(ref mut v) => v.push(string)
+ }
+ }
+}
+
+
+#[derive(Clone)]
+struct Stack {
+ stack: Vec
+}
+
+
+impl Stack {
+ fn new() -> Self {
+ Self { stack: Vec::new() }
+ }
+
+ fn pop(&mut self) -> isize {
+ match self.stack.pop() {
+ Some(value) => { value }
+ None => { 0 }
+ }
+ }
+
+ fn push(&mut self, cell: isize) {
+ self.stack.push(cell)
+ }
+
+ fn len(&self) -> usize { self.stack.len() }
+}
+
+impl Display for Stack {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "[{}]", join(&self.stack, ", "))
+ }
+}
+
+impl Index for Stack {
+ type Output = isize;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self.stack[index]
+ }
+}
+
+#[derive(Clone)]
+struct StackStack {
+ stackstack: Vec
+}
+
+impl StackStack {
+ fn new() -> Self {
+ Self { stackstack: vec![Stack::new()] }
+ }
+
+ fn check_stack(&mut self) {
+ if self.is_empty() {
+ self.push_stack(Stack::new());
+ }
+ }
+
+ fn pop(&mut self) -> isize {
+ self.check_stack();
+ let x = self.len_stack();
+ self.stackstack[x - 1].pop()
+ }
+
+ fn push(&mut self, cell: isize) {
+ self.check_stack();
+ let x = self.len_stack();
+ self.stackstack[x - 1].push(cell);
+ }
+
+ fn pop_stack(&mut self) -> Stack {
+ match self.stackstack.pop() {
+ Some(stack) => { stack }
+ None => { Stack::new() }
+ }
+ }
+
+ fn push_stack(&mut self, stack: Stack) {
+ self.stackstack.push(stack)
+ }
+
+ fn len_stack(&self) -> usize { self.stackstack.len() }
+
+ fn len(&self) -> usize {
+ if self.len_stack() == 0 {
+ 0
+ } else {
+ self.stackstack[self.len_stack() - 1].len()
+ }
+ }
+
+ fn is_empty(&self) -> bool {
+ self.stackstack.is_empty()
+ }
+
+ fn clear(&mut self) {
+ let l = self.len_stack();
+ if l > 0 {
+ self.pop_stack();
+ }
+ self.push_stack(Stack::new());
+ }
+}
+
+impl Index for StackStack {
+ type Output = Stack;
+
+ fn index(&self, index: usize) -> &Self::Output {
+ &self.stackstack[index]
+ }
+}
+
+impl IndexMut for StackStack {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ &mut self.stackstack[index]
+ }
+}
+
+impl Display for StackStack {
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ write!(f, "{}", join(&self.stackstack, "\n"))
+ }
+}
+
+
+#[derive(Clone)]
+struct IP {
+ id: usize,
+ position: Vec,
+ delta: Vec,
+ offset: Vec,
+ string: bool,
+ stack: StackStack,
+ fingerprint_ops: HashMap,
+}
+
+
+impl IP {
+ fn new(funge: &Funge) -> Result> {
+ let mut new = IP {
+ id: funge.ips.len(),
+ position: vec![0, 0],
+ delta: vec![1, 0],
+ offset: vec![0, 0],
+ string: false,
+ stack: StackStack::new(),
+ fingerprint_ops: HashMap::new()
+ };
+ if (new.op(funge) == ord(' ')?) | (new.op(funge) == ord(';')?) {
+ new.advance(funge);
+ };
+ Ok(new)
+ }
+
+ fn copy(&self, funge: &Funge) -> Self {
+ Self {
+ id: funge.ips.len(),
+ position: self.position.to_owned(),
+ delta: self.delta.to_owned(),
+ offset: self.offset.to_owned(),
+ string: self.string,
+ stack: self.stack.to_owned(),
+ fingerprint_ops: self.fingerprint_ops.to_owned()
+ }
+ }
+
+ fn op(&self, funge: &Funge) -> isize {
+ if funge.code.contains_key(&self.position) {
+ funge.code[&self.position]
+ } else {
+ 32
+ }
+ }
+
+ fn reverse(&mut self) {
+ self.delta = self.delta.iter().map(|i| -i).collect();
+ }
+
+ fn turn_right(&mut self) {
+ self.delta = vec![-self.delta[1], self.delta[0]];
+ }
+
+ fn turn_left(&mut self) {
+ self.delta = vec![self.delta[1], -self.delta[0]];
+ }
+
+ fn advance(&mut self, funge: &Funge) {
+ if self.string {
+ if funge.code[&self.position] == 32 {
+ while funge.code[&self.position] == 32 {
+ self.movep(funge)
+ }
+ } else {
+ self.movep(funge)
+ }
+ } else {
+ loop {
+ if self.op(funge) != 59 {
+ self.movep(funge)
+ }
+ if self.op(funge) == 59 {
+ self.movep(funge);
+ while self.op(funge) == 59 {
+ self.movep(funge)
+ }
+ self.movep(funge)
+ }
+ while self.op(funge) == 32 {
+ self.movep(funge)
+ }
+ if self.op(funge) != 59 {
+ break;
+ }
+ }
+ }
+ }
+
+ fn movep(&mut self, funge: &Funge) {
+ self.position = self.next_pos(funge);
+ }
+
+ fn check_pos(&self, pos: &Vec, funge: &Funge) -> bool {
+ (funge.extent[0] <= pos[0]) & (pos[0] < funge.extent[1]) &
+ (funge.extent[2] <= pos[1]) & (pos[1] < funge.extent[3])
+ }
+
+ fn next_pos(&self, funge: &Funge) -> Vec {
+ let mut pos= add(&self.position, &self.delta);
+ if !self.check_pos(&pos, funge) {
+ loop {
+ pos = sub(&pos, &self.delta);
+ if !self.check_pos(&pos, funge) {
+ pos = add(&pos, &self.delta);
+ break
+ }
+ }
+ }
+ pos
+ }
+
+ fn read_string(&mut self) -> Result> {
+ let mut string = String::new();
+ loop {
+ let f = self.stack.pop();
+ if f == 0 {
+ return Ok(string)
+ } else {
+ string.push_str(&chr(f)?.to_string())
+ }
+ }
+ }
+
+ fn get_info(&self, funge: &Funge, n: isize) -> Result, Box> {
+ let time = Local::now();
+ Ok(match n {
+ 1 => { vec![15] }
+ 2 => { vec![isize::BITS as isize] }
+ 3 => {
+ let mut f = 0;
+ for (i, c) in "wpfunge".chars().enumerate() {
+ f += (256 as isize).pow(i as u32) * ord(c)?;
+ }
+ vec![f]
+ }
+ 4 => { vec![VERSION.replace(".", "").parse()?] }
+ 5 => { vec![1] }
+ 6 => { vec![ord(std::path::MAIN_SEPARATOR)?] }
+ 7 => { vec![2] }
+ 8 => { vec![self.id as isize] }
+ 9 => { vec![0] }
+ 10 => { self.position.to_owned() }
+ 11 => { self.delta.to_owned() }
+ 12 => { self.offset.to_owned() }
+ 13 => { funge.extent.chunks(2).map(|i| i[0]).collect() }
+ 14 => { funge.extent.chunks(2).map(|i| i[1]).collect() }
+ 15 => { vec![((time.year() as isize) - 1900) * 256 * 256 + (time.month() as isize) * 256 + (time.day() as isize)] }
+ 16 => { vec![(time.hour() as isize) * 256 * 256 + (time.minute() as isize) * 256 + (time.second() as isize)] }
+ 17 => { vec![self.stack.len_stack() as isize] }
+ 18 => {
+ let mut l = Vec::new();
+ for stack in &self.stack.stackstack {
+ l.push(stack.len() as isize);
+ }
+ l.reverse();
+ l
+ }
+ 19 => {
+ let mut r = Vec::new();
+ let mut args = std::env::args();
+ if args.len() > 2 {
+ for i in 2..args.len() {
+ let j: Vec = args.nth(i).expect("We checked the length.")
+ .chars().map(|i| ord(i).expect("")).collect();
+ r.extend(j);
+ r.push(0);
+ }
+ }
+ r.push(0);
+ let file = args.nth(1).expect("We checked the length.");
+ let path = Path::new(&file);
+ let j: Vec = path.file_name().ok_or("No file name.")?
+ .to_str().ok_or("Cannot convert String.")?
+ .chars().map(|i| ord(i).expect("")).collect();
+ r.extend(j);
+ r.push(0);
+ r.push(0);
+ r.reverse();
+ r
+ }
+ 20 => {
+ let mut r = Vec::new();
+ let vars = std::env::vars();
+ for (key, value) in vars {
+ let j: Vec = key.chars().map(|i| ord(i).expect("")).collect();
+ r.extend(j);
+ r.push(ord('=')?);
+ let j: Vec = value.chars().map(|i| ord(i).expect("")).collect();
+ r.extend(j);
+ r.push(0);
+ }
+ r.push(0);
+ r.reverse();
+ r
+ }
+ i => {
+ let j = i as usize - 20;
+ let l = self.stack.len();
+ if l >= j {
+ vec![self.stack.stackstack[0][l - j]]
+ } else {
+ vec![0]
+ }
+ }
+ })
+ }
+
+ fn not_implemented(&mut self, funge: &Funge) {
+ println!("operator {} at {} not implemented", self.op(funge), join(&self.position, ", "));
+ self.reverse()
+ }
+
+ fn step(mut self, mut funge: Funge, k: bool) -> Result<(Funge, Option>), Box> {
+ let mut new_ips = Vec::new();
+ if self.string {
+ match self.op(&funge) {
+ 34 => { self.string = false } // '
+ s => { self.stack.push(s) }
+ }
+ } else if self.fingerprint_ops.contains_key(&self.op(&funge)) {
+ // self.fingerprint_ops[self.op(funge)]?
+ } else if (0 <= self.op(&funge)) & (self.op(&funge) < 255) {
+ match self.op(&funge) {
+ 43 => { // +
+ let b = self.stack.pop();
+ let a = self.stack.pop();
+ self.stack.push(a + b);
+ }
+ 45 => { // -
+ let b = self.stack.pop();
+ let a = self.stack.pop();
+ self.stack.push(a - b);
+ }
+ 42 => { // *
+ let b = self.stack.pop();
+ let a = self.stack.pop();
+ self.stack.push(a * b);
+ }
+ 47 => { // /
+ let b = self.stack.pop();
+ let a = self.stack.pop();
+ self.stack.push(a / b);
+ }
+ 37 => { // %
+ let b = self.stack.pop();
+ let a = self.stack.pop();
+ self.stack.push(a % b);
+ }
+ 33 => { // !
+ let a = self.stack.pop();
+ self.stack.push(!a as isize);
+ }
+ 96 => { // `
+ let b = self.stack.pop();
+ let a = self.stack.pop();
+ self.stack.push((a > b) as isize);
+ }
+ 62 => self.delta = vec![1, 0], // >
+ 60 => self.delta = vec![-1, 0], // <
+ 94 => self.delta = vec![0, -1], // ^
+ 118 => self.delta = vec![0, 1], // v
+ 63 => { // ?
+ let mut rng = rand::thread_rng();
+ self.delta = match rng.gen_range(0..4) {
+ 0 => { vec![-1, 0] }
+ 1 => { vec![1, 0] }
+ 2 => { vec![0, -1] }
+ _ => { vec![0, 1] }
+ };
+ }
+ 95 => { // _
+ if self.stack.pop() == 0 {
+ self.delta = vec![1, 0]
+ } else {
+ self.delta = vec![-1, 0]
+ }
+ }
+ 124 => { // |
+ if self.stack.pop() == 0 {
+ self.delta = vec![0, 1];
+ } else {
+ self.delta = vec![0, -1];
+ }
+ }
+ 34 => self.string = true, // "
+ 58 => { // :
+ let v = self.stack.pop();
+ self.stack.push(v);
+ self.stack.push(v);
+ }
+ 92 => { // \
+ let a = self.stack.pop();
+ let b = self.stack.pop();
+ self.stack.push(a);
+ 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())?)), // ,
+ 35 => self.movep(&funge), // #
+ 112 => { // p
+ let y = self.stack.pop();
+ let x = self.stack.pop();
+ let v = self.stack.pop();
+ funge.insert(v, x + self.offset[0], y + self.offset[1]);
+ }
+ 103 => { // g
+ let y = self.stack.pop();
+ let x = self.stack.pop();
+ self.stack.push(funge.code[&vec![x + self.offset[0], y + self.offset[1]]]);
+ }
+ 38 => { // &
+ let s = funge.inputs.get()?;
+ let i: Vec = s.chars()
+ .skip_while(|i| !i.is_digit(10))
+ .take_while(|i| i.is_digit(10)).collect();
+ self.stack.push(join(&i, "").parse()?);
+ }
+ 126 => { // ~
+ let s = funge.inputs.get()?;
+ self.stack.push(ord(s.chars().nth(0).ok_or("No valid input.")?)?);
+ }
+ 64 => { return Ok((funge, Some(Vec::new()))); } // @
+ 32 => { // space
+ self.advance(&funge);
+ return self.step(funge, false);
+ }
+ // 98 from here
+ 91 => self.turn_left(), // [
+ 93 => self.turn_right(), // ]
+ 39 => { // '
+ self.movep(&funge);
+ self.stack.push(self.op(&funge));
+ }
+ 123 => { // {
+ let n = self.stack.pop();
+ let cells = if n > 0 {
+ let mut cells = Vec::new();
+ for _ in 0..n {
+ cells.push(self.stack.pop());
+ }
+ cells.reverse();
+ cells
+ } else {
+ vec![0; -n as usize]
+ };
+ for coordinate in &self.offset {
+ self.stack.push(*coordinate);
+ }
+ self.stack.push_stack(Stack::new());
+ for cell in cells {
+ self.stack.push(cell);
+ }
+ self.offset = self.next_pos(&funge);
+ }
+ 125 => { // }
+ let n = self.stack.pop();
+ let cells = if n > 0 {
+ let mut cells = Vec::new();
+ for _ in 0..n {
+ cells.push(self.stack.pop());
+ }
+ cells.reverse();
+ cells
+ } else {
+ vec![0; -n as usize]
+ };
+ self.stack.pop_stack();
+ let y = self.stack.pop();
+ let x = self.stack.pop();
+ self.offset = vec![x, y];
+ for cell in cells {
+ self.stack.push(cell);
+ }
+ }
+ 61 => { // =
+ self.reverse();
+ // self.stack.push(syscall(self.read_string()));
+ }
+ 40 => { // ( no fingerprints are implemented
+ // self.read_fingerprint();
+ // self.fingerprint_ops[] = self.reverse;
+ self.reverse();
+ }
+ 41 => { // )
+ // self.read_fingerprint()
+ // self.fingerprint_ops.pop()
+ self.reverse();
+ }
+ 105 => { // i
+ let file = self.read_string()?;
+ let flags = self.stack.pop();
+ let y0 = self.stack.pop();
+ let x0 = self.stack.pop();
+ let text = fs::read_to_string(file)?;
+ let (width, height) = if flags % 2 != 0 {
+ let code: Vec = text.chars().collect();
+ funge.insert_code(vec![join(&code, "")], x0, y0)?;
+ (text.len(), 1)
+ } 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 mut code: Vec = Vec::new();
+ for line in text {
+ let a = format!("{}{}", line, join(&vec![" "; width - line.len()], ""));
+ code.push(a);
+ }
+ funge.insert_code(code, x0, y0)?;
+ (width, height)
+ };
+ self.stack.push(x0);
+ self.stack.push(y0);
+ self.stack.push(width as isize);
+ self.stack.push(height as isize);
+ }
+ 106 => { // j
+ for _ in 0..self.stack.pop() {
+ self.movep(&funge);
+ }
+ }
+ 107 => { // k
+ self.advance(&funge);
+ let n = self.stack.pop();
+ let mut ips = vec![self];
+ for _ in 0..n {
+ let mut new_ips = Vec::new();
+ for ip in ips {
+ funge = match ip.step(funge, true)? {
+ (f, None) => { return Ok((f, None)) }
+ (f, Some(ips)) => {
+ new_ips.extend(ips);
+ f
+ }
+ }
+ }
+ ips = new_ips;
+ }
+ return Ok((funge, Some(ips)))
+ }
+ 110 => self.stack.clear(), // n
+ 111 => { // o
+ let file = self.read_string()?;
+ let flags = self.stack.pop();
+ let x0 = self.stack.pop();
+ let y0 = self.stack.pop();
+ let width = self.stack.pop();
+ let height = self.stack.pop();
+ let mut text = Vec::new();
+ if flags % 2 != 0 {
+ for x in x0..x0 + width {
+ let mut line = String::new();
+ for y in y0..y0 + height {
+ line.push(chr(funge.code[&vec![x, y]])?);
+ }
+ line.rsplit(' ');
+ text.push(line);
+ }
+ } else {
+ for x in x0..x0 + width {
+ let mut line = String::new();
+ for y in y0..y0 + height {
+ line.push(chr(funge.code[&vec![x, y]])?);
+ }
+ text.push(line);
+ }
+ }
+ let text = join(&text, "\n");
+ fs::write(file, text)?;
+ }
+ 113 => { return Ok((funge, None)) } // q
+ 114 => self.reverse(), // r
+ 115 => { // s
+ self.movep(&funge);
+ funge.insert(self.stack.pop(), self.position[0], self.position[1]);
+ }
+ 116 => { // t
+ let mut new = self.copy(&funge);
+ new.reverse();
+ new_ips.push(new);
+ }
+ 117 => { // u
+ if self.stack.is_empty() {
+ self.reverse();
+ } else {
+ let n = self.stack.pop();
+ let l = self.stack.len_stack();
+ if n > 0 {
+ for _ in 0..n {
+ let a = self.stack[l - 2].pop();
+ self.stack.push(a);
+ }
+ } else if n < 0 {
+ for _ in 0..-n {
+ let a = self.stack.pop();
+ self.stack[l - 2].push(a);
+ }
+ }
+ }
+ }
+ 119 => { // w
+ let b = self.stack.pop();
+ let a = self.stack.pop();
+ if a < b {
+ self.turn_left();
+ } else if a > b {
+ self.turn_right();
+ }
+ }
+ 120 => { // x
+ let dy = self.stack.pop();
+ let dx = self.stack.pop();
+ self.delta = vec![dx, dy];
+ }
+ 121 => { // y
+ let n = self.stack.pop();
+ if n <= 0 {
+ for j in 1..21 {
+ for i in self.get_info(&funge,j) {
+ for cell in i {
+ self.stack.push(cell);
+ }
+ }
+ }
+ } else {
+ for i in self.get_info(&funge, n) {
+ for cell in i {
+ self.stack.push(cell);
+ }
+ }
+ }
+ }
+ 122 => { } // z
+ d => {
+ if (48 <= d) & (d <= 57) { // 0123456789
+ self.stack.push(d - 48);
+ } else if (97 <= d) & (d <= 102) {
+ self.stack.push(d - 87);
+ } else {
+ self.not_implemented(&funge);
+ }
+ }
+ }
+ } else {
+ self.not_implemented(&funge);
+ }
+ if !k {
+ self.advance(&funge);
+ }
+ let mut ips = vec![self];
+ ips.extend(new_ips);
+ Ok((funge, Some(ips)))
+ }
+}
+
+#[derive(Clone)]
+pub struct Funge {
+ extent: Vec,
+ code: HashMap, isize>,
+ steps: isize,
+ ips: Vec,
+ inputs: Input,
+ output: Output,
+ pub terminated: bool
+}
+
+impl Funge {
+ pub fn new(code: T) -> Result> {
+ let mut new = Self {
+ extent: vec![0, 0, 0, 0],
+ code: HashMap::new(),
+ steps: 0,
+ ips: Vec::new(),
+ inputs: Input { source: InputEnum::StdIn },
+ output: Output { sink: OutputEnum::StdOut },
+ terminated: false
+ };
+ let mut code: Vec = code.to_string().lines().map(|i| String::from(i)).collect();
+ if code[0].starts_with(r"#!/usr/bin/env befunge") | code[0].starts_with(r"#!/usr/bin/env -S befunge") {
+ code.remove(0);
+ }
+ new.insert_code(code, 0, 0)?;
+ new.ips.push(IP::new(&new)?);
+ Ok(new)
+ }
+
+ 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_output(mut self) -> Result> {
+ self.output = Output { sink: OutputEnum::Vector(Vec::new()) };
+ Ok(self)
+ }
+
+ fn insert(&mut self, i: isize, x: isize, y: isize) {
+ self.code.insert(vec![x, y], i);
+ }
+
+ fn insert_code(&mut self, code: Vec, x0: isize, y0: isize) -> Result<(), Box> {
+ for (y, line) in code.iter().enumerate() {
+ for (x, char) in line.chars().enumerate() {
+ let x1: isize = x.try_into()?;
+ let y1: isize = y.try_into()?;
+ let position = vec![x0 + x1, y0 + y1];
+ if position[0] < self.extent[0] {
+ self.extent[0] = position[0];
+ } else if position[0] >= self.extent[1] {
+ self.extent[1] = position[0] + 1;
+ }
+ if position[1] < self.extent[2] {
+ self.extent[2] = position[1];
+ } else if position[1] >= self.extent[3] {
+ self.extent[3] = position[1] + 1;
+ }
+ self.code.insert(position, ord(char)?);
+ }
+ }
+ Ok(())
+ }
+
+ pub fn run(mut self) -> Result>{
+ while !self.terminated {
+ self = self.step()?;
+ }
+ Ok(self)
+ }
+
+ pub fn step(mut self) -> Result> {
+ if !self.terminated {
+ self.ips.reverse();
+ let mut new_ips = Vec::new();
+ for _ in 0..self.ips.len() {
+ let ip = self.ips.pop().expect("");
+ self = match ip.step(self, false)? {
+ (f, Some(ips)) => {
+ new_ips.extend(ips);
+ f
+ }
+ (mut f, None) => {
+ f.terminated = true;
+ return Ok(f)
+ }
+ }
+ }
+ self.ips.extend(new_ips);
+ self.steps += 1;
+ if self.ips.len() == 0 {
+ self.terminated = true;
+ }
+ }
+ Ok(self)
+ }
+
+ pub fn ips_pos(&self) -> Vec> {
+ let mut pos = Vec::new();
+ for ip in self.ips.iter() {
+ pos.push(ip.position.to_owned());
+ }
+ pos
+ }
+
+ fn to_string(&self, show_ips: bool) -> String {
+ let mut lines = Vec::new();
+ for (key, value) in (&self.code).into_iter() {
+ let x= key[0] as usize;
+ let y= key[1] as usize;
+ while lines.len() <= y {
+ lines.push(Vec::new());
+ }
+ while lines[y].len() <= x {
+ lines[y].push(String::from(" "));
+ }
+ if ((32 <= *value) & (*value <= 126)) | ((161 <= *value) & (*value <= 255)) {
+ lines[y][x] = chr(*value).unwrap().to_string();
+ } else {
+ lines[y][x] = chr(164).unwrap().to_string();
+ }
+ }
+
+ if show_ips {
+ for ip in &self.ips {
+ let x = ip.position[0] as usize;
+ let y = ip.position[1] as usize;
+ lines[y][x] = format!("\x1b[37m\x1b[40m{}\u{001b}[0m", lines[y][x]);
+ }
+ }
+
+ let mut string = String::from("grid:\n");
+ string.push_str(&join(&lines.iter().map(|i| join(&i, "")).collect(), "\n"));
+ string.push_str("\n\nstacks:\n");
+ for ip in &self.ips {
+ string.push_str(&ip.stack.to_string());
+ }
+
+ match &self.output.sink {
+ OutputEnum::StdOut => { },
+ OutputEnum::Vector(v) => {
+ string.push_str("\n\nOutput:\n");
+ string.push_str(&*join(&v, ""));
+ }
+ };
+
+ string.push_str("\n\nsteps:\n");
+ string.push_str(&self.steps.to_string());
+ string
+ }
+}
+
+impl Display for Funge {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.to_string(false))
+ }
+}
+
+impl Debug for Funge {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ write!(f, "{}", self.to_string(true))
+ }
+}
\ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..1a8d054
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,34 @@
+mod debug;
+
+use std::error::Error;
+use clap::Parser;
+use befunge::Funge;
+use debug::FungeView;
+
+
+#[derive(Parser)]
+#[command(version)]
+struct Args {
+ #[arg(id = "funge code file")]
+ input: String,
+ #[arg(help = "debug, step on key press or steps / second",
+ short, long, value_name = "interval", num_args = 0..=1)]
+ debug: Option