wrote new "ContextTree" system, emulator filetree is kind of broken for now. next commit will fix this.
This commit is contained in:
@@ -3,13 +3,12 @@ use egui_tiles::TileId;
|
|||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
|
|
||||||
|
|
||||||
/// the action enum forms the core of the application's state.
|
/// the action enum forms the core of the application's state.
|
||||||
/// - Actions are used as the main way that Tiles inside the application pass state updates back to the parent UI,
|
/// - Actions are used as the main way that Tiles inside the application pass state updates back to the parent UI,
|
||||||
/// specifically when those state updates involve other files. for example, pressing "build" in the editor would emit
|
/// specifically when those state updates involve other files. for example, pressing "build" in the editor would emit
|
||||||
/// a SpawnProcess action, running some task which on completion emits a LoadBinary action on the result
|
/// a SpawnProcess action, running some task which on completion emits a LoadBinary action on the result
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum Action {
|
pub enum Action {
|
||||||
None,
|
|
||||||
RequestCloseTab(TileId),
|
RequestCloseTab(TileId),
|
||||||
CloseTab(TileId),
|
CloseTab(TileId),
|
||||||
Quit,
|
Quit,
|
||||||
@@ -18,7 +17,7 @@ pub enum Action {
|
|||||||
SaveFile(PathBuf, TileId),
|
SaveFile(PathBuf, TileId),
|
||||||
RequestSaveAs(TileId),
|
RequestSaveAs(TileId),
|
||||||
ShowError(String),
|
ShowError(String),
|
||||||
SpawnProcess(ProcessSpawner)
|
SpawnProcess(ProcessSpawner),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
@@ -26,4 +25,3 @@ pub enum DataSource {
|
|||||||
File(PathBuf),
|
File(PathBuf),
|
||||||
Data(Vec<u8>),
|
Data(Vec<u8>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,13 +17,12 @@ use egui_tiles::TileId;
|
|||||||
use process_manager::ProcessManager;
|
use process_manager::ProcessManager;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::BufReader;
|
use std::io::BufReader;
|
||||||
use std::path::Path;
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tiles::PanelFactory;
|
use tiles::PanelFactory;
|
||||||
use tiles::documentation::Documentation;
|
use tiles::documentation::Documentation;
|
||||||
use tiles::editor::Editor;
|
use tiles::editor::Editor;
|
||||||
use tiles::io_devices::framebuffer::FrameBufferUi;
|
use tiles::io_devices::framebuffer::FrameBufferUi;
|
||||||
use zxq5_egui::file_tree::{FileTree, FileTreeEvent};
|
use zxq5_egui::context_tree::{ContextTree, NewFileTree};
|
||||||
|
|
||||||
pub mod appearance;
|
pub mod appearance;
|
||||||
mod controller;
|
mod controller;
|
||||||
@@ -45,7 +44,7 @@ pub struct DamnSimpleUI {
|
|||||||
|
|
||||||
show_process_manager: bool,
|
show_process_manager: bool,
|
||||||
process_manager: ProcessManager,
|
process_manager: ProcessManager,
|
||||||
file_tree: FileTree,
|
file_tree: ContextTree<Action, NewFileTree>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl eframe::App for DamnSimpleUI {
|
impl eframe::App for DamnSimpleUI {
|
||||||
@@ -75,17 +74,13 @@ impl eframe::App for DamnSimpleUI {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Panel::left("left_panel").resizable(true).show(ui, |ui| {
|
Panel::left("left_panel").resizable(true).show(ui, |ui| {
|
||||||
let inner_res = self.file_tree.ui(ui, &mut |ui: &mut Ui, path: &Path| {
|
let inner_res = self.file_tree.ui(ui);
|
||||||
ui.button("Load Binary")
|
|
||||||
.clicked()
|
|
||||||
.then_some(Action::LoadBinary(DataSource::File(path.to_path_buf())))
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(event) = inner_res.inner {
|
if let Some(event) = inner_res.inner {
|
||||||
match event {
|
// match event {
|
||||||
FileTreeEvent::Clicked(path) => self.perform_action(Action::OpenFile(path)),
|
// ContextTreeEvent::Clicked(node) => self.perform_action(Action::OpenFile(node)),
|
||||||
FileTreeEvent::ContextAction(action) => self.perform_action(action),
|
// ContextTreeEvent::ContextAction(action, node) => self.perform_action(action),
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -151,15 +146,23 @@ impl DamnSimpleUI {
|
|||||||
emulator,
|
emulator,
|
||||||
show_process_manager: true,
|
show_process_manager: true,
|
||||||
process_manager: ProcessManager::new(),
|
process_manager: ProcessManager::new(),
|
||||||
file_tree: FileTree::build(&std::env::current_dir().unwrap()),
|
|
||||||
|
file_tree: ContextTree::new(
|
||||||
|
NewFileTree::build(&std::env::current_dir().unwrap()),
|
||||||
|
|ui, node| match node {
|
||||||
|
NewFileTree::Leaf { filename } => ui
|
||||||
|
.button("Open Binary")
|
||||||
|
.clicked()
|
||||||
|
.then_some(Action::LoadBinary(DataSource::File(filename.clone()))),
|
||||||
|
NewFileTree::Branch { .. } => None,
|
||||||
|
},
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn perform_action(&mut self, action: Action) {
|
pub fn perform_action(&mut self, action: Action) {
|
||||||
match action {
|
match action {
|
||||||
Action::None => {}
|
Action::SpawnProcess(spawner) => spawner.run(&mut self.process_manager),
|
||||||
|
|
||||||
Action::SpawnProcess(spawner) => spawner(&mut self.process_manager),
|
|
||||||
Action::RequestCloseTab(tile_id) => {
|
Action::RequestCloseTab(tile_id) => {
|
||||||
self.confirm_dialog.open(
|
self.confirm_dialog.open(
|
||||||
"Close Tab",
|
"Close Tab",
|
||||||
|
|||||||
@@ -2,11 +2,32 @@ use crate::state::Action;
|
|||||||
use crate::ui::ui_helpers::text::strip_ansi;
|
use crate::ui::ui_helpers::text::strip_ansi;
|
||||||
use dsa_tasks::{ProcessAgent, TraceableProcess};
|
use dsa_tasks::{ProcessAgent, TraceableProcess};
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use std::fmt::Debug;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
use std::thread::JoinHandle;
|
use std::thread::JoinHandle;
|
||||||
use zxq5_egui::tabs::painted_tab;
|
use zxq5_egui::tabs::painted_tab;
|
||||||
|
|
||||||
pub type ProcessSpawner = Box<dyn FnOnce(&mut ProcessManager) + Send>;
|
pub struct ProcessSpawner(Box<dyn FnOnce(&mut ProcessManager) + Send>);
|
||||||
|
|
||||||
|
impl Debug for ProcessSpawner {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "ProcessSpawner")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ProcessSpawner {
|
||||||
|
pub(crate) fn run(self, process_manager: &mut ProcessManager) {
|
||||||
|
self.0(process_manager);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(func: impl FnOnce(&mut ProcessManager) + Send + 'static) -> Self {
|
||||||
|
Self(Box::new(func))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn into_action(self) -> Action {
|
||||||
|
Action::SpawnProcess(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ProcessHandle {
|
pub struct ProcessHandle {
|
||||||
id: usize,
|
id: usize,
|
||||||
@@ -23,10 +44,10 @@ impl ProcessHandle {
|
|||||||
id: usize,
|
id: usize,
|
||||||
name: impl Into<String>,
|
name: impl Into<String>,
|
||||||
process: P,
|
process: P,
|
||||||
on_complete: impl FnOnce(Result<P::Output, P::Error>) -> Action + Send + 'static
|
on_complete: impl FnOnce(Result<P::Output, P::Error>) -> Action + Send + 'static,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let (tx, rx) = mpsc::channel::<String>();
|
let (tx, rx) = mpsc::channel::<String>();
|
||||||
let agent = ProcessAgent::new( tx );
|
let agent = ProcessAgent::new(tx);
|
||||||
|
|
||||||
let join_handle = std::thread::spawn(move || {
|
let join_handle = std::thread::spawn(move || {
|
||||||
let result = TraceableProcess::run(Box::new(process), agent);
|
let result = TraceableProcess::run(Box::new(process), agent);
|
||||||
@@ -34,7 +55,11 @@ impl ProcessHandle {
|
|||||||
});
|
});
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
rx, id, name: name.into(), join_handle: Some(join_handle), logs: Vec::new()
|
rx,
|
||||||
|
id,
|
||||||
|
name: name.into(),
|
||||||
|
join_handle: Some(join_handle),
|
||||||
|
logs: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -63,7 +88,12 @@ impl ProcessManager {
|
|||||||
self.terminated_actions.pop_front()
|
self.terminated_actions.pop_front()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn spawn<P: TraceableProcess>(&mut self, name: impl Into<String>, process: P, on_complete: impl FnOnce(Result<P::Output, P::Error>) -> Action + Send + 'static) {
|
pub fn spawn<P: TraceableProcess>(
|
||||||
|
&mut self,
|
||||||
|
name: impl Into<String>,
|
||||||
|
process: P,
|
||||||
|
on_complete: impl FnOnce(Result<P::Output, P::Error>) -> Action + Send + 'static,
|
||||||
|
) {
|
||||||
let handle = ProcessHandle::spawn(self.new_id(), name, process, on_complete);
|
let handle = ProcessHandle::spawn(self.new_id(), name, process, on_complete);
|
||||||
self.handles.push(handle);
|
self.handles.push(handle);
|
||||||
}
|
}
|
||||||
@@ -79,7 +109,10 @@ impl ProcessManager {
|
|||||||
/// Checks for finished processes and queues their resulting Actions.
|
/// Checks for finished processes and queues their resulting Actions.
|
||||||
fn poll_finished(&mut self) {
|
fn poll_finished(&mut self) {
|
||||||
for handle in &mut self.handles {
|
for handle in &mut self.handles {
|
||||||
let is_finished = handle.join_handle.as_ref().is_some_and(|jh| jh.is_finished());
|
let is_finished = handle
|
||||||
|
.join_handle
|
||||||
|
.as_ref()
|
||||||
|
.is_some_and(|jh| jh.is_finished());
|
||||||
if is_finished {
|
if is_finished {
|
||||||
if let Some(jh) = handle.join_handle.take() {
|
if let Some(jh) = handle.join_handle.take() {
|
||||||
if let Ok(action) = jh.join() {
|
if let Ok(action) = jh.join() {
|
||||||
@@ -102,11 +135,13 @@ impl ProcessManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// keep active_tab pointing at something valid
|
// keep active_tab pointing at something valid
|
||||||
if !self.active_tab.map_or(false, |id| self.handles.iter().any(|h| h.id == id)) {
|
if !self
|
||||||
|
.active_tab
|
||||||
|
.map_or(false, |id| self.handles.iter().any(|h| h.id == id))
|
||||||
|
{
|
||||||
self.active_tab = self.handles.first().map(|h| h.id);
|
self.active_tab = self.handles.first().map(|h| h.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
let mut to_close = None;
|
let mut to_close = None;
|
||||||
|
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
@@ -147,4 +182,4 @@ impl ProcessManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::state::Action;
|
use crate::state::Action;
|
||||||
use crate::state::DataSource::Data;
|
use crate::state::DataSource::Data;
|
||||||
|
use crate::ui::process_manager::ProcessSpawner;
|
||||||
use crate::ui::tiles::TilePane;
|
use crate::ui::tiles::TilePane;
|
||||||
use crate::ui::tiles::editor::syntax::SyntaxFrom;
|
use crate::ui::tiles::editor::syntax::SyntaxFrom;
|
||||||
use assembler::prelude::Assembler;
|
use assembler::prelude::Assembler;
|
||||||
@@ -30,7 +31,10 @@ impl Default for Editor {
|
|||||||
Self {
|
Self {
|
||||||
buffer: String::new(),
|
buffer: String::new(),
|
||||||
filename: None,
|
filename: None,
|
||||||
editor: CodeEditor::default().with_fontsize(12.0).with_theme(THEME).with_numlines(true),
|
editor: CodeEditor::default()
|
||||||
|
.with_fontsize(12.0)
|
||||||
|
.with_theme(THEME)
|
||||||
|
.with_numlines(true),
|
||||||
syntax: Syntax::default(),
|
syntax: Syntax::default(),
|
||||||
completer: Completer::default().with_auto_indent().with_user_words(),
|
completer: Completer::default().with_auto_indent().with_user_words(),
|
||||||
}
|
}
|
||||||
@@ -70,106 +74,90 @@ impl Editor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn create_build_action(&self, filename: PathBuf, on_complete: impl FnOnce(DseExecutable) -> Action + Send + 'static) -> Action {
|
pub fn create_build_action(
|
||||||
Action::SpawnProcess(Box::new(move |pm| {
|
&self,
|
||||||
|
filename: PathBuf,
|
||||||
|
on_complete: impl FnOnce(DseExecutable) -> Action + Send + 'static,
|
||||||
|
) -> Action {
|
||||||
|
ProcessSpawner::new(move |pm| {
|
||||||
let builder = Assembler::new(filename);
|
let builder = Assembler::new(filename);
|
||||||
pm.spawn(
|
pm.spawn(builder.label(), builder, move |result| match result {
|
||||||
builder.label(),
|
Ok(bin) => on_complete(bin),
|
||||||
builder,
|
Err(e) => Action::ShowError(e.to_string()),
|
||||||
move |result| match result {
|
});
|
||||||
Ok(bin) => on_complete(bin),
|
})
|
||||||
Err(e) => Action::ShowError(e.to_string()),
|
.into_action()
|
||||||
}
|
|
||||||
);
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TilePane for Editor {
|
impl TilePane for Editor {
|
||||||
fn title(&self) -> egui::WidgetText {
|
fn title(&self) -> egui::WidgetText {
|
||||||
self.filename.clone()
|
self.filename
|
||||||
|
.clone()
|
||||||
.map(|f| f.file_name().unwrap().to_str().unwrap().into())
|
.map(|f| f.file_name().unwrap().to_str().unwrap().into())
|
||||||
.unwrap_or("New File*".into())
|
.unwrap_or("New File*".into())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ui(&mut self, ui: &mut Ui, tile_id: TileId) -> Option<Action> {
|
fn ui(&mut self, ui: &mut Ui, tile_id: TileId) -> Option<Action> {
|
||||||
let mut action = Action::None;
|
let mut action = None;
|
||||||
|
|
||||||
egui::Panel::top("panel").show(ui, |ui| {
|
egui::Panel::top("panel").show(ui, |ui| {
|
||||||
ui.horizontal(|ui| {
|
ui.horizontal(|ui| {
|
||||||
|
|
||||||
if PaddedWidget::button("Build & Load").ui(ui).clicked() {
|
if PaddedWidget::button("Build & Load").ui(ui).clicked() {
|
||||||
let Some(filename) = self.filename.clone() else {
|
let Some(filename) = self.filename.clone() else {
|
||||||
action = Action::ShowError("Error - no filename set".to_string());
|
action = Some(Action::ShowError("Error - no filename set".to_string()));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
action = self.create_build_action(
|
action = Some(self.create_build_action(filename, |res| {
|
||||||
filename, |res| {
|
Action::LoadBinary(Data(res.to_memory_image()))
|
||||||
Action::LoadBinary(Data(res.to_memory_image()))
|
}));
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if PaddedWidget::button("Build").ui(ui).clicked() {
|
if PaddedWidget::button("Build").ui(ui).clicked() {
|
||||||
let Some(filename) = self.filename.clone() else {
|
let Some(filename) = self.filename.clone() else {
|
||||||
action = Action::ShowError("Error - no filename set".to_string());
|
action = Some(Action::ShowError("Error - no filename set".to_string()));
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
action = self.create_build_action(
|
action = Some(self.create_build_action(filename.clone(), move |res| {
|
||||||
filename.clone(), move |res| {
|
let filename = filename.with_extension("dsb");
|
||||||
let filename = filename.with_extension("dsb");
|
let mut writer = File::create(&filename).unwrap();
|
||||||
let mut writer = File::create(&filename).unwrap();
|
res.write(&mut writer).unwrap();
|
||||||
res.write(&mut writer).unwrap();
|
Action::OpenFile(filename)
|
||||||
Action::OpenFile(filename)
|
}));
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
PaddedWidget::label(
|
PaddedWidget::label(
|
||||||
self.filename
|
self.filename
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|f| f.display().to_string())
|
.map(|f| f.display().to_string())
|
||||||
.unwrap_or_else(|| String::from("New File"))
|
.unwrap_or_else(|| String::from("New File")),
|
||||||
).color(Color32::DARK_GRAY).ui(ui);
|
)
|
||||||
|
.color(Color32::DARK_GRAY)
|
||||||
|
.ui(ui);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
self.editor.show_with_completer(ui, &mut self.buffer, &self.syntax, &mut self.completer);
|
self.editor
|
||||||
|
.show_with_completer(ui, &mut self.buffer, &self.syntax, &mut self.completer);
|
||||||
|
|
||||||
if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) {
|
if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) {
|
||||||
action = if let Some(filename) = self.filename.clone() {
|
action = if let Some(filename) = self.filename.clone() {
|
||||||
Action::SaveFile(filename, tile_id)
|
Some(Action::SaveFile(filename, tile_id))
|
||||||
} else {
|
} else {
|
||||||
Action::RequestSaveAs(tile_id)
|
Some(Action::RequestSaveAs(tile_id))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(action)
|
action
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
fn close_requires_confirmation(&mut self, _tile_id: TileId) -> bool {
|
fn close_requires_confirmation(&mut self, _tile_id: TileId) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,18 @@
|
|||||||
pub mod documentation;
|
|
||||||
pub mod editor;
|
|
||||||
pub mod about;
|
pub mod about;
|
||||||
pub mod disassembler;
|
pub mod disassembler;
|
||||||
|
pub mod documentation;
|
||||||
|
pub mod editor;
|
||||||
|
pub mod io_devices;
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
pub mod registers;
|
pub mod registers;
|
||||||
pub mod io_devices;
|
|
||||||
|
|
||||||
use crate::state::Action;
|
use crate::state::Action;
|
||||||
use crate::state::Action::RequestCloseTab;
|
use crate::state::Action::RequestCloseTab;
|
||||||
use eframe::epaint::{Color32, CornerRadius};
|
use eframe::epaint::{Color32, CornerRadius};
|
||||||
use egui::{Id, Response, Stroke, Ui};
|
use egui::{Id, Response, Stroke, Ui};
|
||||||
use egui_tiles::{Behavior, Container, SimplificationOptions, TabState, Tile, TileId, Tiles, Tree, UiResponse};
|
use egui_tiles::{
|
||||||
|
Behavior, Container, SimplificationOptions, TabState, Tile, TileId, Tiles, Tree, UiResponse,
|
||||||
|
};
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use zxq5_egui::tabs::painted_tab;
|
use zxq5_egui::tabs::painted_tab;
|
||||||
|
|
||||||
@@ -19,7 +21,6 @@ use zxq5_egui::tabs::painted_tab;
|
|||||||
/// type after it's been handed off to the tree (e.g. to write into the
|
/// type after it's been handed off to the tree (e.g. to write into the
|
||||||
/// editor's buffer from an `Action` handler).
|
/// editor's buffer from an `Action` handler).
|
||||||
pub trait TilePane: Any {
|
pub trait TilePane: Any {
|
||||||
|
|
||||||
/// the title that's shown on the tab in the UI.
|
/// the title that's shown on the tab in the UI.
|
||||||
fn title(&self) -> egui::WidgetText;
|
fn title(&self) -> egui::WidgetText;
|
||||||
|
|
||||||
@@ -75,15 +76,23 @@ impl Behavior<Pane> for TileBehavior {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn on_tab_close(&mut self, tiles: &mut Tiles<Pane>, tile_id: TileId) -> bool {
|
fn on_tab_close(&mut self, tiles: &mut Tiles<Pane>, tile_id: TileId) -> bool {
|
||||||
|
println!("on_tab_close_1");
|
||||||
|
|
||||||
let Some(Tile::Pane(pane)) = tiles.get_mut(tile_id) else {
|
let Some(Tile::Pane(pane)) = tiles.get_mut(tile_id) else {
|
||||||
return false
|
println!("on_tab_close_ret1");
|
||||||
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
println!("on_tab_close_2");
|
||||||
|
|
||||||
if pane.close_requires_confirmation(tile_id) {
|
if pane.close_requires_confirmation(tile_id) {
|
||||||
|
println!("on_tab_close_ret2");
|
||||||
self.pending_action = Some(RequestCloseTab(tile_id));
|
self.pending_action = Some(RequestCloseTab(tile_id));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
println!("on_tab_close_3");
|
||||||
|
|
||||||
tiles.remove(tile_id);
|
tiles.remove(tile_id);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -112,7 +121,6 @@ impl Behavior<Pane> for TileBehavior {
|
|||||||
36.0 // taller tab row = more vertical padding around the label
|
36.0 // taller tab row = more vertical padding around the label
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn simplification_options(&self) -> SimplificationOptions {
|
fn simplification_options(&self) -> SimplificationOptions {
|
||||||
SimplificationOptions {
|
SimplificationOptions {
|
||||||
// Keep the tab bar around even with only one tab, and don't let
|
// Keep the tab bar around even with only one tab, and don't let
|
||||||
@@ -122,7 +130,13 @@ impl Behavior<Pane> for TileBehavior {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tab_bg_color(&self, visuals: &egui::Visuals, _tiles: &Tiles<Box<dyn TilePane>>, _tile_id: TileId, tab_state: &TabState) -> egui::Color32 {
|
fn tab_bg_color(
|
||||||
|
&self,
|
||||||
|
visuals: &egui::Visuals,
|
||||||
|
_tiles: &Tiles<Box<dyn TilePane>>,
|
||||||
|
_tile_id: TileId,
|
||||||
|
tab_state: &TabState,
|
||||||
|
) -> egui::Color32 {
|
||||||
visuals.widgets.inactive.bg_fill
|
visuals.widgets.inactive.bg_fill
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,7 +183,8 @@ impl CentralTiles {
|
|||||||
|
|
||||||
match self.tree.root {
|
match self.tree.root {
|
||||||
Some(root) => {
|
Some(root) => {
|
||||||
if let Some(Tile::Container(Container::Tabs(tabs))) = self.tree.tiles.get_mut(root) {
|
if let Some(Tile::Container(Container::Tabs(tabs))) = self.tree.tiles.get_mut(root)
|
||||||
|
{
|
||||||
tabs.children.push(tile_id);
|
tabs.children.push(tile_id);
|
||||||
tabs.active = Some(tile_id);
|
tabs.active = Some(tile_id);
|
||||||
} else {
|
} else {
|
||||||
@@ -205,9 +220,20 @@ impl CentralTiles {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<Action> {
|
pub fn ui(&mut self, ui: &mut egui::Ui) -> Option<Action> {
|
||||||
self.behavior.pending_action = None;
|
println!("centraltiles_ui_1");
|
||||||
|
|
||||||
|
// self.behavior.pending_action = None;
|
||||||
self.tree.ui(&mut self.behavior, ui);
|
self.tree.ui(&mut self.behavior, ui);
|
||||||
self.behavior.pending_action.take()
|
|
||||||
|
println!("centraltiles_ui_2");
|
||||||
|
|
||||||
|
let action = self.behavior.pending_action.take();
|
||||||
|
|
||||||
|
println!("centraltiles_ui_3 {action:?}");
|
||||||
|
|
||||||
|
action
|
||||||
|
|
||||||
|
// self.behavior.pending_action.take()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,6 +245,9 @@ impl PanelFactory {
|
|||||||
T: TilePane + 'static,
|
T: TilePane + 'static,
|
||||||
F: Fn() -> T + 'static,
|
F: Fn() -> T + 'static,
|
||||||
{
|
{
|
||||||
Self(name.to_string(), Box::new(move || Box::new(constructor()) as Box<dyn TilePane>))
|
Self(
|
||||||
|
name.to_string(),
|
||||||
|
Box::new(move || Box::new(constructor()) as Box<dyn TilePane>),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,150 @@
|
|||||||
|
use egui::{InnerResponse, TextBuffer, Widget};
|
||||||
|
use std::borrow::Cow;
|
||||||
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
|
#[derive(Clone, PartialOrd, Ord, Eq, PartialEq)]
|
||||||
|
pub enum NewFileTree {
|
||||||
|
Branch {
|
||||||
|
path: PathBuf,
|
||||||
|
children: Vec<NewFileTree>,
|
||||||
|
},
|
||||||
|
Leaf {
|
||||||
|
filename: PathBuf,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NewFileTree {
|
||||||
|
pub fn build(path: &Path) -> Self {
|
||||||
|
match path.is_dir() {
|
||||||
|
true => Self::Branch {
|
||||||
|
path: path.to_path_buf(),
|
||||||
|
children: {
|
||||||
|
let mut children: Vec<Self> = std::fs::read_dir(path)
|
||||||
|
.map(|ok| {
|
||||||
|
ok.filter_map(|file| file.ok())
|
||||||
|
.map(|file| Self::build(&path.join(file.file_name())))
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.unwrap_or_default();
|
||||||
|
children.sort_unstable();
|
||||||
|
children
|
||||||
|
},
|
||||||
|
},
|
||||||
|
false => Self::Leaf {
|
||||||
|
filename: path.to_path_buf(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TreeView for NewFileTree {
|
||||||
|
fn node(&self) -> Node<'_, Self> {
|
||||||
|
match self {
|
||||||
|
Self::Branch { path, children } => Node::Branch {
|
||||||
|
name: path.file_name().unwrap().to_string_lossy(),
|
||||||
|
children,
|
||||||
|
},
|
||||||
|
Self::Leaf { filename } => Node::Leaf {
|
||||||
|
name: filename.file_name().unwrap().to_string_lossy(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub enum ContextTreeEvent<'t, A, T: TreeView> {
|
||||||
|
Clicked(Node<'t, T>),
|
||||||
|
ContextAction(A, Node<'t, T>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd)]
|
||||||
|
pub enum Node<'t, T: TreeView> {
|
||||||
|
Leaf {
|
||||||
|
name: Cow<'t, str>,
|
||||||
|
},
|
||||||
|
Branch {
|
||||||
|
name: Cow<'t, str>,
|
||||||
|
children: &'t Vec<T>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait TreeView: Sized + Clone {
|
||||||
|
fn node(&self) -> Node<'_, Self>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ContextTree<A, T: TreeView> {
|
||||||
|
root: T,
|
||||||
|
context: Box<dyn FnMut(&mut egui::Ui, &T) -> Option<A>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Action, Tree> ContextTree<Action, Tree>
|
||||||
|
where
|
||||||
|
Tree: TreeView,
|
||||||
|
{
|
||||||
|
type Ctx = Box<dyn FnMut(&mut egui::Ui, &Tree) -> Option<Action>>;
|
||||||
|
|
||||||
|
pub fn new<T: FnMut(&mut egui::Ui, &Tree) -> Option<Action> + 'static>(
|
||||||
|
root: Tree,
|
||||||
|
context: T,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
root,
|
||||||
|
context: Box::new(context),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ctx_menu<'t>(
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
ctx: &mut Self::Ctx,
|
||||||
|
tree: &'t Tree,
|
||||||
|
) -> Option<ContextTreeEvent<'t, Action, Tree>> {
|
||||||
|
ctx(ui, tree).and_then(|a| Some(ContextTreeEvent::ContextAction(a, tree.node())))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ui(
|
||||||
|
&mut self,
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
) -> InnerResponse<Option<ContextTreeEvent<'_, Action, Tree>>> {
|
||||||
|
let mut action = None;
|
||||||
|
let frame_response = egui::Frame::new().show(ui, |ui| {
|
||||||
|
egui::ScrollArea::both()
|
||||||
|
.auto_shrink([false; 2])
|
||||||
|
.show(ui, |ui| {
|
||||||
|
Self::ui_inner(ui, &mut action, &mut self.context, &self.root);
|
||||||
|
})
|
||||||
|
});
|
||||||
|
InnerResponse::new(action, frame_response.response)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn ui_inner<'ctx, 't>(
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
action: &mut Option<ContextTreeEvent<'t, Action, Tree>>,
|
||||||
|
ctx: &mut Self::Ctx,
|
||||||
|
tree: &'t Tree,
|
||||||
|
) {
|
||||||
|
match tree.node() {
|
||||||
|
Node::Leaf { ref name } => {
|
||||||
|
let response = egui::Button::new(name.as_str()).frame(false).ui(ui);
|
||||||
|
|
||||||
|
response.context_menu(|ui| *action = Self::ctx_menu(ui, ctx, tree));
|
||||||
|
|
||||||
|
if response.clicked() {
|
||||||
|
*action = Some(ContextTreeEvent::Clicked(tree.node()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Node::Branch {
|
||||||
|
ref name,
|
||||||
|
ref children,
|
||||||
|
} => {
|
||||||
|
let show_response = egui::CollapsingHeader::new(name.as_str()).show(ui, |ui| {
|
||||||
|
for child in children.iter() {
|
||||||
|
Self::ui_inner(ui, action, ctx, child);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
show_response
|
||||||
|
.header_response
|
||||||
|
.context_menu(|ui| *action = Self::ctx_menu(ui, ctx, tree));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
use egui::{CollapsingHeader, InnerResponse};
|
use egui::{CollapsingHeader, InnerResponse, Widget};
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
use std::path::{Path, PathBuf};
|
use std::path::{Path, PathBuf};
|
||||||
|
|
||||||
@@ -70,15 +70,19 @@ impl FileTree {
|
|||||||
) {
|
) {
|
||||||
match self {
|
match self {
|
||||||
Self::Leaf { filename } => {
|
Self::Leaf { filename } => {
|
||||||
let response = ui.button(filename.file_name().unwrap().to_string_lossy());
|
let response = egui::Button::new(filename.file_name().unwrap().to_string_lossy())
|
||||||
if response.clicked() {
|
.frame(false)
|
||||||
*action = Some(FileTreeEvent::Clicked(PathBuf::from(filename)));
|
.ui(ui);
|
||||||
}
|
|
||||||
response.context_menu(|ui| {
|
response.context_menu(|ui| {
|
||||||
if let Some(a) = ctx(ui, filename) {
|
if let Some(a) = ctx(ui, filename) {
|
||||||
*action = Some(FileTreeEvent::ContextAction(a));
|
*action = Some(FileTreeEvent::ContextAction(a));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if response.clicked() {
|
||||||
|
*action = Some(FileTreeEvent::Clicked(PathBuf::from(filename)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Self::Branch { path, children } => {
|
Self::Branch { path, children } => {
|
||||||
CollapsingHeader::new(path.file_name().unwrap().to_string_lossy())
|
CollapsingHeader::new(path.file_name().unwrap().to_string_lossy())
|
||||||
|
|||||||
@@ -1,3 +1,8 @@
|
|||||||
|
#![feature(trait_alias)]
|
||||||
|
#![feature(str_as_str)]
|
||||||
|
#![feature(inherent_associated_types)]
|
||||||
|
|
||||||
pub mod buttons;
|
pub mod buttons;
|
||||||
pub mod tabs;
|
pub mod context_tree;
|
||||||
pub mod file_tree;
|
pub mod file_tree;
|
||||||
|
pub mod tabs;
|
||||||
|
|||||||
@@ -4,17 +4,11 @@ use crate::{assembler::AssembleError, expect_token};
|
|||||||
use common::prelude::*;
|
use common::prelude::*;
|
||||||
use dsa_tasks::ProcessAgent;
|
use dsa_tasks::ProcessAgent;
|
||||||
|
|
||||||
pub fn codegen(nodes: Vec<Node>, ctx: &ProcessAgent) -> Result<Vec<Instruction>, AssembleError> {
|
pub fn codegen(nodes: &[Node], ctx: &ProcessAgent) -> Result<Vec<Instruction>, AssembleError> {
|
||||||
let mut instructions = vec![];
|
nodes
|
||||||
|
.iter()
|
||||||
for node in nodes {
|
.map(build_instruction)
|
||||||
ctx.log(format!("{}", node));
|
.collect::<Result<Vec<_>, _>>()
|
||||||
instructions.push(build_instruction(&node)?);
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx.log("Assembly Successful ✅");
|
|
||||||
|
|
||||||
Ok(instructions)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_instruction(node: &Node) -> Result<Instruction, AssembleError> {
|
fn build_instruction(node: &Node) -> Result<Instruction, AssembleError> {
|
||||||
@@ -41,6 +35,7 @@ fn build_instruction(node: &Node) -> Result<Instruction, AssembleError> {
|
|||||||
Opcode::Jmp | Opcode::Call | Opcode::Jic | Opcode::Jnc => {
|
Opcode::Jmp | Opcode::Call | Opcode::Jic | Opcode::Jnc => {
|
||||||
ins_jump_unconditional(opcode, &args)
|
ins_jump_unconditional(opcode, &args)
|
||||||
}
|
}
|
||||||
|
|
||||||
Opcode::Jez | Opcode::Jnz => ins_jump_conditional(opcode, &args),
|
Opcode::Jez | Opcode::Jnz => ins_jump_conditional(opcode, &args),
|
||||||
Opcode::Shl | Opcode::Shr => ins_bitshift(opcode, &args),
|
Opcode::Shl | Opcode::Shr => ins_bitshift(opcode, &args),
|
||||||
Opcode::Add
|
Opcode::Add
|
||||||
@@ -72,10 +67,7 @@ fn build_instruction(node: &Node) -> Result<Instruction, AssembleError> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_mov(
|
fn ins_mov(opcode: Opcode, args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
opcode: Opcode,
|
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(src_token) = args.first() else {
|
let Some(src_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -92,10 +84,7 @@ fn ins_mov(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_cmov(
|
fn ins_cmov(opcode: Opcode, args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
opcode: Opcode,
|
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(src_token) = args.first() else {
|
let Some(src_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -116,10 +105,7 @@ fn ins_cmov(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_ldx_stx(
|
fn ins_ldx_stx(opcode: Opcode, args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
opcode: Opcode,
|
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(src_token) = args.first() else {
|
let Some(src_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -147,10 +133,7 @@ fn ins_ldx_stx(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_stack(
|
fn ins_stack(opcode: Opcode, args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
opcode: Opcode,
|
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(reg_token) = args.first() else {
|
let Some(reg_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -163,10 +146,7 @@ fn ins_stack(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_load_imm(
|
fn ins_load_imm(opcode: Opcode, args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
opcode: Opcode,
|
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(value_token) = args.first() else {
|
let Some(value_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -184,10 +164,7 @@ fn ins_load_imm(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_jump_unconditional(
|
fn ins_jump_unconditional(opcode: Opcode, args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
opcode: Opcode,
|
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(addr_imm_token) = args.first() else {
|
let Some(addr_imm_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -206,21 +183,16 @@ fn ins_jump_unconditional(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_jump_conditional(
|
fn ins_jump_conditional(opcode: Opcode, args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
opcode: Opcode,
|
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(condition_token) = args.first() else {
|
let Some(condition_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
let Some(addr_imm_token) = args.get(1) else {
|
let Some(addr_imm_token) = args.get(1) else {
|
||||||
return Err(AssembleError::MissingArgument(1));
|
return Err(AssembleError::MissingArgument(1));
|
||||||
};
|
};
|
||||||
let addr_reg_token = if let Some(token) = args.get(2) {
|
let addr_reg_token = args
|
||||||
token
|
.get(2)
|
||||||
} else {
|
.map_or(&Token::Register(Register::Zero), |token| token);
|
||||||
&Token::Register(Register::Zero)
|
|
||||||
};
|
|
||||||
|
|
||||||
let condition = expect_token!(condition_token, Register)?;
|
let condition = expect_token!(condition_token, Register)?;
|
||||||
let addr_imm = expect_token!(addr_imm_token, Immediate)?;
|
let addr_imm = expect_token!(addr_imm_token, Immediate)?;
|
||||||
@@ -233,10 +205,7 @@ fn ins_jump_conditional(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_comparison(
|
fn ins_comparison(opcode: Opcode, args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
opcode: Opcode,
|
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(left_token) = args.first() else {
|
let Some(left_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -261,10 +230,7 @@ fn ins_comparison(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_bitshift(
|
fn ins_bitshift(opcode: Opcode, args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
opcode: Opcode,
|
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(src_reg) = args.first() else {
|
let Some(src_reg) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -290,10 +256,7 @@ fn ins_bitshift(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_arithmetic(
|
fn ins_arithmetic(opcode: Opcode, args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
opcode: Opcode,
|
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(left_token) = args.first() else {
|
let Some(left_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -321,10 +284,7 @@ fn ins_arithmetic(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ins_imm_arithmetic(
|
fn ins_imm_arithmetic(opcode: Opcode, args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
opcode: Opcode,
|
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(reg_token) = args.first() else {
|
let Some(reg_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -368,9 +328,7 @@ fn ins_interrupt(args: &[Token]) -> Result<Instruction, AssembleError> {
|
|||||||
Ok(Instruction::int(code))
|
Ok(Instruction::int(code))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_data_instruction(
|
fn build_data_instruction(args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(immediate_token) = args.first() else {
|
let Some(immediate_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -379,9 +337,7 @@ fn build_data_instruction(
|
|||||||
Ok(Instruction::data(immediate))
|
Ok(Instruction::data(immediate))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_segment_instruction(
|
fn build_segment_instruction(args: &[Token]) -> Result<Instruction, AssembleError> {
|
||||||
args: &[Token],
|
|
||||||
) -> Result<Instruction, AssembleError> {
|
|
||||||
let Some(immediate_token) = args.first() else {
|
let Some(immediate_token) = args.first() else {
|
||||||
return Err(AssembleError::MissingArgument(0));
|
return Err(AssembleError::MissingArgument(0));
|
||||||
};
|
};
|
||||||
@@ -390,6 +346,6 @@ fn build_segment_instruction(
|
|||||||
Ok(Instruction::data(
|
Ok(Instruction::data(
|
||||||
// mask the first 6 bits for the opcode
|
// mask the first 6 bits for the opcode
|
||||||
// this is really deprecated tbh. may remove in future.
|
// this is really deprecated tbh. may remove in future.
|
||||||
Opcode::Segment as u32 | (immediate & 0x05FFFFFF),
|
Opcode::Segment as u32 | (immediate & 0x05FF_FFFF),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
#![allow(dead_code, unused)]
|
#![allow(dead_code, unused)]
|
||||||
|
|
||||||
use dsa_tasks::{ProcessAgent, TraceableProcess};
|
use crate::assembler::resolver2::{
|
||||||
use crate::assembler::resolver2::{SectionId, resolve_address, resolve_dependencies, resolve_symbols, split_sections};
|
SectionId, resolve_address, resolve_dependencies, resolve_symbols, split_sections,
|
||||||
|
};
|
||||||
use dsa_formats::binary_dse::{DseExecutable, SymbolEntry};
|
use dsa_formats::binary_dse::{DseExecutable, SymbolEntry};
|
||||||
|
use dsa_tasks::{ProcessAgent, TraceableProcess};
|
||||||
use std::{
|
use std::{
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
fmt, fs,
|
fmt, fs,
|
||||||
hash::{DefaultHasher, Hash, Hasher},
|
hash::{DefaultHasher, Hash, Hasher},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
sync::
|
sync::mpsc::{self},
|
||||||
mpsc::{self},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Module declarations
|
// Module declarations
|
||||||
@@ -64,6 +65,16 @@ impl TraceableProcess for Assembler {
|
|||||||
fn run(mut self: Box<Self>, ctx: ProcessAgent) -> Result<Self::Output, Self::Error> {
|
fn run(mut self: Box<Self>, ctx: ProcessAgent) -> Result<Self::Output, Self::Error> {
|
||||||
assemble(&self.src_path, &ctx)
|
assemble(&self.src_path, &ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn label(&self) -> String {
|
||||||
|
format!(
|
||||||
|
"Assembling {}",
|
||||||
|
self.src_path
|
||||||
|
.file_name()
|
||||||
|
.expect("a valid path must have a file name")
|
||||||
|
.to_string_lossy()
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assemble(src: &Path, ctx: &ProcessAgent) -> Result<DseExecutable, AssembleError> {
|
fn assemble(src: &Path, ctx: &ProcessAgent) -> Result<DseExecutable, AssembleError> {
|
||||||
@@ -74,21 +85,14 @@ fn assemble(src: &Path, ctx: &ProcessAgent) -> Result<DseExecutable, AssembleErr
|
|||||||
|
|
||||||
let nodes = program.nodes.clone();
|
let nodes = program.nodes.clone();
|
||||||
|
|
||||||
// for node in &nodes {
|
|
||||||
// ctx.log(format!("{:?}", node));
|
|
||||||
// }
|
|
||||||
|
|
||||||
let (data_nodes, mut text_nodes) = split_sections(nodes);
|
let (data_nodes, mut text_nodes) = split_sections(nodes);
|
||||||
let symbol_table = resolve_symbols(&data_nodes, &mut text_nodes)?;
|
let symbol_table = resolve_symbols(&data_nodes, &mut text_nodes)?;
|
||||||
let text_len = text_nodes.len() as u32;
|
let text_len = text_nodes.len() as u32;
|
||||||
|
|
||||||
ctx.log("Generating assembly output...");
|
ctx.log("Generating Instructions...");
|
||||||
|
|
||||||
// println!("Generating assembly output...");
|
let instructions = codegen(&text_nodes, ctx)?;
|
||||||
let instructions = codegen(text_nodes, ctx)?;
|
let text: Vec<u32> = instructions.iter().map(|i| i.0).collect();
|
||||||
let text: Vec<u32> = instructions
|
|
||||||
.iter()
|
|
||||||
.map(|i| i.0).collect();
|
|
||||||
|
|
||||||
let data = build_data_bytes(&data_nodes)?;
|
let data = build_data_bytes(&data_nodes)?;
|
||||||
|
|
||||||
@@ -117,20 +121,16 @@ fn assemble(src: &Path, ctx: &ProcessAgent) -> Result<DseExecutable, AssembleErr
|
|||||||
.iter()
|
.iter()
|
||||||
.find(|(sym, _)| sym.name == "_init")
|
.find(|(sym, _)| sym.name == "_init")
|
||||||
.map(|(_, entry)| resolve_address(*entry, text_len))
|
.map(|(_, entry)| resolve_address(*entry, text_len))
|
||||||
.ok_or(AssembleError::UndefinedSymbol(Symbol {
|
.ok_or_else(|| {
|
||||||
name: "_init".to_string(),
|
AssembleError::UndefinedSymbol(Symbol {
|
||||||
module: Module::Resolved(0),
|
name: "_init".to_string(),
|
||||||
}))?;
|
module: Module::Resolved(0),
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
ctx.log("Compilation Successful");
|
ctx.log("Compilation Successful");
|
||||||
|
|
||||||
Ok(DseExecutable::new(
|
Ok(DseExecutable::new(symbols.as_slice(), &string_table, &data, &text, entry_point).unwrap())
|
||||||
symbols.as_slice(),
|
|
||||||
&string_table,
|
|
||||||
&data,
|
|
||||||
&text,
|
|
||||||
entry_point,
|
|
||||||
).unwrap())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_data_bytes(data_nodes: &[Node]) -> Result<Vec<u8>, AssembleError> {
|
fn build_data_bytes(data_nodes: &[Node]) -> Result<Vec<u8>, AssembleError> {
|
||||||
@@ -144,7 +144,6 @@ fn build_data_bytes(data_nodes: &[Node]) -> Result<Vec<u8>, AssembleError> {
|
|||||||
Ok(bytes)
|
Ok(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fn prepare_dependency(
|
fn prepare_dependency(
|
||||||
path: &Path,
|
path: &Path,
|
||||||
modules: &mut HashSet<u64>,
|
modules: &mut HashSet<u64>,
|
||||||
@@ -157,21 +156,26 @@ fn prepare_dependency(
|
|||||||
.expect("Failed to get file name from path");
|
.expect("Failed to get file name from path");
|
||||||
|
|
||||||
if let Ok(path) = path.canonicalize() {
|
if let Ok(path) = path.canonicalize() {
|
||||||
ctx.log(format!("{:20} {:20} [{}]", "Building", filename, path.display()));
|
ctx.log(format!(
|
||||||
|
"{:30} {:20} [{}]",
|
||||||
|
"Building",
|
||||||
|
filename,
|
||||||
|
path.display()
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let src =
|
let src =
|
||||||
fs::read_to_string(path).map_err(|_| AssembleError::InvalidFile(path.to_path_buf()))?;
|
fs::read_to_string(path).map_err(|_| AssembleError::InvalidFile(path.to_path_buf()))?;
|
||||||
let file_hash = quick_hash(path);
|
let file_hash = quick_hash(path);
|
||||||
|
|
||||||
ctx.log(format!("{:20} {:20}", "Tokenising", filename));
|
ctx.log(format!("{:30} {:20}", "Lexing", filename));
|
||||||
let tokens = lexer::lexer(src, file_hash)?;
|
let tokens = lexer::lexer(src, file_hash)?;
|
||||||
// let tokens = Lexer::new(src, file_hash).run()?;
|
// let tokens = Lexer::new(src, file_hash).run()?;
|
||||||
|
|
||||||
ctx.log(format!("{:20} {:20}", "Parsing", filename));
|
ctx.log(format!("{:30} {:20}", "Parsing", filename));
|
||||||
let parsed = Parser::parse_nodes(tokens)?;
|
let parsed = Parser::parse_nodes(tokens)?;
|
||||||
|
|
||||||
ctx.log(format!("{:20} {:20}", "Resolving Deps", filename));
|
ctx.log(format!("{:30} {:20}", "Resolving Dependencies", filename));
|
||||||
// Get the parent directory of the source file to use as the base directory
|
// Get the parent directory of the source file to use as the base directory
|
||||||
let base_dir = path
|
let base_dir = path
|
||||||
.parent()
|
.parent()
|
||||||
@@ -181,20 +185,20 @@ fn prepare_dependency(
|
|||||||
|
|
||||||
let deps = Parser::get_dependencies(&nodes, path)?;
|
let deps = Parser::get_dependencies(&nodes, path)?;
|
||||||
|
|
||||||
ctx.log(format!("{:20} {:20}", "Expanding Pseudo-ops", filename));
|
ctx.log(format!("{:30} {:20}", "Expanding Macros", filename));
|
||||||
|
|
||||||
// add a section instruction
|
// add a section instruction
|
||||||
nodes.insert(
|
nodes.insert(
|
||||||
0,
|
0,
|
||||||
node!(None, Opcode::Segment, Token::Immediate(file_hash as u32)),
|
node!(None, Opcode::Segment, Token::Immediate(file_hash as u32)),
|
||||||
);
|
);
|
||||||
|
|
||||||
program.add_module(nodes);
|
program.add_module(nodes);
|
||||||
|
|
||||||
for dep in deps {
|
for dep in deps {
|
||||||
ctx.log(format!(
|
ctx.log(format!(
|
||||||
"{:20} {:20}",
|
"{:20} {:20}",
|
||||||
"Including",
|
"Including Dependency",
|
||||||
dep.file_name()
|
dep.file_name()
|
||||||
.and_then(|f| f.to_str())
|
.and_then(|f| f.to_str())
|
||||||
.expect("Dependency path has no file name or is not valid UTF-8")
|
.expect("Dependency path has no file name or is not valid UTF-8")
|
||||||
@@ -202,7 +206,7 @@ fn prepare_dependency(
|
|||||||
|
|
||||||
let dep_hash = quick_hash(&dep);
|
let dep_hash = quick_hash(&dep);
|
||||||
if modules.insert(dep_hash) {
|
if modules.insert(dep_hash) {
|
||||||
prepare_dependency(dep.as_path(), modules, program, &ctx)?;
|
prepare_dependency(dep.as_path(), modules, program, ctx)?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user