Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- use std::collections::HashMap;
- pub type Value = i32;
- pub type Result = std::result::Result<(), Error>;
- type Stackop = fn(&mut Forth);
- pub struct Builtin {
- stack_min: usize,
- func: Stackop
- }
- pub struct Forth {
- v: Vec<Value>,
- f: HashMap<String,usize>,
- b: HashMap<String,Builtin>,
- s: Vec<Vec<String>>,
- func: Vec<String>,
- }
- #[derive(Debug, PartialEq)]
- pub enum Error {
- DivisionByZero,
- StackUnderflow,
- UnknownWord,
- InvalidWord,
- }
- impl Forth {
- pub fn op_drop(&mut self) {
- self.v.pop();
- }
- pub fn op_dup(&mut self) {
- let temp = self.v[self.v.len() - 1];
- self.v.push(temp);
- }
- pub fn op_over(&mut self) {
- let temp = self.v[self.v.len() - 2];
- self.v.push(temp);
- }
- pub fn op_swap(&mut self) {
- let size = self.v.len();
- let temp = self.v[size - 2];
- self.v[size - 2] = self.v[size - 1];
- self.v[size - 1] = temp;
- }
- pub fn new() -> Forth {
- let mut temp: Vec<Vec<String>> = Vec::new();
- temp.push(Vec::new());
- let mut ftab: HashMap::<String,Builtin> = HashMap::new();
- ftab.insert("drop".to_string(), Builtin{stack_min:1,func:Self::op_drop});
- ftab.insert("dup" .to_string(), Builtin{stack_min:1,func:Self::op_dup});
- ftab.insert("over".to_string(), Builtin{stack_min:2,func:Self::op_over});
- ftab.insert("swap".to_string(), Builtin{stack_min:2,func:Self::op_swap});
- Forth{v: Vec::<Value>::new(), f: HashMap::new(), b:ftab, s: temp, func: Vec::new()}
- }
- pub fn stack(&self) -> &[Value] {
- &self.v
- }
- pub fn eval(&mut self, input: &str) -> Result {
- self.v.clear();
- self.s[0].clear();
- println!("\neval({})", input);
- // Parse the input text and store it in a vector
- {
- let temp: Vec<&str> = input.split(' ').collect();
- let n = temp.len() as i32;
- for x in 0..n as usize {
- self.s[0].push(String::from(temp[x]));
- }
- }
- Self::exec(self, 0)
- }
- pub fn exec(&mut self, sx: usize) -> Result {
- let n: usize = self.s[sx].len();
- let mut collecting = false;
- // User-defined functions store the function name at index 0,
- // so start with 1 unless it's the primary input line (sx == 0)
- let lo:usize = if sx == 0 {0} else {1};
- for x in lo..n as usize {
- let word = &self.s[sx][x];
- if word == ";" {
- if collecting {
- collecting = false;
- let index: usize = self.s.len(); // 1 for main + 1 for each function
- self.s.push(Vec::<String>::new()); // Create vec for new user-def function
- for item in self.func.iter() {
- if self.s[index].len() > 0 && self.appropriate(&item,index)
- {
- let sx = *self.f.get(item).unwrap();
- let n = self.s[sx].len();
- for x in 1..n as usize {
- let symbol = self.s[sx][x].clone();
- self.s[index].push(symbol);
- }
- }
- else {
- self.s[index].push(item.to_string());
- }
- }
- self.f.insert(self.s[index][0].clone().to_lowercase(), index);
- self.func.clear();
- continue;
- }
- continue;
- }
- if collecting {
- self.func.push(String::from(word));
- continue;
- }
- if word == ":" {
- // There must be at least 3 more operands after a colon
- if self.s[sx].len() < x + 4 {return Err(Error::InvalidWord);}
- // In this implementation, numbers are not allowed to be function
- // names.
- match self.s[sx][x+1].parse::<Value>() {
- Ok(_) => { return Err(Error::InvalidWord); },
- _ => ()
- }
- // It's not a number, so we can proceed.
- collecting = true;
- self.func.clear();
- continue;
- }
- // Integer values are just pushed onto the stack
- match word.parse::<Value>() {
- Ok(value) => { self.v.push(value); continue; },
- _ => {}
- }
- // User-defined functions
- let lower = word.to_lowercase();
- if self.f.contains_key(&lower) {
- let sx = *self.f.get(&lower).unwrap();
- match Self::exec(self, sx) {
- Ok(_) => continue,
- Err(e) => return Err(e)
- };
- }
- // Binary arithmetic operators
- if Self::is_op(word) {
- if self.v.len() < 2 {
- return Err(Error::StackUnderflow);
- }
- let b = self.v.pop().unwrap();
- let a = self.v.pop().unwrap();
- let c = match word.as_str() {
- "+" => a + b,
- "-" => a - b,
- "*" => a * b,
- "/" => {if b == 0 {return Err(Error::DivisionByZero);} a / b},
- _ => 0
- };
- self.v.push(c);
- continue;
- }
- // Built-in functions
- let p = self.b.get(&lower);
- match p {
- Some(param) => {
- if self.v.len() < param.stack_min {
- return Err(Error::StackUnderflow);
- }
- (param.func)(self);
- continue;
- },
- _ => { return Err(Error::UnknownWord); }
- }
- } // end loop
- Ok(())
- }
- fn is_op(input: &str) -> bool {
- match input {"+"|"-"|"*"|"/" => true, _ => false}
- }
- fn appropriate(&self, item:&str, index:usize) -> bool
- {
- // Is it appropriate to emit the previous definition of a user-defined function?
- if self.s[index][0] == item {
- true // Yes because we are redefining a previously defined function
- }
- else {
- if index >= self.s.len() {
- false // Guard to be sure index is meaningful
- }
- else {
- if let Some(&sx) = self.f.get(item) {
- // Yes if the previous definition is a single term
- // and therefore will not increase storage requirements.
- // Length 2 means 1 for the function name and 1 for its definition.
- self.s[sx].len() == 2
- }
- else {
- false
- }
- }
- }
- }
- }
- fn main() {
- let cmd = [
- ": f over over + ;",
- "1 1 f f f f f f f f f f f"
- ];
- let mut f = Forth::new();
- for line in cmd {
- f.eval(line);
- println!("stack = {:?}", f.stack());
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement