Skip to content

Instantly share code, notes, and snippets.

@commander-trashdin
Last active March 26, 2023 21:14
Show Gist options
  • Save commander-trashdin/bf230e3a639e1ca267332a657375fb87 to your computer and use it in GitHub Desktop.
Save commander-trashdin/bf230e3a639e1ca267332a657375fb87 to your computer and use it in GitHub Desktop.
Shityy Forth impl
use std::collections::HashMap;
pub type Value = i32;
pub type Result = std::result::Result<(), Error>;
type Builtin = fn(&mut Vec<Value>) -> Result;
pub struct Forth<'a> {
stack: Vec<Value>,
names: HashMap<String, &'a [&'a str]>,
builtins: HashMap<&'static str, Builtin>,
}
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
DivisionByZero,
StackUnderflow,
UnknownWord,
InvalidWord,
}
impl<'a> Forth<'a > {
pub fn new() -> Forth<'a> {
let mut builtins: HashMap<&'static str, Builtin> = HashMap::with_capacity(8);
builtins.insert("+", |stack: &mut Vec<Value>| -> Result {
if stack.len() < 2 {
return Err(Error::StackUnderflow);
}
let a = stack.pop().unwrap();
let b = stack.pop().unwrap();
stack.push(a + b);
Ok(())
});
builtins.insert("-", |stack: &mut Vec<Value>| -> Result {
if stack.len() < 2 {
return Err(Error::StackUnderflow);
}
let a = stack.pop().unwrap();
let b = stack.pop().unwrap();
stack.push(b - a);
Ok(())
});
builtins.insert("*", |stack: &mut Vec<Value>| -> Result {
if stack.len() < 2 {
return Err(Error::StackUnderflow);
}
let a = stack.pop().unwrap();
let b = stack.pop().unwrap();
stack.push(a * b);
Ok(())
});
builtins.insert("/", |stack: &mut Vec<Value>| -> Result {
if stack.len() < 2 {
return Err(Error::StackUnderflow);
}
if *stack.last().unwrap() == 0 {
return Err(Error::DivisionByZero);
}
let a = stack.pop().unwrap();
let b = stack.pop().unwrap();
stack.push(b / a);
Ok(())
});
builtins.insert("dup", |stack: &mut Vec<Value>| -> Result {
if stack.len() < 1 {
return Err(Error::StackUnderflow);
}
stack.push(*stack.last().unwrap());
Ok(())
});
builtins.insert("drop", |stack: &mut Vec<Value>| -> Result {
if stack.len() < 1 {
return Err(Error::StackUnderflow);
}
stack.pop();
Ok(())
});
builtins.insert("swap", |stack: &mut Vec<Value>| -> Result {
if stack.len() < 2 {
return Err(Error::StackUnderflow);
}
let len = stack.len();
stack.swap(len - 1, len - 2);
Ok(())
});
builtins.insert("over", |stack: &mut Vec<Value>| -> Result {
if stack.len() < 2 {
return Err(Error::StackUnderflow);
}
stack.push(*stack.get(stack.len() - 2).unwrap());
Ok(())
});
Forth {
names: HashMap::new(),
stack: Vec::new(),
builtins,
}
}
pub fn stack(&self) -> &[Value] {
&self.stack[..]
}
fn _eval(&mut self, operands: &'a [&str]) -> Result {
if operands[0] == ":" {
match operands.get(1) {
None => return Err(Error::InvalidWord),
Some(name) => match name.parse::<Value>() {
Ok(_) => return Err(Error::InvalidWord),
Err(_) => match operands.iter().position(|&op| op == ";") {
None => return Err(Error::UnknownWord),
Some(pos) => {
self.names
.insert(name.to_lowercase().to_string(), &operands[2..pos]);
return self._eval(&operands[pos + 1..]);
}
},
},
}
} else {
for operand in operands {
let operand = operand.to_lowercase().to_string();
let maybe_code = self.names.get(&operand);
match maybe_code {
Some(code) => self._eval(&code)?,
None => match operand.parse::<Value>() {
Ok(num) => self.stack.push(num),
Err(_) => {
match self.builtins.get(operand.as_str()) {
Some(foo) => foo(&mut self.stack)?,
None => return Err(Error::UnknownWord),
};
()
}
},
};
}
}
Ok(())
}
pub fn eval(&mut self, input: &'a str) -> Result {
if input.len() == 0 {
return Ok(());
}
let operands = input.split(" ").collect::<Vec<&str>>();
self._eval(&operands)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment