#![allow(missing_docs)]
pub mod arithmetic;
pub mod bytevector;
pub mod characters;
pub mod charset;
pub mod concurrency;
pub mod control;
pub mod effects;
pub mod exceptions;
pub mod io;
pub mod lists;
pub mod parameters;
pub mod records_simple;
pub mod srfi23_enhanced;
pub mod srfi9_macro;
pub mod sets;
pub mod bags;
pub mod generators;
pub mod strings;
pub mod system;
pub mod types;
pub mod vectors;
pub mod advanced_io;
pub mod async_io;
pub mod network_io;
pub mod streaming_io;
pub mod platform_io;
pub mod security_io;
pub mod io_integration;
pub mod text;
pub mod text_regex;
pub mod text_algorithms;
pub mod text_srfi135;
pub mod text_performance;
#[cfg(test)]
pub mod text_tests;
pub mod standard_library;
pub use standard_library::*;
use crate::eval::value::{Value, PrimitiveProcedure, PrimitiveImpl, ThreadSafeEnvironment};
use crate::effects::Effect;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BuiltinProcedure {
Add,
Subtract,
Multiply,
Divide,
Equal,
LessThan,
GreaterThan,
Cons,
Car,
Cdr,
List,
Display,
Newline,
Features,
CurrentSecond,
CurrentJiffy,
JiffiesPerSecond,
CommandLine,
GetEnvironmentVariable,
GetEnvironmentVariables,
Exit,
EmergencyExit,
}
fn bind_core_procedures(env: &Arc<ThreadSafeEnvironment>) {
env.define("eq?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "eq?".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_eq),
effects: vec![Effect::Pure],
})));
env.define("eqv?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "eqv?".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_eqv),
effects: vec![Effect::Pure],
})));
env.define("equal?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "equal?".to_string(),
arity_min: 2,
arity_max: Some(2),
implementation: PrimitiveImpl::RustFn(primitive_equal),
effects: vec![Effect::Pure],
})));
env.define("not".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "not".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_not),
effects: vec![Effect::Pure],
})));
env.define("boolean?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "boolean?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_boolean_p),
effects: vec![Effect::Pure],
})));
env.define("boolean=?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "boolean=?".to_string(),
arity_min: 2,
arity_max: None,
implementation: PrimitiveImpl::RustFn(primitive_boolean_equal),
effects: vec![Effect::Pure],
})));
env.define("symbol?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "symbol?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_symbol_p),
effects: vec![Effect::Pure],
})));
env.define("symbol->string".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "symbol->string".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_symbol_to_string),
effects: vec![Effect::Pure],
})));
env.define("string->symbol".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "string->symbol".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_string_to_symbol),
effects: vec![Effect::Pure],
})));
env.define("procedure?".to_string(), Value::Primitive(Arc::new(PrimitiveProcedure {
name: "procedure?".to_string(),
arity_min: 1,
arity_max: Some(1),
implementation: PrimitiveImpl::RustFn(primitive_procedure_p),
effects: vec![Effect::Pure],
})));
}
use crate::diagnostics::{Error as DiagnosticError, Result};
fn primitive_eq(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("eq? expects 2 arguments, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0] == args[1]))
}
fn primitive_eqv(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("eqv? expects 2 arguments, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0] == args[1]))
}
fn primitive_equal(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("equal? expects 2 arguments, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0] == args[1]))
}
fn primitive_not(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("not expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_falsy()))
}
fn primitive_boolean_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("boolean? expects 1 argument, got {}", args.len()),
None,
)));
}
let is_boolean = matches!(args[0], Value::Literal(crate::ast::Literal::Boolean(_)));
Ok(Value::boolean(is_boolean))
}
fn primitive_boolean_equal(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(DiagnosticError::runtime_error(
"boolean=? requires at least 2 arguments".to_string(),
None,
)));
}
for (i, arg) in args.iter().enumerate() {
if !matches!(arg, Value::Literal(crate::ast::Literal::Boolean(_))) {
return Err(Box::new(DiagnosticError::runtime_error(
format!("boolean=? argument {} is not a boolean", i + 1),
None,
)));
}
}
if let Value::Literal(crate::ast::Literal::Boolean(first)) = &args[0] {
for arg in &args[1..] {
if let Value::Literal(crate::ast::Literal::Boolean(val)) = arg {
if first != val {
return Ok(Value::boolean(false));
}
}
}
Ok(Value::boolean(true))
} else {
Ok(Value::boolean(false))
}
}
fn primitive_symbol_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("symbol? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_symbol()))
}
fn primitive_procedure_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("procedure? expects 1 argument, got {}", args.len()),
None,
)));
}
Ok(Value::boolean(args[0].is_procedure()))
}
fn primitive_symbol_to_string(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("symbol->string expects 1 argument, got {}", args.len()),
None,
)));
}
match &args[0] {
Value::Symbol(symbol_id) => {
use crate::utils::symbol::symbol_name;
if let Some(name) = symbol_name(*symbol_id) {
Ok(Value::string(name))
} else {
Err(Box::new(DiagnosticError::runtime_error(
"Invalid symbol ID".to_string(),
None,
)))
}
},
_ => Err(Box::new(DiagnosticError::runtime_error(
"symbol->string requires a symbol argument".to_string(),
None,
))),
}
}
fn primitive_string_to_symbol(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(DiagnosticError::runtime_error(
format!("string->symbol expects 1 argument, got {}", args.len()),
None,
)));
}
match &args[0] {
Value::Literal(crate::ast::Literal::String(s)) => {
use crate::utils::symbol::intern_symbol;
let symbol_id = intern_symbol(s.clone());
Ok(Value::symbol(symbol_id))
},
_ => Err(Box::new(DiagnosticError::runtime_error(
"string->symbol requires a string argument".to_string(),
None,
))),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::symbol::intern_symbol;
#[test]
fn test_boolean_predicate() {
let args = vec![Value::boolean(true)];
let result = primitive_boolean_p(&args).unwrap();
assert_eq!(result, Value::boolean(true));
let args = vec![Value::boolean(false)];
let result = primitive_boolean_p(&args).unwrap();
assert_eq!(result, Value::boolean(true));
let args = vec![Value::integer(42)];
let result = primitive_boolean_p(&args).unwrap();
assert_eq!(result, Value::boolean(false));
let args = vec![Value::string("hello")];
let result = primitive_boolean_p(&args).unwrap();
assert_eq!(result, Value::boolean(false));
}
#[test]
fn test_boolean_equal() {
let args = vec![Value::boolean(true), Value::boolean(true)];
let result = primitive_boolean_equal(&args).unwrap();
assert_eq!(result, Value::boolean(true));
let args = vec![Value::boolean(false), Value::boolean(false)];
let result = primitive_boolean_equal(&args).unwrap();
assert_eq!(result, Value::boolean(true));
let args = vec![Value::boolean(true), Value::boolean(false)];
let result = primitive_boolean_equal(&args).unwrap();
assert_eq!(result, Value::boolean(false));
let args = vec![Value::boolean(true), Value::boolean(true), Value::boolean(true)];
let result = primitive_boolean_equal(&args).unwrap();
assert_eq!(result, Value::boolean(true));
let args = vec![Value::boolean(true), Value::boolean(true), Value::boolean(false)];
let result = primitive_boolean_equal(&args).unwrap();
assert_eq!(result, Value::boolean(false));
}
#[test]
fn test_boolean_equal_errors() {
let args = vec![Value::integer(42), Value::boolean(true)];
let result = primitive_boolean_equal(&args);
assert!(result.is_err());
let args = vec![Value::boolean(true), Value::string("hello")];
let result = primitive_boolean_equal(&args);
assert!(result.is_err());
let args = vec![Value::boolean(true)];
let result = primitive_boolean_equal(&args);
assert!(result.is_err());
let result = primitive_boolean_equal(&[]);
assert!(result.is_err());
}
#[test]
fn test_symbol_predicate() {
let symbol_id = intern_symbol("test");
let args = vec![Value::symbol(symbol_id)];
let result = primitive_symbol_p(&args).unwrap();
assert_eq!(result, Value::boolean(true));
let args = vec![Value::string("test")];
let result = primitive_symbol_p(&args).unwrap();
assert_eq!(result, Value::boolean(false));
let args = vec![Value::integer(42)];
let result = primitive_symbol_p(&args).unwrap();
assert_eq!(result, Value::boolean(false));
}
#[test]
fn test_symbol_to_string() {
let symbol_id = intern_symbol("hello");
let args = vec![Value::symbol(symbol_id)];
let result = primitive_symbol_to_string(&args).unwrap();
assert_eq!(result, Value::string("hello"));
let symbol_id = intern_symbol("world");
let args = vec![Value::symbol(symbol_id)];
let result = primitive_symbol_to_string(&args).unwrap();
assert_eq!(result, Value::string("world"));
}
#[test]
fn test_string_to_symbol() {
let args = vec![Value::string("hello")];
let result = primitive_string_to_symbol(&args).unwrap();
if let Value::Symbol(_) = result {
let back_to_string = primitive_symbol_to_string(&[result]).unwrap();
assert_eq!(back_to_string, Value::string("hello"));
} else {
panic!("Expected symbol result");
}
}
#[test]
fn test_symbol_roundtrip() {
let original = "test-symbol";
let string_val = Value::string(original);
let symbol_val = primitive_string_to_symbol(&[string_val]).unwrap();
let result_val = primitive_symbol_to_string(&[symbol_val]).unwrap();
assert_eq!(result_val, Value::string(original));
}
#[test]
fn test_symbol_string_errors() {
let args = vec![Value::string("not-a-symbol")];
let result = primitive_symbol_to_string(&args);
assert!(result.is_err());
let args = vec![Value::integer(42)];
let result = primitive_string_to_symbol(&args);
assert!(result.is_err());
let result = primitive_symbol_to_string(&[]);
assert!(result.is_err());
let result = primitive_string_to_symbol(&[]);
assert!(result.is_err());
let args = vec![Value::string("a"), Value::string("b")];
let result = primitive_string_to_symbol(&args);
assert!(result.is_err());
}
#[test]
fn test_core_predicates() {
let args = vec![Value::boolean(false)];
let result = primitive_not(&args).unwrap();
assert_eq!(result, Value::boolean(true));
let args = vec![Value::boolean(true)];
let result = primitive_not(&args).unwrap();
assert_eq!(result, Value::boolean(false));
let args = vec![Value::integer(0)]; let result = primitive_not(&args).unwrap();
assert_eq!(result, Value::boolean(false));
let proc = Arc::new(PrimitiveProcedure {
name: "test".to_string(),
arity_min: 0,
arity_max: None,
implementation: PrimitiveImpl::RustFn(|_| Ok(Value::Unspecified)),
effects: vec![Effect::Pure],
});
let args = vec![Value::Primitive(proc)];
let result = primitive_procedure_p(&args).unwrap();
assert_eq!(result, Value::boolean(true));
let args = vec![Value::integer(42)];
let result = primitive_procedure_p(&args).unwrap();
assert_eq!(result, Value::boolean(false));
}
}