Implement #use directive for dependency management

Introduces the `#use` preprocessor directive, allowing Myc scripts to
explicitly declare dependencies on other Myc libraries. This change
moves dependency management from a command-line argument to an in-script
declaration, ensuring scripts are self-contained and can correctly
resolve macros and other symbols.

Key features include:
- Declarations at the beginning of a file, evaluated before parsing.
- Support for relative paths and a new `->` separator.
- Automatic resolution of dependencies from specified search paths.
- Idempotent loading to prevent duplicate parsing and evaluation.
- No AST pollution; dependency management is a compile-time concern.

The compiler's lexer has been updated to recognize `#` as a comment
character, and the environment now manages search paths and loaded
modules. This lays the groundwork for more complex library structures
and improved code organization.
This commit is contained in:
Michael Schimmel
2026-03-06 20:43:45 +01:00
parent d4ca9c4620
commit a59367ba61
7 changed files with 221 additions and 38 deletions
+28
View File
@@ -28,6 +28,7 @@ pub fn run_functional_tests_with_optimization(enabled: bool) -> Vec<TestResult>
for entry in entries.filter_map(|e| e.ok()) {
let mut env = Environment::new(); // Fresh environment per test file
env.add_search_path(".");
env.optimization = enabled;
let path = entry.path();
if path.extension().is_some_and(|ext| ext == "myc") {
@@ -39,6 +40,15 @@ 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);
@@ -112,6 +122,19 @@ pub fn run_benchmarks(update: bool, filter: Option<&str>) -> Vec<BenchmarkResult
// Compile once for this file (symbols/types are compatible with all fresh environments)
let initial_env = Environment::new();
initial_env.add_search_path(".");
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() {
Ok(c) => c,
Err(e) => {
@@ -130,6 +153,11 @@ pub fn run_benchmarks(update: bool, filter: Option<&str>) -> Vec<BenchmarkResult
let measure_sum =
|n: u32, node: &crate::ast::compiler::TypedNode| -> Result<Duration, String> {
let env = Environment::new();
env.add_search_path(".");
if let Err(e) = env.preload_dependencies(&content, Some(&path)) {
return Err(format!("Dependency Error in measurement: {}", e));
}
// Link once per sample
let linked = env.link(node.clone());
let func = env.instantiate(linked);