litto 0.1.0

Building blocks for DSL scripting language interpreters that interact with native Rust code.
Documentation
//! The standard library of `tinylang`.

use super::types;
use super::types::Nil;
use super::TinyLang;
use crate::Interpreter;

type Value = <TinyLang as Interpreter>::Value;
type Error = <TinyLang as Interpreter>::Error;
type Expr = <TinyLang as Interpreter>::Expr;
type Env = <TinyLang as Interpreter>::Env;

/// `(begin ...)`. Evaluate multiple expressions one by one.
pub fn begin(env: &Env, args: &[Expr]) -> Result<Value, Error> {
    let mut result = Value::new(Nil);
    for arg in args {
        result = TinyLang::evaluate(env, arg)?;
    }
    Ok(result)
}

/// `(define name value)`. Define a value with the given name in the current
/// environment.
pub fn define(env: &Env, args: &[Expr]) -> Result<Value, Error> {
    if args.len() != 2 {
        return Err(format!("define expected 2 arguments, got {}", args.len()).into());
    }
    match &args[0] {
        Expr::Symbol(name) => {
            let value = TinyLang::evaluate(env, &args[1])?;
            env.set_local(&name, value);
            Ok(Value::new(Nil))
        }
        _ => Err("define requires a symbol".to_string()),
    }
}

/// `(lambda (x) x)`. Return a lambda.
pub fn lambda(env: &Env, args: &[Expr]) -> Result<Value, Error> {
    if args.len() != 2 {
        return Err(format!("lambda expects 2 arguments, got {}", args.len()));
    }
    let body = &args[1];
    let args = &args[0];
    if let Expr::Compound(args) = args {
        Ok(Value::new(types::Procedure::new(
            env.clone(),
            args,
            body.clone(),
        )?))
    } else {
        Err(format!(
            "lambda requires a list of arguments, not {:?}",
            args
        ))
    }
}