Lines
98.55 %
Functions
62.96 %
Branches
100 %
use num_rational::Ratio;
pub type Fraction = Ratio<i64>;
#[derive(Debug, Clone, PartialEq)]
pub enum Value {
Nil,
Bool(bool),
Number(Fraction),
String(String),
Symbol(String),
Pair(Box<Pair>),
Vector(Vec<Value>),
Closure(Closure),
Struct { name: String, fields: Vec<Value> },
}
impl Value {
#[must_use]
pub fn is_truthy(&self) -> bool {
!matches!(self, Value::Nil | Value::Bool(false))
pub fn type_name(&self) -> &'static str {
match self {
Value::Nil => "nil",
Value::Bool(_) => "bool",
Value::Number(_) => "number",
Value::String(_) => "string",
Value::Symbol(_) => "symbol",
Value::Pair(_) => "pair",
Value::Vector(_) => "vector",
Value::Closure(_) => "closure",
Value::Struct { .. } => "struct",
pub struct Pair {
pub car: Value,
pub cdr: Value,
impl Pair {
pub fn new(car: Value, cdr: Value) -> Self {
Self { car, cdr }
pub fn cons(car: Value, cdr: Value) -> Value {
Value::Pair(Box::new(Self::new(car, cdr)))
pub struct Closure {
pub code_id: u32,
pub env: Vec<Value>,
impl Closure {
pub fn new(code_id: u32, env: Vec<Value>) -> Self {
Self { code_id, env }
pub fn list_to_vec(mut val: &Value) -> Option<Vec<Value>> {
let mut result = Vec::new();
loop {
match val {
Value::Nil => return Some(result),
Value::Pair(pair) => {
result.push(pair.car.clone());
val = &pair.cdr;
_ => return None,
pub fn vec_to_list(vec: Vec<Value>) -> Value {
vec.into_iter()
.rev()
.fold(Value::Nil, |acc, v| Pair::cons(v, acc))
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_value_truthy() {
assert!(!Value::Nil.is_truthy());
assert!(!Value::Bool(false).is_truthy());
assert!(Value::Bool(true).is_truthy());
assert!(Value::Number(Fraction::from_integer(0)).is_truthy());
assert!(Value::String(String::new()).is_truthy());
assert!(
Value::Struct {
name: "test".to_string(),
fields: vec![]
.is_truthy()
);
fn test_list_conversion() {
let list = Pair::cons(
Value::Number(Fraction::from_integer(1)),
Pair::cons(
Value::Number(Fraction::from_integer(2)),
Pair::cons(Value::Number(Fraction::from_integer(3)), Value::Nil),
),
let vec = list_to_vec(&list).unwrap();
assert_eq!(vec.len(), 3);
let back = vec_to_list(vec);
assert_eq!(back, list);
fn test_improper_list() {
let improper = Pair::cons(
assert!(list_to_vec(&improper).is_none());