diff --git a/examples/closure.myc b/examples/closure.myc index a1272f8..35caedd 100644 --- a/examples/closure.myc +++ b/examples/closure.myc @@ -1,5 +1,5 @@ ;; Closure Scope Test -;; Benchmark: 7.9us +;; Benchmark: 9.0us ;; Output: 15 (do (def make-adder (fn [x] diff --git a/examples/data.myc b/examples/data.myc index 2c10e98..f84de58 100644 --- a/examples/data.myc +++ b/examples/data.myc @@ -1,5 +1,5 @@ ;; Complex Data Structure Test -;; Benchmark: 66.8us +;; Benchmark: 72.7us ;; Output: {:input 10, :name "Fibonacci", :output 55} (do (def fib (fn [n] diff --git a/examples/fib.myc b/examples/fib.myc index 904490c..f1145a3 100644 --- a/examples/fib.myc +++ b/examples/fib.myc @@ -1,5 +1,5 @@ ;; Fibonacci Recursive -;; Benchmark: 62.9us +;; Benchmark: 68.3us ;; Output: 55 (do (def fib (fn [n] diff --git a/examples/hof.myc b/examples/hof.myc index b0390ef..e3b1be7 100644 --- a/examples/hof.myc +++ b/examples/hof.myc @@ -1,5 +1,5 @@ ;; Higher-Order Function Example -;; Benchmark: 7.4us +;; Benchmark: 8.4us ;; Output: 25 (do (def apply (fn [f x] (f x))) diff --git a/examples/tco_test.myc b/examples/tco_test.myc index 3872969..2ac1006 100644 --- a/examples/tco_test.myc +++ b/examples/tco_test.myc @@ -1,8 +1,10 @@ +;; Benchmark: 2.7ms +;; Output: "done" (do (def count_down (fn [n] (if (< n 1) "done" (count_down (- n 1))))) - (count_down 1000000) + (count_down 10000) ) diff --git a/src/main.rs b/src/main.rs index 4cf3b5e..8285471 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,6 @@ use eframe::egui; use myc::ast::environment::Environment; +use std::path::PathBuf; fn main() -> eframe::Result { let options = eframe::NativeOptions { @@ -15,11 +16,15 @@ fn main() -> eframe::Result { struct Example { name: String, + path: PathBuf, content: String, } struct CompilerApp { source_code: String, + current_example_path: Option, + save_as_name: String, + show_save_as: bool, output_log: String, env: Environment, is_first_frame: bool, @@ -34,9 +39,10 @@ impl Default for CompilerApp { .filter_map(|e| e.ok()) .filter(|e| e.path().extension().map_or(false, |ext| ext == "myc")) .filter_map(|e| { + let path = e.path(); let name = e.file_name().into_string().ok()?; - let content = std::fs::read_to_string(e.path()).ok()?; - Some(Example { name, content }) + let content = std::fs::read_to_string(&path).ok()?; + Some(Example { name, path, content }) }) .collect() }) @@ -53,6 +59,9 @@ impl Default for CompilerApp { (fib 10) ) "#), + current_example_path: None, + save_as_name: String::new(), + show_save_as: false, output_log: String::from("Ready to compile..."), env: Environment::new(), is_first_frame: true, @@ -81,6 +90,66 @@ impl CompilerApp { } } + fn save(&mut self) { + if let Some(path) = &self.current_example_path { + match std::fs::write(path, &self.source_code) { + Ok(_) => { + self.output_log = format!("Successfully saved to {:?}", path); + // Update the cached content in examples list so it stays in sync + if let Some(ex) = self.examples.iter_mut().find(|e| e.path == *path) { + ex.content = self.source_code.clone(); + } + } + Err(e) => { + self.output_log = format!("Failed to save: {}", e); + } + } + } else { + self.output_log = "No file loaded to save. Use 'Examples' to load one.".to_string(); + } + } + + fn save_as(&mut self) { + if self.save_as_name.trim().is_empty() { + self.output_log = "Error: Please enter a filename.".to_string(); + return; + } + + let mut filename = self.save_as_name.trim().to_string(); + if !filename.ends_with(".myc") { + filename.push_str(".myc"); + } + + let path = PathBuf::from("examples").join(filename); + match std::fs::write(&path, &self.source_code) { + Ok(_) => { + self.output_log = format!("Successfully saved new file to {:?}", path); + self.current_example_path = Some(path); + self.show_save_as = false; + self.save_as_name.clear(); + self.refresh_examples(); + } + Err(e) => { + self.output_log = format!("Failed to save: {}", e); + } + } + } + + fn refresh_examples(&mut self) { + if let Ok(entries) = std::fs::read_dir("examples") { + self.examples = entries + .filter_map(|e| e.ok()) + .filter(|e| e.path().extension().map_or(false, |ext| ext == "myc")) + .filter_map(|e| { + let path = e.path(); + let name = e.file_name().into_string().ok()?; + let content = std::fs::read_to_string(&path).ok()?; + Some(Example { name, path, content }) + }) + .collect(); + } + } + fn run_all_tests(&mut self) { use myc::ast::tester; let results = tester::run_functional_tests(); @@ -125,6 +194,7 @@ impl eframe::App for CompilerApp { for example in &self.examples { if ui.button(&example.name).clicked() { self.source_code = example.content.clone(); + self.current_example_path = Some(example.path.clone()); } } }); @@ -139,10 +209,25 @@ impl eframe::App for CompilerApp { // Compile Button (at the top of the bottom panel) ui.horizontal(|ui| { - if ui.button("Compile").clicked() { + if ui.button("Compile (Shift+Enter)").clicked() { self.compile(); } + if let Some(path) = &self.current_example_path { + let file_name = path.file_name().unwrap_or_default().to_string_lossy(); + if ui.button(format!("Save {} (Ctrl+S)", file_name)).clicked() { + self.save(); + } + } else { + ui.add_enabled_ui(false, |ui| { + ui.button("Save (None loaded)"); + }); + } + + if ui.button("Save As...").clicked() { + self.show_save_as = !self.show_save_as; + } + if ui.button("Test All").clicked() { self.run_all_tests(); } @@ -168,7 +253,20 @@ impl eframe::App for CompilerApp { }); }); - ui.add_space(5.0); + if self.show_save_as { + ui.horizontal(|ui| { + ui.label("New Filename:"); + ui.text_edit_singleline(&mut self.save_as_name); + if ui.button("Save Now").clicked() { + self.save_as(); + } + if ui.button("Cancel").clicked() { + self.show_save_as = false; + } + }); + ui.add_space(5.0); + } + ui.horizontal(|ui| { ui.label("Output / Logs:"); if ui.button("Copy to Clipboard").clicked() { @@ -198,11 +296,16 @@ impl eframe::App for CompilerApp { let editor_id = ui.id().with("source_code_editor"); // Handle Shift+Enter shortcut BEFORE the editor handles it - let shortcut = egui::KeyboardShortcut::new(egui::Modifiers::SHIFT, egui::Key::Enter); - if ui.input_mut(|i| i.consume_shortcut(&shortcut)) { + let compile_shortcut = egui::KeyboardShortcut::new(egui::Modifiers::SHIFT, egui::Key::Enter); + if ui.input_mut(|i| i.consume_shortcut(&compile_shortcut)) { self.compile(); } + let save_shortcut = egui::KeyboardShortcut::new(egui::Modifiers::COMMAND, egui::Key::S); + if ui.input_mut(|i| i.consume_shortcut(&save_shortcut)) { + self.save(); + } + // Set initial focus if self.is_first_frame { ui.ctx().memory_mut(|m| m.request_focus(editor_id));