First commit of working code.
This commit is contained in:
3
.idea/.gitignore
generated
vendored
Normal file
3
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
# Default ignored files
|
||||||
|
/shelf/
|
||||||
|
/workspace.xml
|
||||||
13
.idea/befunge.iml
generated
Normal file
13
.idea/befunge.iml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
</component>
|
||||||
|
</module>
|
||||||
8
.idea/modules.xml
generated
Normal file
8
.idea/modules.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectModuleManager">
|
||||||
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/.idea/befunge.iml" filepath="$PROJECT_DIR$/.idea/befunge.iml" />
|
||||||
|
</modules>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
12
Cargo.toml
Normal file
12
Cargo.toml
Normal file
@@ -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"
|
||||||
12
examples/99.bf
Executable file
12
examples/99.bf
Executable file
@@ -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< > ^
|
||||||
4
examples/cat_v.bf
Executable file
4
examples/cat_v.bf
Executable file
@@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
w4110'vin10
|
||||||
|
"<v"
|
||||||
|
>>#,:#<_@
|
||||||
3
examples/concurrent.bf
Executable file
3
examples/concurrent.bf
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
v
|
||||||
|
@000_1t111@
|
||||||
9
examples/dna.bf
Executable file
9
examples/dna.bf
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
7^DN>vA
|
||||||
|
v_#v? v
|
||||||
|
7^<""""
|
||||||
|
3 ACGT
|
||||||
|
90!""""
|
||||||
|
4*:>>>v
|
||||||
|
+8^-1,<
|
||||||
|
> ,+,@)
|
||||||
3
examples/ex1.bf
Executable file
3
examples/ex1.bf
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
v <
|
||||||
|
>?"/",^
|
||||||
|
>"\",^
|
||||||
8
examples/factorial.bf
Executable file
8
examples/factorial.bf
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
" ?tupni",v
|
||||||
|
v.:&,,,,,,<
|
||||||
|
>" = !",,,v
|
||||||
|
v1: <\0< ,<
|
||||||
|
> -:|
|
||||||
|
>* v$
|
||||||
|
|:\<<
|
||||||
|
>$.25*,@
|
||||||
10
examples/factorial0.bf
Executable file
10
examples/factorial0.bf
Executable file
@@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
v
|
||||||
|
&
|
||||||
|
>>:1v
|
||||||
|
|:-<
|
||||||
|
$
|
||||||
|
>v
|
||||||
|
\
|
||||||
|
*:
|
||||||
|
^_$.55+,@
|
||||||
3
examples/factorial_eso.bf
Executable file
3
examples/factorial_eso.bf
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
&>:1-:v v *_$.@
|
||||||
|
^ _$>\:^
|
||||||
3
examples/factorial_heap.bf
Executable file
3
examples/factorial_heap.bf
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
&:>00p1-::v
|
||||||
|
^ *g00 _g.25*,@
|
||||||
2
examples/fibonacci.bf
Executable file
2
examples/fibonacci.bf
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
j1\:b0p+:.' 1
|
||||||
25
examples/guess.bf
Executable file
25
examples/guess.bf
Executable file
@@ -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-*+^
|
||||||
16
examples/guess2.bf
Executable file
16
examples/guess2.bf
Executable file
@@ -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
|
||||||
2
examples/hello_b98.bf
Executable file
2
examples/hello_b98.bf
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
<>:#,_# @#"Hello, World!"
|
||||||
5
examples/hello_world.bf
Executable file
5
examples/hello_world.bf
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
> v
|
||||||
|
v ,,,,,"Hello"<
|
||||||
|
>48*, v
|
||||||
|
v,,,,,,"World!"<
|
||||||
|
>25*,@
|
||||||
3
examples/hello_world2.bf
Executable file
3
examples/hello_world2.bf
Executable file
@@ -0,0 +1,3 @@
|
|||||||
|
>25*"!dlrow ,olleH":v
|
||||||
|
v:,_@
|
||||||
|
> ^
|
||||||
9
examples/mill.bf
Executable file
9
examples/mill.bf
Executable file
@@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
] v
|
||||||
|
>v
|
||||||
|
v?t1?
|
||||||
|
1 t
|
||||||
|
t 1
|
||||||
|
?1t?v
|
||||||
|
v< <
|
||||||
|
>29*y.@
|
||||||
1
examples/multiplier.bf
Executable file
1
examples/multiplier.bf
Executable file
@@ -0,0 +1 @@
|
|||||||
|
&&*.25*,@
|
||||||
4
examples/pi.bf
Executable file
4
examples/pi.bf
Executable file
@@ -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*
|
||||||
2
examples/quine1.bf
Executable file
2
examples/quine1.bf
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
01->1# +# :# 0# g# ,# :# 5# 8# *# 4# +# -# _@
|
||||||
8
examples/random.bf
Executable file
8
examples/random.bf
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
v>>>>>v
|
||||||
|
12345
|
||||||
|
^?^
|
||||||
|
> ? ?^
|
||||||
|
v?v
|
||||||
|
6789
|
||||||
|
>>>> v
|
||||||
|
^ .<
|
||||||
6
examples/random_n.bf
Executable file
6
examples/random_n.bf
Executable file
@@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
& :v>00g2/.@
|
||||||
|
v00_^#!`/2g00:<
|
||||||
|
>0p:1>>:10p` !|
|
||||||
|
>+00p^?<*2g01:<
|
||||||
|
^ g00:<
|
||||||
5
examples/sieve.bf
Executable file
5
examples/sieve.bf
Executable file
@@ -0,0 +1,5 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
2>:3g" "-!v\ g30 <
|
||||||
|
|!`"O":+1_:.:03p>03g+:"O"`|
|
||||||
|
@ ^ p3\" ":<
|
||||||
|
2 234567890123456789012345678901234567890123456789012345678901234567890123456789
|
||||||
23
examples/soup.bf
Executable file
23
examples/soup.bf
Executable file
@@ -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 $4<v#<<v-*2a:: v7-1g<
|
||||||
|
#>70g>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’
|
||||||
|
*!@-*
|
||||||
|
=*!@-
|
||||||
|
-=*!@
|
||||||
|
@-=*!
|
||||||
2
examples/test.bf
Executable file
2
examples/test.bf
Executable file
@@ -0,0 +1,2 @@
|
|||||||
|
#!/usr/bin/env befunge
|
||||||
|
45*1-y>>#,:#<_$:w@
|
||||||
1
examples/v
Executable file
1
examples/v
Executable file
@@ -0,0 +1 @@
|
|||||||
|
Hello world!
|
||||||
139
src/debug.rs
Normal file
139
src/debug.rs
Normal file
@@ -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<Funge>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FungeMutex {
|
||||||
|
fn new(funge: Funge) -> 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> {
|
||||||
|
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<Mutex<FungeMutex>>
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FungeView {
|
||||||
|
pub (crate) fn new(funge: Funge) -> Result<Self, Box<dyn Error>> {
|
||||||
|
Ok(FungeView { funge: Arc::new(Mutex::new(FungeMutex::new(funge.with_output()?))) } )
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(&mut self) -> Result<bool, Box<dyn Error>> {
|
||||||
|
self.funge.lock().unwrap().step()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_mutex(&self) -> Self {
|
||||||
|
Self { funge: Arc::clone(&self.funge) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn debug(self, interval: Option<f64>) -> Result<(), Box<dyn Error>> {
|
||||||
|
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::<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))
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
943
src/lib.rs
Normal file
943
src/lib.rs
Normal file
@@ -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<T: ToString>(v: &Vec<T>, 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<isize, Box<dyn Error>> {
|
||||||
|
Ok(u32::try_from(c)?.try_into()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn chr(i: isize) -> Result<char, Box<dyn Error>> {
|
||||||
|
Ok(u32::try_from(i)?.try_into()?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add(a: &Vec<isize>, b: &Vec<isize>) -> Vec<isize> {
|
||||||
|
a.iter().zip(b.iter()).map(|(&a, &b)| a + b).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sub(a: &Vec<isize>, b: &Vec<isize>) -> Vec<isize> {
|
||||||
|
a.iter().zip(b.iter()).map(|(&a, &b)| a - b).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum InputEnum {
|
||||||
|
StdIn,
|
||||||
|
Vector(Vec<String>)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct Input {
|
||||||
|
source: InputEnum
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Input {
|
||||||
|
fn get(&mut self) -> Result<String, Box<dyn Error>> {
|
||||||
|
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<String>)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<isize>
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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<usize> for Stack {
|
||||||
|
type Output = isize;
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.stack[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct StackStack {
|
||||||
|
stackstack: Vec<Stack>
|
||||||
|
}
|
||||||
|
|
||||||
|
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<usize> for StackStack {
|
||||||
|
type Output = Stack;
|
||||||
|
|
||||||
|
fn index(&self, index: usize) -> &Self::Output {
|
||||||
|
&self.stackstack[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl IndexMut<usize> 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<isize>,
|
||||||
|
delta: Vec<isize>,
|
||||||
|
offset: Vec<isize>,
|
||||||
|
string: bool,
|
||||||
|
stack: StackStack,
|
||||||
|
fingerprint_ops: HashMap<isize, ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl IP {
|
||||||
|
fn new(funge: &Funge) -> Result<Self, Box<dyn Error>> {
|
||||||
|
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<isize>, 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<isize> {
|
||||||
|
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<String, Box<dyn Error>> {
|
||||||
|
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<Vec<isize>, Box<dyn Error>> {
|
||||||
|
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<isize> = 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<isize> = 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<isize> = key.chars().map(|i| ord(i).expect("")).collect();
|
||||||
|
r.extend(j);
|
||||||
|
r.push(ord('=')?);
|
||||||
|
let j: Vec<isize> = 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<Vec<Self>>), Box<dyn Error>> {
|
||||||
|
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<char> = 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<char> = 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<String> = 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<isize>,
|
||||||
|
code: HashMap<Vec<isize>, isize>,
|
||||||
|
steps: isize,
|
||||||
|
ips: Vec<IP>,
|
||||||
|
inputs: Input,
|
||||||
|
output: Output,
|
||||||
|
pub terminated: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Funge {
|
||||||
|
pub fn new<T: ToString>(code: T) -> Result<Self, Box<dyn Error>> {
|
||||||
|
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<String> = 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<Self, Box<dyn Error>> {
|
||||||
|
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_output(mut self) -> Result<Self, Box<dyn Error>> {
|
||||||
|
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<String>, x0: isize, y0: isize) -> Result<(), Box<dyn Error>> {
|
||||||
|
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<Self, Box<dyn Error>>{
|
||||||
|
while !self.terminated {
|
||||||
|
self = self.step()?;
|
||||||
|
}
|
||||||
|
Ok(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn step(mut self) -> Result<Self, Box<dyn Error>> {
|
||||||
|
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<Vec<isize>> {
|
||||||
|
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))
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/main.rs
Normal file
34
src/main.rs
Normal file
@@ -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<Option<f64>>,
|
||||||
|
#[arg(id = "arguments to the funge (& or ~)")]
|
||||||
|
arguments: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn main() -> Result<(), Box<dyn Error>> {
|
||||||
|
let args = Args::parse();
|
||||||
|
let mut funge = Funge::from_file(&args.input)?;
|
||||||
|
if args.arguments.len() > 0 {
|
||||||
|
funge = funge.with_inputs(args.arguments)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
match args.debug {
|
||||||
|
Some(interval) => FungeView::new(funge)?.debug(interval)?,
|
||||||
|
None => { funge.run()?; }
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user