Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/cli/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
Expand Down
14 changes: 5 additions & 9 deletions src/cli/run.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}");
Expand Down
9 changes: 9 additions & 0 deletions src/compiler/mod.rs
Original file line number Diff line number Diff line change
@@ -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)
}
124 changes: 124 additions & 0 deletions src/compiler/rust.rs
Original file line number Diff line number Diff line change
@@ -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<str>) -> &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<F>(&mut self, header: impl AsRef<str>, 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<F>(
&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()
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,6 @@
#![allow(clippy::map_unwrap_or)]

pub mod cli;
pub mod compiler;
pub mod interpreter;
pub mod program;
Loading