psi-lang 0.3.0

A language for configuration and extension
Documentation
use crate::{Action, Expr};
use rustc_hash::FxHashMap as HashMap;
use smartstring::alias::String;

const LOOP: [&str; 3] = ["loop", "for", "while"];

#[derive(Debug, Clone)]
pub enum Symbol {
    Function(Vec<String>, Vec<Action>),
    Expr(Expr),
}

#[derive(Debug)]
pub struct Namespace<'a> {
    env: Vec<(&'a str, HashMap<String, Symbol>)>,
}

impl<'a> Namespace<'a> {
    pub fn new() -> Self {
        Self {
            env: vec![("*", HashMap::default())],
        }
    }

    pub fn enter(&mut self, name: &'a str) {
        self.env.push((name, HashMap::default()));
    }

    pub fn leave(&mut self) {
        self.env.pop();
    }

    pub fn raw_expr(&mut self, id: &str, expr: &Expr) {
        let build = Symbol::Expr(expr.to_owned());
        let last = self.env.len() - 1;
        self.env.get_mut(last).unwrap().1.insert(id.into(), build);
    }

    pub fn insert_expr(&mut self, id: &str, expr: &Expr) {
        let build = Symbol::Expr(expr.to_owned());
        let mut banks = self.env.iter_mut();
        while let Some((name, contents)) = banks.next_back() {
            if ["*", "function"].contains(name) {
                contents.insert(id.into(), build);
                break;
            }
        }
    }

    pub fn insert_fn(&mut self, id: &str, args: &[String], block: &[Action]) {
        let build = Symbol::Function(args.to_owned(), block.to_owned());
        let mut banks = self.env.iter_mut();
        while let Some((name, contents)) = banks.next_back() {
            if ["*", "function"].contains(name) {
                contents.insert(id.into(), build);
                break;
            }
        }
    }

    pub fn get_expr(&mut self, id: &str) -> Option<Expr> {
        let mut banks = self.env.iter_mut();
        while let Some((name, contents)) = banks.next_back() {
            if let Some(Symbol::Expr(e)) = contents.get(id) {
                return Some(e.clone());
            }
            if name == &"function" {
                break;
            }
        }
        None
    }

    pub fn get_fn(&mut self, id: &str) -> Option<(Vec<String>, Vec<Action>)> {
        let mut banks = self.env.iter_mut();
        while let Some((_, contents)) = banks.next_back() {
            if let Some(Symbol::Function(a, b)) = contents.get(id) {
                return Some((a.clone(), b.clone()));
            }
        }
        None
    }

    pub fn in_function(&self) -> bool {
        self.env.iter().any(|(k, _)| k == &"function")
    }

    pub fn in_loop(&self) -> bool {
        self.env.iter().any(|(k, _)| LOOP.contains(k))
    }
}

impl<'a> Default for Namespace<'a> {
    fn default() -> Self {
        Self::new()
    }
}