Begin writing GUI

This commit is contained in:
Jeff 2023-12-30 02:04:39 -05:00
parent dec9e70e4f
commit 7ea6283650
16 changed files with 1561 additions and 70 deletions

1431
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -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"

View File

@ -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(),
})

View File

@ -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(),
})

View File

@ -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(),
})

View File

@ -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(),
})

View File

@ -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(),
})

View File

@ -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(),
})

View File

@ -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(),
})

View File

@ -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(),
}),

View File

@ -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(),
})

View File

@ -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
View 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
View 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))),
)
}

View File

@ -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,
})

View File

@ -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