Skip to content

Commit 0e1eb99

Browse files
committed
Add first (quick and dirty) implementation
0 parents  commit 0e1eb99

File tree

6 files changed

+430
-0
lines changed

6 files changed

+430
-0
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
/target
2+
Cargo.lock

Cargo.toml

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[package]
2+
name = "pancakestack"
3+
version = "0.1.0"
4+
authors = ["OpenByte <[email protected]>"]
5+
edition = "2018"
6+
7+
[dependencies]
8+
"regex" = "*"
9+
"lazy_static" = "*"
10+
"unicode-segmentation" = "*"

src/lib.rs

+288
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,288 @@
1+
#[macro_use]
2+
extern crate lazy_static;
3+
4+
use std::char;
5+
use std::str;
6+
use regex::Regex;
7+
use std::io::{Read, BufReader, Write};
8+
use std::collections::{HashMap};
9+
use unicode_segmentation::UnicodeSegmentation;
10+
use std::io::prelude::*;
11+
12+
// https://esolangs.org/wiki/Pancake_Stack
13+
14+
#[derive(Debug, Clone)]
15+
/*enum Command<'a> {
16+
PutThisPancakeOnTop(&'a str),
17+
EatThePancakeOnTop,
18+
PutTheTopPancakesTogether,
19+
GiveMeAPancake,
20+
HowAboutAHotcake,
21+
ShowMeAPancake,
22+
TakeFromTheTopPancakes,
23+
FlipThePancakesOnTop,
24+
PutAnotherPancakeOnTop,
25+
Label(&'a str),
26+
IfThePancakeIsntTastyGoOverTo(&'a str),
27+
IfThePancakeIsTastyGoOverTo(&'a str),
28+
PutSyrupOnThePancakes,
29+
PutButterOnThePancakes,
30+
TakeOffTheSyrup,
31+
TakeOffTheButter,
32+
EatAllOfThePancakes
33+
}*/
34+
pub enum Command {
35+
PutThisPancakeOnTop(String),
36+
EatThePancakeOnTop,
37+
PutTheTopPancakesTogether,
38+
GiveMeAPancake,
39+
HowAboutAHotcake,
40+
ShowMeAPancake,
41+
TakeFromTheTopPancakes,
42+
FlipThePancakesOnTop,
43+
PutAnotherPancakeOnTop,
44+
Label(String),
45+
IfThePancakeIsntTastyGoOverTo(String),
46+
IfThePancakeIsTastyGoOverTo(String),
47+
PutSyrupOnThePancakes,
48+
PutButterOnThePancakes,
49+
TakeOffTheSyrup,
50+
TakeOffTheButter,
51+
EatAllOfThePancakes
52+
}
53+
54+
pub struct PancakeStackParser {
55+
}
56+
impl PancakeStackParser {
57+
pub fn parse_line(line: &str) -> Option<Command> {
58+
lazy_static! {
59+
static ref PUT_THIS_PANCAKE_ON_TOP_REGEX: Regex = Regex::new(r"^Put this (\S*) pancake on top!$").unwrap();
60+
static ref EAT_THE_PANCAKE_ON_TOP_REGEX: Regex = Regex::new(r"^Eat the pancake on top!$").unwrap();
61+
static ref PUT_THE_TOP_PANCAKES_TOGETHER_REGEX: Regex = Regex::new(r"^Put the top pancakes together!$").unwrap();
62+
static ref GIVE_ME_A_PANCAKE_REGEX: Regex = Regex::new(r"^Give me a pancake!$").unwrap();
63+
static ref HOW_ABOUT_A_HOTCAKE_REGEX: Regex = Regex::new(r"^How about a hotcake\?$").unwrap();
64+
static ref SHOW_ME_A_PANCAKE_REGEX: Regex = Regex::new(r"^Show me a pancake!$").unwrap();
65+
static ref TAKE_FROM_THE_TOP_PANCAKES_REGEX: Regex = Regex::new(r"^Take from the top pancakes!$").unwrap();
66+
static ref FLIP_THE_PANCAKES_ON_TOP_REGEX: Regex = Regex::new(r"^Flip the pancakes on top!$").unwrap();
67+
static ref PUT_ANOTHER_PANCAKE_ON_TOP_REGEX: Regex = Regex::new(r"^Put another pancake on top!$").unwrap();
68+
static ref LABEL_REGEX: Regex = Regex::new(r"^\[(.*)\]$").unwrap();
69+
static ref IF_THE_PANCAKE_ISNT_TASTY_GO_OVER_TO_REGEX: Regex = Regex::new("^If the pancake isn't tasty, go over to \"(.*)\".$").unwrap();
70+
static ref IF_THE_PANCAKE_IS_TASTY_GO_OVER_TO_REGEX: Regex = Regex::new("^If the pancake is tasty, go over to \"(.*)\".$").unwrap();
71+
static ref PUT_SYRUP_ON_THE_PANCAKES_REGEX: Regex = Regex::new(r"^Put syrup on the pancakes!$").unwrap();
72+
static ref PUT_BUTTER_ON_THE_PANCAKES_REGEX: Regex = Regex::new(r"^Put butter on the pancakes$").unwrap();
73+
static ref TAKE_OFF_THE_SYRUP_REGEX: Regex = Regex::new(r"^Take off the syrup!$").unwrap();
74+
static ref TAKE_OFF_THE_BUTTER_REGEX: Regex = Regex::new(r"^Take off the butter!$").unwrap();
75+
static ref EAT_ALL_OF_THE_PANCAKES_REGEX: Regex = Regex::new(r"^Eat all of the pancakes!$").unwrap();
76+
}
77+
78+
if let Some(captures) = PUT_THIS_PANCAKE_ON_TOP_REGEX.captures_iter(line).next() {
79+
return Some(Command::PutThisPancakeOnTop(captures[1].to_string()));
80+
// return Some(Command::PutThisPancakeOnTop(captures.get(1).unwrap().as_str()));
81+
}
82+
if EAT_THE_PANCAKE_ON_TOP_REGEX.is_match(line) {
83+
return Some(Command::EatThePancakeOnTop);
84+
}
85+
if PUT_THE_TOP_PANCAKES_TOGETHER_REGEX.is_match(line) {
86+
return Some(Command::PutTheTopPancakesTogether);
87+
}
88+
if GIVE_ME_A_PANCAKE_REGEX.is_match(line) {
89+
return Some(Command::GiveMeAPancake);
90+
}
91+
if HOW_ABOUT_A_HOTCAKE_REGEX.is_match(line) {
92+
return Some(Command::HowAboutAHotcake);
93+
}
94+
if SHOW_ME_A_PANCAKE_REGEX.is_match(line) {
95+
return Some(Command::ShowMeAPancake);
96+
}
97+
if TAKE_FROM_THE_TOP_PANCAKES_REGEX.is_match(line) {
98+
return Some(Command::TakeFromTheTopPancakes);
99+
}
100+
if FLIP_THE_PANCAKES_ON_TOP_REGEX.is_match(line) {
101+
return Some(Command::FlipThePancakesOnTop);
102+
}
103+
if PUT_ANOTHER_PANCAKE_ON_TOP_REGEX.is_match(line) {
104+
return Some(Command::PutAnotherPancakeOnTop);
105+
}
106+
if let Some(captures) = LABEL_REGEX.captures_iter(line).next() {
107+
return Some(Command::Label(captures[1].to_string()));
108+
// return Some(Command::Label(captures.get(1).unwrap().as_str()));
109+
}
110+
if let Some(captures) = IF_THE_PANCAKE_ISNT_TASTY_GO_OVER_TO_REGEX.captures_iter(line).next() {
111+
return Some(Command::IfThePancakeIsntTastyGoOverTo(captures[1].to_string()));
112+
// return Some(Command::IfThePancakeIsntTastyGoOverTo(captures.get(1).unwrap().as_str()));
113+
}
114+
if let Some(captures) = IF_THE_PANCAKE_IS_TASTY_GO_OVER_TO_REGEX.captures_iter(line).next() {
115+
return Some(Command::IfThePancakeIsTastyGoOverTo(captures[1].to_string()));
116+
//return Some(Command::IfThePancakeIsTastyGoOverTo(captures.get(1).unwrap().as_str()));
117+
}
118+
if PUT_SYRUP_ON_THE_PANCAKES_REGEX.is_match(line) {
119+
return Some(Command::PutSyrupOnThePancakes)
120+
}
121+
if PUT_BUTTER_ON_THE_PANCAKES_REGEX.is_match(line) {
122+
return Some(Command::PutButterOnThePancakes)
123+
}
124+
if TAKE_OFF_THE_SYRUP_REGEX.is_match(line) {
125+
return Some(Command::TakeOffTheSyrup)
126+
}
127+
if TAKE_OFF_THE_BUTTER_REGEX.is_match(line) {
128+
return Some(Command::TakeOffTheButter)
129+
}
130+
if EAT_ALL_OF_THE_PANCAKES_REGEX.is_match(line) {
131+
return Some(Command::EatAllOfThePancakes)
132+
}
133+
None
134+
}
135+
136+
}
137+
138+
pub struct PancakeStack {
139+
}
140+
impl PancakeStack {
141+
pub fn new() -> Self {
142+
PancakeStack { }
143+
}
144+
pub fn run_program<P, I, O: Write>(&mut self, program_read: P, input_read: I, output: &mut O) where P:Read, I:Read, O:Write {
145+
let mut stack = Vec::new();
146+
let mut labels = HashMap::new();
147+
let mut program = Vec::new();
148+
let mut current_statement: Option<usize> = None;
149+
150+
let mut program_reader = BufReader::new(program_read);
151+
let mut input_reader = BufReader::new(input_read);
152+
153+
let mut program_line = String::new();
154+
let mut input_line = String::new();
155+
'outer: loop {
156+
let command = match current_statement {
157+
Some(ref mut index) => {
158+
match program.get(*index) {
159+
Some(c) => {
160+
*index += 1;
161+
c
162+
},
163+
None => {
164+
current_statement = None;
165+
continue;
166+
}
167+
}
168+
},
169+
None => {
170+
program_line.clear();
171+
let length = program_reader.read_line(&mut program_line).unwrap();
172+
if length == 0 {
173+
break 'outer;
174+
}
175+
trim_newline(&mut program_line);
176+
fn trim_newline(s: &mut String) {
177+
if s.ends_with('\n') {
178+
s.pop();
179+
if s.ends_with('\r') {
180+
s.pop();
181+
}
182+
}
183+
}
184+
185+
let c = PancakeStackParser::parse_line(&program_line);
186+
if c.is_none() {
187+
eprintln!("{:?}", &program_line);
188+
continue;
189+
}
190+
let c = c.unwrap();
191+
program.push(c.clone());
192+
program.last().unwrap()
193+
}
194+
};
195+
196+
println!("{:?}", command);
197+
198+
match command {
199+
Command::PutThisPancakeOnTop(adjective) => {
200+
stack.push(adjective.graphemes(true).count() as u32);
201+
},
202+
Command::EatThePancakeOnTop => {
203+
stack.pop();
204+
},
205+
Command::PutTheTopPancakesTogether => {
206+
let first = stack.pop().unwrap();
207+
let second = stack.pop().unwrap();
208+
stack.push(first + second);
209+
},
210+
Command::GiveMeAPancake => {
211+
input_reader.read_line(&mut input_line).unwrap();
212+
let number_input = input_line.parse().unwrap();
213+
stack.push(number_input);
214+
input_line.clear();
215+
},
216+
Command::HowAboutAHotcake => {
217+
let buf = input_reader.fill_buf().unwrap();
218+
if buf.len() == 0 {
219+
// TODO define behaviour
220+
stack.push(0);
221+
continue;
222+
}
223+
let number_input = buf[0];
224+
input_reader.consume(1);
225+
stack.push(number_input as u32);
226+
},
227+
Command::ShowMeAPancake => {
228+
let top = stack.last().unwrap();
229+
let c = char::from_u32(*top).unwrap();
230+
write!(output, "{}", c).unwrap();
231+
},
232+
Command::TakeFromTheTopPancakes => {
233+
let first = stack.pop().unwrap();
234+
let second = stack.pop().unwrap();
235+
stack.push(first - second);
236+
},
237+
Command::FlipThePancakesOnTop => {
238+
let first = stack.pop().unwrap();
239+
let second = stack.pop().unwrap();
240+
stack.push(first);
241+
stack.push(second);
242+
},
243+
Command::PutAnotherPancakeOnTop => {
244+
stack.push(stack.last().unwrap().clone());
245+
}
246+
Command::Label(label) => {
247+
labels.insert(label.clone(), program.len()); // save position of label
248+
},
249+
Command::IfThePancakeIsntTastyGoOverTo(target_label) => {
250+
let top = *stack.last().unwrap();
251+
if top == 0 {
252+
let label_position = labels.get(target_label).unwrap();
253+
current_statement = Some(*label_position);
254+
}
255+
},
256+
Command::IfThePancakeIsTastyGoOverTo(target_label) => {
257+
let top = *stack.last().unwrap();
258+
if top != 0 {
259+
let label_position = labels.get(target_label).unwrap();
260+
current_statement = Some(*label_position);
261+
}
262+
},
263+
Command::PutSyrupOnThePancakes => {
264+
for value in stack.iter_mut() {
265+
*value += 1;
266+
}
267+
},
268+
Command::PutButterOnThePancakes => {
269+
let top = stack.last_mut().unwrap();
270+
*top += 1;
271+
},
272+
Command::TakeOffTheSyrup => {
273+
for value in stack.iter_mut() {
274+
*value -= 1;
275+
}
276+
},
277+
Command::TakeOffTheButter => {
278+
let top = stack.last_mut().unwrap();
279+
*top -= 1;
280+
},
281+
Command::EatAllOfThePancakes => {
282+
break;
283+
}
284+
}
285+
}
286+
}
287+
}
288+

