scheme4r 0.2.3

Scheme interpreter for rust
Documentation
use std::{cell::RefCell, convert::TryFrom, rc::Rc};

use crate::{
    error::SchemeError,
    eval::Engine,
    runtime::{environment::Environment, pair::PairCell, DictRef, PortRef, SchemeString, Value},
};

mod booleans;
mod bytevectors;
mod chars;
mod control;
mod dicts;
mod equivalence;
mod lists;
mod numbers;
mod ports;
mod procedures;
mod strings;
mod symbols;
mod vectors;

pub(crate) fn install(env: &mut Environment) {
    booleans::install(env);
    numbers::install(env);
    chars::install(env);
    strings::install(env);
    symbols::install(env);
    lists::install(env);
    vectors::install(env);
    bytevectors::install(env);
    equivalence::install(env);
    dicts::install(env);
    procedures::install(env);
    control::install(env);
    ports::install(env);
}

pub(super) fn define_builtin(
    env: &mut Environment,
    name: &'static str,
    func: fn(&Engine, &[Value]) -> Result<Value, SchemeError>,
) {
    env.define(name, Value::builtin(name, func));
}

pub(super) fn expect_arity(name: &str, args: &[Value], expected: usize) -> Result<(), SchemeError> {
    if args.len() == expected {
        Ok(())
    } else {
        Err(SchemeError::arity(format!(
            "'{name}' expects {expected} arguments, got {}",
            args.len()
        )))
    }
}

pub(super) fn expect_number(name: &str, value: &Value) -> Result<i64, SchemeError> {
    match value {
        Value::Number(number) => Ok(*number),
        other => Err(SchemeError::type_error(format!(
            "'{name}' expected a number, got {other}"
        ))),
    }
}

pub(super) fn expect_byte(name: &str, value: &Value) -> Result<u8, SchemeError> {
    let number = expect_number(name, value)?;
    u8::try_from(number)
        .map_err(|_| SchemeError::type_error(format!("'{name}' expected a byte value")))
}

pub(super) fn expect_index(name: &str, value: &Value) -> Result<usize, SchemeError> {
    let number = expect_number(name, value)?;
    usize::try_from(number)
        .map_err(|_| SchemeError::type_error(format!("'{name}' expected a non-negative index")))
}

pub(super) fn expect_char(name: &str, value: &Value) -> Result<char, SchemeError> {
    match value {
        Value::Character(ch) => Ok(*ch),
        other => Err(SchemeError::type_error(format!(
            "'{name}' expected a character, got {other}"
        ))),
    }
}

pub(super) fn expect_port(name: &str, value: &Value) -> Result<PortRef, SchemeError> {
    match value {
        Value::Port(port) => Ok(port.clone()),
        other => Err(SchemeError::type_error(format!(
            "'{name}' expected a port, got {other}"
        ))),
    }
}

pub(super) fn expect_input_port(name: &str, value: &Value) -> Result<PortRef, SchemeError> {
    let port = expect_port(name, value)?;
    if port.borrow().is_input() {
        Ok(port)
    } else {
        Err(SchemeError::type_error(format!(
            "'{name}' expected an input port"
        )))
    }
}

pub(super) fn expect_output_port(name: &str, value: &Value) -> Result<PortRef, SchemeError> {
    let port = expect_port(name, value)?;
    if port.borrow().is_output() {
        Ok(port)
    } else {
        Err(SchemeError::type_error(format!(
            "'{name}' expected an output port"
        )))
    }
}

pub(super) fn expect_string_ref(name: &str, value: &Value) -> Result<SchemeString, SchemeError> {
    match value {
        Value::String(text) => Ok(text.clone()),
        other => Err(SchemeError::type_error(format!(
            "'{name}' expected a string, got {other}"
        ))),
    }
}

pub(super) fn expect_string(name: &str, value: &Value) -> Result<String, SchemeError> {
    Ok(expect_string_ref(name, value)?.to_plain_string())
}

pub(super) fn expect_symbol<'a>(name: &str, value: &'a Value) -> Result<&'a str, SchemeError> {
    match value {
        Value::Symbol(symbol) => Ok(symbol.as_str()),
        other => Err(SchemeError::type_error(format!(
            "'{name}' expected a symbol, got {other}"
        ))),
    }
}

pub(super) fn expect_list(name: &str, value: &Value) -> Result<Vec<Value>, SchemeError> {
    value.to_proper_list_vec().ok_or_else(|| {
        SchemeError::type_error(format!("'{name}' expected a proper list, got {value}"))
    })
}

pub(super) fn expect_vector(
    name: &str,
    value: &Value,
) -> Result<Rc<RefCell<Vec<Value>>>, SchemeError> {
    match value {
        Value::Vector(vector) => Ok(vector.clone()),
        other => Err(SchemeError::type_error(format!(
            "'{name}' expected a vector, got {other}"
        ))),
    }
}

pub(super) fn expect_bytevector(
    name: &str,
    value: &Value,
) -> Result<Rc<RefCell<Vec<u8>>>, SchemeError> {
    match value {
        Value::ByteVector(bytes) => Ok(bytes.clone()),
        other => Err(SchemeError::type_error(format!(
            "'{name}' expected a bytevector, got {other}"
        ))),
    }
}

pub(super) fn expect_pair(name: &str, value: &Value) -> Result<Rc<RefCell<PairCell>>, SchemeError> {
    match value {
        Value::Pair(pair) => Ok(pair.clone()),
        other => Err(SchemeError::type_error(format!(
            "'{name}' expected a pair, got {other}"
        ))),
    }
}

pub(super) fn expect_dict(name: &str, value: &Value) -> Result<DictRef, SchemeError> {
    match value {
        Value::Dict(dict) => Ok(dict.clone()),
        other => Err(SchemeError::type_error(format!(
            "'{name}' expected a dict, got {other}"
        ))),
    }
}

pub(super) fn optional_output_port(
    engine: &Engine,
    port_arg: Option<&Value>,
    name: &str,
) -> Result<PortRef, SchemeError> {
    match port_arg {
        Some(value) => expect_output_port(name, value),
        None => Ok(engine.current_output_port()),
    }
}

pub(super) fn optional_input_port(
    engine: &Engine,
    port_arg: Option<&Value>,
    name: &str,
) -> Result<PortRef, SchemeError> {
    match port_arg {
        Some(value) => expect_input_port(name, value),
        None => Ok(engine.current_input_port()),
    }
}

pub(super) fn compare_chain<F>(name: &str, args: &[Value], cmp: F) -> Result<bool, SchemeError>
where
    F: Fn(i64, i64) -> bool,
{
    if args.len() < 2 {
        return Err(SchemeError::arity(format!(
            "'{name}' expects at least 2 arguments"
        )));
    }

    let mut previous = expect_number(name, &args[0])?;
    for value in &args[1..] {
        let current = expect_number(name, value)?;
        if !cmp(previous, current) {
            return Ok(false);
        }
        previous = current;
    }
    Ok(true)
}

pub(super) fn parse_range(
    name: &str,
    len: usize,
    start_arg: Option<&Value>,
    end_arg: Option<&Value>,
) -> Result<(usize, usize), SchemeError> {
    let start = match start_arg {
        Some(value) => expect_index(name, value)?,
        None => 0,
    };
    let end = match end_arg {
        Some(value) => expect_index(name, value)?,
        None => len,
    };

    if start > end || end > len {
        return Err(SchemeError::runtime(format!(
            "'{name}' range out of bounds"
        )));
    }

    Ok((start, end))
}