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.
This commit is contained in:
Michael Schimmel
2026-03-08 13:09:53 +01:00
parent 4dfdc75545
commit b622f7f8bd
6 changed files with 42 additions and 98 deletions
-19
View File
@@ -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
)
)
)
+31 -25
View File
@@ -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<dyn FnMut() -> bool>;
const SYSTEM_LIB_SOURCE: &str = include_str!("system.myc");
const SYSTEM_LIB_PATH: &str = "<embedded>/system.myc";
pub struct CompilationResult {
pub ast: Option<TypedNode>,
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<Path>) {
@@ -276,16 +274,7 @@ impl Environment {
base_path: &Path,
all_files: &mut Vec<(PathBuf, Node<UntypedKind>)>,
) -> 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)?;
@@ -353,6 +342,22 @@ impl Environment {
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<UntypedKind>) -> Result<TypedNode, String> {
fn compile_untyped(&self, untyped_ast: Node<UntypedKind>) -> Result<TypedNode, String> {
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<String, String> {
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();
+1 -11
View File
@@ -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),
+2 -2
View File
@@ -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");
}
+3 -12
View File
@@ -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);
+4 -28
View File
@@ -27,7 +27,7 @@ pub fn run_functional_tests_with_optimization(enabled: bool) -> Vec<TestResult>
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<TestResult>
.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<BenchmarkResult
.unwrap_or(1);
// Compile once for this file (symbols/types are compatible with all fresh environments)
let initial_env = Environment::new().with_cwd();
if let Err(e) = initial_env.preload_dependencies(&content, Some(&path)) {
results.push(BenchmarkResult {
name,
median: Duration::ZERO,
baseline: None,
diff_pct: None,
status: format!("DEPENDENCY ERROR: {}", e),
});
continue;
}
let compiled_once = match initial_env.compile(&content).into_result() {
let env = Environment::new();
let compiled_once = match env.compile(&content).into_result() {
Ok(c) => c,
Err(e) => {
results.push(BenchmarkResult {
@@ -150,10 +129,7 @@ pub fn run_benchmarks(update: bool, filter: Option<&str>) -> Vec<BenchmarkResult
// Helper to measure sum of VM execution times over N executions in one environment
let measure_sum =
|n: u32, node: &crate::ast::compiler::TypedNode| -> Result<Duration, String> {
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());