scheme4r 0.2.1

Scheme interpreter for rust
Documentation
use super::*;

pub(super) fn install(env: &mut Environment) {
    define_builtin(env, "vector?", is_vector);
    define_builtin(env, "vector", vector);
    define_builtin(env, "make-vector", make_vector);
    define_builtin(env, "vector-length", vector_length);
    define_builtin(env, "vector-ref", vector_ref);
    define_builtin(env, "vector-set!", vector_set);
    define_builtin(env, "vector-fill!", vector_fill);
    define_builtin(env, "vector-copy", vector_copy);
    define_builtin(env, "vector-copy!", vector_copy_in_place);
    define_builtin(env, "vector-append", vector_append);
    define_builtin(env, "vector-map", vector_map);
    define_builtin(env, "vector-for-each", vector_for_each);
    define_builtin(env, "vector->list", vector_to_list);
    define_builtin(env, "list->vector", list_to_vector);
}

fn is_vector(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    expect_arity("vector?", args, 1)?;
    Ok(Value::Boolean(matches!(args[0], Value::Vector(_))))
}

fn vector(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    Ok(Value::vector(args.to_vec()))
}

fn make_vector(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    if args.is_empty() || args.len() > 2 {
        return Err(SchemeError::arity("'make-vector' expects 1 or 2 arguments"));
    }
    let len = expect_index("make-vector", &args[0])?;
    let fill = args.get(1).cloned().unwrap_or(Value::Unspecified);
    Ok(Value::vector(vec![fill; len]))
}

fn vector_length(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    expect_arity("vector-length", args, 1)?;
    let vector = expect_vector("vector-length", &args[0])?;
    let len = vector.borrow().len() as i64;
    Ok(Value::Number(len))
}

fn vector_ref(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    expect_arity("vector-ref", args, 2)?;
    let vector = expect_vector("vector-ref", &args[0])?;
    let index = expect_index("vector-ref", &args[1])?;
    let value = vector
        .borrow()
        .get(index)
        .cloned()
        .ok_or_else(|| SchemeError::runtime("'vector-ref' index out of range"))?;
    Ok(value)
}

fn vector_set(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    expect_arity("vector-set!", args, 3)?;
    let vector = expect_vector("vector-set!", &args[0])?;
    let index = expect_index("vector-set!", &args[1])?;
    let mut vector = vector.borrow_mut();
    let slot = vector
        .get_mut(index)
        .ok_or_else(|| SchemeError::runtime("'vector-set!' index out of range"))?;
    *slot = args[2].clone();
    Ok(Value::Unspecified)
}

fn vector_fill(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    if args.len() < 2 || args.len() > 4 {
        return Err(SchemeError::arity(
            "'vector-fill!' expects 2 to 4 arguments",
        ));
    }
    let vector = expect_vector("vector-fill!", &args[0])?;
    let fill = args[1].clone();
    let (start, end) = {
        let vector_ref = vector.borrow();
        parse_range("vector-fill!", vector_ref.len(), args.get(2), args.get(3))?
    };

    let mut vector_ref = vector.borrow_mut();
    for slot in &mut vector_ref[start..end] {
        *slot = fill.clone();
    }
    Ok(Value::Unspecified)
}

fn vector_copy(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    if args.is_empty() || args.len() > 3 {
        return Err(SchemeError::arity("'vector-copy' expects 1 to 3 arguments"));
    }
    let vector = expect_vector("vector-copy", &args[0])?;
    let copied = {
        let vector_ref = vector.borrow();
        let (start, end) = parse_range("vector-copy", vector_ref.len(), args.get(1), args.get(2))?;
        vector_ref[start..end].to_vec()
    };
    Ok(Value::vector(copied))
}

fn vector_copy_in_place(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    if args.len() < 3 || args.len() > 5 {
        return Err(SchemeError::arity(
            "'vector-copy!' expects 3 to 5 arguments",
        ));
    }

    let target = expect_vector("vector-copy!", &args[0])?;
    let at = expect_index("vector-copy!", &args[1])?;
    let source = expect_vector("vector-copy!", &args[2])?;
    let copied = {
        let source_ref = source.borrow();
        let (start, end) = parse_range("vector-copy!", source_ref.len(), args.get(3), args.get(4))?;
        source_ref[start..end].to_vec()
    };

    let mut target_ref = target.borrow_mut();
    if at > target_ref.len() || copied.len() > target_ref.len().saturating_sub(at) {
        return Err(SchemeError::runtime("'vector-copy!' range out of bounds"));
    }

    for (offset, value) in copied.into_iter().enumerate() {
        target_ref[at + offset] = value;
    }
    Ok(Value::Unspecified)
}

fn vector_append(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    let mut values = Vec::new();
    for value in args {
        let vector = expect_vector("vector-append", value)?;
        values.extend(vector.borrow().iter().cloned());
    }
    Ok(Value::vector(values))
}

fn vector_map(engine: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    if args.len() < 2 {
        return Err(SchemeError::arity(
            "'vector-map' expects a procedure and at least one vector",
        ));
    }

    let procedure = args[0].clone();
    let value_vectors = collect_parallel_vectors("vector-map", &args[1..])?;
    let mut results = Vec::with_capacity(value_vectors[0].len());

    for index in 0..value_vectors[0].len() {
        let call_args = value_vectors
            .iter()
            .map(|values| values[index].clone())
            .collect::<Vec<_>>();
        results.push(engine.apply(procedure.clone(), engine.current_env(), call_args)?);
    }

    Ok(Value::vector(results))
}

fn vector_for_each(engine: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    if args.len() < 2 {
        return Err(SchemeError::arity(
            "'vector-for-each' expects a procedure and at least one vector",
        ));
    }

    let procedure = args[0].clone();
    let value_vectors = collect_parallel_vectors("vector-for-each", &args[1..])?;

    for index in 0..value_vectors[0].len() {
        let call_args = value_vectors
            .iter()
            .map(|values| values[index].clone())
            .collect::<Vec<_>>();
        engine.apply(procedure.clone(), engine.current_env(), call_args)?;
    }

    Ok(Value::Unspecified)
}

fn vector_to_list(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    if args.is_empty() || args.len() > 3 {
        return Err(SchemeError::arity(
            "'vector->list' expects 1 to 3 arguments",
        ));
    }
    let vector = expect_vector("vector->list", &args[0])?;
    let values = vector.borrow().clone();
    let (start, end) = parse_range("vector->list", values.len(), args.get(1), args.get(2))?;
    Ok(Value::list(values[start..end].to_vec()))
}

fn list_to_vector(_: &Engine, args: &[Value]) -> Result<Value, SchemeError> {
    expect_arity("list->vector", args, 1)?;
    Ok(Value::vector(expect_list("list->vector", &args[0])?))
}

fn collect_parallel_vectors(name: &str, args: &[Value]) -> Result<Vec<Vec<Value>>, SchemeError> {
    let mut value_vectors = Vec::with_capacity(args.len());
    let mut expected_len = None;

    for value in args {
        let vector = expect_vector(name, value)?;
        let values = vector.borrow().clone();
        match expected_len {
            Some(len) if len != values.len() => {
                return Err(SchemeError::runtime(format!(
                    "'{name}' expected vectors of equal length"
                )));
            }
            None => expected_len = Some(values.len()),
            _ => {}
        }
        value_vectors.push(values);
    }

    Ok(value_vectors)
}