Last active
March 26, 2023 21:14
-
-
Save commander-trashdin/bf230e3a639e1ca267332a657375fb87 to your computer and use it in GitHub Desktop.
Shityy Forth impl
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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