From a80a2d6725bcba8c330c9f12c569b7f526021d9f Mon Sep 17 00:00:00 2001 From: Emil Englesson Date: Tue, 8 Apr 2025 00:10:01 +0200 Subject: [PATCH] Progressing on compilation to rust --- src/cli/mod.rs | 2 +- src/cli/run.rs | 14 ++--- src/compiler/mod.rs | 9 ++++ src/compiler/rust.rs | 124 +++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 5 files changed, 140 insertions(+), 10 deletions(-) create mode 100644 src/compiler/mod.rs create mode 100644 src/compiler/rust.rs diff --git a/src/cli/mod.rs b/src/cli/mod.rs index 31832de..e731614 100644 --- a/src/cli/mod.rs +++ b/src/cli/mod.rs @@ -22,7 +22,7 @@ pub fn run() -> Result<(), Error> { #[derive(Debug)] pub enum Error { - Io(std::io::Error), + Io(io::Error), Parsing(program::Error), Interpreter(interpreter::Error), } diff --git a/src/cli/run.rs b/src/cli/run.rs index a5f9339..c540363 100644 --- a/src/cli/run.rs +++ b/src/cli/run.rs @@ -63,20 +63,16 @@ pub fn execute(matches: &ArgMatches) -> Result<(), crate::cli::Error> { let mut input = io::stdin(); let mut output = io::stdout(); - let (exec_elapsed, analytics) = if should_profile { - let start = Instant::now(); + let start = Instant::now(); + let exec_elapsed = if should_profile { let analytics = interpreter::profile(&program, &mut input, &mut output, memory_size)?; - (util::format_duration(start.elapsed()), Some(analytics)) + print_analytics(&analytics); + util::format_duration(start.elapsed()) } else { - let start = Instant::now(); interpreter::execute(&program, &mut input, &mut output, memory_size)?; - (util::format_duration(start.elapsed()), None) + util::format_duration(start.elapsed()) }; - if let Some(analytics) = analytics { - print_analytics(&analytics); - } - if print_timings { println!(); println!("Parsing time: {parse_elapsed}"); diff --git a/src/compiler/mod.rs b/src/compiler/mod.rs new file mode 100644 index 0000000..72542cb --- /dev/null +++ b/src/compiler/mod.rs @@ -0,0 +1,9 @@ +use crate::program::Program; +use std::{fs, io}; + +mod rust; + +pub fn compile(program: &Program) -> io::Result<()> { + let code = rust::generate_code(program); + fs::write("compiled_bf.rs", code) +} diff --git a/src/compiler/rust.rs b/src/compiler/rust.rs new file mode 100644 index 0000000..4e6e822 --- /dev/null +++ b/src/compiler/rust.rs @@ -0,0 +1,124 @@ +use crate::program::{Instruction, Program}; +use std::fmt::Write; + +const VAR_MEMORY: &str = "memory"; +const VAR_POINTER: &str = "pointer"; +const FUN_READ: &str = "read"; +const FUN_PRINT: &str = "print"; +const MEMORY_SIZE: usize = 32768; + +struct CodeGen { + source: String, + indent: usize, +} + +impl CodeGen { + fn new() -> Self { + Self { + source: String::new(), + indent: 0, + } + } + + fn line(&mut self, code: impl AsRef) -> &mut Self { + let indent = " ".repeat(self.indent); + let code = code.as_ref(); + writeln!(&mut self.source, "{indent}{code}").unwrap(); + self + } + + fn empty_line(&mut self) -> &mut Self { + self.source.push('\n'); + self + } + + fn block(&mut self, header: impl AsRef, body: F) -> &mut Self + where + F: FnOnce(&mut Self), + { + let header = header.as_ref(); + self.line(format!("{header} {{")); + self.indent += 1; + body(self); + self.indent -= 1; + self.line("}") + } + + fn function( + &mut self, + name: &str, + params: Option<&[&str]>, + return_type: Option<&str>, + body: F, + ) -> &mut Self + where + F: FnOnce(&mut Self), + { + let params = params.map(|params| params.join(", ")).unwrap_or_default(); + let header = match return_type { + Some(return_type) => format!("fn {name}({params}) -> {return_type}"), + None => format!("fn {name}({params})"), + }; + self.block(header, |code| body(code)) + } + + fn gen_instruction(&mut self, instruction: &Instruction) -> &mut Self { + macro_rules! mem { + (@) => { + format!("{VAR_MEMORY}[{VAR_POINTER}]") + }; + } + + use Instruction as Instr; + match instruction { + Instr::MoveRight(value) => self.line(format!("{VAR_POINTER} += {value};")), + Instr::MoveLeft(value) => self.line(format!("{VAR_POINTER} -= {value};")), + Instr::Add(value) => { + self.line(format!("{} = {}.wrapping_add({value});", mem!(@), mem!(@))) + } + Instr::Sub(value) => { + self.line(format!("{} = {}.wrapping_sub({value});", mem!(@), mem!(@))) + } + Instr::Print => self.line(format!("{FUN_PRINT}({});", mem!(@))), + Instr::Read => self.line(format!("{} = {FUN_READ}();", mem!(@))), + Instr::Set(value) => self.line(format!("{} = {value};", mem!(@))), + Instr::Loop { body } => self.block(format!("while {} != 0", mem!(@)), |code| { + for instr in body { + code.gen_instruction(instr); + } + }), + } + } + + fn build(self) -> String { + self.source + } +} + +pub fn generate_code(program: &Program) -> String { + let mut code = CodeGen::new(); + + code.line("use std::io::{self, Read, Write};") + .empty_line() + .function("read", None, Some("u8"), |code| { + code.line("let mut buffer = [0; 1];") + .line("let bytes = io::stdin().read(&mut buffer).unwrap();") + .line("if bytes > 0 { buffer[0] } else { 0 }"); + }) + .empty_line() + .function("print", Some(&["value: u8"]), None, |code| { + code.line("io::stdout().write_all(&[value]).unwrap();"); + }) + .empty_line() + .function("main", None, None, |code| { + code.line(format!("let mut memory = [0u8; {MEMORY_SIZE}];")) + .line("let mut pointer = 0usize;") + .empty_line(); + + for instruction in program.instructions() { + code.gen_instruction(instruction); + } + }); + + code.build() +} diff --git a/src/lib.rs b/src/lib.rs index e6b7e3c..d481090 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,5 +6,6 @@ #![allow(clippy::map_unwrap_or)] pub mod cli; +pub mod compiler; pub mod interpreter; pub mod program;