Begin writing GUI
This commit is contained in:
parent
dec9e70e4f
commit
7ea6283650
1431
Cargo.lock
generated
1431
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
13
Cargo.toml
13
Cargo.toml
@ -45,6 +45,19 @@ tracing-error = "0.2.0"
|
||||
tracing-subscriber = { version = "0.3.18", features = ["env-filter"] }
|
||||
tree-sitter = "0.20.10"
|
||||
tui-textarea = { version = "0.4.0", features = ["search"] }
|
||||
egui = "0.24.1"
|
||||
eframe = { version = "0.24.1", default-features = false, features = [
|
||||
"default_fonts", # Embed the default egui fonts.
|
||||
"glow", # Use the glow rendering backend. Alternative: "wgpu".
|
||||
"persistence", # Enable restoring app state when restarting the app.
|
||||
] }
|
||||
log = "0.4"
|
||||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
env_logger = "0.10"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
wasm-bindgen-futures = "0.4"
|
||||
|
||||
[build-dependencies]
|
||||
cc = "1.0"
|
||||
|
@ -46,8 +46,8 @@ impl AbstractTree for Assignment {
|
||||
"-=" => AssignmentOperator::MinusEqual,
|
||||
_ => {
|
||||
return Err(Error::UnexpectedSyntaxNode {
|
||||
expected: "=, += or -=",
|
||||
actual: operator_node.kind(),
|
||||
expected: "=, += or -=".to_string(),
|
||||
actual: operator_node.kind().to_string(),
|
||||
location: operator_node.start_position(),
|
||||
relevant_source: source[operator_node.byte_range()].to_string(),
|
||||
})
|
||||
|
@ -53,8 +53,9 @@ impl AbstractTree for Expression {
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::UnexpectedSyntaxNode {
|
||||
expected: "value_node, identifier, index, math, logic, function_call or yield",
|
||||
actual: child.kind(),
|
||||
expected: "value_node, identifier, index, math, logic, function_call or yield"
|
||||
.to_string(),
|
||||
actual: child.kind().to_string(),
|
||||
location: child.start_position(),
|
||||
relevant_source: source[child.byte_range()].to_string(),
|
||||
})
|
||||
|
@ -23,8 +23,8 @@ impl AbstractTree for For {
|
||||
"async for" => true,
|
||||
_ => {
|
||||
return Err(Error::UnexpectedSyntaxNode {
|
||||
expected: "for or async for",
|
||||
actual: for_node.kind(),
|
||||
expected: "for or async for".to_string(),
|
||||
actual: for_node.kind().to_string(),
|
||||
location: for_node.start_position(),
|
||||
relevant_source: source[for_node.byte_range()].to_string(),
|
||||
})
|
||||
|
@ -37,8 +37,8 @@ impl AbstractTree for FunctionExpression {
|
||||
)?)),
|
||||
_ => {
|
||||
return Err(Error::UnexpectedSyntaxNode {
|
||||
expected: "identifier, function call, value or index",
|
||||
actual: child.kind(),
|
||||
expected: "identifier, function call, value or index".to_string(),
|
||||
actual: child.kind().to_string(),
|
||||
location: child.start_position(),
|
||||
relevant_source: source[child.byte_range()].to_string(),
|
||||
})
|
||||
|
@ -31,8 +31,8 @@ impl AbstractTree for IndexAssignment {
|
||||
"-=" => AssignmentOperator::MinusEqual,
|
||||
_ => {
|
||||
return Err(Error::UnexpectedSyntaxNode {
|
||||
expected: "=, += or -=",
|
||||
actual: operator_node.kind(),
|
||||
expected: "=, += or -=".to_string(),
|
||||
actual: operator_node.kind().to_string(),
|
||||
location: operator_node.start_position(),
|
||||
relevant_source: source[operator_node.byte_range()].to_string(),
|
||||
})
|
||||
|
@ -43,8 +43,8 @@ impl AbstractTree for Logic {
|
||||
"<=" => LogicOperator::LessOrEqaul,
|
||||
_ => {
|
||||
return Err(Error::UnexpectedSyntaxNode {
|
||||
expected: "==, !=, &&, ||, >, <, >= or <=",
|
||||
actual: operator_node.kind(),
|
||||
expected: "==, !=, &&, ||, >, <, >= or <=".to_string(),
|
||||
actual: operator_node.kind().to_string(),
|
||||
location: operator_node.start_position(),
|
||||
relevant_source: source[operator_node.byte_range()].to_string(),
|
||||
})
|
||||
|
@ -30,8 +30,8 @@ impl AbstractTree for Math {
|
||||
"%" => MathOperator::Modulo,
|
||||
_ => {
|
||||
return Err(Error::UnexpectedSyntaxNode {
|
||||
expected: "+, -, *, / or %",
|
||||
actual: operator_node.kind(),
|
||||
expected: "+, -, *, / or %".to_string(),
|
||||
actual: operator_node.kind().to_string(),
|
||||
location: operator_node.start_position(),
|
||||
relevant_source: source[operator_node.byte_range()].to_string(),
|
||||
})
|
||||
|
@ -66,8 +66,8 @@ impl AbstractTree for Statement {
|
||||
)?)),
|
||||
_ => Err(Error::UnexpectedSyntaxNode {
|
||||
expected:
|
||||
"assignment, expression, block, return, if...else, while, for, index_assignment or match",
|
||||
actual: child.kind(),
|
||||
"assignment, expression, block, return, if...else, while, for, index_assignment or match".to_string(),
|
||||
actual: child.kind().to_string(),
|
||||
location: child.start_position(),
|
||||
relevant_source: source[child.byte_range()].to_string(),
|
||||
}),
|
||||
|
@ -203,8 +203,9 @@ impl AbstractTree for Type {
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::UnexpectedSyntaxNode {
|
||||
expected: "any, bool, float, function, int, list, map, num, str or option",
|
||||
actual: type_node.kind(),
|
||||
expected: "any, bool, float, function, int, list, map, num, str or option"
|
||||
.to_string(),
|
||||
actual: type_node.kind().to_string(),
|
||||
location: type_node.start_position(),
|
||||
relevant_source: source[type_node.byte_range()].to_string(),
|
||||
})
|
||||
|
@ -148,8 +148,8 @@ impl AbstractTree for ValueNode {
|
||||
}
|
||||
_ => {
|
||||
return Err(Error::UnexpectedSyntaxNode {
|
||||
expected: "string, integer, float, boolean, list, map, or option",
|
||||
actual: child.kind(),
|
||||
expected: "string, integer, float, boolean, list, map, or option".to_string(),
|
||||
actual: child.kind().to_string(),
|
||||
location: child.start_position(),
|
||||
relevant_source: source[child.byte_range()].to_string(),
|
||||
})
|
||||
|
71
src/bin/gui/app.rs
Normal file
71
src/bin/gui/app.rs
Normal file
@ -0,0 +1,71 @@
|
||||
use dust_lang::{interpret, Result, Value};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub struct App {
|
||||
source: String,
|
||||
output: Result<Value>,
|
||||
}
|
||||
|
||||
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`.
|
||||
|
||||
cc.egui_ctx.set_zoom_factor(1.5);
|
||||
|
||||
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);
|
||||
} else {
|
||||
app
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl eframe::App for App {
|
||||
/// Called by the frame work to save state before shutdown.
|
||||
fn save(&mut self, storage: &mut dyn eframe::Storage) {
|
||||
eframe::set_value(storage, eframe::APP_KEY, self);
|
||||
}
|
||||
|
||||
/// Called each time the UI needs repainting, which may be many times per second.
|
||||
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
|
||||
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
|
||||
egui::menu::bar(ui, |ui| {
|
||||
if ui.button("Quit").clicked() {
|
||||
ctx.send_viewport_cmd(egui::ViewportCommand::Close);
|
||||
}
|
||||
ui.add_space(16.0);
|
||||
|
||||
egui::widgets::global_dark_light_mode_buttons(ui);
|
||||
});
|
||||
});
|
||||
|
||||
egui::CentralPanel::default().show(ctx, |ui| {
|
||||
ui.code_editor(&mut self.source);
|
||||
|
||||
if ui.button("run").clicked() {
|
||||
self.output = interpret(&self.source);
|
||||
}
|
||||
|
||||
ui.separator();
|
||||
|
||||
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");
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
32
src/bin/gui/main.rs
Normal file
32
src/bin/gui/main.rs
Normal file
@ -0,0 +1,32 @@
|
||||
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
|
||||
|
||||
use std::{fs::read_to_string, path::PathBuf};
|
||||
|
||||
use clap::Parser;
|
||||
|
||||
mod app;
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Args {
|
||||
// Path to the file to read.
|
||||
path: PathBuf,
|
||||
}
|
||||
|
||||
fn main() -> eframe::Result<()> {
|
||||
env_logger::init();
|
||||
|
||||
let path = Args::parse().path;
|
||||
let source = read_to_string(&path).unwrap();
|
||||
let native_options = eframe::NativeOptions {
|
||||
viewport: egui::ViewportBuilder::default()
|
||||
.with_inner_size([400.0, 300.0])
|
||||
.with_min_inner_size([300.0, 220.0]),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
eframe::run_native(
|
||||
"Dust GUI",
|
||||
native_options,
|
||||
Box::new(|cc| Box::new(app::App::new(cc, source))),
|
||||
)
|
||||
}
|
20
src/error.rs
20
src/error.rs
@ -3,6 +3,7 @@
|
||||
//! To deal with errors from dependencies, either create a new error variant
|
||||
//! or use the ToolFailure variant if the error can only occur inside a tool.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tree_sitter::{LanguageError, Node, Point};
|
||||
|
||||
use crate::{value::Value, BuiltInFunction, Type};
|
||||
@ -18,17 +19,19 @@ use std::{
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum Error {
|
||||
WithContext {
|
||||
error: Box<Error>,
|
||||
#[serde(skip)]
|
||||
location: Point,
|
||||
source: String,
|
||||
},
|
||||
|
||||
UnexpectedSyntaxNode {
|
||||
expected: &'static str,
|
||||
actual: &'static str,
|
||||
expected: String,
|
||||
actual: String,
|
||||
#[serde(skip)]
|
||||
location: Point,
|
||||
relevant_source: String,
|
||||
},
|
||||
@ -61,7 +64,7 @@ pub enum Error {
|
||||
|
||||
/// A function was called with the wrong amount of arguments.
|
||||
ExpectedBuiltInFunctionArgumentAmount {
|
||||
function_name: &'static str,
|
||||
function_name: String,
|
||||
expected: usize,
|
||||
actual: usize,
|
||||
},
|
||||
@ -160,6 +163,7 @@ pub enum Error {
|
||||
/// Invalid user input.
|
||||
Syntax {
|
||||
source: String,
|
||||
#[serde(skip)]
|
||||
location: Point,
|
||||
},
|
||||
|
||||
@ -177,7 +181,7 @@ impl Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_syntax_node(source: &str, expected: &'static str, actual: Node) -> Result<()> {
|
||||
pub fn expect_syntax_node(source: &str, expected: &str, actual: Node) -> Result<()> {
|
||||
if expected == actual.kind() {
|
||||
Ok(())
|
||||
} else if actual.is_error() {
|
||||
@ -187,8 +191,8 @@ impl Error {
|
||||
})
|
||||
} else {
|
||||
Err(Error::UnexpectedSyntaxNode {
|
||||
expected,
|
||||
actual: actual.kind(),
|
||||
expected: expected.to_string(),
|
||||
actual: actual.kind().to_string(),
|
||||
location: actual.start_position(),
|
||||
relevant_source: source[actual.byte_range()].to_string(),
|
||||
})
|
||||
@ -204,7 +208,7 @@ impl Error {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::ExpectedBuiltInFunctionArgumentAmount {
|
||||
function_name: function.name(),
|
||||
function_name: function.name().to_string(),
|
||||
expected,
|
||||
actual,
|
||||
})
|
||||
|
@ -1,6 +1,6 @@
|
||||
(expression) @expression
|
||||
(value) @value
|
||||
(identifier) @identifier
|
||||
(identifier) @variable
|
||||
(value) @value
|
||||
(string) @string
|
||||
|
||||
@ -14,7 +14,7 @@
|
||||
(boolean) @boolean
|
||||
(list) @list
|
||||
|
||||
"," @punctuation.delimiter
|
||||
["," ":" ";"] @punctuation.delimiter
|
||||
|
||||
[
|
||||
"["
|
||||
@ -28,25 +28,22 @@
|
||||
] @punctuation.bracket
|
||||
|
||||
[
|
||||
(assignment_operator)
|
||||
(logic_operator)
|
||||
(math_operator)
|
||||
] @operator
|
||||
(type)
|
||||
(type_definition)
|
||||
] @type
|
||||
|
||||
(assignment_operator) @operator.assignment
|
||||
(logic_operator) @operator.logic
|
||||
(math_operator) @operator.math
|
||||
|
||||
[
|
||||
"any"
|
||||
"async"
|
||||
"else"
|
||||
"false"
|
||||
"float"
|
||||
"for"
|
||||
"if"
|
||||
"in"
|
||||
"int"
|
||||
"map"
|
||||
"match"
|
||||
"num"
|
||||
"str"
|
||||
"true"
|
||||
"while"
|
||||
"->"
|
||||
@ -54,3 +51,4 @@
|
||||
] @keyword
|
||||
|
||||
(built_in_function) @function.builtin
|
||||
(function_call) @function.call
|
||||
|
Loading…
Reference in New Issue
Block a user