val 0.4.0

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

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

impl<'src> Environment<'src> {
  pub fn add_function(&self, name: &'src str, function: Function<'src>) {
    self
      .frame
      .borrow_mut()
      .symbols
      .entry(name)
      .or_default()
      .function = Some(function);
  }

  pub fn add_symbol(&self, name: &'src str, value: Value<'src>) {
    self
      .frame
      .borrow_mut()
      .symbols
      .entry(name)
      .or_default()
      .value = Some(value);
  }

  fn assign_existing_symbol(
    &self,
    name: &'src str,
    value: Value<'src>,
  ) -> bool {
    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 true;
        }
        _ => frame.parent.clone(),
      }
    };

    parent.is_some_and(|parent| parent.assign_existing_symbol(name, value))
  }

  pub(crate) fn assign_symbol(&self, name: &'src str, value: Value<'src>) {
    if !self.assign_existing_symbol(name, value.clone()) {
      self.add_symbol(name, value);
    }
  }

  pub(crate) fn function(
    &self,
    name: &str,
    span: Span,
  ) -> Result<Function<'src>, 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<'src>> {
    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<'src>> {
    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 BUILTINS {
      match builtin {
        Builtin::Constant { value, .. } => {
          environment.add_symbol(builtin.name(), Value::Number(value(config)));
        }
        Builtin::Function {
          arity, function, ..
        } => {
          environment.add_function(
            builtin.name(),
            Function::Builtin {
              arity: *arity,
              function: *function,
              name: builtin.name(),
            },
          );
        }
      }
    }

    environment
  }

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

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

  pub(crate) fn with_parent(parent: Environment<'src>) -> 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()
  }
}