Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

implement MachineBuilder #15

Merged
merged 2 commits into from
Nov 6, 2024
Merged
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
7 changes: 3 additions & 4 deletions crates/brainfuck_vm/src/bin/brainfuck_vm.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
// Adapted from rkdud007 brainfuck-zkvm https://github.com/rkdud007/brainfuck-zkvm/blob/main/src/main.rs

use brainfuck_vm::{compiler::Compiler, machine::Machine};
use clap::{Parser, ValueHint};
use std::{
fs,
io::{stdin, stdout},
path::PathBuf,
};

use brainfuck_vm::{compiler::Compiler, machine::Machine};

#[derive(Parser)]
#[clap(author, version, about, long_about = None)]
struct Args {
Expand Down Expand Up @@ -37,8 +36,8 @@ fn main() {
let stdin = stdin();
let stdout = stdout();
let mut bf_vm = match args.ram_size {
Some(size) => Machine::new_with_config(ins, stdin, stdout, size),
None => Machine::new(ins, stdin, stdout),
Some(size) => Machine::new_with_config(&ins, stdin, stdout, size),
None => Machine::new(&ins, stdin, stdout),
};
tracing::info!("Provide inputs separated by linefeeds: ");
bf_vm.execute().unwrap();
Expand Down
102 changes: 76 additions & 26 deletions crates/brainfuck_vm/src/machine.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,64 @@
// Adapted from rkdud007 brainfuck-zkvm https://github.com/rkdud007/brainfuck-zkvm/blob/main/src/machine.rs

use crate::{instruction::InstructionType, registers::Registers};
use num_traits::identities::{One, Zero};
use std::{
error::Error,
io::{Read, Write},
};
use stwo_prover::core::fields::{m31::BaseField, FieldExpOps};

use num_traits::identities::{One, Zero};
pub struct MachineBuilder {
code: Vec<BaseField>,
input: Option<Box<dyn Read>>,
output: Option<Box<dyn Write>>,
ram_size: usize,
}

use stwo_prover::core::fields::{m31::BaseField, FieldExpOps};
impl MachineBuilder {
/// Creates a new [`MachineBuilder`] with the specified code.
pub fn new(code: &[BaseField]) -> Self {
Self { code: code.to_vec(), input: None, output: None, ram_size: Machine::DEFAULT_RAM_SIZE }
}

use crate::{instruction::InstructionType, registers::Registers};
/// Sets the input stream for the machine.
#[must_use]
pub fn with_input<R: Read + 'static>(mut self, input: R) -> Self {
self.input = Some(Box::new(input));
self
}

/// Sets the output stream for the machine.
#[must_use]
pub fn with_output<W: Write + 'static>(mut self, output: W) -> Self {
self.output = Some(Box::new(output));
self
}

/// Sets the RAM size for the machine.
#[must_use]
pub const fn with_ram_size(mut self, ram_size: usize) -> Self {
self.ram_size = ram_size;
self
}

/// Builds the [`Machine`] instance with the provided configuration.
pub fn build(self) -> Result<Machine, &'static str> {
if self.input.is_none() || self.output.is_none() {
return Err("Input and output streams must be provided");
}

Ok(Machine {
program: ProgramMemory { code: self.code },
state: MutableState {
ram: vec![BaseField::zero(); self.ram_size],
registers: Registers::new(),
},
io: IO { input: self.input.unwrap(), output: self.output.unwrap() },
trace: vec![],
})
}
}

