feat: Implement AST macros and templates
This commit introduces support for AST macros and templates, enabling users to define and use custom, reusable components within the visual DSL. Key changes include: - A new `MacroRegistry` to manage macro definitions. - A `MacroExpander` to process macro calls and expand templates. - A `MacroEvaluator` trait for evaluating expressions during expansion. - The `Expansion` node in `BoundKind` to preserve the original macro call and its expanded form for debugging. - Updates to the `Binder`, `Dumper`, and `UpvalueAnalyzer` to handle the new macro constructs. - New examples demonstrating various macro functionalities like `unless`, splicing, and nested macros.
This commit is contained in:
+37
-19
@@ -84,30 +84,45 @@ impl Default for CompilerApp {
|
||||
|
||||
impl CompilerApp {
|
||||
fn compile(&mut self) {
|
||||
let start = std::time::Instant::now();
|
||||
let start_total = std::time::Instant::now();
|
||||
self.trace_logs.clear();
|
||||
|
||||
let result = if self.debug_enabled {
|
||||
match self.env.run_debug(&self.source_code) {
|
||||
Ok((res, logs)) => {
|
||||
self.trace_logs = logs;
|
||||
self.active_tab = AppTab::Trace;
|
||||
res
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
} else {
|
||||
self.env.run_script(&self.source_code)
|
||||
};
|
||||
let res = (|| -> Result<(myc::ast::types::Value, std::time::Duration, std::time::Duration, std::time::Duration), String> {
|
||||
let start_compile = std::time::Instant::now();
|
||||
let compiled = self.env.compile(&self.source_code)?;
|
||||
let duration_compile = start_compile.elapsed();
|
||||
|
||||
match result {
|
||||
Ok(val) => {
|
||||
let duration = start.elapsed();
|
||||
let start_link = std::time::Instant::now();
|
||||
let linked = self.env.link(compiled);
|
||||
let duration_link = start_link.elapsed();
|
||||
|
||||
let start_run = std::time::Instant::now();
|
||||
let result = if self.debug_enabled {
|
||||
let mut vm = myc::ast::vm::VM::new(self.env.global_values.clone());
|
||||
let mut observer = myc::ast::vm::TracingObserver::new();
|
||||
let res = vm.run_with_observer(&mut observer, &linked);
|
||||
self.trace_logs = observer.logs;
|
||||
self.active_tab = AppTab::Trace;
|
||||
res?
|
||||
} else {
|
||||
self.env.run(&linked)?
|
||||
};
|
||||
let duration_run = start_run.elapsed();
|
||||
|
||||
Ok((result, duration_compile, duration_link, duration_run))
|
||||
})();
|
||||
|
||||
match res {
|
||||
Ok((val, d_compile, d_link, d_run)) => {
|
||||
let d_total = start_total.elapsed();
|
||||
let now = chrono::Local::now().format("%H:%M:%S").to_string();
|
||||
self.output_log = format!(
|
||||
"Execution Successful.\nResult: {}\n\nDuration: {:?}\nFinished at: {}",
|
||||
"Execution Successful.\nResult: {}\n\nPhases:\n - Compile: {:?}\n - Link: {:?}\n - Run: {:?}\n\nTotal Duration: {:?}\nFinished at: {}",
|
||||
val,
|
||||
duration,
|
||||
d_compile,
|
||||
d_link,
|
||||
d_run,
|
||||
d_total,
|
||||
now,
|
||||
);
|
||||
if !self.debug_enabled {
|
||||
@@ -466,6 +481,9 @@ fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob {
|
||||
bracket_depth = bracket_depth.saturating_sub(1);
|
||||
color = color_bracket[bracket_depth % 3];
|
||||
}
|
||||
'`' | '~' | '@' => {
|
||||
color = color_kw;
|
||||
}
|
||||
'0'..='9' => {
|
||||
while let Some(&next) = chars.peek() {
|
||||
if next.is_ascii_digit() || next == '.' {
|
||||
@@ -480,7 +498,7 @@ fn highlight_myc(ctx: &egui::Context, code: &str) -> egui::text::LayoutJob {
|
||||
text.push(chars.next().unwrap());
|
||||
} else { break; }
|
||||
}
|
||||
if ["fn", "def", "do", "if", "assign", "recur", "cond"].contains(&text.as_str()) {
|
||||
if ["fn", "def", "do", "if", "assign", "recur", "cond", "macro"].contains(&text.as_str()) {
|
||||
color = color_kw;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user