diff --git a/Cargo.lock b/Cargo.lock index 238d1fe..e0c3cad 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -89,6 +89,15 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04" +[[package]] +name = "ansi_term" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" +dependencies = [ + "winapi", +] + [[package]] name = "anstream" version = "0.6.5" @@ -560,6 +569,7 @@ checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650" name = "dust-lang" version = "0.4.0" dependencies = [ + "ansi_term", "cc", "clap", "csv", diff --git a/Cargo.toml b/Cargo.toml index 2d2cd33..4c21579 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,7 @@ eframe = { version = "0.24.1", default-features = false, features = [ "glow", # Use the glow rendering backend. Alternative: "wgpu". "persistence", # Enable restoring app state when restarting the app. ] } +ansi_term = "0.12.1" [target.'cfg(not(target_arch = "wasm32"))'.dependencies] env_logger = "0.10" diff --git a/src/bin/gui/app.rs b/src/bin/gui/app.rs index 4bcde01..538a4a1 100644 --- a/src/bin/gui/app.rs +++ b/src/bin/gui/app.rs @@ -1,28 +1,50 @@ -use dust_lang::{interpret, Result, Value}; +use std::{fs::read_to_string, path::PathBuf}; + +use dust_lang::{Interpreter, Map, Result, Value}; +use egui::{Align, Layout}; use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize)] pub struct App { + path: String, source: String, + context: Map, + #[serde(skip)] + interpreter: Interpreter, output: Result, } impl App { - pub fn new(cc: &eframe::CreationContext<'_>, source: String) -> Self { - // This is also where you can customize the look and feel of egui using - // `cc.egui_ctx.set_visuals` and `cc.egui_ctx.set_fonts`. + pub fn new(cc: &eframe::CreationContext<'_>, path: PathBuf) -> Self { + fn create_app(path: PathBuf) -> App { + let context = Map::new(); + let mut interpreter = Interpreter::new(context.clone()); + let read_source = read_to_string(&path); + let source = if let Ok(source) = read_source { + source + } else { + String::new() + }; + let output = interpreter.run(&source); - cc.egui_ctx.set_zoom_factor(1.5); + App { + path: path.to_string_lossy().to_string(), + source, + context, + interpreter, + output, + } + } - let app = App { - source, - output: Ok(Value::default()), - }; - - if let Some(storage) = cc.storage { - return eframe::get_value(storage, eframe::APP_KEY).unwrap_or(app); + if path.is_file() { + create_app(path) } else { - app + if let Some(storage) = cc.storage { + return eframe::get_value(storage, eframe::APP_KEY) + .unwrap_or_else(|| create_app(path)); + } else { + create_app(path) + } } } } @@ -43,28 +65,37 @@ impl eframe::App for App { ui.add_space(16.0); egui::widgets::global_dark_light_mode_buttons(ui); + + ui.with_layout(Layout::right_to_left(Align::Max), |ui| { + egui::warn_if_debug_build(ui); + ui.hyperlink_to("source code", "https://git.jeffa.io/jeff/dust"); + }); }); }); egui::CentralPanel::default().show(ctx, |ui| { - ui.code_editor(&mut self.source); + ui.with_layout(Layout::left_to_right(Align::Min), |ui| { + ui.with_layout(Layout::top_down(Align::Min).with_main_justify(true), |ui| { + ui.with_layout(Layout::left_to_right(Align::Min), |ui| { + ui.text_edit_singleline(&mut self.path); - if ui.button("run").clicked() { - self.output = interpret(&self.source); - } + if ui.button("read").clicked() { + self.source = read_to_string(&self.path).unwrap(); + } - ui.separator(); + if ui.button("run").clicked() { + self.output = self.interpreter.run(&self.source); + } + }); + ui.code_editor(&mut self.source); + }); - let output_text = match &self.output { - Ok(value) => value.to_string(), - Err(error) => error.to_string(), - }; + let output_text = match &self.output { + Ok(value) => value.to_string(), + Err(error) => error.to_string(), + }; - ui.label(output_text); - - ui.with_layout(egui::Layout::bottom_up(egui::Align::LEFT), |ui| { - egui::warn_if_debug_build(ui); - ui.hyperlink_to("source code", "https://git.jeffa.io/jeff/dust"); + ui.label(output_text); }); }); } diff --git a/src/bin/gui/main.rs b/src/bin/gui/main.rs index 98ef827..de2ef95 100644 --- a/src/bin/gui/main.rs +++ b/src/bin/gui/main.rs @@ -1,6 +1,6 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use std::{fs::read_to_string, path::PathBuf}; +use std::path::PathBuf; use clap::Parser; @@ -9,14 +9,17 @@ mod app; #[derive(Parser)] struct Args { // Path to the file to read. - path: PathBuf, + path: Option, } fn main() -> eframe::Result<()> { env_logger::init(); - let path = Args::parse().path; - let source = read_to_string(&path).unwrap(); + let path = if let Some(path) = Args::parse().path { + path + } else { + PathBuf::new() + }; let native_options = eframe::NativeOptions { viewport: egui::ViewportBuilder::default() .with_inner_size([400.0, 300.0]) @@ -27,6 +30,6 @@ fn main() -> eframe::Result<()> { eframe::run_native( "Dust GUI", native_options, - Box::new(|cc| Box::new(app::App::new(cc, source))), + Box::new(|cc| Box::new(app::App::new(cc, path))), ) } diff --git a/src/interpret.rs b/src/interpret.rs index f8223c3..fd233a6 100644 --- a/src/interpret.rs +++ b/src/interpret.rs @@ -41,7 +41,7 @@ pub fn interpret(source: &str) -> Result { /// ); /// ``` pub fn interpret_with_context(source: &str, context: Map) -> Result { - let mut interpreter = Interpreter::new(context)?; + let mut interpreter = Interpreter::new(context); let value = interpreter.run(source)?; Ok(value) @@ -56,25 +56,27 @@ pub struct Interpreter { } impl Interpreter { - pub fn new(context: Map) -> Result { + pub fn new(context: Map) -> Self { let mut parser = Parser::new(); - parser.set_language(language())?; + parser + .set_language(language()) + .expect("Language version is incompatible with tree sitter version."); - Ok(Interpreter { + Interpreter { parser, context, syntax_tree: None, abstract_tree: None, - }) + } } pub fn parse_only(&mut self, source: &str) { - self.syntax_tree = self.parser.parse(source, self.syntax_tree.as_ref()); + self.syntax_tree = self.parser.parse(source, None); } pub fn run(&mut self, source: &str) -> Result { - self.syntax_tree = self.parser.parse(source, self.syntax_tree.as_ref()); + self.syntax_tree = self.parser.parse(source, None); self.abstract_tree = if let Some(syntax_tree) = &self.syntax_tree { Some(Root::from_syntax_node( source, @@ -100,3 +102,9 @@ impl Interpreter { } } } + +impl Default for Interpreter { + fn default() -> Self { + Interpreter::new(Map::new()) + } +} diff --git a/src/main.rs b/src/main.rs index 3df2867..594d1bc 100644 --- a/src/main.rs +++ b/src/main.rs @@ -72,7 +72,7 @@ fn main() { let mut parser = TSParser::new(); parser.set_language(language()).unwrap(); - let mut interpreter = Interpreter::new(context).unwrap(); + let mut interpreter = Interpreter::new(context); if args.show_syntax_tree { interpreter.parse_only(&source); diff --git a/tests/interpret.rs b/tests/interpret.rs index 9c9bfc0..ae1c846 100644 --- a/tests/interpret.rs +++ b/tests/interpret.rs @@ -154,7 +154,7 @@ mod value { let function = value.as_function().unwrap(); assert_eq!(&Vec::::with_capacity(0), function.parameters()); - assert_eq!(Ok(&Type::Integer), function.return_type()); + assert_eq!(&Type::Integer, function.return_type()); let result = interpret("(x ) -> {true}"); let value = result.unwrap(); @@ -164,7 +164,7 @@ mod value { &vec![Identifier::new("x".to_string())], function.parameters() ); - assert_eq!(Ok(&Type::Boolean), function.return_type()); + assert_eq!(&Type::Boolean, function.return_type()); } #[test]