tests/basic.rs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
use std::fs::File;
2+
use std::error::Error;
3+
use std::str;
4+
use pancakestack::PancakeStack;
5+
6+
#[test]
7+
fn simplest() -> Result<(), Box<dyn Error>> {
8+
let program = "Put this test pancake on top!\nShow me a pancake!";
9+
let program_buf = program.as_bytes();
10+
11+
let mut output_buf = Vec::new();
12+
PancakeStack::new().run_program(program_buf, std::io::empty(), &mut output_buf);
13+
let output = str::from_utf8(&output_buf)?;
14+
assert_eq!(output, "\x04");
15+
Ok(())
16+
}
17+
18+
#[test]
19+
fn hello_world() -> Result<(), Box<dyn Error>> {
20+
let file = File::open("tests/hello_world.pancake")?;
21+
let mut output_buf = Vec::new();
22+
PancakeStack::new().run_program(file, std::io::empty(), &mut output_buf);
23+
let output = str::from_utf8(&output_buf)?;
24+
assert_eq!(output, "Hello World!");
25+
Ok(())
26+
}
27+
28+
#[test]
29+
fn cat() -> Result<(), Box<dyn Error>> {
30+
let file = File::open("tests/cat.pancake")?;
31+
let input = "hFeuofpegiurbfoieboiejfpeiDIWUHFUIwfjoweopUBUfbeufbiubfiuwebfowmFMPIEFBO UF EFEW\x00";
32+
let input_buf = input.as_bytes();
33+
let mut output_buf = Vec::new();
34+
PancakeStack::new().run_program(file, input_buf, &mut output_buf);
35+
let output = str::from_utf8(&output_buf)?;
36+
assert_eq!(output, input);
37+
Ok(())
38+
}
39+
40+

tests/cat.pancake

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Put this old pancake on top!
2+
[CAT]
3+
Eat the pancake on top!
4+
How about a hotcake?
5+
Show me a pancake!
6+
If the pancake is tasty, go over to "CAT".
7+
Eat all of the pancakes!

0 commit comments

Comments
 (0)