1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
use super::*;
/// A runtime memory.
pub struct Memory {
size: MemorySize,
/// memory data for [0..]
right_data: Vec<u8>,
/// memory data for [..-1]
left_data: Vec<u8>,
}
impl Memory {
/// Creates a new memory with the given size.
fn new(size: MemorySize) -> Self {
let right_data = if let MemorySize::Fixed(len) = size {
if len > isize::MAX as usize {
panic!("memory size larger han isize::MAX is not supported.");
}
vec![0; len]
} else {
vec![]
};
let left_data = vec![];
Self {
size,
right_data,
left_data,
}
}
/// Get the mutable reference of the memory data at the given address.
///
/// If the address is out of range, this function returns error [`RuntimeError::OutOfMemoryBounds`].
fn get_mut(&mut self, address: isize) -> Result<&mut u8, RuntimeError> {
if address >= 0 {
if (address as usize) >= self.right_data.len() {
if let MemorySize::Fixed(_) = self.size {
return Err(RuntimeError::OutOfMemoryBounds(address));
}
self.right_data.resize(address as usize + 1, 0);
}
Ok(&mut self.right_data[address as usize])
} else if let MemorySize::BothInfinite = self.size {
let left_address = (-(address + 1)) as usize;
if left_address >= self.left_data.len() {
self.left_data.resize(left_address + 1, 0);
}
Ok(&mut self.left_data[left_address])
} else {
Err(RuntimeError::OutOfMemoryBounds(address))
}
}
}
/// Next of
pub enum NextAction<'a> {
Next,
StepIn(&'a [Instruction]),
}
/// A program runtime.
pub struct Runtime<R, W> {
input: R,
output: W,
memory: Memory,
pointer: isize,
}
impl<R, W> Runtime<R, W>
where
R: Read,
W: Write,
{
/// Create a new runtime with the given input, output and memory size.
pub fn new(input: R, output: W, memsize: MemorySize) -> Self {
Self {
input,
output,
memory: Memory::new(memsize),
pointer: 0,
}
}
// Add operand to the pointer.
fn add_pointer(&mut self, operand: isize) -> Result<(), RuntimeError> {
self.pointer += operand;
Ok(())
}
// Add operand to the data which is pointed by the pointer.
fn add_data(&mut self, operand: isize) -> Result<(), RuntimeError> {
let data = self.memory.get_mut(self.pointer)?;
*data = (*data as isize).wrapping_add(operand) as u8;
Ok(())
}
// Read a byte from the input and store it to the data which is pointed by the pointer.
fn input(&mut self) -> Result<(), RuntimeError> {
let data = self.memory.get_mut(self.pointer)?;
if self.input.read(std::slice::from_mut(data))? == 0 {
Err(RuntimeError::Eof)
} else {
Ok(())
}
}
// Write a byte which is pointed by the pointer to the output.
fn output(&mut self) -> Result<(), RuntimeError> {
let data = self.memory.get_mut(self.pointer)?;
self.output.write_all(std::slice::from_ref(data))?;
Ok(())
}
/// Execute specified instruction and return a next action to be performed.
///
/// If `inst` is [`Instruction::UntilZero`] and the data which is pointed by the pointer is not zero,
/// this function returns [`NextAction::StepIn`] with instructions that `inst` has.
///
/// If `inst` is [`Instruction::UntilZero`] and the data which is pointed by the pointer is zero or
/// `inst` is other instruction, this function returns [`NextAction::Next`].
///
/// In any case, if an error occurred, this function returns that error.
pub fn exec_one<'a>(&mut self, inst: &'a Instruction) -> Result<NextAction<'a>, RuntimeError> {
match inst {
Instruction::PAdd(operand) => self.add_pointer(*operand)?,
Instruction::DAdd(operand) => self.add_data(*operand)?,
Instruction::Output => self.output()?,
Instruction::Input => self.input()?,
Instruction::UntilZero(sub) => {
if *self.memory.get_mut(self.pointer)? != 0 {
return Ok(NextAction::StepIn(sub));
}
}
}
Ok(NextAction::Next)
}
// the following methods are for Brainfuck program debugging.
/// Get the pointer of the runtime.
pub fn get_pointer(&self) -> isize {
self.pointer
}
/// Get the memory data which is pointed by the pointer.
///
/// Returns `None` if the address is out of memory bounds.
pub fn get_data_at_mut(&mut self, address: isize) -> Option<&mut u8> {
self.memory.get_mut(address).ok()
}
}