use crate::diagnostics::{Result, Error, error::helpers};
use crate::eval::Value;
use crate::runtime::{GlobalEnvironmentManager, LibraryPathResolver};
use crate::module_system::{SchemeLibraryLoader, BootstrapConfig};
use crate::stdlib::StandardLibrary;
use std::sync::Arc;
use std::collections::HashMap;
use std::time::{Instant, Duration};
#[derive(Debug)]
pub struct BootstrapSystem {
global_env: Arc<GlobalEnvironmentManager>,
scheme_loader: SchemeLibraryLoader,
config: BootstrapConfig,
stats: BootstrapStatistics,
minimal_primitives: MinimalPrimitivesRegistry,
library_resolver: LibraryPathResolver,
}
#[derive(Debug, Default, Clone)]
pub struct BootstrapStatistics {
pub total_time: Duration,
pub primitives_load_time: Duration,
pub libraries_load_time: Duration,
pub primitives_count: usize,
pub libraries_count: usize,
pub memory_usage_bytes: usize,
}
#[derive(Debug, Default)]
pub struct MinimalPrimitivesRegistry {
primitives: HashMap<String, MinimalPrimitive>,
categories: HashMap<PrimitiveCategory, Vec<String>>,
}
#[derive(Debug, Clone)]
pub struct MinimalPrimitive {
pub name: String,
pub implementation: fn(&[Value]) -> Result<Value>,
pub arity_min: usize,
pub arity_max: Option<usize>,
pub category: PrimitiveCategory,
pub documentation: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum PrimitiveCategory {
Arithmetic,
Lists,
Comparison,
Predicates,
Control,
Strings,
IO,
Symbols,
Errors,
System,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BootstrapPhase {
InitializePrimitives,
SetupModuleSystem,
LoadCoreLibraries,
FinalizeEnvironment,
}
impl BootstrapSystem {
pub fn new() -> Result<Self> {
let global_env = Arc::new(GlobalEnvironmentManager::new());
let config = BootstrapConfig::new_default();
let library_resolver = LibraryPathResolver::new()?;
let scheme_loader = SchemeLibraryLoader::new(global_env.clone())?;
Ok(Self {
global_env,
scheme_loader,
config,
stats: BootstrapStatistics::default(),
minimal_primitives: MinimalPrimitivesRegistry::new(),
library_resolver,
})
}
pub fn with_config(config: BootstrapConfig) -> Result<Self> {
let global_env = Arc::new(GlobalEnvironmentManager::new());
let library_resolver = LibraryPathResolver::new()?;
let scheme_loader = SchemeLibraryLoader::with_bootstrap_config(global_env.clone(), config.clone())?;
Ok(Self {
global_env,
scheme_loader,
config,
stats: BootstrapStatistics::default(),
minimal_primitives: MinimalPrimitivesRegistry::new(),
library_resolver,
})
}
pub fn bootstrap(&mut self) -> Result<Arc<GlobalEnvironmentManager>> {
let start_time = Instant::now();
self.run_phase(BootstrapPhase::InitializePrimitives)?;
self.run_phase(BootstrapPhase::SetupModuleSystem)?;
self.run_phase(BootstrapPhase::LoadCoreLibraries)?;
self.run_phase(BootstrapPhase::FinalizeEnvironment)?;
self.stats.total_time = start_time.elapsed();
Ok(self.global_env.clone())
}
fn run_phase(&mut self, phase: BootstrapPhase) -> Result<()> {
match phase {
BootstrapPhase::InitializePrimitives => {
let start = Instant::now();
self.initialize_minimal_primitives()?;
self.stats.primitives_load_time = start.elapsed();
}
BootstrapPhase::SetupModuleSystem => {
self.setup_module_system()?;
}
BootstrapPhase::LoadCoreLibraries => {
let start = Instant::now();
self.load_core_libraries()?;
self.stats.libraries_load_time = start.elapsed();
}
BootstrapPhase::FinalizeEnvironment => {
self.finalize_environment()?;
}
}
Ok(())
}
fn initialize_minimal_primitives(&mut self) -> Result<()> {
let root_env = self.global_env.root_environment();
for (name, primitive) in self.minimal_primitives.primitives.iter() {
let value = Value::minimal_primitive(
name.clone(),
primitive.implementation,
primitive.arity_min,
primitive.arity_max,
);
root_env.define(name.clone(), value);
}
self.stats.primitives_count = self.minimal_primitives.primitives.len();
Ok(())
}
fn setup_module_system(&mut self) -> Result<()> {
for search_path in self.library_resolver.search_paths() {
self.scheme_loader.add_search_path(search_path);
}
if let Ok(bootstrap_dir) = self.library_resolver.resolve_lib_subdir("bootstrap") {
self.scheme_loader.add_search_path(bootstrap_dir);
}
if let Ok(r7rs_dir) = self.library_resolver.resolve_lib_subdir("r7rs") {
self.scheme_loader.add_search_path(r7rs_dir);
}
if let Ok(modules_dir) = self.library_resolver.resolve_lib_subdir("modules") {
self.scheme_loader.add_search_path(modules_dir);
}
Ok(())
}
fn load_core_libraries(&mut self) -> Result<()> {
if self.config.lazy_loading {
let _verified_libraries = self.scheme_loader.bootstrap()?;
return Ok(());
}
let mut loaded_count = 0;
for library_id in &self.config.load_order {
match self.scheme_loader.load_library(library_id) {
Ok(compiled_library) => {
self.install_library_exports(&compiled_library)?;
loaded_count += 1;
}
Err(e) => {
eprintln!("Warning: Failed to load library {}: {}",
crate::module_system::format_module_id(library_id), e);
}
}
}
self.stats.libraries_count = loaded_count;
Ok(())
}
fn finalize_environment(&mut self) -> Result<()> {
let stdlib = StandardLibrary::new();
stdlib.populate_environment(&self.global_env.root_environment());
let _snapshot_generation = self.global_env.create_environment_snapshot()?;
self.stats.memory_usage_bytes = self.estimate_memory_usage();
Ok(())
}
fn install_library_exports(&self, library: &crate::module_system::CompiledSchemeLibrary) -> Result<()> {
let root_env = self.global_env.root_environment();
for (name, value) in &library.module.exports {
root_env.define(name.clone(), value.clone());
}
Ok(())
}
fn estimate_memory_usage(&self) -> usize {
let base_size = std::mem::size_of::<BootstrapSystem>();
let env_size = self.global_env.global_variable_names().len() * 100; base_size + env_size
}
pub fn statistics(&self) -> &BootstrapStatistics {
&self.stats
}
pub fn global_environment(&self) -> Arc<GlobalEnvironmentManager> {
self.global_env.clone()
}
pub fn scheme_loader(&self) -> &SchemeLibraryLoader {
&self.scheme_loader
}
pub fn enable_development_mode(&mut self) {
self.scheme_loader.enable_dev_mode();
}
pub fn library_resolver(&self) -> &LibraryPathResolver {
&self.library_resolver
}
pub fn validate_library_setup(&self) -> Result<crate::runtime::LibraryValidationReport> {
self.library_resolver.validate_library_setup()
}
}
impl MinimalPrimitivesRegistry {
pub fn new() -> Self {
let mut registry = Self {
primitives: HashMap::new(),
categories: HashMap::new(),
};
registry.register_essential_primitives();
registry
}
fn register_essential_primitives(&mut self) {
self.register_primitive(MinimalPrimitive {
name: "+".to_owned(),
implementation: primitive_add,
arity_min: 0,
arity_max: None,
category: PrimitiveCategory::Arithmetic,
documentation: "Addition of numbers".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "-".to_owned(),
implementation: primitive_subtract,
arity_min: 1,
arity_max: None,
category: PrimitiveCategory::Arithmetic,
documentation: "Subtraction of numbers".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "*".to_owned(),
implementation: primitive_multiply,
arity_min: 0,
arity_max: None,
category: PrimitiveCategory::Arithmetic,
documentation: "Multiplication of numbers".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "=".to_owned(),
implementation: primitive_numeric_equal,
arity_min: 2,
arity_max: None,
category: PrimitiveCategory::Comparison,
documentation: "Numeric equality comparison".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "<".to_owned(),
implementation: primitive_less_than,
arity_min: 2,
arity_max: None,
category: PrimitiveCategory::Comparison,
documentation: "Numeric less-than comparison".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "cons".to_owned(),
implementation: primitive_cons,
arity_min: 2,
arity_max: Some(2),
category: PrimitiveCategory::Lists,
documentation: "Construct a pair".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "car".to_owned(),
implementation: primitive_car,
arity_min: 1,
arity_max: Some(1),
category: PrimitiveCategory::Lists,
documentation: "First element of a pair".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "cdr".to_owned(),
implementation: primitive_cdr,
arity_min: 1,
arity_max: Some(1),
category: PrimitiveCategory::Lists,
documentation: "Rest of a pair".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "null?".to_owned(),
implementation: primitive_null_p,
arity_min: 1,
arity_max: Some(1),
category: PrimitiveCategory::Predicates,
documentation: "Test for null value".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "pair?".to_owned(),
implementation: primitive_pair_p,
arity_min: 1,
arity_max: Some(1),
category: PrimitiveCategory::Predicates,
documentation: "Test for pair".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "apply".to_owned(),
implementation: primitive_apply,
arity_min: 2,
arity_max: None,
category: PrimitiveCategory::Control,
documentation: "Apply procedure to arguments".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "error".to_owned(),
implementation: primitive_error,
arity_min: 1,
arity_max: None,
category: PrimitiveCategory::Errors,
documentation: "Signal an error".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "string?".to_owned(),
implementation: primitive_string_p,
arity_min: 1,
arity_max: Some(1),
category: PrimitiveCategory::Predicates,
documentation: "Test for string".to_owned(),
});
self.register_primitive(MinimalPrimitive {
name: "display".to_owned(),
implementation: primitive_display,
arity_min: 1,
arity_max: Some(2),
category: PrimitiveCategory::IO,
documentation: "Display a value".to_owned(),
});
}
fn register_primitive(&mut self, primitive: MinimalPrimitive) {
let name = primitive.name.clone();
let category = primitive.category.clone();
self.primitives.insert(name.clone(), primitive);
self.categories.entry(category)
.or_default()
.push(name);
}
pub fn primitives_in_category(&self, category: &PrimitiveCategory) -> Vec<&MinimalPrimitive> {
if let Some(names) = self.categories.get(category) {
names.iter()
.filter_map(|name| self.primitives.get(name))
.collect()
} else {
Vec::new()
}
}
pub fn get_primitive(&self, name: &str) -> Option<&MinimalPrimitive> {
self.primitives.get(name)
}
pub fn primitive_names(&self) -> Vec<&String> {
self.primitives.keys().collect()
}
}
fn extract_integer_value(value: &Value) -> Option<i64> {
match value {
Value::Literal(literal) => literal.to_i64(),
_ => None,
}
}
fn primitive_add(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Ok(Value::integer(0));
}
let mut result = 0i64;
for arg in args {
if let Some(n) = extract_integer_value(arg) {
result += n;
} else {
return Err(helpers::runtime_error_simple("+ expects numeric arguments"));
}
}
Ok(Value::integer(result))
}
fn primitive_subtract(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Err(helpers::runtime_error_simple("- requires at least one argument"));
}
if args.len() == 1 {
if let Some(n) = extract_integer_value(&args[0]) {
Ok(Value::integer(-n))
} else {
Err(helpers::runtime_error_simple("- expects numeric arguments"))
}
} else {
let mut result = if let Some(n) = extract_integer_value(&args[0]) {
n
} else {
return Err(Box::new(Error::runtime_error("- expects numeric arguments", None)));
};
for arg in &args[1..] {
if let Some(n) = extract_integer_value(arg) {
result -= n;
} else {
return Err(Box::new(Error::runtime_error("- expects numeric arguments", None)));
}
}
Ok(Value::integer(result))
}
}
fn primitive_multiply(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Ok(Value::integer(1));
}
let mut result = 1i64;
for arg in args {
if let Some(n) = extract_integer_value(arg) {
result *= n;
} else {
return Err(Box::new(Error::runtime_error("* expects numeric arguments", None)));
}
}
Ok(Value::integer(result))
}
fn primitive_numeric_equal(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(Error::runtime_error("= requires at least 2 arguments", None)));
}
let first = if let Some(n) = extract_integer_value(&args[0]) {
n
} else {
return Err(Box::new(Error::runtime_error("= expects numeric arguments", None)));
};
for arg in &args[1..] {
if let Some(n_val) = extract_integer_value(arg) {
if first != n_val {
return Ok(Value::boolean(false));
}
} else {
return Err(Box::new(Error::runtime_error("= expects numeric arguments", None)));
}
}
Ok(Value::boolean(true))
}
fn primitive_less_than(args: &[Value]) -> Result<Value> {
if args.len() < 2 {
return Err(Box::new(Error::runtime_error("< requires at least 2 arguments", None)));
}
for i in 0..args.len() - 1 {
let current = if let Some(n) = extract_integer_value(&args[i]) {
n
} else {
return Err(Box::new(Error::runtime_error("< expects numeric arguments", None)));
};
let next = if let Some(n) = extract_integer_value(&args[i + 1]) {
n
} else {
return Err(Box::new(Error::runtime_error("< expects numeric arguments", None)));
};
if current >= next {
return Ok(Value::boolean(false));
}
}
Ok(Value::boolean(true))
}
fn primitive_cons(args: &[Value]) -> Result<Value> {
if args.len() != 2 {
return Err(Box::new(Error::runtime_error("cons requires exactly 2 arguments", None)));
}
Ok(Value::pair(args[0].clone(), args[1].clone()))
}
fn primitive_car(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error("car requires exactly 1 argument", None)));
}
match &args[0] {
Value::Pair(car, _) => Ok((**car).clone()),
_ => Err(Box::new(Error::runtime_error("car expects a pair", None))),
}
}
fn primitive_cdr(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error("cdr requires exactly 1 argument", None)));
}
match &args[0] {
Value::Pair(_, cdr) => Ok((**cdr).clone()),
_ => Err(Box::new(Error::runtime_error("cdr expects a pair", None))),
}
}
fn primitive_null_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error("null? requires exactly 1 argument", None)));
}
Ok(Value::boolean(matches!(args[0], Value::Nil)))
}
fn primitive_pair_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error("pair? requires exactly 1 argument", None)));
}
Ok(Value::boolean(matches!(args[0], Value::Pair(_, _))))
}
fn primitive_string_p(args: &[Value]) -> Result<Value> {
if args.len() != 1 {
return Err(Box::new(Error::runtime_error("string? requires exactly 1 argument", None)));
}
Ok(Value::boolean(matches!(args[0], Value::Literal(crate::ast::Literal::String(_)))))
}
fn primitive_apply(_args: &[Value]) -> Result<Value> {
Err(Box::new(Error::runtime_error("apply not fully implemented in minimal primitives", None)))
}
fn primitive_error(args: &[Value]) -> Result<Value> {
if args.is_empty() {
return Err(Box::new(Error::runtime_error("error requires at least 1 argument", None)));
}
let message = match &args[0] {
Value::Literal(crate::ast::Literal::String(s)) => s.clone(),
_ => format!("{}", args[0]),
};
Err(Error::runtime_error(message, None).boxed())
}
fn primitive_display(args: &[Value]) -> Result<Value> {
if args.is_empty() || args.len() > 2 {
return Err(Box::new(Error::runtime_error("display requires 1 or 2 arguments", None)));
}
let output = args[0].display_string();
println!("{output}");
Ok(Value::Unspecified)
}
impl Default for BootstrapSystem {
fn default() -> Self {
Self::new().expect("Failed to create default bootstrap system")
}
}
impl Value {
pub fn minimal_primitive(
name: String,
implementation: fn(&[Value]) -> Result<Value>,
arity_min: usize,
arity_max: Option<usize>,
) -> Self {
use crate::eval::value::{PrimitiveProcedure, PrimitiveImpl};
use crate::effects::Effect;
Value::Primitive(Arc::new(PrimitiveProcedure {
name,
arity_min,
arity_max,
implementation: PrimitiveImpl::RustFn(implementation),
effects: vec![Effect::Pure], }))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_minimal_primitives_registry() {
let registry = MinimalPrimitivesRegistry::new();
assert!(!registry.primitives.is_empty());
assert!(registry.get_primitive("+").is_some());
assert!(registry.get_primitive("cons").is_some());
assert!(registry.get_primitive("nonexistent").is_none());
let arithmetic_prims = registry.primitives_in_category(&PrimitiveCategory::Arithmetic);
assert!(!arithmetic_prims.is_empty());
}
#[test]
fn test_bootstrap_system_creation() {
let system = BootstrapSystem::new();
assert!(system.is_ok());
}
#[test]
fn test_bootstrap_config() {
let config = BootstrapConfig::new_default();
assert!(!config.essential_primitives.is_empty());
let minimal_config = BootstrapConfig::minimal();
assert!(minimal_config.essential_primitives.len() < config.essential_primitives.len());
}
#[test]
fn test_bootstrap_display_r7rs_compliance() {
let test_string = Value::string("Hello World");
let result = test_string.display_string();
assert_eq!(result, "Hello World");
let test_char = Value::Literal(crate::ast::Literal::Character('x'));
let result = test_char.display_string();
assert_eq!(result, "x");
let test_number = Value::integer(42);
let result = test_number.display_string();
assert_eq!(result, "42");
let test_bool = Value::boolean(true);
let result = test_bool.display_string();
assert_eq!(result, "#t");
}
#[test]
fn test_primitive_arithmetic() {
let args = vec![Value::integer(1), Value::integer(2), Value::integer(3)];
let result = primitive_add(&args).unwrap();
assert_eq!(result, Value::integer(6));
let result = primitive_add(&[]).unwrap();
assert_eq!(result, Value::integer(0));
let args = vec![Value::integer(10), Value::integer(3)];
let result = primitive_subtract(&args).unwrap();
assert_eq!(result, Value::integer(7));
let args = vec![Value::integer(5)];
let result = primitive_subtract(&args).unwrap();
assert_eq!(result, Value::integer(-5));
}
#[test]
fn test_primitive_lists() {
let args = vec![Value::integer(1), Value::integer(2)];
let result = primitive_cons(&args).unwrap();
assert!(matches!(result, Value::Pair(_, _)));
let pair = Value::pair(Value::integer(1), Value::integer(2));
let result = primitive_car(&[pair]).unwrap();
assert_eq!(result, Value::integer(1));
let result = primitive_null_p(&[Value::Nil]).unwrap();
assert_eq!(result, Value::boolean(true));
let result = primitive_null_p(&[Value::integer(42)]).unwrap();
assert_eq!(result, Value::boolean(false));
}
#[test]
fn test_primitive_predicates() {
let result = primitive_string_p(&[Value::string("hello")]).unwrap();
assert_eq!(result, Value::boolean(true));
let result = primitive_string_p(&[Value::integer(42)]).unwrap();
assert_eq!(result, Value::boolean(false));
let pair = Value::pair(Value::integer(1), Value::integer(2));
let result = primitive_pair_p(&[pair]).unwrap();
assert_eq!(result, Value::boolean(true));
let result = primitive_pair_p(&[Value::integer(42)]).unwrap();
assert_eq!(result, Value::boolean(false));
}
#[test]
fn test_primitive_comparison() {
let args = vec![Value::integer(5), Value::integer(5), Value::integer(5)];
let result = primitive_numeric_equal(&args).unwrap();
assert_eq!(result, Value::boolean(true));
let args = vec![Value::integer(5), Value::integer(6)];
let result = primitive_numeric_equal(&args).unwrap();
assert_eq!(result, Value::boolean(false));
let args = vec![Value::integer(1), Value::integer(2), Value::integer(3)];
let result = primitive_less_than(&args).unwrap();
assert_eq!(result, Value::boolean(true));
let args = vec![Value::integer(1), Value::integer(3), Value::integer(2)];
let result = primitive_less_than(&args).unwrap();
assert_eq!(result, Value::boolean(false));
}
}