This post is mirrored from it's original location on dev.to.
You'll note that in part one there wasn't really any code. Sorry about that. Let's fix that now.
If you want to jump to the end the completed source code for stack-vm is on Gitlab.
The first thing we need to build a stack machine is a stack: a data structure that we can push and pop operands into/out of. Because we're using Rust that means we're going to use a Vector and we need it to be generic.
pub struct Stack<T>(Vec<T>)
impl<T> Stack<T> {
pub fn new() -> Stack<T> {
Stack(vec![])
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn push(&mut self, value: T) {
self.0.push(value);
}
pub fn pop(&mut self) -> T {
self.0.pop().expect("Unable to pop from empty stack")
}
}
This gives us a generic stack which can only be pushed and popped. We also added is_empty
to make testing a little easier. Speaking of testing, here's the tests. Note that in our tests we make this a usize
stack to make it nice and easy to work with.
#[cfg(test)]
mod test {
use super::*;
#[test]
fn new() {
let stack: Stack<usize> = Stack::new();
assert!(stack.is_empty());
}
#[test]
fn push() {
let mut stack: Stack<usize> = Stack::new();
stack.push(13);
assert!(!stack.is_empty());
}
#[test]
fn pop() {
let mut stack: Stack<usize> = Stack::new();
stack.push(13);
let value = stack.pop();
assert_eq!(value, 13);
}
#[test]
#[should_panic(expected = "empty stack")]
fn empty_pop() {
let mut stack: Stack<usize> = Stack::new();
stack.pop();
}
}
In the next instalment we will figure out what an instruction is, how to build one and make a nice table to store them in.