Skip to content

Instantly share code, notes, and snippets.

@jtlapp
Last active October 20, 2022 19:10
Show Gist options
  • Save jtlapp/41922c2bd81e76e74a4b174e713e4617 to your computer and use it in GitHub Desktop.
Save jtlapp/41922c2bd81e76e74a4b174e713e4617 to your computer and use it in GitHub Desktop.
all working but alloc_attack
// thanks to assistance from Martin Kavik and discord/rust @kpreid
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,
}
enum Operation {
BuiltIn(fn(&mut Stack) -> Result),
Custom(Vec<Token>),
}
type Stack = Vec<Value>;
#[derive(PartialEq, Clone)]
enum Token {
Colon,
Semicolon,
Value(Value),
Word(String),
}
impl Token {
fn new(token: &str) -> Self {
match token {
":" => Token::Colon,
";" => Token::Semicolon,
_ => match token.parse::<Value>() {
Ok(value) => Token::Value(value),
Err(_) => Token::Word(token.into()),
},
}
}
}
pub struct Forth {
op_map: HashMap<String, Operation>,
stack: Stack,
}
impl Forth {
pub fn new() -> Self {
let forth = Self {
op_map: HashMap::from([
("+".into(), Operation::BuiltIn(Forth::add)),
("-".into(), Operation::BuiltIn(Forth::subtract)),
("*".into(), Operation::BuiltIn(Forth::multiply)),
("/".into(), Operation::BuiltIn(Forth::divide)),
("dup".into(), Operation::BuiltIn(Forth::dup)),
("drop".into(), Operation::BuiltIn(Forth::drop)),
("swap".into(), Operation::BuiltIn(Forth::swap)),
("over".into(), Operation::BuiltIn(Forth::over)),
]),
stack: vec![],
};
forth
}
pub fn stack(&self) -> &[Value] {
&self.stack
}
pub fn eval(&mut self, input: &str) -> Result {
let lowercase = input.to_lowercase();
let tokens = lowercase
.split(' ')
.map(|word| Token::new(word))
.collect::<Vec<Token>>();
self.eval_tokens(&tokens)
}
fn eval_tokens(&mut self, tokens: &Vec<Token>) -> Result {
let mut i = 0;
while i < tokens.len() {
match &tokens[i] {
Token::Colon => i = self.parse_word_definition(tokens, i + 1)?,
token => {
i += 1;
match token {
Token::Word(word) => match self.op_map.get(word) {
Some(op) => match op {
Operation::BuiltIn(run) => Ok(run(&mut self.stack)?),
Operation::Custom(tokens) => Ok(self.eval_tokens(&tokens.clone())?),
},
None => Err(Error::UnknownWord),
},
Token::Value(value) => Ok(self.stack.push(*value)),
_ => Err(Error::UnknownWord),
}?
}
}
}
Ok(())
}
fn add(stack: &mut Stack) -> Result {
let b = Self::pop(stack)?;
let a = Self::pop(stack)?;
Ok(stack.push(a + b))
}
fn subtract(stack: &mut Stack) -> Result {
let b = Self::pop(stack)?;
let a = Self::pop(stack)?;
Ok(stack.push(a - b))
}
fn multiply(stack: &mut Stack) -> Result {
let b = Self::pop(stack)?;
let a = Self::pop(stack)?;
Ok(stack.push(a * b))
}
fn divide(stack: &mut Stack) -> Result {
let b = Self::pop(stack)?;
if b == 0 {
return Err(Error::DivisionByZero);
}
let a = Self::pop(stack)?;
Ok(stack.push(a / b))
}
fn dup(stack: &mut Stack) -> Result {
match stack.last() {
Some(value) => Ok(stack.push(*value)),
None => Err(Error::StackUnderflow),
}
}
fn drop(stack: &mut Stack) -> Result {
Self::pop(stack)?;
Ok(())
}
fn swap(stack: &mut Stack) -> Result {
let b = Self::pop(stack)?;
let a = Self::pop(stack)?;
stack.push(b);
Ok(stack.push(a))
}
fn over(stack: &mut Stack) -> Result {
if stack.len() < 2 {
Err(Error::StackUnderflow)
} else {
Ok(stack.push(*stack.get(stack.len() - 2).unwrap()))
}
}
fn pop(stack: &mut Stack) -> std::result::Result<Value, Error> {
match stack.pop() {
Some(value) => Ok(value),
None => Err(Error::StackUnderflow),
}
}
fn parse_word_definition(
&mut self,
tokens: &Vec<Token>,
mut offset: usize,
) -> std::result::Result<usize, Error> {
if offset < tokens.len() {
if let Token::Word(word) = tokens[offset].clone() {
let mut terms: Vec<Token> = vec![];
offset += 1;
while offset < tokens.len() {
let token = tokens[offset].clone();
match token {
Token::Value(_) => terms.push(token),
Token::Word(word) => match self.op_map.get(&word) {
Some(op) => match op {
Operation::BuiltIn(_) => terms.push(Token::Word(word)),
Operation::Custom(subterms) => subterms
.iter()
.for_each(|subterm| terms.push(subterm.clone())),
},
None => return Err(Error::UnknownWord),
},
Token::Semicolon => {
self.op_map.insert(word.into(), Operation::Custom(terms));
return Ok(offset + 1);
}
_ => panic!("unexpected token in word definition"),
}
offset += 1;
}
}
}
Err(Error::InvalidWord)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment