@@ 17,70 17,67 @@ pub enum BrainfuckInstruction {
}
#[derive(Debug)]
-pub struct CompiledBrainfuck {
+pub struct BrainfuckIR {
pub name: Option<String>,
pub instructions: Vec<BrainfuckInstruction>,
pub jumps: HashMap<usize, usize>,
pub comments: String,
}
-pub fn compile_into_tuple(
- source: &str,
-) -> (Vec<BrainfuckInstruction>, HashMap<usize, usize>, String) {
- let mut instructions = Vec::new();
- let mut jumps = HashMap::new();
- let mut jump_stack = Vec::new();
- let mut comments = String::new();
+impl BrainfuckIR {
+ pub fn parse(source: &str) -> Self {
+ use BrainfuckInstruction::*;
- for c in source.chars() {
- let inst = match c {
- '<' => BrainfuckInstruction::RGT,
- '>' => BrainfuckInstruction::LFT,
- '+' => BrainfuckInstruction::INC,
- '-' => BrainfuckInstruction::DEC,
- '.' => BrainfuckInstruction::OUT,
- ',' => BrainfuckInstruction::INP,
- '[' => {
- jump_stack.push(instructions.len());
- BrainfuckInstruction::JMP
- }
- ']' => {
- let open = jump_stack.pop().unwrap();
- jumps.insert(open, instructions.len());
- jumps.insert(instructions.len(), open);
- BrainfuckInstruction::RET
- }
- _ => {
- comments.push(c);
+ let mut instructions = Vec::new();
+ let mut jumps = HashMap::new();
+ let mut jump_stack = Vec::new();
+ let mut comments = String::new();
+
+ for c in source.chars() {
+ let inst = match c {
+ '<' => RGT,
+ '>' => LFT,
+ '+' => INC,
+ '-' => DEC,
+ '.' => OUT,
+ ',' => INP,
+ '[' => {
+ jump_stack.push(instructions.len());
+ JMP
+ }
+ ']' => {
+ let open = jump_stack.pop().expect("Closing bracket with no opening");
+ jumps.insert(open, instructions.len());
+ jumps.insert(instructions.len(), open);
+ RET
+ }
+ _ => {
+ comments.push(c);
+ continue;
+ }
+ };
+
+ // Tiny optimization I suppose
+ if let Some(BrainfuckInstruction::INP) = instructions.last() {
continue;
}
- };
- instructions.push(inst);
- }
- (instructions, jumps, comments)
-}
+ instructions.push(inst);
+ }
-pub fn compile(source: &str) -> CompiledBrainfuck {
- let compiled = compile_into_tuple(source);
- CompiledBrainfuck {
- name: Option::None,
- instructions: compiled.0,
- jumps: compiled.1,
- comments: compiled.2,
+ BrainfuckIR {
+ name: None,
+ instructions,
+ jumps,
+ comments,
+ }
}
-}
-
-pub fn compile_from_string(source: String) -> CompiledBrainfuck {
- compile(source.as_str())
-}
-pub fn compile_named(source: &str, name: &str) -> CompiledBrainfuck {
- let compiled = compile_into_tuple(source.into());
- CompiledBrainfuck {
- name: Option::Some(name.into()),
- instructions: compiled.0,
- jumps: compiled.1,
- comments: compiled.2,
+ pub fn parse_named(source: &str, name: &str) -> Self {
+ let ir = Self::parse(source);
+ BrainfuckIR {
+ name: Some(name.to_string()),
+ ..ir
+ }
}
}
@@ 1,7 1,7 @@
use std::io::Read;
use crate::compiler::Word;
-use crate::compiler::{BrainfuckInstruction, CompiledBrainfuck};
+use crate::compiler::{BrainfuckIR, BrainfuckInstruction};
const DEFAULT_TAPE_SIZE: usize = 30_000;
@@ 9,7 9,7 @@ type InputHook = fn() -> Word;
type OutputHook = fn(Word) -> ();
pub fn execute(
- compiled: CompiledBrainfuck,
+ compiled: BrainfuckIR,
tape_size: usize,
input_hook: InputHook,
output_hook: OutputHook,
@@ 78,7 78,7 @@ pub fn default_output_hook(out: u8) {
print!("{}", out as char);
}
-pub fn execute_default(compiled: CompiledBrainfuck) {
+pub fn execute_default(compiled: BrainfuckIR) {
execute(
compiled,
DEFAULT_TAPE_SIZE,
@@ 6,13 6,14 @@ use std::io::Read;
mod cli;
mod compiler;
mod interpreter;
+mod lc3;
fn main() -> Result<(), Box<dyn Error>> {
let mut src_file = File::open(env::args().skip(1).next().expect("Missing source filename"))?;
let mut src = String::new();
src_file.read_to_string(&mut src).unwrap();
- let compiled = crate::compiler::compile_from_string(src);
+ let compiled = crate::compiler::BrainfuckIR::parse(src.as_str());
eprintln!("Compiled: {:?}", compiled);
crate::interpreter::execute_default(compiled);