Skip to content

Instantly share code, notes, and snippets.

@jtlapp
Created October 19, 2022 01:32
Show Gist options
  • Save jtlapp/2e5509faac7f5307aa5d09905b6dc514 to your computer and use it in GitHub Desktop.
Save jtlapp/2e5509faac7f5307aa5d09905b6dc514 to your computer and use it in GitHub Desktop.
Exercism rust Forth exercise using closures
// Implements https://exercism.org/tracks/rust/exercises/forth. Errors on line 168.
use std::cell::RefCell;
use std::collections::HashMap;
pub type Value = i32;
pub type Result = std::result::Result<(), Error>;
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
DivisionByZero,
StackUnderflow,
UnknownWord,
InvalidWord,
}
type Operation = dyn Fn(&mut Stack) -> Result;
type OperationMap = HashMap<String, Box<Operation>>;
type Stack = Vec<Value>;
pub struct Forth {
op_map: RefCell<OperationMap>,
stack: Stack,
}
impl Forth {
pub fn new() -> Self {
// discord/rust @kpreid provided assistance with initialization
let forth = Forth {
op_map: RefCell::new(HashMap::from([
("+".into(), Box::new(|s: &mut Stack| Forth::add(s)) as _),
(
"-".into(),
Box::new(|s: &mut Stack| Forth::subtract(s)) as _,
),
(
"*".into(),
Box::new(|s: &mut Stack| Forth::multiply(s)) as _,
),
("/".into(), Box::new(|s: &mut Stack| Forth::divide(s)) as _),
("dup".into(), Box::new(|s: &mut Stack| Forth::dup(s)) as _),
("drop".into(), Box::new(|s: &mut Stack| Forth::drop(s)) as _),
("swap".into(), Box::new(|s: &mut Stack| Forth::swap(s)) as _),
("over".into(), Box::new(|s: &mut Stack| Forth::over(s)) as _),
])),
stack: vec![],
};
forth
}
pub fn stack(&self) -> &[Value] {
&self.stack
}
pub fn eval(&mut self, input: &str) -> Result {
let terms = input.split(' ').collect::<Vec<&str>>();
let mut i = 0;
while i < terms.len() {
let term = &terms[i];
if *term == ":" {
i = self.define_word(&terms, i + 1)?;
} else {
Self::eval_term(&self.op_map.borrow(), term, &mut self.stack)?
}
}
Ok(())
}
fn eval_term(op_map: &OperationMap, term: &str, stack: &mut Stack) -> Result {
match op_map.get(term) {
Some(op) => Ok(op(stack)?),
None => match term.parse::<Value>() {
Ok(value) => Ok(stack.push(value)),
Err(_) => return Err(Error::UnknownWord),
},
}
}
fn add(stack: &mut Stack) -> Result {
let sum = Self::pop(stack)? + Self::pop(stack)?;
stack.push(sum);
Ok(())
}
fn subtract(stack: &mut Stack) -> Result {
let diff = -Self::pop(stack)? + Self::pop(stack)?;
stack.push(diff);
Ok(())
}
fn multiply(stack: &mut Stack) -> Result {
let product = Self::pop(stack)? * Self::pop(stack)?;
stack.push(product);
Ok(())
}
fn divide(stack: &mut Stack) -> Result {
let divisor = Self::pop(stack)?;
if divisor == 0 {
return Err(Error::DivisionByZero);
}
let dividend = Self::pop(stack)?;
stack.push(dividend / divisor);
Ok(())
}
fn dup(stack: &mut Stack) -> Result {
match stack.last() {
Some(value) => {
stack.push(*value);
Ok(())
}
None => Err(Error::StackUnderflow),
}
}
fn drop(stack: &mut Stack) -> Result {
Self::pop(stack)?;
Ok(())
}
fn swap(stack: &mut Stack) -> Result {
let last = Self::pop(stack)?;
let prior = Self::pop(stack)?;
stack.push(last);
stack.push(prior);
Ok(())
}
fn over(stack: &mut Stack) -> Result {
match stack.get(stack.len() - 2) {
Some(value) => {
stack.push(*value);
Ok(())
}
None => Err(Error::StackUnderflow),
}
}
fn pop(stack: &mut Stack) -> std::result::Result<Value, Error> {
match stack.pop() {
Some(value) => Ok(value),
None => Err(Error::StackUnderflow),
}
}
fn define_word(
&mut self,
words: &Vec<&str>,
mut i: usize,
) -> std::result::Result<usize, Error> {
if i < words.len() {
let word = words[i];
let mut terms: Vec<String> = vec![];
i += 1;
while i < words.len() {
let term = words[i];
if term == ";" {
return if terms.len() == 0 {
Err(Error::InvalidWord)
} else {
// for moving terms into closure without moving self
let terms_holder = MacroEval { terms, forth: self };
self.op_map.borrow_mut().insert(
word.into(),
Box::new(move |stack: &mut Stack| -> Result {
//ERROR: self would need to be 'static. Why?
terms_holder.do_op(stack)
}),
);
Ok(i + 1)
};
} else {
terms.push(term.into());
}
}
}
Err(Error::InvalidWord)
}
}
struct MacroEval<'a> {
terms: Vec<String>,
forth: &'a Forth,
}
impl<'a> MacroEval<'a> {
fn do_op(&self, stack: &mut Stack) -> Result {
for term in self.terms.iter() {
Forth::eval_term(&self.forth.op_map.borrow(), term, stack)?
}
Ok(())
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment