val 0.4.0

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

#[derive(Clone, Debug)]
pub enum Value<'src> {
  Boolean(bool),
  Function(Function<'src>),
  List(Vec<Self>),
  Null,
  Number(Number),
  String(Cow<'src, str>),
}

impl<'a> Value<'a> {
  pub(crate) fn boolean(&self, span: Span) -> Result<bool, Error> {
    if let Value::Boolean(x) = self {
      Ok(*x)
    } else {
      Err(Error {
        span,
        message: format!("'{self}' is not a boolean"),
      })
    }
  }

  #[must_use]
  pub fn display(&self, config: Config) -> String {
    match self {
      Value::Boolean(boolean) => boolean.to_string(),
      Value::Function(function) => format!("<function: {}>", function.name()),
      Value::List(list) => format!(
        "[{}]",
        list
          .iter()
          .map(|item| match item {
            Value::String(string) => format!("\'{string}\'"),
            _ => item.display(config),
          })
          .collect::<Vec<_>>()
          .join(", ")
      ),
      Value::Null => "null".into(),
      Value::Number(number) => number.display(config),
      Value::String(string) => string.to_string(),
    }
  }

  pub(crate) fn into_list(self, span: Span) -> Result<Vec<Value<'a>>, Error> {
    match self {
      Value::List(x) => Ok(x),
      value => Err(Error {
        span,
        message: format!("'{value}' is not a list"),
      }),
    }
  }

  pub(crate) fn list(&self, span: Span) -> Result<Vec<Value<'a>>, Error> {
    if let Value::List(x) = self {
      Ok(x.clone())
    } else {
      Err(Error {
        span,
        message: format!("'{self}' is not a list"),
      })
    }
  }

  pub(crate) fn number(&self, span: Span) -> Result<Number, Error> {
    if let Value::Number(x) = self {
      Ok(x.clone())
    } else {
      Err(Error {
        span,
        message: format!("'{self}' is not a number"),
      })
    }
  }

  pub(crate) fn string(&self, span: Span) -> Result<&str, Error> {
    if let Value::String(x) = self {
      Ok(x.as_ref())
    } else {
      Err(Error {
        span,
        message: format!("'{self}' is not a string"),
      })
    }
  }

  pub(crate) fn type_name(&self) -> &'static str {
    match self {
      Value::Boolean(_) => "boolean",
      Value::Function(_) => "function",
      Value::List(_) => "list",
      Value::Null => "null",
      Value::Number(_) => "number",
      Value::String(_) => "string",
    }
  }
}

impl Display for Value<'_> {
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
    f.write_str(&self.display(Config::default()))
  }
}

impl PartialEq for Value<'_> {
  fn eq(&self, other: &Self) -> bool {
    match (self, other) {
      (Value::Boolean(a), Value::Boolean(b)) => a == b,
      (Value::Function(a), Value::Function(b)) => a == b,
      (Value::List(a), Value::List(b)) => {
        a.len() == b.len() && a.iter().zip(b.iter()).all(|(a, b)| a == b)
      }
      (Value::Null, Value::Null) => true,
      (Value::Number(a), Value::Number(b)) => a == b,
      (Value::String(a), Value::String(b)) => a == b,
      _ => false,
    }
  }
}