From b622f7f8bd5a1a7150bbc2044bd118508485f6b1 Mon Sep 17 00:00:00 2001 From: Michael Schimmel Date: Sun, 8 Mar 2026 13:09:53 +0100 Subject: [PATCH] Remove unused system library and simplify environment initialization The `system.myc` file, containing macros and core utilities, has been removed as its functionality is now handled by an embedded string. This simplifies the build process and eliminates a file that was no longer strictly necessary. Additionally, the `with_cwd()` method on the `Environment` has been removed. The environment now automatically adds the current working directory and its `rtl` subdirectory to the search paths during initialization. This streamlines the setup for file-based usage and ensures standard search paths are always considered. --- rtl/system.myc | 19 -------------- src/ast/environment.rs | 58 +++++++++++++++++++++++------------------ src/bin/ast.rs | 12 +-------- src/integration_test.rs | 4 +-- src/main.rs | 15 +++-------- src/utils/tester.rs | 32 +++-------------------- 6 files changed, 42 insertions(+), 98 deletions(-) delete mode 100644 rtl/system.myc diff --git a/rtl/system.myc b/rtl/system.myc deleted file mode 100644 index c7d81a4..0000000 --- a/rtl/system.myc +++ /dev/null @@ -1,19 +0,0 @@ -;; Myc System Library -;; Standard macros and core utilities. - -(do - (macro while [cond body] - `((fn [] (if ~cond - (do ~body (again)) - ))) - ) - - ;; Creates a stateful cache (Series) from a stateless stream. - (macro cache [lookback type src] - `(do - (def data (series ~lookback ~type)) - (pipe [~src] (fn [s] (do (push data s)))) - data - ) - ) -) diff --git a/src/ast/environment.rs b/src/ast/environment.rs index 13d3744..7ab5e5e 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -19,7 +19,6 @@ use crate::ast::compiler::macros::{MacroEvaluator, MacroExpander, MacroRegistry} use crate::ast::compiler::optimizer::Optimizer; use crate::ast::compiler::specializer::{FunctionRegistry, MonoCache, Specializer}; use crate::ast::compiler::tco::{ExecNode, TCO}; -use crate::ast::rtl; use crate::ast::rtl::intrinsics; use crate::ast::types::{ Identity, NodeIdentity, Object, Purity, SourceLocation, StaticType, Value, @@ -28,6 +27,9 @@ use crate::ast::types::{ use crate::ast::diagnostics::{DiagnosticLevel, Diagnostics}; pub type PipelineGenerator = Box bool>; +const SYSTEM_LIB_SOURCE: &str = include_str!("system.myc"); +const SYSTEM_LIB_PATH: &str = "/system.myc"; + pub struct CompilationResult { pub ast: Option, pub diagnostics: Diagnostics, @@ -169,22 +171,18 @@ impl Environment { search_paths: Rc::new(RefCell::new(Vec::new())), loaded_modules: Rc::new(RefCell::new(HashSet::new())), }; - env.register_stdlib(); - env - } + crate::ast::rtl::register(&env); - /// Adds the current working directory to the global search paths. - /// This is a builder method to easily initialize an environment for file-based usage. - pub fn with_cwd(self) -> Self { + // Automatically add standard search paths (CWD and CWD/rtl) if let Ok(cwd) = std::env::current_dir() { - self.add_search_path(&cwd); - // Also add the rtl subdirectory if it exists + env.add_search_path(&cwd); let rtl_path = cwd.join("rtl"); if rtl_path.exists() { - self.add_search_path(rtl_path); + env.add_search_path(rtl_path); } } - self + + env } pub fn add_search_path(&self, path: impl AsRef) { @@ -276,16 +274,7 @@ impl Environment { base_path: &Path, all_files: &mut Vec<(PathBuf, Node)>, ) -> Result<(), String> { - let mut directives = self.extract_use_directives(source); - - // Implicitly add #use system if it's not already there and we can resolve it. - // This is only done for the root script (when loaded_modules is empty) to avoid redundancy. - if !directives.contains(&"system".to_string()) - && self.loaded_modules.borrow().is_empty() - && self.resolve_module_paths("system", base_path).is_ok() - { - directives.insert(0, "system".to_string()); - } + let directives = self.extract_use_directives(source); for module_path in directives { let abs_paths = self.resolve_module_paths(&module_path, base_path)?; @@ -352,7 +341,23 @@ impl Environment { pub fn preload_dependencies(&self, source: &str, file_path: Option<&Path>) -> Result<(), String> { let base_path = file_path.and_then(|p| p.parent()).unwrap_or_else(|| Path::new(".")); let mut files = Vec::new(); - + + // 1. Always load the embedded system library first as a virtual module + let system_path = PathBuf::from(SYSTEM_LIB_PATH); + if !self.loaded_modules.borrow().contains(&system_path) { + self.loaded_modules.borrow_mut().insert(system_path.clone()); + let mut parser = Parser::new(SYSTEM_LIB_SOURCE); + let untyped_ast = parser.parse_expression(); + if parser.diagnostics.has_errors() { + return Err(format!( + "Failed to parse embedded system library:\n{}", + parser.diagnostics.items[0].message + )); + } + files.push((system_path, untyped_ast)); + } + + // 2. Collect dependencies from the main source self.collect_dependencies(source, base_path, &mut files)?; // Pass 1: Discovery (Globals and Macros) @@ -463,7 +468,7 @@ impl Environment { Some(checker.check(&wrapped_ast, &[], diagnostics)) } - pub fn compile_untyped(&self, untyped_ast: Node) -> Result { + fn compile_untyped(&self, untyped_ast: Node) -> Result { let mut diagnostics = Diagnostics::new(); let typed_ast_opt = self.compile_pipeline(untyped_ast, &mut diagnostics); @@ -530,9 +535,6 @@ impl Environment { values.push(val); } - fn register_stdlib(&self) { - rtl::register(self); - } pub fn dump_ast(&self, source: &str) -> Result { self.preload_dependencies(source, None)?; @@ -542,6 +544,10 @@ impl Environment { } pub fn compile(&self, source: &str) -> CompilationResult { + if let Err(e) = self.preload_dependencies(source, None) { + return CompilationResult::error(format!("Dependency Error: {}", e)); + } + let mut parser = Parser::new(source); let untyped_ast = parser.parse_expression(); diff --git a/src/bin/ast.rs b/src/bin/ast.rs index ead0969..4a3dd40 100644 --- a/src/bin/ast.rs +++ b/src/bin/ast.rs @@ -42,7 +42,7 @@ struct Cli { fn main() { let cli = Cli::parse(); - let mut env = Environment::new().with_cwd(); + let mut env = Environment::new(); env.optimization = !cli.no_opt; // Load libraries (Now just search paths) @@ -74,11 +74,6 @@ fn main() { } if let Some(script_str) = cli.eval { - if let Err(e) = env.preload_dependencies(&script_str, None) { - eprintln!("❌ Error resolving dependencies: {}", e); - std::process::exit(1); - } - if cli.dump { match env.dump_ast(&script_str) { Ok(dump) => println!("{}", dump), @@ -92,11 +87,6 @@ fn main() { } else if let Some(file_path) = cli.file { match fs::read_to_string(&file_path) { Ok(content) => { - if let Err(e) = env.preload_dependencies(&content, Some(&file_path)) { - eprintln!("❌ Error resolving dependencies for {:?}:\n{}", file_path, e); - std::process::exit(1); - } - if cli.dump { match env.dump_ast(&content) { Ok(dump) => println!("{}", dump), diff --git a/src/integration_test.rs b/src/integration_test.rs index de857cf..e11dde9 100644 --- a/src/integration_test.rs +++ b/src/integration_test.rs @@ -578,7 +578,7 @@ mod tests { #[test] fn test_record_inlining_in_while_loop() { - let env_ast = Environment::new().with_cwd(); + let env_ast = Environment::new(); // Ensure that constants (like the config record) are inherited into inner lambda scopes (like the body of a while loop). let source = r#" (do @@ -603,7 +603,7 @@ mod tests { ); // The result of running it should obviously still be correct. - let env_run = Environment::new().with_cwd(); + let env_run = Environment::new(); let res = env_run.run_script(source).unwrap(); assert_eq!(format!("{}", res), "10"); } diff --git a/src/main.rs b/src/main.rs index 01cad17..93af138 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,7 +61,7 @@ impl Default for CompilerApp { }) .unwrap_or_default(); - let env = Environment::new().with_cwd(); + let env = Environment::new(); Self { source_code: String::from( @@ -96,14 +96,10 @@ impl CompilerApp { self.trace_logs.clear(); // Reset environment for a fresh run - self.env = Environment::new().with_cwd(); + self.env = Environment::new(); self.env.optimization = true; let res = (|| -> Result<(myc::ast::types::Value, std::time::Duration), String> { - if let Err(e) = self.env.preload_dependencies(&self.source_code, self.current_example_path.as_deref()) { - return Err(format!("Dependency Error: {}", e)); - } - let start_run = std::time::Instant::now(); let result = if self.debug_enabled { let (res, logs) = self.env.run_debug(&self.source_code)?; @@ -162,14 +158,9 @@ impl CompilerApp { } fn dump_ast(&mut self) { - self.env = Environment::new().with_cwd(); + self.env = Environment::new(); self.env.optimization = true; // Enable optimization for AST dump - if let Err(e) = self.env.preload_dependencies(&self.source_code, self.current_example_path.as_deref()) { - self.output_log = format!("Dependency Error: {}", e); - return; - } - match self.env.dump_ast(&self.source_code) { Ok(dump) => { self.output_log = format!("--- BOUND AST DUMP ---\n\n{}", dump); diff --git a/src/utils/tester.rs b/src/utils/tester.rs index 69fc37c..a9125d9 100644 --- a/src/utils/tester.rs +++ b/src/utils/tester.rs @@ -27,7 +27,7 @@ pub fn run_functional_tests_with_optimization(enabled: bool) -> Vec let output_re = Regex::new(r";; Output: (.*)").unwrap(); for entry in entries.filter_map(|e| e.ok()) { - let mut env = Environment::new().with_cwd(); // Fresh environment per test file + let mut env = Environment::new(); // Fresh environment per test file env.optimization = enabled; let path = entry.path(); if path.extension().is_some_and(|ext| ext == "myc") { @@ -39,15 +39,6 @@ pub fn run_functional_tests_with_optimization(enabled: bool) -> Vec .map(|m| m.get(1).unwrap().as_str().trim().to_string()); if let Some(expected) = expected_output { - if let Err(e) = env.preload_dependencies(&content, Some(&path)) { - results.push(TestResult { - name, - success: false, - message: format!("Dependency Error: {}", e), - }); - continue; - } - match env.run_script(&content) { Ok(val) => { let val_str = format!("{}", val); @@ -120,20 +111,8 @@ pub fn run_benchmarks(update: bool, filter: Option<&str>) -> Vec c, Err(e) => { results.push(BenchmarkResult { @@ -150,10 +129,7 @@ pub fn run_benchmarks(update: bool, filter: Option<&str>) -> Vec Result { - let env = Environment::new().with_cwd(); - if let Err(e) = env.preload_dependencies(&content, Some(&path)) { - return Err(format!("Dependency Error in measurement: {}", e)); - } + let env = Environment::new(); // Link once per sample let linked = env.link(node.clone());