use crate::ast::Expr;
use crate::environment::Environment;
use crate::lexer::SchemeNumber;
use std::fmt;
use std::rc::Rc;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Undefined,
Boolean(bool),
Number(SchemeNumber),
String(String),
Character(char),
Symbol(String),
Pair(Box<Value>, Box<Value>),
Nil,
Procedure(Procedure),
Vector(Vec<Value>),
Port(Port),
External(crate::bridge::ExternalObject),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Procedure {
Lambda {
params: Vec<String>,
variadic: bool,
body: Vec<Expr>,
closure: Rc<Environment>,
},
Builtin {
name: String,
arity: Option<usize>,
func: fn(&[Value]) -> crate::Result<Value>,
},
Continuation {
stack: Vec<Value>,
},
}
#[derive(Debug, Clone, PartialEq)]
pub enum Port {
Input,
Output,
String(String),
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Undefined => write!(f, "#<undefined>"),
Value::Boolean(b) => write!(f, "#{}", if *b { "t" } else { "f" }),
Value::Number(n) => write!(f, "{}", n),
Value::String(s) => write!(f, "\"{}\"", s),
Value::Character(c) => match c {
' ' => write!(f, "#\\space"),
'\n' => write!(f, "#\\newline"),
'\t' => write!(f, "#\\tab"),
_ => write!(f, "#\\{}", c),
},
Value::Symbol(s) => write!(f, "{}", s),
Value::Pair(car, cdr) => {
write!(f, "(")?;
write!(f, "{}", car)?;
let mut current = cdr.as_ref();
loop {
match current {
Value::Nil => break,
Value::Pair(car, cdr) => {
write!(f, " {}", car)?;
current = cdr.as_ref();
}
_ => {
write!(f, " . {}", current)?;
break;
}
}
}
write!(f, ")")
}
Value::Nil => write!(f, "()"),
Value::Procedure(proc) => match proc {
Procedure::Lambda { params, variadic, .. } => {
write!(f, "#<procedure (")?;
for (i, param) in params.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
write!(f, "{}", param)?;
}
if *variadic {
write!(f, " ...")?;
}
write!(f, ")>")
}
Procedure::Builtin { name, .. } => write!(f, "#<builtin {}>", name),
Procedure::Continuation { .. } => write!(f, "#<continuation>"),
},
Value::Vector(values) => {
write!(f, "#(")?;
for (i, value) in values.iter().enumerate() {
if i > 0 {
write!(f, " ")?;
}
write!(f, "{}", value)?;
}
write!(f, ")")
}
Value::Port(_) => write!(f, "#<port>"),
Value::External(obj) => write!(f, "#<external:{}>", obj.type_name),
}
}
}
impl Value {
pub fn is_truthy(&self) -> bool {
!matches!(self, Value::Boolean(false))
}
pub fn is_number(&self) -> bool {
matches!(self, Value::Number(_))
}
pub fn as_number(&self) -> Option<&SchemeNumber> {
match self {
Value::Number(n) => Some(n),
_ => None,
}
}
pub fn is_string(&self) -> bool {
matches!(self, Value::String(_))
}
pub fn as_string(&self) -> Option<&str> {
match self {
Value::String(s) => Some(s),
_ => None,
}
}
pub fn is_symbol(&self) -> bool {
matches!(self, Value::Symbol(_))
}
pub fn as_symbol(&self) -> Option<&str> {
match self {
Value::Symbol(s) => Some(s),
_ => None,
}
}
pub fn is_procedure(&self) -> bool {
matches!(self, Value::Procedure(_))
}
pub fn as_procedure(&self) -> Option<&Procedure> {
match self {
Value::Procedure(p) => Some(p),
_ => None,
}
}
pub fn is_pair(&self) -> bool {
matches!(self, Value::Pair(_, _))
}
pub fn as_pair(&self) -> Option<(&Value, &Value)> {
match self {
Value::Pair(car, cdr) => Some((car, cdr)),
_ => None,
}
}
pub fn is_nil(&self) -> bool {
matches!(self, Value::Nil)
}
pub fn is_list(&self) -> bool {
match self {
Value::Nil => true,
Value::Pair(_, cdr) => cdr.is_list(),
_ => false,
}
}
pub fn to_vector(&self) -> Option<Vec<Value>> {
let mut result = Vec::new();
let mut current = self;
loop {
match current {
Value::Nil => return Some(result),
Value::Pair(car, cdr) => {
result.push((**car).clone());
current = cdr;
}
_ => return None, }
}
}
pub fn from_vector(values: Vec<Value>) -> Value {
values.into_iter().rev().fold(Value::Nil, |acc, val| {
Value::Pair(Box::new(val), Box::new(acc))
})
}
pub fn cons(car: Value, cdr: Value) -> Value {
Value::Pair(Box::new(car), Box::new(cdr))
}
pub fn car(&self) -> Option<&Value> {
match self {
Value::Pair(car, _) => Some(car),
_ => None,
}
}
pub fn cdr(&self) -> Option<&Value> {
match self {
Value::Pair(_, cdr) => Some(cdr),
_ => None,
}
}
pub fn list_length(&self) -> Option<usize> {
let mut length = 0;
let mut current = self;
loop {
match current {
Value::Nil => return Some(length),
Value::Pair(_, cdr) => {
length += 1;
current = cdr;
}
_ => return None, }
}
}
pub fn equal(&self, other: &Value) -> bool {
match (self, other) {
(Value::Boolean(a), Value::Boolean(b)) => a == b,
(Value::Number(a), Value::Number(b)) => a == b,
(Value::String(a), Value::String(b)) => a == b,
(Value::Character(a), Value::Character(b)) => a == b,
(Value::Symbol(a), Value::Symbol(b)) => a == b,
(Value::Nil, Value::Nil) => true,
(Value::Pair(car1, cdr1), Value::Pair(car2, cdr2)) => {
car1.equal(car2) && cdr1.equal(cdr2)
}
(Value::Vector(a), Value::Vector(b)) => {
a.len() == b.len() && a.iter().zip(b.iter()).all(|(x, y)| x.equal(y))
}
(Value::External(a), Value::External(b)) => a.id == b.id,
_ => false,
}
}
pub fn eqv(&self, other: &Value) -> bool {
match (self, other) {
(Value::Boolean(a), Value::Boolean(b)) => a == b,
(Value::Number(a), Value::Number(b)) => a == b,
(Value::Character(a), Value::Character(b)) => a == b,
(Value::Symbol(a), Value::Symbol(b)) => a == b,
(Value::Nil, Value::Nil) => true,
_ => std::ptr::eq(self, other),
}
}
pub fn eq(&self, other: &Value) -> bool {
match (self, other) {
(Value::Boolean(a), Value::Boolean(b)) => a == b,
(Value::Number(a), Value::Number(b)) => a == b,
(Value::Character(a), Value::Character(b)) => a == b,
(Value::Symbol(a), Value::Symbol(b)) => a == b,
(Value::Nil, Value::Nil) => true,
_ => std::ptr::eq(self, other),
}
}
}
impl From<bool> for Value {
fn from(b: bool) -> Self {
Value::Boolean(b)
}
}
impl From<i64> for Value {
fn from(i: i64) -> Self {
Value::Number(SchemeNumber::Integer(i))
}
}
impl From<u64> for Value {
fn from(u: u64) -> Self {
Value::Number(SchemeNumber::Integer(u as i64))
}
}
impl From<f64> for Value {
fn from(f: f64) -> Self {
Value::Number(SchemeNumber::Real(f))
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Value::String(s)
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Value::String(s.to_string())
}
}
impl From<char> for Value {
fn from(c: char) -> Self {
Value::Character(c)
}
}
impl From<SchemeNumber> for Value {
fn from(n: SchemeNumber) -> Self {
Value::Number(n)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_display() {
assert_eq!(format!("{}", Value::Boolean(true)), "#t");
assert_eq!(format!("{}", Value::Boolean(false)), "#f");
assert_eq!(format!("{}", Value::Number(SchemeNumber::Integer(42))), "42");
assert_eq!(format!("{}", Value::String("hello".to_string())), "\"hello\"");
assert_eq!(format!("{}", Value::Character('a')), "#\\a");
assert_eq!(format!("{}", Value::Symbol("foo".to_string())), "foo");
assert_eq!(format!("{}", Value::Nil), "()");
}
#[test]
fn test_list_operations() {
let list = Value::from_vector(vec![
Value::from(1i64),
Value::from(2i64),
Value::from(3i64),
]);
assert!(list.is_list());
assert_eq!(list.list_length(), Some(3));
let vec = list.to_vector().unwrap();
assert_eq!(vec.len(), 3);
assert_eq!(vec[0], Value::from(1i64));
}
#[test]
fn test_pair_operations() {
let pair = Value::cons(Value::from(1i64), Value::from(2i64));
assert!(pair.is_pair());
assert_eq!(pair.car(), Some(&Value::from(1i64)));
assert_eq!(pair.cdr(), Some(&Value::from(2i64)));
}
#[test]
fn test_equality() {
let a = Value::from(42i64);
let b = Value::from(42i64);
let c = Value::from(43i64);
assert!(a.equal(&b));
assert!(!a.equal(&c));
assert!(a.eqv(&b));
assert!(!a.eqv(&c));
}
#[test]
fn test_truthiness() {
assert!(Value::Boolean(true).is_truthy());
assert!(!Value::Boolean(false).is_truthy());
assert!(Value::from(0i64).is_truthy());
assert!(Value::Nil.is_truthy());
assert!(Value::String("".to_string()).is_truthy());
}
}