pub struct ProgramMemory {
code: Vec<BaseField>,
Expand All @@ -35,28 +84,29 @@ pub struct Machine {
impl Machine {
pub const DEFAULT_RAM_SIZE: usize = 30000;

pub fn new_with_config<R, W>(code: Vec<BaseField>, input: R, output: W, ram_size: usize) -> Self
pub fn new_with_config<R, W>(code: &[BaseField], input: R, output: W, ram_size: usize) -> Self
where
R: Read + 'static,
W: Write + 'static,
{
Self {
program: ProgramMemory { code },
state: MutableState {
ram: vec![BaseField::zero(); ram_size],
registers: Registers::new(),
},
io: IO { input: Box::new(input), output: Box::new(output) },
trace: vec![],
}
MachineBuilder::new(code)
.with_input(input)
.with_output(output)
.with_ram_size(ram_size)
.build()
.expect("Failed to build Machine")
}

pub fn new<R, W>(code: Vec<BaseField>, input: R, output: W) -> Self
pub fn new<R, W>(code: &[BaseField], input: R, output: W) -> Self
where
R: Read + 'static,
W: Write + 'static,
{
Self::new_with_config(code, input, output, Self::DEFAULT_RAM_SIZE)
MachineBuilder::new(code)
.with_input(input)
.with_output(output)
.build()
.expect("Failed to build Machine")
}

pub fn execute(&mut self) -> Result<(), Box<dyn Error>> {
Expand Down Expand Up @@ -185,7 +235,7 @@ mod tests {
#[test]
fn test_default_machine_initialization() {
let code = vec![BaseField::from(43)];
let (machine, _) = create_test_machine(code.clone(), &[]);
let (machine, _) = create_test_machine(&code, &[]);

assert_eq!(machine.program.code, code);
assert_eq!(machine.state.ram.len(), Machine::DEFAULT_RAM_SIZE);
Expand All @@ -198,7 +248,7 @@ mod tests {
let input: &[u8] = &[];
let output = TestWriter::new();
let ram_size = 55000;
let machine = Machine::new_with_config(code.clone(), input, output, ram_size);
let machine = Machine::new_with_config(&code, input, output, ram_size);

assert_eq!(machine.program.code, code);
assert_eq!(machine.state.ram.len(), ram_size);
Expand All @@ -209,7 +259,7 @@ mod tests {
fn test_right_instruction() -> Result<(), Box<dyn Error>> {
// '>>'
let code = vec![BaseField::from(62), BaseField::from(62)];
let (mut machine, _) = create_test_machine(code, &[]);
let (mut machine, _) = create_test_machine(&code, &[]);
machine.execute()?;

assert_eq!(machine.state.registers.mp, BaseField::from(2));
Expand All @@ -220,7 +270,7 @@ mod tests {
fn test_left_instruction() -> Result<(), Box<dyn Error>> {
// '>><'
let code = vec![BaseField::from(62), BaseField::from(62), BaseField::from(60)];
let (mut machine, _) = create_test_machine(code, &[]);
let (mut machine, _) = create_test_machine(&code, &[]);
machine.execute()?;

assert_eq!(machine.state.registers.mp, BaseField::from(1));
Expand All @@ -231,7 +281,7 @@ mod tests {
fn test_plus_instruction() -> Result<(), Box<dyn Error>> {
// '+'
let code = vec![BaseField::from(43)];
let (mut machine, _) = create_test_machine(code, &[]);
let (mut machine, _) = create_test_machine(&code, &[]);
machine.execute()?;

assert_eq!(machine.state.ram[0], BaseField::from(1));
Expand All @@ -243,7 +293,7 @@ mod tests {
fn test_minus_instruction() -> Result<(), Box<dyn Error>> {
// '--'
let code = vec![BaseField::from(45), BaseField::from(45)];
let (mut machine, _) = create_test_machine(code, &[]);
let (mut machine, _) = create_test_machine(&code, &[]);
machine.execute()?;

assert_eq!(machine.state.ram[0], BaseField::from(P - 2));
Expand All @@ -256,7 +306,7 @@ mod tests {
// ',.'
let code = vec![BaseField::from(44), BaseField::from(46)];
let input = b"a";
let (mut machine, output) = create_test_machine(code, input);
let (mut machine, output) = create_test_machine(&code, input);

machine.execute()?;

Expand All @@ -277,7 +327,7 @@ mod tests {
BaseField::from(2),
BaseField::from(43),
];
let (mut machine, _) = create_test_machine(code, &[]);
let (mut machine, _) = create_test_machine(&code, &[]);
machine.execute()?;

assert_eq!(machine.state.ram[0], BaseField::one());
Expand All @@ -298,7 +348,7 @@ mod tests {
BaseField::from(93),
BaseField::from(3),
];
let (mut machine, _) = create_test_machine(code, &[]);
let (mut machine, _) = create_test_machine(&code, &[]);
machine.execute()?;

assert_eq!(machine.state.ram[0], BaseField::from(2));
Expand All @@ -311,7 +361,7 @@ mod tests {
fn test_get_trace() -> Result<(), Box<dyn Error>> {
// '++'
let code = vec![BaseField::from(43), BaseField::from(43)];
let (mut machine, _) = create_test_machine(code, &[]);
let (mut machine, _) = create_test_machine(&code, &[]);
machine.execute()?;

// Initial state + executed instructions
Expand Down Expand Up @@ -356,7 +406,7 @@ mod tests {
fn test_pad_trace() -> Result<(), Box<dyn Error>> {
// '++'
let code = vec![BaseField::from(43), BaseField::from(43)];
let (mut machine, _) = create_test_machine(code, &[]);
let (mut machine, _) = create_test_machine(&code, &[]);
machine.execute()?;

// Initial state + executed instructions
Expand Down
2 changes: 1 addition & 1 deletion crates/brainfuck_vm/src/test_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Clone for TestWriter {
}

// Helper function to create a test machine
pub fn create_test_machine(code: Vec<BaseField>, input: &[u8]) -> (Machine, TestWriter) {
pub fn create_test_machine(code: &[BaseField], input: &[u8]) -> (Machine, TestWriter) {
let input = Cursor::new(input.to_vec());
let output = TestWriter::new();
let test_output = output.clone();
Expand Down
12 changes: 6 additions & 6 deletions crates/brainfuck_vm/tests/integration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ fn test_a_bc() {

let code = compile_from_path(path);
let input = b"a";
let (mut machine, output) = create_test_machine(code, input);
let (mut machine, output) = create_test_machine(&code, input);
machine.execute().unwrap();
let expected_output = b"bc".to_vec();
assert_eq!(output.get_output(), expected_output);
Expand All @@ -27,7 +27,7 @@ fn test_collatz() {

let code = compile_from_path(path);
let input = &[0x37, 10]; // 7 EOF
let (mut machine, output) = create_test_machine(code, input);
let (mut machine, output) = create_test_machine(&code, input);
machine.execute().unwrap();
let expected_output = [0x31, 0x36, 10].to_vec(); // 16 EOF
assert_eq!(output.get_output(), expected_output);
Expand All @@ -38,7 +38,7 @@ fn test_hello_world_1() {
let path = "../../brainfuck_programs/hello1.bf";

let code = compile_from_path(path);
let (mut machine, output) = create_test_machine(code, &[]);
let (mut machine, output) = create_test_machine(&code, &[]);
machine.execute().unwrap();
let expected_output = b"Hello World!\n".to_vec();
assert_eq!(output.get_output(), expected_output);
Expand All @@ -49,7 +49,7 @@ fn test_hello_world_2() {
let path = "../../brainfuck_programs/hello2.bf";

let code = compile_from_path(path);
let (mut machine, output) = create_test_machine(code, &[]);
let (mut machine, output) = create_test_machine(&code, &[]);
machine.execute().unwrap();
let expected_output = b"Hello World!\n".to_vec();
assert_eq!(output.get_output(), expected_output);
Expand All @@ -60,7 +60,7 @@ fn test_hello_world_3() {
let path = "../../brainfuck_programs/hello3.bf";

let code = compile_from_path(path);
let (mut machine, output) = create_test_machine(code, &[]);
let (mut machine, output) = create_test_machine(&code, &[]);
machine.execute().unwrap();
let expected_output = b"Hello, World!\n".to_vec();
assert_eq!(output.get_output(), expected_output);
Expand All @@ -71,7 +71,7 @@ fn test_hello_world_4() {
let path = "../../brainfuck_programs/hello4.bf";

let code = compile_from_path(path);
let (mut machine, output) = create_test_machine(code, &[]);
let (mut machine, output) = create_test_machine(&code, &[]);
machine.execute().unwrap();
let expected_output = b"Hello World!\n".to_vec();
assert_eq!(output.get_output(), expected_output);
Expand Down