Ponser icon

Forth interpreter in Rust

Ponser | PRO | 07/07/22 03:46:38 PM UTC | 0 ⭐ | 7987 👁️ | Never ⏰ | []
Rust |

6.88 KB

|

None

|

0 👍

/

0 👎

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());
    }
}
 

Comments

  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎

    
        
  •  icon
    01/01/70 12:00:00 AM UTC
    Plain Text |

    0 B

    |

    👍

    /

    👎