Lines
97.34 %
Functions
100 %
Branches
use nomiscript::{
Closure, Fraction, Pair, Symbol, SymbolKind, SymbolTable, Value, list_to_vec, vec_to_list,
};
fn num(n: i64) -> Value {
Value::Number(Fraction::from_integer(n))
}
fn frac(n: i64, d: i64) -> Value {
Value::Number(Fraction::new(n, d))
mod value_types {
use super::*;
#[test]
fn test_type_names() {
let cases = [
(Value::Nil, "nil"),
(Value::Bool(true), "bool"),
(num(42), "number"),
(Value::String("hello".into()), "string"),
(Value::Symbol("x".into()), "symbol"),
(
Value::Pair(Box::new(Pair::new(Value::Nil, Value::Nil))),
"pair",
),
(Value::Vector(vec![]), "vector"),
(Value::Closure(Closure::new(0, vec![])), "closure"),
];
for (value, expected_type) in cases {
assert_eq!(value.type_name(), expected_type);
fn test_truthiness() {
let falsy = [Value::Nil, Value::Bool(false)];
let truthy = [
Value::Bool(true),
num(0),
num(1),
Value::String(String::new()),
Value::String("hello".into()),
Value::Symbol("x".into()),
Value::Vector(vec![]),
Value::Closure(Closure::new(0, vec![])),
for val in falsy {
assert!(!val.is_truthy(), "{val:?} should be falsy");
for val in truthy {
assert!(val.is_truthy(), "{val:?} should be truthy");
mod fractions {
fn test_fraction_equality() {
assert_eq!(frac(1, 2), frac(2, 4));
assert_eq!(frac(3, 6), frac(1, 2));
assert_eq!(frac(100, 100), num(1));
fn test_fraction_normalization() {
let half = Fraction::new(1, 2);
let also_half = Fraction::new(50, 100);
assert_eq!(half, also_half);
let negative = Fraction::new(-1, 2);
let also_negative = Fraction::new(1, -2);
assert_eq!(negative, also_negative);
mod pairs {
fn test_cons() {
let pair = Pair::cons(num(1), num(2));
match pair {
Value::Pair(p) => {
assert_eq!(p.car, num(1));
assert_eq!(p.cdr, num(2));
_ => panic!("expected pair"),
fn test_nested_pairs() {
let inner = Pair::cons(num(2), num(3));
let outer = Pair::cons(num(1), inner);
match outer {
match p.cdr {
Value::Pair(inner_p) => {
assert_eq!(inner_p.car, num(2));
assert_eq!(inner_p.cdr, num(3));
_ => panic!("expected inner pair"),
_ => panic!("expected outer pair"),
mod lists {
fn test_empty_list() {
let list = Value::Nil;
let vec = list_to_vec(&list).unwrap();
assert!(vec.is_empty());
let back = vec_to_list(vec);
assert_eq!(back, Value::Nil);
fn test_single_element_list() {
let list = Pair::cons(num(42), Value::Nil);
assert_eq!(vec, vec![num(42)]);
assert_eq!(back, list);
fn test_multi_element_list() {
let list = Pair::cons(num(1), Pair::cons(num(2), Pair::cons(num(3), Value::Nil)));
assert_eq!(vec, vec![num(1), num(2), num(3)]);
fn test_improper_list() {
let improper = Pair::cons(num(1), num(2));
assert!(list_to_vec(&improper).is_none());
let improper = Pair::cons(num(1), Pair::cons(num(2), num(3)));
fn test_heterogeneous_list() {
let list = Pair::cons(
Pair::cons(
Pair::cons(Value::Bool(true), Value::Nil),
);
assert_eq!(vec.len(), 3);
assert_eq!(vec[0], num(1));
assert_eq!(vec[1], Value::String("hello".into()));
assert_eq!(vec[2], Value::Bool(true));
fn test_nested_list() {
let inner = Pair::cons(num(2), Pair::cons(num(3), Value::Nil));
let outer = Pair::cons(num(1), Pair::cons(inner.clone(), Value::Nil));
let vec = list_to_vec(&outer).unwrap();
assert_eq!(vec.len(), 2);
assert_eq!(vec[1], inner);
fn test_roundtrip_preserves_structure() {
let original = vec![
Value::String("test".into()),
Value::Bool(false),
frac(1, 3),
let as_list = vec_to_list(original.clone());
let back = list_to_vec(&as_list).unwrap();
assert_eq!(original, back);
mod symbols {
fn test_symbol_creation() {
let sym = Symbol::new("foo", SymbolKind::Variable);
assert_eq!(sym.name(), "foo");
assert_eq!(sym.kind(), SymbolKind::Variable);
fn test_symbol_table() {
let mut table = SymbolTable::new();
table.define(Symbol::new("+", SymbolKind::Operator));
table.define(Symbol::new("car", SymbolKind::Native));
assert!(table.contains("+"));
assert!(table.contains("car"));
assert!(!table.contains("undefined"));
assert_eq!(table.lookup("+").unwrap().kind(), SymbolKind::Operator);
assert_eq!(table.lookup("car").unwrap().kind(), SymbolKind::Native);
fn test_symbol_table_builtins() {
let table = SymbolTable::with_builtins();
assert!(table.contains("IF"));
assert!(table.contains("CAR"));
assert_eq!(table.lookup("IF").unwrap().kind(), SymbolKind::SpecialForm);
assert_eq!(table.lookup("CAR").unwrap().kind(), SymbolKind::Native);
mod closures {
fn test_closure_creation() {
let closure = Closure::new(42, vec![num(1), num(2)]);
assert_eq!(closure.code_id, 42);
assert_eq!(closure.env, vec![num(1), num(2)]);
fn test_empty_closure() {
let closure = Closure::new(0, vec![]);
assert_eq!(closure.code_id, 0);
assert!(closure.env.is_empty());
mod large_structures {
fn test_long_list() {
let values: Vec<Value> = (0..1000).map(num).collect();
let list = vec_to_list(values.clone());
let back = list_to_vec(&list).unwrap();
assert_eq!(values, back);
fn test_deeply_nested_pairs() {
let mut val = num(0);
for i in 1..100 {
val = Pair::cons(num(i), val);
match &val {
Value::Pair(p) => assert_eq!(p.car, num(99)),
fn test_large_vector() {
let vec: Vec<Value> = (0..10000).map(num).collect();
let val = Value::Vector(vec.clone());
match val {
Value::Vector(v) => assert_eq!(v.len(), 10000),
_ => panic!("expected vector"),