use std::collections::BTreeMap;
use std::fmt;
use std::sync::Arc;
use crate::error::StdlibError;
#[derive(Debug, Clone)]
pub enum Value {
Number(f64),
String(String),
Bool(bool),
Nil,
List(Vec<Value>),
Record {
type_name: Option<String>,
fields: BTreeMap<String, Value>,
},
Color { r: f64, g: f64, b: f64, a: f64 },
Result(Box<ResultValue>),
SumVariant {
type_name: String,
variant: String,
fields: Vec<Value>,
},
Function(StdlibFn),
}
#[derive(Clone)]
pub struct StdlibFn(pub Arc<dyn Fn(Vec<Value>) -> Result<Value, StdlibError> + Send + Sync>);
impl StdlibFn {
pub fn new(
f: impl Fn(Vec<Value>) -> Result<Value, StdlibError> + Send + Sync + 'static,
) -> Self {
Self(Arc::new(f))
}
pub fn call(&self, args: Vec<Value>) -> Result<Value, StdlibError> {
(self.0)(args)
}
}
impl fmt::Debug for StdlibFn {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "<function>")
}
}
impl PartialEq for StdlibFn {
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.0, &other.0)
}
}
#[derive(Debug, Clone)]
pub enum ResultValue {
Ok(Value),
Err(Value),
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Value::Number(a), Value::Number(b)) => a == b, (Value::String(a), Value::String(b)) => a == b,
(Value::Bool(a), Value::Bool(b)) => a == b,
(Value::Nil, Value::Nil) => true,
(Value::List(a), Value::List(b)) => a == b,
(Value::Record { fields: a, .. }, Value::Record { fields: b, .. }) => a == b,
(
Value::Color {
r: r1,
g: g1,
b: b1,
a: a1,
},
Value::Color {
r: r2,
g: g2,
b: b2,
a: a2,
},
) => r1 == r2 && g1 == g2 && b1 == b2 && a1 == a2,
(Value::Result(a), Value::Result(b)) => a == b,
(
Value::SumVariant {
type_name: t1,
variant: v1,
fields: f1,
},
Value::SumVariant {
type_name: t2,
variant: v2,
fields: f2,
},
) => t1 == t2 && v1 == v2 && f1 == f2,
(Value::Function(a), Value::Function(b)) => a == b,
_ => false, }
}
}
impl PartialEq for ResultValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(ResultValue::Ok(a), ResultValue::Ok(b)) => a == b,
(ResultValue::Err(a), ResultValue::Err(b)) => a == b,
_ => false,
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Number(n) => {
if n.fract() == 0.0 && n.is_finite() {
write!(f, "{}", *n as i64)
} else {
write!(f, "{n}")
}
}
Value::String(s) => write!(f, "{s}"),
Value::Bool(b) => write!(f, "{b}"),
Value::Nil => write!(f, "nil"),
Value::List(items) => {
write!(f, "[")?;
for (i, item) in items.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
match item {
Value::String(s) => write!(f, "\"{s}\"")?,
other => write!(f, "{other}")?,
}
}
write!(f, "]")
}
Value::Record { type_name, fields } => {
if let Some(name) = type_name {
write!(f, "{name}")?;
}
write!(f, "{{")?;
for (i, (key, val)) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
match val {
Value::String(s) => write!(f, "{key}: \"{s}\"")?,
other => write!(f, "{key}: {other}")?,
}
}
write!(f, "}}")
}
Value::SumVariant {
variant, fields, ..
} => {
write!(f, "{variant}")?;
if !fields.is_empty() {
write!(f, "(")?;
for (i, val) in fields.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{val}")?;
}
write!(f, ")")?;
}
Ok(())
}
Value::Color { r, g, b, a } => {
write!(f, "color({r}, {g}, {b}, {a})")
}
Value::Result(res) => match res.as_ref() {
ResultValue::Ok(v) => write!(f, "Ok({v})"),
ResultValue::Err(v) => write!(f, "Err({v})"),
},
Value::Function(_) => write!(f, "<function>"),
}
}
}
impl Value {
pub fn type_name(&self) -> &str {
match self {
Value::Number(_) => "number",
Value::String(_) => "string",
Value::Bool(_) => "bool",
Value::Nil => "nil",
Value::List(_) => "list",
Value::Record {
type_name: Some(name),
..
} => name.as_str(),
Value::Record {
type_name: None, ..
} => "record",
Value::Color { .. } => "color",
Value::Result(_) => "result",
Value::SumVariant { type_name, .. } => type_name.as_str(),
Value::Function(_) => "function",
}
}
pub fn is_truthy(&self) -> bool {
match self {
Value::Bool(false) => false,
Value::Nil => false,
Value::Number(n) => *n != 0.0,
Value::String(s) => !s.is_empty(),
_ => true, }
}
pub fn ok(self) -> Value {
Value::Result(Box::new(ResultValue::Ok(self)))
}
pub fn err(self) -> Value {
Value::Result(Box::new(ResultValue::Err(self)))
}
pub fn record(fields: BTreeMap<String, Value>) -> Value {
Value::Record {
type_name: None,
fields,
}
}
pub fn named_record(type_name: impl Into<String>, fields: BTreeMap<String, Value>) -> Value {
Value::Record {
type_name: Some(type_name.into()),
fields,
}
}
pub fn unit_variant(type_name: impl Into<String>, variant: impl Into<String>) -> Value {
Value::SumVariant {
type_name: type_name.into(),
variant: variant.into(),
fields: vec![],
}
}
pub fn sum_variant(
type_name: impl Into<String>,
variant: impl Into<String>,
fields: Vec<Value>,
) -> Value {
Value::SumVariant {
type_name: type_name.into(),
variant: variant.into(),
fields,
}
}
pub fn as_number(&self) -> Option<f64> {
match self {
Value::Number(n) => Some(*n),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match self {
Value::String(s) => Some(s),
_ => None,
}
}
pub fn as_bool(&self) -> Option<bool> {
match self {
Value::Bool(b) => Some(*b),
_ => None,
}
}
pub fn as_list(&self) -> Option<&[Value]> {
match self {
Value::List(l) => Some(l),
_ => None,
}
}
pub fn as_record(&self) -> Option<&BTreeMap<String, Value>> {
match self {
Value::Record { fields, .. } => Some(fields),
_ => None,
}
}
pub fn as_variant(&self) -> Option<(&str, &str, &[Value])> {
match self {
Value::SumVariant {
type_name,
variant,
fields,
} => Some((type_name, variant, fields)),
_ => None,
}
}
pub fn as_function(&self) -> Option<&StdlibFn> {
match self {
Value::Function(f) => Some(f),
_ => None,
}
}
pub fn declared_type_name(&self) -> Option<&str> {
match self {
Value::Record {
type_name: Some(name),
..
} => Some(name),
Value::SumVariant { type_name, .. } => Some(type_name),
_ => None,
}
}
}
impl From<f64> for Value {
fn from(n: f64) -> Self {
Value::Number(n)
}
}
impl From<i64> for Value {
fn from(n: i64) -> Self {
Value::Number(n as f64)
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Value::String(s.to_string())
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Value::String(s)
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Bool(b)
}
}
impl From<BTreeMap<String, Value>> for Value {
fn from(fields: BTreeMap<String, Value>) -> Self {
Value::Record {
type_name: None,
fields,
}
}
}