feat: Initialize compiler GUI project
Sets up the basic structure for the compiler GUI application using eframe. This includes the main application loop, a default UI layout with a source code editor and an output log panel, and the foundational AST (Abstract Syntax Tree) definitions for types, nodes, and an evaluation mechanism.
This commit is contained in:
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"context": {
|
||||||
|
"fileFiltering": {
|
||||||
|
"respectGitIgnore": false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
Delphi
|
||||||
Generated
+4340
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
|||||||
|
[package]
|
||||||
|
name = "Ast"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
eframe = "0.33.3"
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod types;
|
||||||
|
pub mod nodes;
|
||||||
|
|
||||||
|
pub use types::*;
|
||||||
|
pub use nodes::*;
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use crate::ast::types::{Identity, Value};
|
||||||
|
|
||||||
|
/// A generic AST Node wrapper to preserve identity and metadata
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Node<K> {
|
||||||
|
pub identity: Identity,
|
||||||
|
pub kind: K,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The base for custom node types (extensions)
|
||||||
|
pub trait CustomNode: Debug + Send + Sync {
|
||||||
|
fn eval(&self, node: &Node<UntypedKind>, ctx: &mut Context) -> Value;
|
||||||
|
fn display_name(&self) -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Evaluation Context (Scope management)
|
||||||
|
pub struct Context {
|
||||||
|
// Add variables, scope management here later
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The core AST variant enum for performance and structure
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum UntypedKind {
|
||||||
|
Constant(Value),
|
||||||
|
Identifier(Arc<str>),
|
||||||
|
If {
|
||||||
|
cond: Box<Node<UntypedKind>>,
|
||||||
|
then_br: Box<Node<UntypedKind>>,
|
||||||
|
else_br: Option<Box<Node<UntypedKind>>>,
|
||||||
|
},
|
||||||
|
Call {
|
||||||
|
callee: Box<Node<UntypedKind>>,
|
||||||
|
args: Vec<Node<UntypedKind>>,
|
||||||
|
},
|
||||||
|
/// The "escape hatch" for decentralized node types
|
||||||
|
Extension(Box<dyn CustomNode>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Node<UntypedKind> {
|
||||||
|
pub fn eval(&self, ctx: &mut Context) -> Value {
|
||||||
|
match &self.kind {
|
||||||
|
UntypedKind::Constant(v) => v.clone(),
|
||||||
|
UntypedKind::Identifier(_) => todo!("Lookup in Context"),
|
||||||
|
UntypedKind::If { cond, then_br, else_br } => {
|
||||||
|
if cond.eval(ctx).is_truthy() {
|
||||||
|
then_br.eval(ctx)
|
||||||
|
} else if let Some(eb) = else_br {
|
||||||
|
eb.eval(ctx)
|
||||||
|
} else {
|
||||||
|
Value::Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
UntypedKind::Call { callee, args } => {
|
||||||
|
let _func = callee.eval(ctx);
|
||||||
|
let _eval_args: Vec<Value> = args.iter().map(|a| a.eval(ctx)).collect();
|
||||||
|
todo!("Execute func with args")
|
||||||
|
}
|
||||||
|
UntypedKind::Extension(ext) => ext.eval(self, ctx),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
/// Shared identity for nodes (Location, etc.)
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NodeIdentity {
|
||||||
|
pub line: u32,
|
||||||
|
pub col: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Identity = Arc<NodeIdentity>;
|
||||||
|
|
||||||
|
/// Interned string identifier
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Keyword(pub u32);
|
||||||
|
|
||||||
|
/// Core data value in Myc Script (similar to TDataValue)
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum Value {
|
||||||
|
Nil,
|
||||||
|
Bool(bool),
|
||||||
|
Int(i64),
|
||||||
|
Float(f64),
|
||||||
|
Text(Arc<str>),
|
||||||
|
Keyword(Keyword),
|
||||||
|
List(Arc<Vec<Value>>),
|
||||||
|
Record(Arc<HashMap<Keyword, Value>>),
|
||||||
|
Function(Arc<dyn Fn(Vec<Value>) -> Value + Send + Sync>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Value {
|
||||||
|
pub fn is_truthy(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
Value::Nil => false,
|
||||||
|
Value::Bool(b) => *b,
|
||||||
|
_ => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Value {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
Value::Nil => write!(f, "nil"),
|
||||||
|
Value::Bool(b) => write!(f, "{}", b),
|
||||||
|
Value::Int(i) => write!(f, "{}", i),
|
||||||
|
Value::Float(fl) => write!(f, "{}", fl),
|
||||||
|
Value::Text(t) => write!(f, "\"{}\"", t),
|
||||||
|
Value::Keyword(k) => write!(f, ":kw#{}", k.0),
|
||||||
|
Value::List(l) => write!(f, "<list[{}]>", l.len()),
|
||||||
|
Value::Record(r) => write!(f, "<record[{}]>", r.len()),
|
||||||
|
Value::Function(_) => write!(f, "<fn>"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Value {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
fmt::Display::fmt(self, f)
|
||||||
|
}
|
||||||
|
}
|
||||||
+115
@@ -0,0 +1,115 @@
|
|||||||
|
use eframe::egui;
|
||||||
|
|
||||||
|
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,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for CompilerApp {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
source_code: String::new(),
|
||||||
|
output_log: String::from("Ready to compile..."),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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() {
|
||||||
|
use crate::ast::*;
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
// 1. Create shared identity
|
||||||
|
let identity = Arc::new(NodeIdentity { line: 1, col: 1 });
|
||||||
|
|
||||||
|
// 2. Build AST: (if true 42 nil)
|
||||||
|
let ast = Node {
|
||||||
|
identity: identity.clone(),
|
||||||
|
kind: UntypedKind::If {
|
||||||
|
cond: Box::new(Node {
|
||||||
|
identity: identity.clone(),
|
||||||
|
kind: UntypedKind::Constant(Value::Bool(true)),
|
||||||
|
}),
|
||||||
|
then_br: Box::new(Node {
|
||||||
|
identity: identity.clone(),
|
||||||
|
kind: UntypedKind::Constant(Value::Int(42)),
|
||||||
|
}),
|
||||||
|
else_br: Some(Box::new(Node {
|
||||||
|
identity: identity.clone(),
|
||||||
|
kind: UntypedKind::Constant(Value::Nil),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 3. Evaluate
|
||||||
|
let mut context = Context {};
|
||||||
|
let result = ast.eval(&mut context);
|
||||||
|
|
||||||
|
self.output_log = format!(
|
||||||
|
"AST Test:\nSource: (if true 42 nil)\nResult: {}\n\nCompiler log:\nStarted at {:?}",
|
||||||
|
result,
|
||||||
|
std::time::SystemTime::now(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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:");
|
||||||
|
|
||||||
|
egui::ScrollArea::vertical()
|
||||||
|
.id_salt("source_code_scroll")
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.add_sized(
|
||||||
|
ui.available_size(),
|
||||||
|
egui::TextEdit::multiline(&mut self.source_code)
|
||||||
|
.font(egui::TextStyle::Monospace)
|
||||||
|
.desired_width(f32::INFINITY)
|
||||||
|
.lock_focus(true),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user