use super::{Generation, Value};
use crate::diagnostics::{Error, Result};
use std::sync::Arc;
use std::rc::Rc;
pub type Environment = super::value::Environment;
pub struct EnvironmentBuilder {
environment: Rc<Environment>,
#[allow(dead_code)]
generation: Generation,
}
impl EnvironmentBuilder {
pub fn new(generation: Generation) -> Self {
let env = Rc::new(Environment::new(None, generation));
Self {
environment: env,
generation,
}
}
pub fn with_parent(parent: Rc<Environment>, generation: Generation) -> Self {
let env = Rc::new(Environment::new(Some(parent), generation));
Self {
environment: env,
generation,
}
}
pub fn bind(self, name: impl Into<String>, value: Value) -> Self {
self.environment.define(name.into(), value);
self
}
pub fn bind_primitive(
self,
name: impl Into<String>,
proc: super::value::PrimitiveProcedure
) -> Self {
let value = Value::Primitive(Arc::new(proc));
self.bind(name, value)
}
pub fn build(self) -> Rc<Environment> {
self.environment
}
}
pub fn global_environment() -> Rc<Environment> {
thread_local! {
static GLOBAL_ENV: std::cell::OnceCell<Rc<Environment>> = const { std::cell::OnceCell::new() };
}
GLOBAL_ENV.with(|cell| {
cell.get_or_init(create_global_environment).clone()
})
}
fn create_global_environment() -> Rc<Environment> {
let env = Rc::new(Environment::new(None, 0));
env.define("true".to_owned(), Value::t());
env.define("false".to_owned(), Value::f());
env.define("null".to_owned(), Value::Nil);
bind_special_forms_as_identifiers(&env);
let thread_safe_env = env.to_thread_safe();
let stdlib = crate::stdlib::StandardLibrary::new();
stdlib.populate_environment(&thread_safe_env);
thread_safe_env.to_legacy()
}
fn bind_special_forms_as_identifiers(env: &Rc<Environment>) {
use crate::utils::intern_symbol;
let special_forms = [
"lambda", "if", "define", "set!", "quote", "quasiquote", "unquote", "unquote-splicing",
"begin", "let", "let*", "letrec", "cond", "case", "and", "or",
"when", "unless", "do", "delay", "force", "case-lambda",
"make-promise", "promise-force", ];
for &form_name in &special_forms {
let symbol_id = intern_symbol(form_name.to_owned());
let syntax_value = Value::Symbol(symbol_id);
env.define(form_name.to_owned(), syntax_value);
}
env.define("apply".to_owned(), Value::Symbol(intern_symbol("apply".to_owned())));
env.define("list".to_owned(), Value::Symbol(intern_symbol("list".to_owned())));
env.define("cons".to_owned(), Value::Symbol(intern_symbol("cons".to_owned())));
env.define("car".to_owned(), Value::Symbol(intern_symbol("car".to_owned())));
env.define("cdr".to_owned(), Value::Symbol(intern_symbol("cdr".to_owned())));
env.define("null?".to_owned(), Value::Symbol(intern_symbol("null?".to_owned())));
env.define("length".to_owned(), Value::Symbol(intern_symbol("length".to_owned())));
env.define("error".to_owned(), Value::Symbol(intern_symbol("error".to_owned())));
env.define("not".to_owned(), Value::Symbol(intern_symbol("not".to_owned())));
env.define("memv".to_owned(), Value::Symbol(intern_symbol("memv".to_owned())));
}
pub fn primitive_add(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Ok(Value::integer(0));
}
let mut result = 0.0;
for arg in args {
match arg.as_number() {
Some(n) => result += n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {arg}"),
None,
))),
}
}
Ok(Value::number(result))
}
#[allow(dead_code)]
fn primitive_subtract(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Err(Box::new(Error::runtime_error(
"- requires at least one argument",
None,
)));
}
if args.len() == 1 {
match args[0].as_number() {
Some(n) => Ok(Value::number(-n)),
None => Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", args[0]),
None,
))),
}
} else {
let mut result = match args[0].as_number() {
Some(n) => n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", args[0]),
None,
))),
};
for arg in &args[1..] {
match arg.as_number() {
Some(n) => result -= n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {arg}"),
None,
))),
}
}
Ok(Value::number(result))
}
}
#[allow(dead_code)]
fn primitive_multiply(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Ok(Value::integer(1));
}
let mut result = 1.0;
for arg in args {
match arg.as_number() {
Some(n) => result *= n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {arg}"),
None,
))),
}
}
Ok(Value::number(result))
}
#[allow(dead_code)]
fn primitive_divide(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Err(Box::new(Error::runtime_error(
"/ requires at least one argument",
None,
)));
}
if args.len() == 1 {
match args[0].as_number() {
Some(n) => {
if n == 0.0 {
Err(Box::new(Error::runtime_error("Division by zero", None)))
} else {
Ok(Value::number(1.0 / n))
}
}
None => Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", args[0]),
None,
))),
}
} else {
let mut result = match args[0].as_number() {
Some(n) => n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", args[0]),
None,
))),
};
for arg in &args[1..] {
match arg.as_number() {
Some(n) => {
if n == 0.0 {
return Err(Box::new(Error::runtime_error("Division by zero", None)));
}
result /= n;
}
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {arg}"),
None,
))),
}
}
Ok(Value::number(result))
}
}
#[allow(dead_code)]
fn primitive_numeric_equal(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(Error::runtime_error(
"= requires at least two arguments",
None,
)));
}
let first = match args[0].as_number() {
Some(n) => n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", args[0]),
None,
))),
};
for arg in &args[1..] {
match arg.as_number() {
Some(n) => {
if (first - n).abs() > f64::EPSILON {
return Ok(Value::f());
}
}
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {arg}"),
None,
))),
}
}
Ok(Value::t())
}
#[allow(dead_code)]
fn primitive_less_than(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(Error::runtime_error(
"< requires at least two arguments",
None,
)));
}
for window in args.windows(2) {
let a = match window[0].as_number() {
Some(n) => n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", window[0]),
None,
))),
};
let b = match window[1].as_number() {
Some(n) => n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", window[1]),
None,
))),
};
if a >= b {
return Ok(Value::f());
}
}
Ok(Value::t())
}
#[allow(dead_code)]
fn primitive_greater_than(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(Error::runtime_error(
"> requires at least two arguments",
None,
)));
}
for window in args.windows(2) {
let a = match window[0].as_number() {
Some(n) => n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", window[0]),
None,
))),
};
let b = match window[1].as_number() {
Some(n) => n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", window[1]),
None,
))),
};
if a <= b {
return Ok(Value::f());
}
}
Ok(Value::t())
}
#[allow(dead_code)]
fn primitive_cons(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(Error::runtime_error(
format!("cons expects 2 arguments, got {}", args.len()),
None,
)));
}
Ok(Value::pair(args[0].clone(), args[1].clone()))
}
#[allow(dead_code)]
fn primitive_car(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error(
format!("car expects 1 argument, got {}", args.len()),
None,
)));
}
match &args[0] {
Value::Pair(car, _) => Ok((**car).clone()),
_ => Err(Box::new(Error::runtime_error(
format!("car expects a pair, got {}", args[0]),
None,
))),
}
}
#[allow(dead_code)]
fn primitive_cdr(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error(
format!("cdr expects 1 argument, got {}", args.len()),
None,
)));
}
match &args[0] {
Value::Pair(_, cdr) => Ok((**cdr).clone()),
_ => Err(Box::new(Error::runtime_error(
format!("cdr expects a pair, got {}", args[0]),
None,
))),
}
}
#[allow(dead_code)]
fn primitive_number_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error(
format!("number? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_number()))
}
#[allow(dead_code)]
fn primitive_string_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error(
format!("string? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_string()))
}
#[allow(dead_code)]
fn primitive_symbol_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error(
format!("symbol? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_symbol()))
}
#[allow(dead_code)]
fn primitive_pair_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error(
format!("pair? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_pair()))
}
#[allow(dead_code)]
fn primitive_null_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error(
format!("null? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_nil()))
}
#[allow(dead_code)]
fn primitive_procedure_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error(
format!("procedure? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_procedure()))
}
#[allow(dead_code)]
fn primitive_display(args: &[Value]) -> Result<Value> {
if args.is_empty() || args.len() > 2 {
return Err(Box::new(Error::runtime_error(
format!("display expects 1 or 2 arguments, got {}", args.len()),
None,
)));
}
let value = &args[0];
print!("{value}");
Ok(Value::Unspecified)
}
#[allow(dead_code)]
fn primitive_newline(args: &[Value]) -> Result<Value> {
if args.len() > 1 {
return Err(Box::new(Error::runtime_error(
format!("newline expects 0 or 1 arguments, got {}", args.len()),
None,
)));
}
println!();
Ok(Value::Unspecified)
}
#[allow(dead_code)]
fn primitive_less_equal(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(Error::runtime_error(
"<= requires at least two arguments",
None,
)));
}
for window in args.windows(2) {
let a = match window[0].as_number() {
Some(n) => n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", window[0]),
None,
))),
};
let b = match window[1].as_number() {
Some(n) => n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", window[1]),
None,
))),
};
if a > b {
return Ok(Value::boolean(false));
}
}
Ok(Value::boolean(true))
}
#[allow(dead_code)]
fn primitive_greater_equal(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(Error::runtime_error(
">= requires at least two arguments",
None,
)));
}
for window in args.windows(2) {
let a = match window[0].as_number() {
Some(n) => n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", window[0]),
None,
))),
};
let b = match window[1].as_number() {
Some(n) => n,
None => return Err(Box::new(Error::runtime_error(
format!("Expected number, got {}", window[1]),
None,
))),
};
if a < b {
return Ok(Value::boolean(false));
}
}
Ok(Value::boolean(true))
}