diff --git a/Cargo.lock b/Cargo.lock index c5077bf..26db041 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1199,6 +1199,20 @@ dependencies = [ "winit", ] +[[package]] +name = "egui_extras" +version = "0.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d01d34e845f01c62e3fded726961092e70417d66570c499b9817ab24674ca4ed" +dependencies = [ + "ahash", + "egui", + "enum-map", + "log", + "mime_guess2", + "profiling", +] + [[package]] name = "egui_glow" version = "0.33.3" @@ -1237,6 +1251,26 @@ version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" +[[package]] +name = "enum-map" +version = "2.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9" +dependencies = [ + "enum-map-derive", +] + +[[package]] +name = "enum-map-derive" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "enumflags2" version = "0.7.12" @@ -2179,6 +2213,24 @@ dependencies = [ "paste", ] +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "mime_guess2" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1706dc14a2e140dec0a7a07109d9a3d5890b81e85bd6c60b906b249a77adf0ca" +dependencies = [ + "mime", + "phf", + "phf_shared", + "unicase", +] + [[package]] name = "miniz_oxide" version = "0.8.9" @@ -2206,6 +2258,7 @@ dependencies = [ "chrono", "clap", "eframe", + "egui_extras", "fastrand", "insta", "regex", @@ -2703,6 +2756,50 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" +dependencies = [ + "phf_macros", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared", + "rand", +] + +[[package]] +name = "phf_macros" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn", + "unicase", +] + +[[package]] +name = "phf_shared" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" +dependencies = [ + "siphasher", + "unicase", +] + [[package]] name = "pin-project" version = "1.1.10" @@ -2883,6 +2980,21 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" + [[package]] name = "range-alloc" version = "0.1.4" @@ -3226,6 +3338,12 @@ version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa" +[[package]] +name = "siphasher" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" + [[package]] name = "slab" version = "0.4.12" @@ -3639,6 +3757,12 @@ dependencies = [ "winapi", ] +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + [[package]] name = "unicode-ident" version = "1.0.24" diff --git a/Cargo.toml b/Cargo.toml index d032c53..c91d119 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,6 +6,7 @@ default-run = "myc" [dependencies] eframe = "0.33.3" +egui_extras = "0.33" clap = { version = "4.5", features = ["derive"] } chrono = "0.4" regex = "1.10" diff --git a/examples/data_stream_pipe_series.myc b/examples/data_stream_pipe_series.myc index f07cc2b..4af8839 100644 --- a/examples/data_stream_pipe_series.myc +++ b/examples/data_stream_pipe_series.myc @@ -9,7 +9,7 @@ (fn [eu ge] (do (def dax (.close (ge 0))) - (def eur (- dax (ge 1))) + (def eur (- dax (.close (ge 1)))) (print cnt ": dax=" eur " (" (/ eur (.close (eu 0))) "$)" ) (assign cnt (+ cnt 1)) ) diff --git a/src/ast/environment.rs b/src/ast/environment.rs index 40ef8d7..dcc73dd 100644 --- a/src/ast/environment.rs +++ b/src/ast/environment.rs @@ -587,6 +587,41 @@ impl Environment { } + /// Returns all user-defined global bindings with their name, type and current value. + /// + /// Iterates over the user scopes (index 1+), skipping the frozen RTL scope. + /// Root-level bindings use `Address::Local(VirtualId)` in the scope map — + /// the Local→Global translation only happens at resolve time in the binder, + /// so we treat Local slots as global indices here. + pub fn list_user_globals(&self) -> Vec<(String, StaticType, Value)> { + let scopes = self.root_scopes.borrow(); + let types = self.root_types.borrow(); + let user_vals = self.user_values.borrow(); + let rtl_slot_count = self.rtl.slot_count as usize; + + let mut result = Vec::new(); + + for scope in scopes.iter().skip(1) { + for (sym, info) in &scope.locals { + let slot = match info.addr { + Address::Global(idx) => idx.0 as usize, + Address::Local(vid) => vid.0 as usize, + Address::Upvalue(_) => continue, + }; + + if slot >= rtl_slot_count { + let user_idx = slot - rtl_slot_count; + let ty = types.get(slot).cloned().unwrap_or(StaticType::Any); + let val = user_vals.get(user_idx).cloned().unwrap_or(Value::Void); + result.push((sym.name.to_string(), ty, val)); + } + } + } + + result.sort_by(|a, b| a.0.cmp(&b.0)); + result + } + /// Returns the names of all bindings registered in the fixed RTL scope. /// Useful for introspection (e.g., MCP server listing available built-ins). pub fn list_bindings(&self) -> Vec { diff --git a/src/main.rs b/src/main.rs index 13b7101..de7cb01 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ use eframe::egui; +use egui_extras::{Column, TableBuilder}; use myc::ast::compiler::emitter; use myc::ast::environment::Environment; use myc::ast::parser::Parser; @@ -29,6 +30,13 @@ enum AppTab { Trace, } +#[derive(PartialEq)] +enum StateTab { + Globals, + Dataflow, + Series, +} + struct CompilerApp { source_code: String, current_example_path: Option, @@ -38,6 +46,7 @@ struct CompilerApp { trace_logs: Vec, active_tab: AppTab, debug_enabled: bool, + state_tab: StateTab, env: Environment, is_first_frame: bool, examples: Vec, @@ -86,6 +95,7 @@ impl Default for CompilerApp { trace_logs: Vec::new(), active_tab: AppTab::Output, debug_enabled: false, + state_tab: StateTab::Globals, env, is_first_frame: true, examples, @@ -573,6 +583,75 @@ impl eframe::App for CompilerApp { } }); + // Right Side Panel: Environment State Inspector + egui::SidePanel::right("state_panel") + .resizable(true) + .default_width(300.0) + .min_width(200.0) + .show(ctx, |ui| { + ui.horizontal(|ui| { + ui.selectable_value(&mut self.state_tab, StateTab::Globals, "Globals"); + ui.selectable_value(&mut self.state_tab, StateTab::Dataflow, "Dataflow"); + ui.selectable_value(&mut self.state_tab, StateTab::Series, "Series"); + }); + ui.separator(); + + match self.state_tab { + StateTab::Globals => { + let globals = self.env.list_user_globals(); + let scopes = self.env.root_scopes.borrow(); + let rtl_slots = self.env.rtl.slot_count; + let total_slots = *self.env.root_slot_count.borrow(); + let scope_details: Vec = scopes.iter().enumerate().map(|(i, s)| { + let entries: Vec = s.locals.iter().map(|(sym, info)| { + format!("{}={:?}", sym.name, info.addr) + }).collect(); + format!("S{}[{}]", i, entries.join(", ")) + }).collect(); + drop(scopes); + ui.label(format!( + "DEBUG: rtl={}, total={}, found={}", rtl_slots, total_slots, globals.len() + )); + for s in &scope_details { + ui.label(s); + } + if globals.is_empty() { + ui.label("No user globals. Run a script first."); + } else { + let available = ui.available_height(); + TableBuilder::new(ui) + .striped(true) + .resizable(true) + .cell_layout(egui::Layout::left_to_right(egui::Align::Center)) + .min_scrolled_height(available) + .column(Column::auto().at_least(60.0)) + .column(Column::auto().at_least(60.0)) + .column(Column::remainder().at_least(80.0)) + .header(18.0, |mut header| { + header.col(|ui| { ui.strong("Name"); }); + header.col(|ui| { ui.strong("Type"); }); + header.col(|ui| { ui.strong("Value"); }); + }) + .body(|mut body| { + for (name, ty, val) in &globals { + body.row(18.0, |mut row| { + row.col(|ui| { ui.monospace(name); }); + row.col(|ui| { ui.monospace(ty.to_string()); }); + row.col(|ui| { ui.monospace(val.to_string()); }); + }); + } + }); + } + } + StateTab::Dataflow => { + ui.label("Stream/Pipe dataflow graph will appear here after execution."); + } + StateTab::Series => { + ui.label("Series contents will appear here after execution."); + } + } + }); + // Central Panel: Source Code Editor (Fills remaining space) egui::CentralPanel::default().show(ctx, |ui| { ui.heading("Source Code Editor");