val 0.6.0

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

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

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

    frame.symbols.insert(name.to_owned(), 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) => {
          *symbol = value;
          return Ok(());
        }
        None => 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);
    }
  }

  #[must_use]
  pub fn empty(config: Config) -> Self {
    Self {
      config,
      frame: Gc::new(GcCell::new(Frame::default())),
    }
  }

  #[must_use]
  pub fn new(config: Config) -> Self {
    let environment = Self::empty(config);

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

        environment.add_symbol(name, value);
      }
    }

    environment
  }

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

    frame
      .symbols
      .get(name)
      .cloned()
      .or_else(|| frame.parent.as_ref()?.resolve_symbol(name))
  }

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

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

impl Default for Environment {
  fn default() -> Self {
    Self::new(Config::default())
  }
}

impl Finalize for Environment {}

unsafe impl Trace for Environment {
  gc::custom_trace!(this, {
    let Self { config: _, frame } = this;

    unsafe { mark(frame) };
  });
}