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 HM 6 #5

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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
24 changes: 0 additions & 24 deletions .circleci/config.yaml

This file was deleted.

199 changes: 160 additions & 39 deletions btf_interp/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
//! Provide interpreter implementation for BF program.
use btf_types::BrainFuckProgram;
use btf_types::{BrainFuckProgram, RawInstructions};
use std::fmt::Debug;
use std::io::{Read, Write};
use std::num::NonZeroUsize;

/// Provide trait for cell in Virtual Machine.
pub trait CellKind {
pub trait CellKind: Default + Clone + Debug {
/// Wrapper to increase value by 1 in the cell.
fn wrapping_increment(&mut self);
/// Wrapper to decrease value by 1 in the cell.
Expand All @@ -19,11 +20,11 @@ pub trait CellKind {
impl CellKind for u8 {
/// Implementation for u8 cell type of wrapper to increase value by 1 in it.
fn wrapping_increment(&mut self) {
*self += 1;
*self = self.wrapping_add(1);
}
/// Implementation for u8 cell type of wrapper to decrease value by 1 in it.
fn wrapping_decrement(&mut self) {
*self -= 1;
*self = self.wrapping_sub(1);
}
/// Implementation for u8 cell type of wrapper to set value in it.
fn wrapping_set_value(&mut self, value: u8) {
Expand Down Expand Up @@ -51,12 +52,12 @@ pub enum VMError {
pub struct VirtualMachine<'a, T> {
/// The collection to store elements of the tape.
tape: Vec<T>,
/// The size of the tape.
tape_size: usize,
/// Whether to allow adjust size of the tape of not.
adjust_tape: bool,
rkhudov marked this conversation as resolved.
Show resolved Hide resolved
/// The pointer to the current element of tape.
head: usize,
/// The pointer to the current instructoin.
instruction_pointer: usize,
/// BrainFuck Program.
program: &'a BrainFuckProgram,
}
Expand All @@ -71,11 +72,12 @@ where
size: Option<NonZeroUsize>,
adjust_tape: Option<bool>,
) -> Self {
let tape_size = size.map(NonZeroUsize::get).unwrap_or(3000);
VirtualMachine {
tape: Vec::new(),
tape_size: size.map(NonZeroUsize::get).unwrap_or(3000),
tape: vec![T::default(); tape_size],
adjust_tape: adjust_tape.unwrap_or(false),
head: 0,
instruction_pointer: 0,
program,
}
}
Expand All @@ -92,81 +94,148 @@ where
}

/// Go to the next element in tape. If tape size exceeded, error message is shown.
fn next_element(&mut self) -> Result<(), VMError> {
if self.head + 1 == self.tape_size {
let instruction = &self.program.instructions()[self.head];
return Err(VMError::NextElementNotReachable {
line: instruction.line(),
position: instruction.position(),
});
fn next_element(&mut self) -> Result<usize, VMError> {
if self.head + 1 == self.tape.len() {
if !self.adjust_tape {
let instruction = &self.program.instructions()[self.instruction_pointer];
return Err(VMError::NextElementNotReachable {
line: instruction.line(),
position: instruction.position(),
});
}
self.tape.push(T::default());
}
self.head += 1;
Ok(())
self.instruction_pointer += 1;
Ok(self.instruction_pointer)
}

/// Go to the previous element in tape. If it is the first element, error message is shown.
fn previous_element(&mut self) -> Result<(), VMError> {
fn previous_element(&mut self) -> Result<usize, VMError> {
if self.head == 0 {
let instruction = &self.program.instructions()[self.head];
let instruction = &self.program.instructions()[self.instruction_pointer];
return Err(VMError::PreviousElementNotReachanble {
line: instruction.line(),
position: instruction.position(),
});
}
self.head -= 1;
Ok(())
self.instruction_pointer += 1;
Ok(self.instruction_pointer)
}

/// Add 1 to the element where head is pointing to.
fn wrapped_add(&mut self) -> Result<(), u8> {
fn wrapped_add(&mut self) -> Result<usize, VMError> {
self.tape[self.head].wrapping_increment();
Ok(())
self.instruction_pointer += 1;
Ok(self.instruction_pointer)
}

/// Substract 1 to the element where head is pointing to.
fn wrapped_sub(&mut self) -> Result<(), u8> {
fn wrapped_sub(&mut self) -> Result<usize, VMError> {
self.tape[self.head].wrapping_decrement();
Ok(())
self.instruction_pointer += 1;
Ok(self.instruction_pointer)
}

/// Basic IO read.
fn read(&mut self, reader: &mut impl Read) -> Result<(), VMError> {
/// IO byte read.
fn read(&mut self, reader: &mut impl Read) -> Result<usize, VMError> {
let mut buffer = [0; 1];
match reader.read_exact(&mut buffer) {
Ok(()) => self.tape[self.head].wrapping_set_value(buffer[0]),
Err(_err) => {
let instruction = &self.program.instructions()[self.head];
let instruction = &self.program.instructions()[self.instruction_pointer];
return Err(VMError::IOError {
line: instruction.line(),
position: instruction.position(),
});
}
}
Ok(())
self.instruction_pointer += 1;
Ok(self.instruction_pointer)
}

/// Basic IO write.
fn output(&self, writer: &mut impl Write) -> Result<(), VMError> {
/// IO byte write.
fn output(&mut self, writer: &mut impl Write) -> Result<usize, VMError> {
match writer.write_all(&[self.tape[self.head].wrapping_get_value()]) {
Ok(()) => match writer.flush() {
Ok(()) => Ok(()),
Ok(()) => {
self.instruction_pointer += 1;
Ok(self.instruction_pointer)
}
Err(_err) => {
let instruction = &self.program.instructions()[self.head];
let instruction = &self.program.instructions()[self.instruction_pointer];
Err(VMError::IOError {
line: instruction.line(),
position: instruction.position(),
})
}
},
Err(_err) => {
let instruction = &self.program.instructions()[self.head];
let instruction = &self.program.instructions()[self.instruction_pointer];
Err(VMError::IOError {
line: instruction.line(),
position: instruction.position(),
})
}
}
}

/// Jump to the pointer of correspoding non zero jump command.
fn loop_zero_jump(&self) -> Result<usize, VMError> {
let instruction = &self.program.instructions()[self.instruction_pointer];
let next_instruction_pointer = self
.program
.brackets_map()
.get(&self.instruction_pointer)
.ok_or(VMError::NextElementNotReachable {
line: instruction.line(),
position: instruction.position(),
})?;
Ok(*next_instruction_pointer)
}

/// Jump to the pointer of correspoding zero jump command, if value in cell not 0. Otherwise, go to the next command.
fn loop_non_zero_jump(&mut self) -> Result<usize, VMError> {
let next_instruction_pointer = if self.tape[self.head].wrapping_get_value() != 0 {
let instruction = &self.program.instructions()[self.instruction_pointer];
self.program
.brackets_map()
.get(&self.instruction_pointer)
.ok_or(VMError::NextElementNotReachable {
line: instruction.line(),
position: instruction.position(),
})?
+ 1
} else {
self.instruction_pointer += 1;
self.instruction_pointer
};
Ok(next_instruction_pointer)
}

/// Main interpreter of BF program.
pub fn interpret(
&mut self,
mut input: &mut impl Read,
mut output: &mut impl Write,
) -> Result<(), VMError> {
while self.instruction_pointer < self.program.instructions().len() {
let current_instruction_pointer =
self.program.instructions()[self.instruction_pointer].instruction();
self.instruction_pointer = match current_instruction_pointer {
RawInstructions::IncrementDataPointer => self.next_element()?,
RawInstructions::DecrementDataPointer => self.previous_element()?,
RawInstructions::IncrementByte => self.wrapped_add()?,
RawInstructions::DecrementByte => self.wrapped_sub()?,
RawInstructions::OutputByte => self.output(&mut output)?,
RawInstructions::AcceptByte => self.read(&mut input)?,
RawInstructions::ZeroJump => self.loop_zero_jump()?,
RawInstructions::NonZeroJump => self.loop_non_zero_jump()?,
};
}
Ok(())
}
}

#[cfg(test)]
Expand All @@ -189,8 +258,7 @@ mod tests {
let program = BrainFuckProgram::from_file(&file_path).unwrap();

let default_vm: VirtualMachine<u8> = VirtualMachine::new(&program, None, None);
assert_eq!(default_vm.tape_size, 3000);
assert_eq!(default_vm.tape.len(), 0);
assert_eq!(default_vm.tape.len(), 3000);
assert_eq!(default_vm.head, 0);
assert!(!default_vm.adjust_tape);

Expand All @@ -208,8 +276,7 @@ mod tests {

let vm: VirtualMachine<u8> =
VirtualMachine::new(&program, NonZeroUsize::new(100), Some(true));
assert_eq!(vm.tape_size, 100);
assert_eq!(vm.tape.len(), 0);
assert_eq!(vm.tape.len(), 100);
assert_eq!(vm.head, 0);
assert!(vm.adjust_tape);

Expand Down Expand Up @@ -248,8 +315,8 @@ mod tests {
let program = BrainFuckProgram::from_file(&file_path).unwrap();

let mut vm: VirtualMachine<u8> = VirtualMachine::new(&program, None, None);
assert_eq!(vm.next_element(), Ok(()));
assert_eq!(vm.previous_element(), Ok(()));
assert_eq!(vm.next_element(), Ok(1));
assert_eq!(vm.previous_element(), Ok(2));

drop(tmp_file);
tmp_dir.close().unwrap();
Expand Down Expand Up @@ -288,7 +355,61 @@ mod tests {
let program = BrainFuckProgram::from_file(&file_path).unwrap();

let mut vm: VirtualMachine<u8> = VirtualMachine::new(&program, None, None);
assert_eq!(vm.next_element(), Ok(()));
assert_eq!(vm.next_element(), Ok(1));

drop(tmp_file);
tmp_dir.close().unwrap();
}

#[test]
fn test_adjustable_tape_next_element() {
let tmp_dir = TempDir::new("example").unwrap();
let file_path = tmp_dir.path().join("my-temporary-note.txt");
let tmp_file = File::create(&file_path).unwrap();

let program = BrainFuckProgram::from_file(&file_path).unwrap();

let mut vm: VirtualMachine<u8> =
VirtualMachine::new(&program, NonZeroUsize::new(1), Some(true));
assert_eq!(vm.next_element(), Ok(1));
assert_eq!(vm.next_element(), Ok(2));
assert_eq!(vm.next_element(), Ok(3));

drop(tmp_file);
tmp_dir.close().unwrap();
}

#[test]
fn test_zero_jump_loop() {
let tmp_dir = TempDir::new("example").unwrap();
let file_path = tmp_dir.path().join("my-temporary-note.txt");
let mut tmp_file = File::create(&file_path).unwrap();
let _ = writeln!(tmp_file, "[-]");

let mut program = BrainFuckProgram::from_file(&file_path).unwrap();
let brackets = program.validate_brackets().unwrap();
program.set_brackets_map(brackets);

let vm: VirtualMachine<u8> = VirtualMachine::new(&program, None, None);
assert_eq!(vm.loop_zero_jump(), Ok(2));

drop(tmp_file);
tmp_dir.close().unwrap();
}

#[test]
fn test_non_zero_jump_loop() {
let tmp_dir = TempDir::new("example").unwrap();
let file_path = tmp_dir.path().join("my-temporary-note.txt");
let mut tmp_file = File::create(&file_path).unwrap();
let _ = writeln!(tmp_file, "[-]");

let mut program = BrainFuckProgram::from_file(&file_path).unwrap();
let brackets = program.validate_brackets().unwrap();
program.set_brackets_map(brackets);

let mut vm: VirtualMachine<u8> = VirtualMachine::new(&program, None, None);
assert_eq!(vm.loop_non_zero_jump(), Ok(1));

drop(tmp_file);
tmp_dir.close().unwrap();
Expand Down
Loading