Refactor environment loading and program execution

The `preload_dependencies` function has been refactored to directly run
parsed programs instead of collecting all forms into a single `Program`
node. This simplifies the compilation pipeline by allowing each
dependency to be processed individually.

Additionally, the `VM::run_with_observer` method has been updated to
manage its stack and frames more cleanly, avoiding the need for
`eval_in_frame`. The main loop now correctly extracts expressions from
the `Program` node for source code emission.
This commit is contained in:
2026-03-31 13:59:28 +02:00
parent 02ea2f0d80
commit 8fd2f2d113
4 changed files with 54 additions and 53 deletions
+18 -26
View File
@@ -5,7 +5,7 @@ use crate::ast::compiler::{CapturePass, TypeChecker, TypedNode};
use crate::ast::nodes::{AnalyzedPhase, BoundPhase, Symbol, SyntaxKind, SyntaxNode}; use crate::ast::nodes::{AnalyzedPhase, BoundPhase, Symbol, SyntaxKind, SyntaxNode};
use crate::ast::parser::Parser; use crate::ast::parser::Parser;
use crate::ast::closure::Closure; use crate::ast::closure::Closure;
use crate::ast::vm::{self, GlobalStore, TracingObserver, VM}; use crate::ast::vm::{GlobalStore, TracingObserver, VM};
use std::cell::RefCell; use std::cell::RefCell;
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
@@ -334,23 +334,20 @@ impl Environment {
/// Used to pre-load all dependencies of a script before compiling it. /// Used to pre-load all dependencies of a script before compiling it.
pub fn preload_dependencies(&self, source: &str, file_path: Option<&Path>) -> Result<(), String> { 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 base_path = file_path.and_then(|p| p.parent()).unwrap_or_else(|| Path::new("."));
let mut all_forms: Vec<Rc<SyntaxNode>> = Vec::new();
// 1. Always load the embedded system library first as a virtual module // 1. Always load the embedded system library first as a virtual module
let system_path = PathBuf::from(SYSTEM_LIB_PATH); let system_path = PathBuf::from(SYSTEM_LIB_PATH);
if !self.loaded_modules.borrow().contains(&system_path) { if !self.loaded_modules.borrow().contains(&system_path) {
self.loaded_modules.borrow_mut().insert(system_path.clone()); self.loaded_modules.borrow_mut().insert(system_path.clone());
let mut parser = Parser::new(SYSTEM_LIB_SOURCE); let mut parser = Parser::new(SYSTEM_LIB_SOURCE);
let forms = parser.parse_program(); let program = parser.parse_program();
if parser.diagnostics.has_errors() { if parser.diagnostics.has_errors() {
return Err(format!( return Err(format!(
"Failed to parse embedded system library:\n{}", "Failed to parse embedded system library:\n{}",
parser.diagnostics.items[0].message parser.diagnostics.items[0].message
)); ));
} }
for form in forms { self.run_program(program)?;
all_forms.push(Rc::new(form));
}
} }
// 2. Collect user dependencies (file I/O + parse, topological order) // 2. Collect user dependencies (file I/O + parse, topological order)
@@ -358,43 +355,38 @@ impl Environment {
Rc::clone(&self.search_paths), Rc::clone(&self.search_paths),
Rc::clone(&self.loaded_modules), Rc::clone(&self.loaded_modules),
); );
for (_, syntax_ast) in loader.collect_dependency_files(source, base_path)? { for (path, syntax_ast) in loader.collect_dependency_files(source, base_path)? {
all_forms.push(Rc::new(syntax_ast)); let compiled = self.compile_pipeline(syntax_ast, &mut Diagnostics::new())
.ok_or_else(|| format!("Compilation error in {}", path.display()))?;
self.run_script_compiled(compiled).map_err(|e| {
format!("Initialization error in {}:\n{}", path.display(), e)
})?;
} }
// Compile and initialize all forms as a single program (no scope boundary) Ok(())
if !all_forms.is_empty() { }
let block = SyntaxNode {
identity: NodeIdentity::anonymous(), /// Compiles and executes a `Program` node without lambda wrapping.
kind: SyntaxKind::Program { exprs: all_forms }, /// Definitions at the top level receive `Address::Global` and persist
ty: (), /// in the GlobalStore for subsequent compilation units.
comments: Rc::from([]), fn run_program(&self, program: SyntaxNode) -> Result<(), String> {
};
let mut diagnostics = Diagnostics::new(); let mut diagnostics = Diagnostics::new();
// 1. Expand macros let expanded = self.expand(program, &mut diagnostics)
let expanded = self.expand(block, &mut diagnostics)
.ok_or_else(|| diagnostics.format_errors())?; .ok_or_else(|| diagnostics.format_errors())?;
// 2. Bind names and update root scopes
let bound = self.bind_and_update(&expanded, &mut diagnostics) let bound = self.bind_and_update(&expanded, &mut diagnostics)
.ok_or_else(|| diagnostics.format_errors())?; .ok_or_else(|| diagnostics.format_errors())?;
// 3. Type-check (without lambda wrapping)
let typed = self.type_check(&bound, &mut diagnostics); let typed = self.type_check(&bound, &mut diagnostics);
if diagnostics.has_errors() { if diagnostics.has_errors() {
return Err(diagnostics.format_errors()); return Err(diagnostics.format_errors());
} }
// 4. Link and execute
let linked = self.link(typed); let linked = self.link(typed);
let mut vm = self.create_vm(); let mut vm = self.create_vm();
let stack = vec![Value::Void; linked.ty.stack_size as usize]; vm.run(&linked)?;
let result = vm.eval_in_frame(&mut vm::NoOpObserver, &linked, stack)?;
vm.resolve_tail_calls(&mut vm::NoOpObserver, Ok(result))?;
}
Ok(()) Ok(())
} }
+11 -4
View File
@@ -119,13 +119,20 @@ impl<'a> Parser<'a> {
matches!(self.current_token.kind, TokenKind::EOF) matches!(self.current_token.kind, TokenKind::EOF)
} }
/// Parses a complete program as a sequence of top-level expressions. /// Parses a complete program as a sequence of top-level expressions
pub fn parse_program(&mut self) -> Vec<SyntaxNode> { /// wrapped in a `Program` node.
pub fn parse_program(&mut self) -> SyntaxNode {
let identity = NodeIdentity::new(SourceLocation { line: 1, col: 1 });
let mut expressions = Vec::new(); let mut expressions = Vec::new();
while !self.at_eof() { while !self.at_eof() {
expressions.push(self.parse_expression()); expressions.push(Rc::new(self.parse_expression()));
}
SyntaxNode {
identity,
kind: SyntaxKind::Program { exprs: expressions },
ty: (),
comments: Rc::from([]),
} }
expressions
} }
fn synchronize(&mut self) { fn synchronize(&mut self) {
+10 -14
View File
@@ -266,27 +266,23 @@ impl VM {
self.resolve_tail_calls(observer, result) self.resolve_tail_calls(observer, result)
} }
pub fn eval_in_frame<O: VMObserver>(&mut self, observer: &mut O, root: &ExecNode, stack: Vec<Value>) -> Result<Value, String> {
self.stack = stack;
self.frames.clear();
self.frames.push(CallFrame {
stack_base: 0,
closure: None,
});
let result = self.eval_internal(observer, root);
self.frames.pop();
result
}
pub fn run_with_observer<O: VMObserver>( pub fn run_with_observer<O: VMObserver>(
&mut self, &mut self,
observer: &mut O, observer: &mut O,
root: &ExecNode, root: &ExecNode,
) -> Result<Value, String> { ) -> Result<Value, String> {
let mut stack = Vec::new(); self.stack.clear();
stack.resize(root.ty.stack_size as usize, Value::Void); self.frames.clear();
let result = self.eval_in_frame(observer, root, stack); self.stack.resize(root.ty.stack_size as usize, Value::Void);
self.frames.push(CallFrame {
stack_base: 0,
closure: None,
});
let result = self.eval_internal(observer, root);
self.frames.pop();
self.resolve_tail_calls(observer, result) self.resolve_tail_calls(observer, result)
} }
+7 -1
View File
@@ -2,6 +2,7 @@ use eframe::egui;
use egui_extras::{Column, TableBuilder}; use egui_extras::{Column, TableBuilder};
use myc::ast::compiler::emitter; use myc::ast::compiler::emitter;
use myc::ast::environment::Environment; use myc::ast::environment::Environment;
use myc::ast::nodes::SyntaxKind;
use myc::ast::parser::Parser; use myc::ast::parser::Parser;
use std::path::PathBuf; use std::path::PathBuf;
@@ -198,7 +199,12 @@ impl CompilerApp {
self.active_tab = AppTab::Output; self.active_tab = AppTab::Output;
return; return;
} }
self.source_code = expressions.iter().map(emitter::emit).collect::<Vec<_>>().join("\n"); let exprs = if let SyntaxKind::Program { exprs } = &expressions.kind {
exprs.as_slice()
} else {
&[]
};
self.source_code = exprs.iter().map(|e| emitter::emit(e)).collect::<Vec<_>>().join("\n");
self.output_log = "Roundtrip complete — source reformatted.".to_string(); self.output_log = "Roundtrip complete — source reformatted.".to_string();
self.active_tab = AppTab::Output; self.active_tab = AppTab::Output;
} }