ft: restructure with tests
This commit is contained in:
1
example/hello_world.bf
Normal file
1
example/hello_world.bf
Normal file
@@ -0,0 +1 @@
|
|||||||
|
++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.
|
||||||
91
src/interpret.rs
Normal file
91
src/interpret.rs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
use std::io::{self, Read};
|
||||||
|
|
||||||
|
pub fn interpret(code: &str) -> Vec<u8> {
|
||||||
|
let mut memory = [0 as u8; 10_000];
|
||||||
|
let mut head = 0 as usize;
|
||||||
|
let mut pc = 0 as usize;
|
||||||
|
let mut loop_stack = Vec::new();
|
||||||
|
let mut out = Vec::new();
|
||||||
|
|
||||||
|
let code_bytes = code.as_bytes();
|
||||||
|
while pc < code.len() {
|
||||||
|
match code_bytes[pc] as char {
|
||||||
|
'>' => head += 1,
|
||||||
|
'<' => head -= 1,
|
||||||
|
'+' => memory[head] = memory[head].wrapping_add(1),
|
||||||
|
'-' => memory[head] = memory[head].wrapping_sub(1),
|
||||||
|
'.' => {
|
||||||
|
print!("{}", memory[head] as char);
|
||||||
|
out.push(memory[head]);
|
||||||
|
}
|
||||||
|
',' => {
|
||||||
|
let mut input_buf = [0u8; 1];
|
||||||
|
if io::stdin().read_exact(&mut input_buf).is_ok() {
|
||||||
|
memory[head] = input_buf[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'[' => {
|
||||||
|
// loop start
|
||||||
|
if memory[head] == 0 {
|
||||||
|
let mut count_brackets = 1;
|
||||||
|
|
||||||
|
// find nested brackets
|
||||||
|
while count_brackets > 0 {
|
||||||
|
pc += 1;
|
||||||
|
if pc >= code_bytes.len() {
|
||||||
|
panic!("Missing closing loop!");
|
||||||
|
}
|
||||||
|
match code_bytes[pc] as char {
|
||||||
|
// closing current loop
|
||||||
|
']' => count_brackets -= 1,
|
||||||
|
// nested bracket
|
||||||
|
'[' => count_brackets += 1,
|
||||||
|
// ignore rest
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// save pc to jump back to
|
||||||
|
loop_stack.push(pc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
']' => {
|
||||||
|
// loop terminator
|
||||||
|
if memory[head] != 0 {
|
||||||
|
// jump back to loop beginning
|
||||||
|
if let Some(start_pc) = loop_stack.last() {
|
||||||
|
pc = *start_pc;
|
||||||
|
} else {
|
||||||
|
panic!("Loop stack empty, but still called!")
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// loop done, continue normally
|
||||||
|
loop_stack.pop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => panic!("Invalid instruction"),
|
||||||
|
}
|
||||||
|
pc += 1;
|
||||||
|
}
|
||||||
|
print!("\n");
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_h() {
|
||||||
|
let code = "+++++++++[>++++++++<-]>.";
|
||||||
|
let out = interpret(code);
|
||||||
|
assert_eq!(std::str::from_utf8(&out).unwrap(), "H");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hello() {
|
||||||
|
let code = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";
|
||||||
|
let out = interpret(code);
|
||||||
|
assert_eq!(std::str::from_utf8(&out).unwrap(), "Hello World!\n");
|
||||||
|
}
|
||||||
|
}
|
||||||
84
src/main.rs
84
src/main.rs
@@ -1,73 +1,19 @@
|
|||||||
use std::io::{self, Read};
|
use crate::interpret::interpret;
|
||||||
|
use std::{env, fs, process};
|
||||||
|
|
||||||
fn interpret(code: &str) {
|
mod interpret;
|
||||||
let mut memory = [0 as u8; 10_000];
|
|
||||||
let mut head = 0 as usize;
|
|
||||||
let mut pc = 0 as usize;
|
|
||||||
let mut loop_stack = Vec::new();
|
|
||||||
|
|
||||||
let code_bytes = code.as_bytes();
|
|
||||||
while pc < code.len() {
|
|
||||||
match code_bytes[pc] as char {
|
|
||||||
'>' => head += 1,
|
|
||||||
'<' => head -= 1,
|
|
||||||
'+' => memory[head] = memory[head].wrapping_add(1),
|
|
||||||
'-' => memory[head] = memory[head].wrapping_sub(1),
|
|
||||||
'.' => print!("{}", memory[head] as char),
|
|
||||||
',' => {
|
|
||||||
let mut input_buf = [0u8; 1];
|
|
||||||
if io::stdin().read_exact(&mut input_buf).is_ok() {
|
|
||||||
memory[head] = input_buf[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'[' => {
|
|
||||||
// loop start
|
|
||||||
if memory[head] == 0 {
|
|
||||||
let mut count_brackets = 1;
|
|
||||||
|
|
||||||
// find nested brackets
|
|
||||||
while count_brackets > 0 {
|
|
||||||
pc += 1;
|
|
||||||
if pc >= code_bytes.len() {
|
|
||||||
panic!("Missing closing loop!");
|
|
||||||
}
|
|
||||||
match code_bytes[pc] as char {
|
|
||||||
// closing current loop
|
|
||||||
']' => count_brackets -= 1,
|
|
||||||
// nested bracket
|
|
||||||
'[' => count_brackets += 1,
|
|
||||||
// ignore rest
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// save pc to jump back to
|
|
||||||
loop_stack.push(pc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
']' => {
|
|
||||||
// loop terminator
|
|
||||||
if memory[head] != 0 {
|
|
||||||
// jump back to loop beginning
|
|
||||||
if let Some(start_pc) = loop_stack.last() {
|
|
||||||
pc = *start_pc;
|
|
||||||
} else {
|
|
||||||
panic!("Loop stack empty, but still called!")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// loop done, continue normally
|
|
||||||
loop_stack.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => panic!("Invalid instruction"),
|
|
||||||
}
|
|
||||||
pc += 1;
|
|
||||||
}
|
|
||||||
print!("\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// let code = "+++++++++[>++++++++<-]>.";
|
let args: Vec<_> = env::args().collect();
|
||||||
let code = "++++++++[>++++[>++>+++>+++>+<<<<-]>+>+>->>+[<]<-]>>.>---.+++++++..+++.>>.<-.<.+++.------.--------.>>+.>++.";
|
if args.len() != 2 {
|
||||||
interpret(code);
|
eprintln!("Usage: {} <filename>", args[0]);
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = &args[1];
|
||||||
|
let code = fs::read_to_string(filename).unwrap_or_else(|err| {
|
||||||
|
panic!("Error reading file {}: {}", filename, err);
|
||||||
|
});
|
||||||
|
|
||||||
|
interpret(&code);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user