val 0.4.2

An arbitrary precision calculator language
Documentation
use super::*;

#[derive(Clone, Default)]
pub struct Environment {
  pub(crate) config: Config,
  pub(crate) frame: Rc<RefCell<Frame>>,
}

impl Environment {
  pub fn add_function(&self, name: &str, function: Function) {
    let mut frame = self.frame.borrow_mut();

    frame.symbols.entry(name.to_owned()).or_default().function = Some(function);
  }

  pub fn add_symbol(&self, name: &str, value: Value) {
    let mut frame = self.frame.borrow_mut();

    frame.symbols.entry(name.to_owned()).or_default().value = Some(value);
  }

  fn assign_existing_symbol(
    &self,
    name: &str,
    value: Value,
  ) -> std::result::Result<(), Value> {
    let parent = {
      let mut frame = self.frame.borrow_mut();

      match frame.symbols.get_mut(name) {
        Some(symbol) if symbol.value.is_some() => {
          symbol.value = Some(value);
          return Ok(());
        }
        _ => frame.parent.clone(),
      }
    };

    match parent {
      Some(parent) => parent.assign_existing_symbol(name, value),
      None => Err(value),
    }
  }

  pub(crate) fn assign_symbol(&self, name: &str, value: Value) {
    if let Err(value) = self.assign_existing_symbol(name, value) {
      self.add_symbol(name, value);
    }
  }

  pub(crate) fn function(
    &self,
    name: &str,
    span: Span,
  ) -> Result<Function, Error> {
    match self.resolve_function(name) {
      Some(function) => Ok(function),
      None if self.resolve_symbol(name).is_some() => {
        Err(Error::new(span, format!("`{name}` is not a function")))
      }
      None => Err(Error::new(
        span,
        format!("Function `{name}` is not defined"),
      )),
    }
  }

  fn local_function(&self, name: &str) -> Option<Function> {
    let frame = self.frame.borrow();

    let symbol = frame.symbols.get(name)?;

    symbol.function.clone().or_else(|| match &symbol.value {
      Some(Value::Function(function)) => Some(function.clone()),
      _ => None,
    })
  }

  fn local_symbol(&self, name: &str) -> Option<Value> {
    let frame = self.frame.borrow();

    let symbol = frame.symbols.get(name)?;

    symbol
      .value
      .clone()
      .or_else(|| symbol.function.clone().map(Value::Function))
  }

  #[must_use]
  pub fn new(config: Config) -> Self {
    let environment = Self {
      config,
      frame: Rc::new(RefCell::new(Frame::default())),
    };

    for builtin in inventory::iter::<&dyn Builtin> {
      for name in once(builtin.name()).chain(builtin.aliases().iter().copied())
      {
        match builtin.value(config) {
          Value::Function(Function::Builtin {
            arity, function, ..
          }) => {
            environment.add_function(
              name,
              Function::Builtin {
                arity,
                function,
                name,
              },
            );
          }
          Value::Function(function) => {
            environment.add_function(name, function);
          }
          value => {
            environment.add_symbol(name, value);
          }
        }
      }
    }

    environment
  }

  fn resolve_function(&self, name: &str) -> Option<Function> {
    self
      .local_function(name)
      .or_else(|| self.frame.borrow().parent.clone()?.resolve_function(name))
  }

  pub(crate) fn resolve_symbol(&self, name: &str) -> Option<Value> {
    self
      .local_symbol(name)
      .or_else(|| self.frame.borrow().parent.clone()?.resolve_symbol(name))
  }

  pub(crate) fn with_parent(parent: Environment) -> Self {
    Self {
      config: parent.config,
      frame: Rc::new(RefCell::new(Frame {
        parent: Some(parent),
        symbols: HashMap::new(),
      })),
    }
  }
}

impl fmt::Debug for Environment {
  fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
    f.debug_struct("Environment")
      .field("config", &self.config)
      .finish_non_exhaustive()
  }
}