jkl-lang 0.1.51

Open Source Programming Language
use crate::expr::{CallableImpl, LiteralValue, NativeFunctionImpl};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::Rc;

#[derive(Clone)]
pub struct Environment {
    pub values: Rc<RefCell<HashMap<String, LiteralValue>>>,
    locals: Rc<RefCell<HashMap<usize, usize>>>,
    pub enclosing: Option<Box<Environment>>,
}

fn clock_impl(_args: &Vec<LiteralValue>) -> LiteralValue {
    let now = std::time::SystemTime::now()
        .duration_since(std::time::SystemTime::UNIX_EPOCH)
        .expect("Could not get system time")
        .as_millis();
    LiteralValue::Number(now as f64 / 1000.0)
}

fn get_globals() -> Rc<RefCell<HashMap<String, LiteralValue>>> {
    let mut env = HashMap::new();
    let fun_impl = NativeFunctionImpl {
        name: "clock".to_string(),
        arity: 0,
        fun: Rc::new(clock_impl),
    };
    let callable_impl = CallableImpl::NativeFunction(fun_impl);
    env.insert("clock".to_string(), LiteralValue::Callable(callable_impl));

    Rc::new(RefCell::new(env))
}

impl Environment {
    pub fn new(locals: HashMap<usize, usize>) -> Self {
        Self {
            values: get_globals(),
            locals: Rc::new(RefCell::new(locals)),
            enclosing: None,
        }
    }

    pub fn resolve(&self, locals: HashMap<usize, usize>) {
        for (key, val) in locals.iter() {
            self.locals.borrow_mut().insert(*key, *val);
        }
    }

    pub fn enclose(&self) -> Environment {
        Self {
            values: Rc::new(RefCell::new(HashMap::new())),
            locals: self.locals.clone(),
            enclosing: Some(Box::new(self.clone())),
        }
    }

    pub fn define(&self, name: String, value: LiteralValue) {
        self.values.borrow_mut().insert(name, value);
    }

    pub fn get(&self, name: &str, expr_id: usize) -> Option<LiteralValue> {
        let distance = self.locals.borrow().get(&expr_id).cloned();
        self.get_internal(name, distance)
    }

    pub fn get_this_instance(&self, super_id: usize) -> Option<LiteralValue> {
        let distance = self
            .locals
            .borrow()
            .get(&super_id)
            .cloned()
            .expect("Could not find 'this' even though 'super' was defined");
        self.get_internal("this", Some(distance - 1))
    }

    pub fn get_distance(&self, expr_id: usize) -> Option<usize> {
        self.locals.borrow().get(&expr_id).cloned()
    }

    fn get_internal(&self, name: &str, distance: Option<usize>) -> Option<LiteralValue> {
        if let None = distance {
            match &self.enclosing {
                None => self.values.borrow().get(name).cloned(),
                Some(env) => env.get_internal(name, distance),
            }
        } else {
            let distance = distance.unwrap();
            if distance == 0 {
                self.values.borrow().get(name).cloned()
            } else {
                match &self.enclosing {
                    None => panic!("Tried to resolve a variable that was defined deeper than the current environment depth"),
                    Some(env) => {
                        assert!(distance > 0);
                        env.get_internal(name, Some(distance - 1))
                    }
                }
            }
        }
    }

    pub fn assign_global(&self, name: &str, value: LiteralValue) -> bool {
        self.assign_internal(name, value, None)
    }

    pub fn assign(&self, name: &str, value: LiteralValue, expr_id: usize) -> bool {
        let distance = self.locals.borrow().get(&expr_id).cloned();
        self.assign_internal(name, value, distance)
    }

    fn assign_internal(&self, name: &str, value: LiteralValue, distance: Option<usize>) -> bool {
        if let None = distance {
            match &self.enclosing {
                Some(env) => env.assign_internal(name, value, distance),
                None => match self.values.borrow_mut().insert(name.to_string(), value) {
                    Some(_) => true,
                    None => false,
                },
            }
        } else {
            let distance = distance.unwrap();
            if distance == 0 {
                self.values.borrow_mut().insert(name.to_string(), value);
                true
            } else {
                match &self.enclosing {
                    None => panic!("Tried to define a variable in a too deep level"),
                    Some(env) => env.assign_internal(name, value, Some(distance - 1)),
                };
                true
            }
        }
    }

    #[allow(dead_code)]
    pub fn dump(&self, indent: usize) -> String {
        let mut result = String::new();
        for (key, val) in self.values.borrow().iter() {
            for _ in 0..indent {
                result.push_str(" ");
            }
            result.push_str(&format!("{}: {:?}\n", key, val));
        }
        if let Some(env) = &self.enclosing {
            result.push_str(&env.dump(indent + 2));
        }

        result
    }
}