val 0.3.6

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

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

impl Display for Value<'_> {
  fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
    match self {
      Value::Boolean(boolean) => write!(f, "{boolean}"),
      Value::Function(name, _, _, _) => write!(f, "<function: {name}>"),
      Value::List(list) => write!(
        f,
        "[{}]",
        list
          .iter()
          .map(|item| match item {
            Value::String(string) => format!("\'{string}\'"),
            _ => item.to_string(),
          })
          .collect::<Vec<_>>()
          .join(", ")
      ),
      Value::Null => write!(f, "null"),
      Value::Number(number) => write!(f, "{}", number.display()),
      Value::String(string) => write!(f, "{string}"),
    }
  }
}

impl PartialEq for Value<'_> {
  fn eq(&self, other: &Self) -> bool {
    match (self, other) {
      (Value::Boolean(a), Value::Boolean(b)) => a == b,
      (Value::Function(a_name, _, _, _), Value::Function(b_name, _, _, _)) => {
        a_name == b_name
      }
      (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,
    }
  }
}

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

  pub 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!("'{}' is not a list", self),
      })
    }
  }

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

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

  pub 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",
    }
  }
}