Skip to content

Instantly share code, notes, and snippets.

@f0rki
Last active May 31, 2018 02:23
Show Gist options
  • Save f0rki/8fb98f404b6a28807eb9edd024eccc5a to your computer and use it in GitHub Desktop.
Save f0rki/8fb98f404b6a28807eb9edd024eccc5a to your computer and use it in GitHub Desktop.
Interpreter for the TapeBagel esoteric language
/*
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
* <contact@f0rki.at> wrote this file. As long as you retain this notice you
* can do whatever you want with this stuff. If we meet some day, and you think
* this stuff is worth it, you can buy me a beer in return.
* ----------------------------------------------------------------------------
*/
/// Interpreter for the TapeBagel esoteric language
/// see https://esolangs.org/wiki/TapeBagel for a partial spec
use std::env;
use std::fs::File;
use std::io;
use std::io::{Read, BufRead};
struct TapeBagel {
integers: [i64; 3],
index: usize,
output: String,
}
impl TapeBagel {
fn new() -> TapeBagel {
TapeBagel {
integers: [0, 0, 0],
index: 0,
output: String::new(),
}
}
fn set_all_ints_to(&mut self, val: i64) {
for i in 0..self.integers.len() {
self.integers[i] = val;
}
}
fn pause(&self) {
println!("Pause\nindex = {}", self.index);
// let mut n = 0;
for (n, i) in self.integers.iter().enumerate() {
println!("i{} = {}", n, i);
// n += 1;
}
println!("Continue?");
let stdin = std::io::stdin();
stdin.lock().lines().next().unwrap().unwrap();
}
fn clear_screen(&mut self) {
println!("{}", self.output);
self.output.clear();
}
fn handle_int_op(&mut self, cmd: &str) {
let mut i1 = 0;
let mut i = 0;
let mut operator = ' ';
for c in cmd.chars() {
if c == '*' {
i += 1
} else {
operator = c;
i1 = i - 1;
i = 0;
}
}
let i2 = i - 1;
// println!("operation: {} with {}, {}", operator, i1, i2);
match operator {
'&' => self.integers[self.index] = self.integers[i1] * self.integers[i2],
'+' => self.integers[self.index] = self.integers[i1] + self.integers[i2],
'$' => self.integers[self.index] = self.integers[i1] / self.integers[i2],
'-' => self.integers[self.index] = self.integers[i1] - self.integers[i2],
_ => panic!("Unkown operator '{}'", operator),
}
// println!("result = {}", self.integers[self.index]);
}
fn int_to_char(&self, i: i64) -> char {
if i == 0 {
return ' ';
}
let j: u32;
j = (i as u32) + 64;
std::char::from_u32(j).unwrap()
}
fn handle_print(&mut self, cmd: &str) {
let mut i = 0;
for c in cmd.chars() {
if c == '*' {
i += 1;
}
}
if i == 0 {
panic!("need some integer specifier");
}
i -= 1;
if cmd.starts_with("@@") {
// println!("print_int({}) = {}", i, self.integers[i]);
let istr = self.integers[i].to_string();
self.output.push_str(&istr);
} else if cmd.starts_with("@") {
let outchar = self.int_to_char(self.integers[i]);
// println!("print_char({}) = {} = {}", i, self.integers[i], outchar);
self.output.push(outchar);
}
}
fn execute(&mut self, program: &str) -> String {
self.output.clear();
let mut institer = program.split_whitespace();
loop {
match institer.next() {
Some(c) => {
match c {
"%#" => self.index += 1,
"%%" => self.index = 0,
"#%" => self.set_all_ints_to(1),
"##" => self.set_all_ints_to(0),
"&&" => self.pause(),
"&@" => self.clear_screen(),
"%++" => self.integers[self.index] += 1,
_ => {
if c.starts_with("@") {
self.handle_print(c);
} else if c.starts_with("*") {
self.handle_int_op(c);
} else {
println!("Invalid command '{}'", c);
break;
}
}
}
}
None => {
println!("Execution stop");
break;
}
}
}
self.output.clone()
}
}
fn from_file(filepath: &str) -> io::Result<String> {
let mut contents = Vec::new();
let mut file = try!(File::open(filepath));
try!(file.read_to_end(&mut contents));
Ok(String::from_utf8(contents).unwrap())
}
fn main() {
println!("TapeBagel.rs");
let args: Vec<String> = env::args().collect();
if args.len() == 2 {
println!("Executing file '{}':", args[1]);
let fcontents = from_file(&args[1]).unwrap();
println!("{}", fcontents);
let mut machine = TapeBagel::new();
let out = machine.execute(&fcontents);
println!("output:\n{}", out);
} else {
println!("usage: {} <path to input file>", args[0]);
}
}
#[test]
fn test_hello_world() {
let program_text = "%% %++ %++ %++ %++ %++ %++ %++ %++ @* ## %++ %++ %++ %++ %++ @* ## %++ \
%++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ @* @* ## %++ %++ %++ %++ %++ \
%++ %++ %++ %++ %++ %++ %++ %++ %++ %++ @* ## @* %++ %++ %++ %++ %++ %++ \
%++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ @* ## \
%++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ @* ## %++ %++ \
%++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ @* ## \
%++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ %++ @* ## %++ %++ %++ %++ @* \
## ";
let mut machine = TapeBagel::new();
let out = machine.execute(program_text);
assert_eq!(out, "HELLO WORLD");
}
#[test]
fn test_iwctf() {
let program_text = "## %% %++ %++ %++ %# *&* @** %# **&* ***-* ***-* %++ %++ @*** *-* @*** \
@** *+** @*** ***+* @*** **+** ***+* %++ @*** #% %% %++ %++ %++ %++ @* %# \
%++ %++ %++ %% *&** @* @*** *-** @* %# %++ @** *-** *-** **-*** **-*** \
**-*** @** @*** #% %% %++ %++ %++ %++ %# *+** %++ @** @* %# *+** @*** ## \
%% @***";
let mut machine = TapeBagel::new();
let out = machine.execute(program_text);
assert_eq!(out, "IW ILOVETAPEBAGEL ");
}
@StuartFarmer
Copy link

I wonder who this elusive S. R. Farmer is who is mentioned on the TapeBagel wiki page... https://esolangs.org/wiki/TapeBagel

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment