use eframe::egui; use crate::ast::environment::Environment; pub mod ast; fn main() -> eframe::Result { let options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default().with_inner_size([800.0, 600.0]), ..Default::default() }; eframe::run_native( "Compiler GUI", options, Box::new(|_cc| Ok(Box::new(CompilerApp::default()))), ) } struct CompilerApp { source_code: String, output_log: String, env: Environment, is_first_frame: bool, } impl Default for CompilerApp { fn default() -> Self { Self { source_code: String::new(), output_log: String::from("Ready to compile..."), env: Environment::new(), is_first_frame: true, } } } impl CompilerApp { fn compile(&mut self) { match self.env.run_script(&self.source_code) { Ok(result) => { self.output_log = format!( "Execution Successful.\nResult: {}\n\nFinished at {:?}", result, std::time::SystemTime::now(), ); } Err(e) => { self.output_log = format!("Error: {}", e); } } } } impl eframe::App for CompilerApp { fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) { // Bottom Panel: Output Log & Controls (Resizable) egui::TopBottomPanel::bottom("bottom_panel") .resizable(true) .min_height(150.0) .default_height(250.0) .show(ctx, |ui| { ui.add_space(5.0); // Compile Button (at the top of the bottom panel) if ui.button("Compile").clicked() { self.compile(); } ui.add_space(5.0); ui.separator(); ui.label("Output / Logs:"); // Log Output Area egui::ScrollArea::vertical() .id_salt("output_log_scroll") .show(ui, |ui| { ui.add_sized( ui.available_size(), egui::TextEdit::multiline(&mut self.output_log) .font(egui::TextStyle::Monospace) .desired_width(f32::INFINITY) .interactive(false), // Read-only ); }); }); // Central Panel: Source Code Editor (Fills remaining space) egui::CentralPanel::default().show(ctx, |ui| { ui.heading("Source Code Editor"); ui.label("Input Source:"); 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)) { self.compile(); } // Set initial focus if self.is_first_frame { ui.ctx().memory_mut(|m| m.request_focus(editor_id)); self.is_first_frame = false; } egui::ScrollArea::vertical() .id_salt("source_code_scroll") .show(ui, |ui| { ui.add_sized( ui.available_size(), egui::TextEdit::multiline(&mut self.source_code) .id(editor_id) .font(egui::TextStyle::Monospace) .desired_width(f32::INFINITY) .lock_focus(true), ); }); }); } } #[cfg(test)] mod tests { use super::*; use crate::ast::types::Value; #[test] fn test_basic_eval() { let env = Environment::new(); let result = env.run_script("(+ 1 2)").unwrap(); if let Value::Int(i) = result { assert_eq!(i, 3); } else if let Value::Float(f) = result { assert_eq!(f, 3.0); } else { panic!("Expected number result"); } } #[test] fn test_type_error() { let env = Environment::new(); // This should fail because 'if' condition must be bool (and currently '+' returns Any/Float/Int) // Wait, '+' returns Any currently in my registration. // Let's try something that definitely fails type check. let result = env.run_script("(if 1 2 3)"); assert!(result.is_err()); assert!(result.unwrap_err().contains("Condition must be boolean")); } }