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:
+18
-26
@@ -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
@@ -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
@@ -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
@@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user