merge commit
This commit is contained in:
Vendored
+2
-1
@@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"rust-analyzer.check.command": "clippy",
|
"rust-analyzer.check.command": "clippy",
|
||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true,
|
||||||
|
"rust-analyzer.cargo.features": "all"
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+185
-18
@@ -558,6 +558,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder-lite"
|
name = "byteorder-lite"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -653,12 +659,6 @@ dependencies = [
|
|||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "colorful"
|
|
||||||
version = "0.3.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "ffb474a9c3219a8254ead020421ecf1b90427f29b55f6aae9a2471fa62c126ef"
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "combine"
|
name = "combine"
|
||||||
version = "4.6.7"
|
version = "4.6.7"
|
||||||
@@ -750,6 +750,15 @@ dependencies = [
|
|||||||
"cfg-if",
|
"cfg-if",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-channel"
|
||||||
|
version = "0.5.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.21"
|
version = "0.8.21"
|
||||||
@@ -800,7 +809,29 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"option-ext",
|
"option-ext",
|
||||||
"redox_users",
|
"redox_users",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.60.2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "discord-presence"
|
||||||
|
version = "1.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f91d7c2fc01ffdc327e2b66d65dd59b8bd3f31a17e88811ce0540412fa0b84c1"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
"bytes",
|
||||||
|
"cfg-if",
|
||||||
|
"crossbeam-channel",
|
||||||
|
"log",
|
||||||
|
"num-derive",
|
||||||
|
"num-traits",
|
||||||
|
"parking_lot",
|
||||||
|
"paste",
|
||||||
|
"quork",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"uuid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -872,16 +903,6 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "dsa_editor"
|
|
||||||
version = "0.1.0"
|
|
||||||
dependencies = [
|
|
||||||
"colorful",
|
|
||||||
"eframe",
|
|
||||||
"egui",
|
|
||||||
"serde",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ecolor"
|
name = "ecolor"
|
||||||
version = "0.31.1"
|
version = "0.31.1"
|
||||||
@@ -984,6 +1005,14 @@ dependencies = [
|
|||||||
"winit",
|
"winit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "egui_code_editor"
|
||||||
|
version = "0.2.13"
|
||||||
|
source = "git+https://github.com/zxq5-dev/egui_code_editor?rev=5eb313e#5eb313e38504410ce0a6b27231cda28842f542fe"
|
||||||
|
dependencies = [
|
||||||
|
"egui",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "egui_glow"
|
name = "egui_glow"
|
||||||
version = "0.31.1"
|
version = "0.31.1"
|
||||||
@@ -1018,10 +1047,12 @@ dependencies = [
|
|||||||
"assembler",
|
"assembler",
|
||||||
"common",
|
"common",
|
||||||
"dirs",
|
"dirs",
|
||||||
"dsa_editor",
|
"discord-presence",
|
||||||
"eframe",
|
"eframe",
|
||||||
"egui",
|
"egui",
|
||||||
|
"egui_code_editor",
|
||||||
"rfd",
|
"rfd",
|
||||||
|
"toml",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -1602,6 +1633,12 @@ dependencies = [
|
|||||||
"hashbrown",
|
"hashbrown",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "itoa"
|
||||||
|
version = "1.0.15"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "jni"
|
name = "jni"
|
||||||
version = "0.21.1"
|
version = "0.21.1"
|
||||||
@@ -1885,6 +1922,17 @@ version = "0.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "num-derive"
|
||||||
|
version = "0.4.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-traits"
|
name = "num-traits"
|
||||||
version = "0.2.19"
|
version = "0.2.19"
|
||||||
@@ -2396,6 +2444,28 @@ dependencies = [
|
|||||||
"toml_edit",
|
"toml_edit",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error-attr2"
|
||||||
|
version = "2.0.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-error2"
|
||||||
|
version = "2.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-error-attr2",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.95"
|
version = "1.0.95"
|
||||||
@@ -2430,6 +2500,32 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quork"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6bd9640e0addc098a3481fd53bdc23970e5dd0edf6b349403aa680fb576c8f83"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"nix 0.29.0",
|
||||||
|
"quork-proc",
|
||||||
|
"thiserror 2.0.12",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quork-proc"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "860d36740d9412e39fff90f57010e9870b15c2b48e5325295a6f5a824a480439"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro-crate",
|
||||||
|
"proc-macro-error2",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "quote"
|
name = "quote"
|
||||||
version = "1.0.40"
|
version = "1.0.40"
|
||||||
@@ -2613,6 +2709,12 @@ version = "1.0.21"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ryu"
|
||||||
|
version = "1.0.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "same-file"
|
name = "same-file"
|
||||||
version = "1.0.6"
|
version = "1.0.6"
|
||||||
@@ -2667,6 +2769,18 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_json"
|
||||||
|
version = "1.0.140"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
|
||||||
|
dependencies = [
|
||||||
|
"itoa",
|
||||||
|
"memchr",
|
||||||
|
"ryu",
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_repr"
|
name = "serde_repr"
|
||||||
version = "0.1.20"
|
version = "0.1.20"
|
||||||
@@ -2678,6 +2792,15 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_spanned"
|
||||||
|
version = "0.6.9"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sha1"
|
name = "sha1"
|
||||||
version = "0.10.6"
|
version = "0.10.6"
|
||||||
@@ -2955,11 +3078,26 @@ dependencies = [
|
|||||||
"zerovec",
|
"zerovec",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml"
|
||||||
|
version = "0.8.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "dc1beb996b9d83529a9e75c17a1686767d148d70663143c7854d8b4a09ced362"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
|
"toml_datetime",
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_datetime"
|
name = "toml_datetime"
|
||||||
version = "0.6.11"
|
version = "0.6.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
checksum = "22cddaf88f4fbc13c51aebbf5f8eceb5c7c5a9da2ac40a13519eb5b0a0e8f11c"
|
||||||
|
dependencies = [
|
||||||
|
"serde",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml_edit"
|
name = "toml_edit"
|
||||||
@@ -2968,10 +3106,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
checksum = "41fe8c660ae4257887cf66394862d21dbca4a6ddd26f04a3560410406a2f819a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
|
"serde",
|
||||||
|
"serde_spanned",
|
||||||
"toml_datetime",
|
"toml_datetime",
|
||||||
|
"toml_write",
|
||||||
"winnow",
|
"winnow",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_write"
|
||||||
|
version = "0.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tracing"
|
name = "tracing"
|
||||||
version = "0.1.41"
|
version = "0.1.41"
|
||||||
@@ -3083,6 +3230,17 @@ version = "1.0.4"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uuid"
|
||||||
|
version = "1.17.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d"
|
||||||
|
dependencies = [
|
||||||
|
"getrandom 0.3.3",
|
||||||
|
"js-sys",
|
||||||
|
"wasm-bindgen",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.5"
|
version = "0.9.5"
|
||||||
@@ -3561,6 +3719,15 @@ dependencies = [
|
|||||||
"windows-targets 0.52.6",
|
"windows-targets 0.52.6",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.60.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb"
|
||||||
|
dependencies = [
|
||||||
|
"windows-targets 0.53.2",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-targets"
|
name = "windows-targets"
|
||||||
version = "0.42.2"
|
version = "0.42.2"
|
||||||
|
|||||||
+14
-13
@@ -3,7 +3,7 @@ use std::{
|
|||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
fs,
|
fs,
|
||||||
hash::{DefaultHasher, Hash, Hasher},
|
hash::{DefaultHasher, Hash, Hasher},
|
||||||
path::PathBuf,
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
use common::prelude::Instruction;
|
use common::prelude::Instruction;
|
||||||
@@ -19,27 +19,27 @@ pub mod model;
|
|||||||
pub mod parser;
|
pub mod parser;
|
||||||
pub mod resolver;
|
pub mod resolver;
|
||||||
|
|
||||||
pub fn assemble(src: &PathBuf) -> Vec<Instruction> {
|
pub fn assemble(src: &Path) -> Vec<Instruction> {
|
||||||
let mut modules = HashSet::<u64>::new();
|
let mut modules = HashSet::<u64>::new();
|
||||||
let mut program = Program::new();
|
let mut program = Program::new();
|
||||||
|
|
||||||
let hash = quick_hash(src);
|
let hash = quick_hash(src);
|
||||||
modules.insert(hash);
|
modules.insert(hash);
|
||||||
|
|
||||||
match prepare_dependency(src.clone(), &mut modules, &mut program) {
|
match prepare_dependency(src, &mut modules, &mut program) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(err) => println!("BIG ERROR {:?}", err),
|
Err(err) => println!("BIG ERROR {err:?}"),
|
||||||
}
|
}
|
||||||
|
|
||||||
for node in program.nodes {
|
for node in program.nodes {
|
||||||
println!("{:?}", node);
|
println!("{node:?}");
|
||||||
}
|
}
|
||||||
|
|
||||||
vec![]
|
vec![]
|
||||||
}
|
}
|
||||||
|
|
||||||
fn prepare_dependency(
|
fn prepare_dependency(
|
||||||
path: PathBuf,
|
path: &Path,
|
||||||
modules: &mut HashSet<u64>,
|
modules: &mut HashSet<u64>,
|
||||||
program: &mut Program,
|
program: &mut Program,
|
||||||
) -> Result<(), AssembleError> {
|
) -> Result<(), AssembleError> {
|
||||||
@@ -53,9 +53,9 @@ fn prepare_dependency(
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let src = fs::read_to_string(&path)
|
let src = fs::read_to_string(path)
|
||||||
.map_err(|_| AssembleError::InvalidFile(path.clone()))?;
|
.map_err(|_| AssembleError::InvalidFile(path.to_path_buf()))?;
|
||||||
let file_hash = quick_hash(&path);
|
let file_hash = quick_hash(path);
|
||||||
|
|
||||||
log(&format!("{:20} {:20}", "Tokenising", filename));
|
log(&format!("{:20} {:20}", "Tokenising", filename));
|
||||||
let tokens = lexer::lexer(src, file_hash)?;
|
let tokens = lexer::lexer(src, file_hash)?;
|
||||||
@@ -84,14 +84,14 @@ fn prepare_dependency(
|
|||||||
|
|
||||||
if !modules.contains(&quick_hash(&dep)) {
|
if !modules.contains(&quick_hash(&dep)) {
|
||||||
modules.insert(quick_hash(&dep));
|
modules.insert(quick_hash(&dep));
|
||||||
prepare_dependency(dep, modules, program)?
|
prepare_dependency(dep.as_path(), modules, program)?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build(src: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
|
fn _build(_src: Vec<Node>) -> Result<Vec<Instruction>, AssembleError> {
|
||||||
Ok(vec![])
|
Ok(vec![])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,14 +125,15 @@ impl fmt::Display for AssembleError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn quick_hash(value: &PathBuf) -> u64 {
|
fn quick_hash(value: &Path) -> u64 {
|
||||||
let mut hasher = DefaultHasher::new();
|
let mut hasher = DefaultHasher::new();
|
||||||
value.canonicalize().unwrap().to_str().hash(&mut hasher);
|
value.canonicalize().unwrap().to_str().hash(&mut hasher);
|
||||||
hasher.finish()
|
hasher.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Use an actual logging or tracing library for pretty (scoped) output.
|
||||||
fn log(message: &str) {
|
fn log(message: &str) {
|
||||||
println!("\x1b[32mINFO:\x1b[0m {}", message);
|
println!("\x1b[32mINFO:\x1b[0m {message}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// create a macro that lexes and parses the input string into Nodes
|
// create a macro that lexes and parses the input string into Nodes
|
||||||
|
|||||||
@@ -1,8 +1,5 @@
|
|||||||
use std::{fs, io::Write, path::PathBuf};
|
use std::{fs, io::Write, path::PathBuf};
|
||||||
|
|
||||||
use assembler::{lexer, parser::Parser};
|
|
||||||
use common::prelude::{ITypeArgs, Instruction, RTypeArgs, Register};
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// parse args:
|
// parse args:
|
||||||
let args: Vec<String> = std::env::args().collect();
|
let args: Vec<String> = std::env::args().collect();
|
||||||
@@ -16,10 +13,10 @@ fn main() {
|
|||||||
let src = PathBuf::from(input_path);
|
let src = PathBuf::from(input_path);
|
||||||
let mut output_file = fs::File::create(output_path).unwrap();
|
let mut output_file = fs::File::create(output_path).unwrap();
|
||||||
|
|
||||||
let res = assembler::assemble(&src)
|
assembler::assemble(&src)
|
||||||
.iter()
|
.iter()
|
||||||
.map(|i| i.encode())
|
.map(|i| i.encode())
|
||||||
.for_each(|i| {
|
.for_each(|i| {
|
||||||
output_file.write_all(&i.to_be_bytes()).unwrap();
|
output_file.write_all(&i.to_le_bytes()).unwrap();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
+54
-5
@@ -3,13 +3,12 @@ use std::{fmt, str::FromStr};
|
|||||||
use common::prelude::Register;
|
use common::prelude::Register;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
#[expect(dead_code)]
|
|
||||||
pub struct Node(pub Option<Symbol>, pub Opcode, pub Vec<Token>);
|
pub struct Node(pub Option<Symbol>, pub Opcode, pub Vec<Token>);
|
||||||
|
|
||||||
impl fmt::Display for Node {
|
impl fmt::Display for Node {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
let symbol = match &self.0 {
|
let symbol = match &self.0 {
|
||||||
Some(symbol) => format!("{}", symbol),
|
Some(symbol) => format!("{symbol}"),
|
||||||
None => "".to_string(),
|
None => "".to_string(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -26,15 +25,65 @@ impl fmt::Display for Symbol {
|
|||||||
impl fmt::Display for Module {
|
impl fmt::Display for Module {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Module::Unresolved(name) => write!(f, "{}", name),
|
Module::Unresolved(name) => write!(f, "{name}"),
|
||||||
Module::Resolved(name) => write!(f, "{}", name),
|
Module::Resolved(name) => write!(f, "{name}"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Opcode {
|
impl fmt::Display for Opcode {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}", self)
|
match self {
|
||||||
|
Opcode::Nop => write!(f, "nop"),
|
||||||
|
Opcode::Mov => write!(f, "mov"),
|
||||||
|
Opcode::Movs => write!(f, "movs"),
|
||||||
|
Opcode::Ldb => write!(f, "ldb"),
|
||||||
|
Opcode::Ldbs => write!(f, "ldbs"),
|
||||||
|
Opcode::Ldh => write!(f, "ldh"),
|
||||||
|
Opcode::Ldhs => write!(f, "ldhs"),
|
||||||
|
Opcode::Ldw => write!(f, "ldw"),
|
||||||
|
Opcode::Stb => write!(f, "stb"),
|
||||||
|
Opcode::Sth => write!(f, "sth"),
|
||||||
|
Opcode::Stw => write!(f, "stw"),
|
||||||
|
Opcode::Lli => write!(f, "lli"),
|
||||||
|
Opcode::Lui => write!(f, "lui"),
|
||||||
|
Opcode::Jmp => write!(f, "jmp"),
|
||||||
|
Opcode::Jeq => write!(f, "jeq"),
|
||||||
|
Opcode::Jne => write!(f, "jne"),
|
||||||
|
Opcode::Jgt => write!(f, "jgt"),
|
||||||
|
Opcode::Jge => write!(f, "jge"),
|
||||||
|
Opcode::Jlt => write!(f, "jlt"),
|
||||||
|
Opcode::Jle => write!(f, "jle"),
|
||||||
|
Opcode::Cmp => write!(f, "cmp"),
|
||||||
|
Opcode::Inc => write!(f, "inc"),
|
||||||
|
Opcode::Dec => write!(f, "dec"),
|
||||||
|
Opcode::Shl => write!(f, "shl"),
|
||||||
|
Opcode::Shr => write!(f, "shr"),
|
||||||
|
Opcode::Add => write!(f, "add"),
|
||||||
|
Opcode::Sub => write!(f, "sub"),
|
||||||
|
Opcode::And => write!(f, "and"),
|
||||||
|
Opcode::Or => write!(f, "or"),
|
||||||
|
Opcode::Not => write!(f, "not"),
|
||||||
|
Opcode::Xor => write!(f, "xor"),
|
||||||
|
Opcode::Nand => write!(f, "nand"),
|
||||||
|
Opcode::Nor => write!(f, "nor"),
|
||||||
|
Opcode::Xnor => write!(f, "xnor"),
|
||||||
|
Opcode::Int => write!(f, "int"),
|
||||||
|
Opcode::Irt => write!(f, "irt"),
|
||||||
|
Opcode::Hlt => write!(f, "hlt"),
|
||||||
|
Opcode::Iadd => write!(f, "iadd"),
|
||||||
|
Opcode::Isub => write!(f, "isub"),
|
||||||
|
Opcode::Db => write!(f, "db"),
|
||||||
|
Opcode::Dh => write!(f, "dh"),
|
||||||
|
Opcode::Dw => write!(f, "dw"),
|
||||||
|
Opcode::Resb => write!(f, "resb"),
|
||||||
|
Opcode::Resh => write!(f, "resh"),
|
||||||
|
Opcode::Resw => write!(f, "resw"),
|
||||||
|
Opcode::Push => write!(f, "push"),
|
||||||
|
Opcode::Pop => write!(f, "pop"),
|
||||||
|
Opcode::Lwi => write!(f, "lwi"),
|
||||||
|
Opcode::Include => write!(f, "include"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
use core::fmt;
|
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
use common::prelude::{Instruction, Register};
|
use common::prelude::{Instruction, Register};
|
||||||
|
|
||||||
@@ -33,6 +31,12 @@ impl Program {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Program {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Parser {
|
impl Parser {
|
||||||
pub fn parse_nodes(tokens: Vec<Token>) -> Result<Vec<Node>, AssembleError> {
|
pub fn parse_nodes(tokens: Vec<Token>) -> Result<Vec<Node>, AssembleError> {
|
||||||
let mut self_ = Parser {
|
let mut self_ = Parser {
|
||||||
|
|||||||
-1
Submodule dsa_editor deleted from 1566784e20
Generated
+3985
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,23 @@
|
|||||||
|
[package]
|
||||||
|
name = "dsa_editor"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
description = "a fork of a code editor egui widget adapted to work with DSA syntax."
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
egui = { version = "0.31", optional = true }
|
||||||
|
serde = { version = "1", optional = true }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
name = "dsa_editor"
|
||||||
|
path = "src/lib.rs"
|
||||||
|
|
||||||
|
[features]
|
||||||
|
default = ["egui", "editor"]
|
||||||
|
egui = ["dep:egui"]
|
||||||
|
editor = []
|
||||||
|
serde = ["dep:serde"]
|
||||||
|
|
||||||
|
[dev-dependencies]
|
||||||
|
eframe = "0.31"
|
||||||
|
colorful = "0.3"
|
||||||
@@ -0,0 +1,253 @@
|
|||||||
|
#[cfg(feature = "editor")]
|
||||||
|
use super::Editor;
|
||||||
|
|
||||||
|
use super::syntax::{Syntax, TokenType, QUOTES, SEPARATORS};
|
||||||
|
use std::mem;
|
||||||
|
|
||||||
|
#[derive(Default, Debug, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
|
/// Lexer and Token
|
||||||
|
pub struct Token {
|
||||||
|
ty: TokenType,
|
||||||
|
buffer: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Token {
|
||||||
|
pub fn new<S: Into<String>>(ty: TokenType, buffer: S) -> Self {
|
||||||
|
Token {
|
||||||
|
ty,
|
||||||
|
buffer: buffer.into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn ty(&self) -> TokenType {
|
||||||
|
self.ty
|
||||||
|
}
|
||||||
|
pub fn buffer(&self) -> &str {
|
||||||
|
&self.buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
fn first(&mut self, c: char, syntax: &Syntax) -> Option<Self> {
|
||||||
|
self.buffer.push(c);
|
||||||
|
let mut token = None;
|
||||||
|
self.ty = match c {
|
||||||
|
c if c.is_whitespace() => {
|
||||||
|
self.ty = TokenType::Whitespace(c);
|
||||||
|
token = self.drain(self.ty);
|
||||||
|
TokenType::Whitespace(c)
|
||||||
|
}
|
||||||
|
c if syntax.is_keyword(c.to_string().as_str()) => TokenType::Keyword,
|
||||||
|
c if syntax.is_type(c.to_string().as_str()) => TokenType::Type,
|
||||||
|
c if syntax.is_special(c.to_string().as_str()) => TokenType::Special,
|
||||||
|
c if syntax.comment == c.to_string().as_str() => TokenType::Comment(false),
|
||||||
|
c if syntax.comment_multiline[0] == c.to_string().as_str() => {
|
||||||
|
TokenType::Comment(true)
|
||||||
|
}
|
||||||
|
_ => TokenType::from(c),
|
||||||
|
};
|
||||||
|
token
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drain(&mut self, ty: TokenType) -> Option<Self> {
|
||||||
|
let mut token = None;
|
||||||
|
if !self.buffer().is_empty() {
|
||||||
|
token = Some(Token {
|
||||||
|
buffer: mem::take(&mut self.buffer),
|
||||||
|
ty: self.ty,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
self.ty = ty;
|
||||||
|
token
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_drain(&mut self, c: char, ty: TokenType) -> Option<Self> {
|
||||||
|
self.buffer.push(c);
|
||||||
|
self.drain(ty)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn drain_push(&mut self, c: char, ty: TokenType) -> Option<Self> {
|
||||||
|
let token = self.drain(self.ty);
|
||||||
|
self.buffer.push(c);
|
||||||
|
self.ty = ty;
|
||||||
|
token
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
/// Syntax highlighting
|
||||||
|
pub fn highlight<T: Editor>(&mut self, editor: &T, text: &str) -> LayoutJob {
|
||||||
|
*self = Token::default();
|
||||||
|
let mut job = LayoutJob::default();
|
||||||
|
for c in text.chars() {
|
||||||
|
for token in self.automata(c, editor.syntax()) {
|
||||||
|
editor.append(&mut job, &token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
editor.append(&mut job, self);
|
||||||
|
job
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Lexer
|
||||||
|
pub fn tokens(&mut self, syntax: &Syntax, text: &str) -> Vec<Self> {
|
||||||
|
let mut tokens: Vec<Self> = text
|
||||||
|
.chars()
|
||||||
|
.flat_map(|c| self.automata(c, syntax))
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if !self.buffer.is_empty() {
|
||||||
|
tokens.push(mem::take(self));
|
||||||
|
}
|
||||||
|
tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
fn automata(&mut self, c: char, syntax: &Syntax) -> Vec<Self> {
|
||||||
|
use TokenType as Ty;
|
||||||
|
let mut tokens = vec![];
|
||||||
|
match (self.ty, Ty::from(c)) {
|
||||||
|
(Ty::Comment(false), Ty::Whitespace('\n')) => {
|
||||||
|
self.buffer.push(c);
|
||||||
|
let n = self.buffer.pop();
|
||||||
|
tokens.extend(self.drain(Ty::Whitespace(c)));
|
||||||
|
if let Some(n) = n {
|
||||||
|
tokens.extend(self.push_drain(n, self.ty));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Ty::Comment(false), _) => {
|
||||||
|
self.buffer.push(c);
|
||||||
|
}
|
||||||
|
(Ty::Comment(true), _) => {
|
||||||
|
self.buffer.push(c);
|
||||||
|
if self.buffer.ends_with(syntax.comment_multiline[1]) {
|
||||||
|
tokens.extend(self.drain(Ty::Unknown));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Ty::Literal | Ty::Punctuation(_), Ty::Whitespace(_)) => {
|
||||||
|
tokens.extend(self.drain(Ty::Whitespace(c)));
|
||||||
|
tokens.extend(self.first(c, syntax));
|
||||||
|
}
|
||||||
|
(Ty::Hyperlink, Ty::Whitespace(_)) => {
|
||||||
|
tokens.extend(self.drain(Ty::Whitespace(c)));
|
||||||
|
tokens.extend(self.first(c, syntax));
|
||||||
|
}
|
||||||
|
(Ty::Hyperlink, _) => {
|
||||||
|
self.buffer.push(c);
|
||||||
|
}
|
||||||
|
(Ty::Literal, _) => match c {
|
||||||
|
c if c == '(' => {
|
||||||
|
self.ty = Ty::Function;
|
||||||
|
tokens.extend(self.drain(Ty::Punctuation(c)));
|
||||||
|
tokens.extend(self.push_drain(c, Ty::Unknown));
|
||||||
|
}
|
||||||
|
c if !c.is_alphanumeric() && !SEPARATORS.contains(&c) => {
|
||||||
|
tokens.extend(self.drain(self.ty));
|
||||||
|
self.buffer.push(c);
|
||||||
|
self.ty = if QUOTES.contains(&c) {
|
||||||
|
Ty::Str(c)
|
||||||
|
} else {
|
||||||
|
Ty::Punctuation(c)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.buffer.push(c);
|
||||||
|
self.ty = {
|
||||||
|
if self.buffer.starts_with(syntax.comment) {
|
||||||
|
Ty::Comment(false)
|
||||||
|
} else if self.buffer.starts_with(syntax.comment_multiline[0]) {
|
||||||
|
Ty::Comment(true)
|
||||||
|
} else if syntax.is_hyperlink(&self.buffer) {
|
||||||
|
Ty::Hyperlink
|
||||||
|
} else if syntax.is_keyword(&self.buffer) {
|
||||||
|
Ty::Keyword
|
||||||
|
} else if syntax.is_type(&self.buffer) {
|
||||||
|
Ty::Type
|
||||||
|
} else if syntax.is_special(&self.buffer) {
|
||||||
|
Ty::Special
|
||||||
|
} else {
|
||||||
|
Ty::Literal
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
(Ty::Numeric(false), Ty::Punctuation('.')) => {
|
||||||
|
self.buffer.push(c);
|
||||||
|
self.ty = Ty::Numeric(true);
|
||||||
|
}
|
||||||
|
(Ty::Numeric(_), Ty::Numeric(_)) => {
|
||||||
|
self.buffer.push(c);
|
||||||
|
}
|
||||||
|
(Ty::Numeric(_), Ty::Literal) => {
|
||||||
|
tokens.extend(self.drain(self.ty));
|
||||||
|
self.buffer.push(c);
|
||||||
|
}
|
||||||
|
(Ty::Numeric(_), _) | (Ty::Punctuation(_), Ty::Literal | Ty::Numeric(_)) => {
|
||||||
|
tokens.extend(self.drain(self.ty));
|
||||||
|
tokens.extend(self.first(c, syntax));
|
||||||
|
}
|
||||||
|
(Ty::Punctuation(_), Ty::Str(_)) => {
|
||||||
|
tokens.extend(self.drain_push(c, Ty::Str(c)));
|
||||||
|
}
|
||||||
|
(Ty::Punctuation(_), _) => {
|
||||||
|
if !(syntax.comment.starts_with(&self.buffer)
|
||||||
|
|| syntax.comment_multiline[0].starts_with(&self.buffer))
|
||||||
|
{
|
||||||
|
tokens.extend(self.drain(self.ty));
|
||||||
|
tokens.extend(self.first(c, syntax));
|
||||||
|
} else {
|
||||||
|
self.buffer.push(c);
|
||||||
|
if self.buffer.starts_with(syntax.comment) {
|
||||||
|
self.ty = Ty::Comment(false);
|
||||||
|
} else if self.buffer.starts_with(syntax.comment_multiline[0]) {
|
||||||
|
self.ty = Ty::Comment(true);
|
||||||
|
} else if let Some(c) = self.buffer.pop() {
|
||||||
|
tokens.extend(self.drain(Ty::Punctuation(c)));
|
||||||
|
tokens.extend(self.first(c, syntax));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Ty::Str(q), _) => {
|
||||||
|
let control = self.buffer.ends_with('\\');
|
||||||
|
self.buffer.push(c);
|
||||||
|
if c == q && !control {
|
||||||
|
tokens.extend(self.drain(Ty::Unknown));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
(Ty::Whitespace(_) | Ty::Unknown, _) => {
|
||||||
|
tokens.extend(self.first(c, syntax));
|
||||||
|
}
|
||||||
|
// Keyword, Type, Special
|
||||||
|
(_reserved, Ty::Literal | Ty::Numeric(_)) => {
|
||||||
|
self.buffer.push(c);
|
||||||
|
self.ty = if syntax.is_keyword(&self.buffer) {
|
||||||
|
Ty::Keyword
|
||||||
|
} else if syntax.is_type(&self.buffer) {
|
||||||
|
Ty::Type
|
||||||
|
} else if syntax.is_special(&self.buffer) {
|
||||||
|
Ty::Special
|
||||||
|
} else {
|
||||||
|
Ty::Literal
|
||||||
|
};
|
||||||
|
}
|
||||||
|
(reserved, _) => {
|
||||||
|
self.ty = reserved;
|
||||||
|
tokens.extend(self.drain(self.ty));
|
||||||
|
tokens.extend(self.first(c, syntax));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokens
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
use egui::text::LayoutJob;
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
impl<T: Editor> egui::util::cache::ComputerMut<(&T, &str), LayoutJob> for Token {
|
||||||
|
fn compute(&mut self, (cache, text): (&T, &str)) -> LayoutJob {
|
||||||
|
self.highlight(cache, text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
pub type HighlightCache = egui::util::cache::FrameCache<LayoutJob, Token>;
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
pub fn highlight<T: Editor>(ctx: &egui::Context, cache: &T, text: &str) -> LayoutJob {
|
||||||
|
ctx.memory_mut(|mem| mem.caches.cache::<HighlightCache>().get((cache, text)))
|
||||||
|
}
|
||||||
@@ -0,0 +1,296 @@
|
|||||||
|
pub mod highlighting;
|
||||||
|
mod syntax;
|
||||||
|
mod themes;
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
use egui::text::LayoutJob;
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
use egui::widgets::text_edit::TextEditOutput;
|
||||||
|
pub use highlighting::Token;
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
use highlighting::highlight;
|
||||||
|
#[cfg(feature = "editor")]
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
pub use syntax::{Syntax, TokenType};
|
||||||
|
pub use themes::ColorTheme;
|
||||||
|
pub use themes::DEFAULT_THEMES;
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
pub trait Editor: Hash {
|
||||||
|
fn append(&self, job: &mut LayoutJob, token: &Token);
|
||||||
|
fn syntax(&self) -> &Syntax;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "editor")]
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
/// CodeEditor struct which stores settings for highlighting.
|
||||||
|
pub struct CodeEditor {
|
||||||
|
id: String,
|
||||||
|
theme: ColorTheme,
|
||||||
|
syntax: Syntax,
|
||||||
|
numlines: bool,
|
||||||
|
numlines_shift: isize,
|
||||||
|
numlines_only_natural: bool,
|
||||||
|
fontsize: f32,
|
||||||
|
rows: usize,
|
||||||
|
stick_to_bottom: bool,
|
||||||
|
desired_width: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "editor")]
|
||||||
|
impl Hash for CodeEditor {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.theme.hash(state);
|
||||||
|
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||||
|
(self.fontsize as u32).hash(state);
|
||||||
|
self.syntax.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "editor")]
|
||||||
|
impl Default for CodeEditor {
|
||||||
|
fn default() -> CodeEditor {
|
||||||
|
CodeEditor {
|
||||||
|
id: String::from("Code Editor"),
|
||||||
|
theme: ColorTheme::THEME,
|
||||||
|
syntax: Syntax::dsa(),
|
||||||
|
numlines: true,
|
||||||
|
numlines_shift: 0,
|
||||||
|
numlines_only_natural: false,
|
||||||
|
fontsize: 10.0,
|
||||||
|
rows: 10,
|
||||||
|
stick_to_bottom: false,
|
||||||
|
desired_width: f32::INFINITY,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "editor")]
|
||||||
|
impl CodeEditor {
|
||||||
|
pub fn id_source(self, id_source: impl Into<String>) -> Self {
|
||||||
|
CodeEditor {
|
||||||
|
id: id_source.into(),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Minimum number of rows to show.
|
||||||
|
///
|
||||||
|
/// **Default: 10**
|
||||||
|
pub fn with_rows(self, rows: usize) -> Self {
|
||||||
|
CodeEditor { rows, ..self }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use custom Color Theme
|
||||||
|
///
|
||||||
|
/// **Default: Gruvbox**
|
||||||
|
pub fn with_theme(self, theme: ColorTheme) -> Self {
|
||||||
|
CodeEditor { theme, ..self }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use custom font size
|
||||||
|
///
|
||||||
|
/// **Default: 10.0**
|
||||||
|
pub fn with_fontsize(self, fontsize: f32) -> Self {
|
||||||
|
CodeEditor { fontsize, ..self }
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
/// Use UI font size
|
||||||
|
pub fn with_ui_fontsize(self, ui: &mut egui::Ui) -> Self {
|
||||||
|
CodeEditor {
|
||||||
|
fontsize: egui::TextStyle::Monospace.resolve(ui.style()).size,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show or hide lines numbering
|
||||||
|
///
|
||||||
|
/// **Default: true**
|
||||||
|
pub fn with_numlines(self, numlines: bool) -> Self {
|
||||||
|
CodeEditor { numlines, ..self }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Shift lines numbering by this value
|
||||||
|
///
|
||||||
|
/// **Default: 0**
|
||||||
|
pub fn with_numlines_shift(self, numlines_shift: isize) -> Self {
|
||||||
|
CodeEditor {
|
||||||
|
numlines_shift,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Show lines numbering only above zero, useful for enabling numbering since nth row
|
||||||
|
///
|
||||||
|
/// **Default: false**
|
||||||
|
pub fn with_numlines_only_natural(self, numlines_only_natural: bool) -> Self {
|
||||||
|
CodeEditor {
|
||||||
|
numlines_only_natural,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Use custom syntax for highlighting
|
||||||
|
///
|
||||||
|
/// **Default: Rust**
|
||||||
|
pub fn with_syntax(self, syntax: Syntax) -> Self {
|
||||||
|
CodeEditor { syntax, ..self }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Should the containing area shrink if the content is small?
|
||||||
|
///
|
||||||
|
/// **Default: false**
|
||||||
|
pub fn auto_shrink(self, shrink: bool) -> Self {
|
||||||
|
CodeEditor {
|
||||||
|
desired_width: if shrink { 0.0 } else { self.desired_width },
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets the desired width of the code editor
|
||||||
|
///
|
||||||
|
/// **Default: `f32::INFINITY`**
|
||||||
|
pub fn desired_width(self, width: f32) -> Self {
|
||||||
|
CodeEditor {
|
||||||
|
desired_width: width,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Stick to bottom
|
||||||
|
/// The scroll handle will stick to the bottom position even while the content size
|
||||||
|
/// changes dynamically. This can be useful to simulate terminal UIs or log/info scrollers.
|
||||||
|
/// The scroll handle remains stuck until user manually changes position. Once "unstuck"
|
||||||
|
/// it will remain focused on whatever content viewport the user left it on. If the scroll
|
||||||
|
/// handle is dragged to the bottom it will again become stuck and remain there until manually
|
||||||
|
/// pulled from the end position.
|
||||||
|
///
|
||||||
|
/// **Default: false**
|
||||||
|
pub fn stick_to_bottom(self, stick_to_bottom: bool) -> Self {
|
||||||
|
CodeEditor {
|
||||||
|
stick_to_bottom,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
pub fn format(&self, ty: TokenType) -> egui::text::TextFormat {
|
||||||
|
let font_id = egui::FontId::monospace(self.fontsize);
|
||||||
|
let color = self.theme.type_color(ty);
|
||||||
|
egui::text::TextFormat::simple(font_id, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
fn numlines_show(&self, ui: &mut egui::Ui, text: &str) {
|
||||||
|
let total = if text.ends_with('\n') || text.is_empty() {
|
||||||
|
text.lines().count() + 1
|
||||||
|
} else {
|
||||||
|
text.lines().count()
|
||||||
|
}
|
||||||
|
.max(self.rows) as isize;
|
||||||
|
let max_indent = total.to_string().len().max(
|
||||||
|
!self.numlines_only_natural as usize * self.numlines_shift.to_string().len(),
|
||||||
|
);
|
||||||
|
let mut counter = (1..=total)
|
||||||
|
.map(|i| {
|
||||||
|
let num = i + self.numlines_shift;
|
||||||
|
if num <= 0 && self.numlines_only_natural {
|
||||||
|
String::new()
|
||||||
|
} else {
|
||||||
|
let label = num.to_string();
|
||||||
|
format!(
|
||||||
|
"{}{label}",
|
||||||
|
" ".repeat(max_indent.saturating_sub(label.len()))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
.join("\n");
|
||||||
|
|
||||||
|
#[allow(clippy::cast_precision_loss)]
|
||||||
|
let width = max_indent as f32
|
||||||
|
* self.fontsize
|
||||||
|
* 0.5
|
||||||
|
* !(total + self.numlines_shift <= 0 && self.numlines_only_natural) as u8
|
||||||
|
as f32;
|
||||||
|
|
||||||
|
let mut layouter = |ui: &egui::Ui, string: &str, _wrap_width: f32| {
|
||||||
|
let layout_job = egui::text::LayoutJob::single_section(
|
||||||
|
string.to_string(),
|
||||||
|
egui::TextFormat::simple(
|
||||||
|
egui::FontId::monospace(self.fontsize),
|
||||||
|
self.theme.type_color(TokenType::Comment(true)),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
ui.fonts(|f| f.layout_job(layout_job))
|
||||||
|
};
|
||||||
|
|
||||||
|
ui.add(
|
||||||
|
egui::TextEdit::multiline(&mut counter)
|
||||||
|
.id_source(format!("{}_numlines", self.id))
|
||||||
|
.font(egui::TextStyle::Monospace)
|
||||||
|
.interactive(false)
|
||||||
|
.frame(false)
|
||||||
|
.desired_rows(self.rows)
|
||||||
|
.desired_width(width)
|
||||||
|
.layouter(&mut layouter),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
/// Show Code Editor
|
||||||
|
pub fn show(
|
||||||
|
&mut self,
|
||||||
|
ui: &mut egui::Ui,
|
||||||
|
text: &mut dyn egui::TextBuffer,
|
||||||
|
) -> TextEditOutput {
|
||||||
|
let mut text_edit_output: Option<TextEditOutput> = None;
|
||||||
|
let mut code_editor = |ui: &mut egui::Ui| {
|
||||||
|
ui.horizontal_top(|h| {
|
||||||
|
self.theme.modify_style(h, self.fontsize);
|
||||||
|
if self.numlines {
|
||||||
|
self.numlines_show(h, text.as_str());
|
||||||
|
}
|
||||||
|
egui::ScrollArea::horizontal()
|
||||||
|
.hscroll(true)
|
||||||
|
.id_salt(format!("{}_inner_scroll", self.id))
|
||||||
|
.show(h, |ui| {
|
||||||
|
let mut layouter =
|
||||||
|
|ui: &egui::Ui, string: &str, _wrap_width: f32| {
|
||||||
|
let layout_job = highlight(ui.ctx(), self, string);
|
||||||
|
ui.fonts(|f| f.layout_job(layout_job))
|
||||||
|
};
|
||||||
|
let output = egui::TextEdit::multiline(text)
|
||||||
|
.id_source(&self.id)
|
||||||
|
.lock_focus(true)
|
||||||
|
.desired_rows(self.rows)
|
||||||
|
.frame(false)
|
||||||
|
.desired_width(self.desired_width)
|
||||||
|
.layouter(&mut layouter)
|
||||||
|
.show(ui);
|
||||||
|
text_edit_output = Some(output);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
egui::ScrollArea::vertical()
|
||||||
|
.id_salt(format!("{}_outer_scroll", self.id))
|
||||||
|
.stick_to_bottom(self.stick_to_bottom)
|
||||||
|
.show(ui, code_editor);
|
||||||
|
|
||||||
|
text_edit_output.expect("TextEditOutput should exist at this point")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "editor")]
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
impl Editor for CodeEditor {
|
||||||
|
fn append(&self, job: &mut LayoutJob, token: &Token) {
|
||||||
|
job.append(token.buffer(), 0.0, self.format(token.ty()));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn syntax(&self) -> &Syntax {
|
||||||
|
&self.syntax
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
use super::Syntax;
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
|
impl Syntax {
|
||||||
|
pub fn dsa() -> Self {
|
||||||
|
Syntax {
|
||||||
|
language: "Assembly",
|
||||||
|
case_sensitive: false,
|
||||||
|
comment: "//",
|
||||||
|
comment_multiline: ["/*", "*/"],
|
||||||
|
hyperlinks: BTreeSet::from(["http"]),
|
||||||
|
keywords: BTreeSet::from([
|
||||||
|
"nop", "mov", "movs", "ldb", "ldbs", "ldh", "ldhs", "ldw", "stb", "sth",
|
||||||
|
"stw", "lli", "lui", "jmp", "jeq", "jne", "jgt", "jge", "jlt", "jle",
|
||||||
|
"cmp", "inc", "dec", "shl", "shr", "add", "sub", "and", "or", "not",
|
||||||
|
"xor", "nand", "nor", "xnor", "irt", "int", "hlt",
|
||||||
|
// pseduo-instructions
|
||||||
|
"db", "dh", "dw", "resb", "resh", "resw", "push", "pop", "lwi", "call",
|
||||||
|
"ret",
|
||||||
|
]),
|
||||||
|
types: BTreeSet::from(["ptr", "byte", "word", "dword", "qword"]),
|
||||||
|
special: BTreeSet::from([
|
||||||
|
"rg0", "rg1", "rg2", "rg3", "rg4", "rg5", "rg6", "rg7", "rg8", "rg9",
|
||||||
|
"rga", "rgb", "rgc", "rgd", "rge", "rgf", "acc", "spr", "bpr", "ret",
|
||||||
|
"idr", "mmr", "zero", "null",
|
||||||
|
]),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,204 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
pub mod dsa;
|
||||||
|
|
||||||
|
use std::collections::BTreeSet;
|
||||||
|
use std::hash::{Hash, Hasher};
|
||||||
|
|
||||||
|
pub const SEPARATORS: [char; 1] = ['_'];
|
||||||
|
pub const QUOTES: [char; 3] = ['\'', '"', '`'];
|
||||||
|
|
||||||
|
type MultiLine = bool;
|
||||||
|
type Float = bool;
|
||||||
|
|
||||||
|
#[derive(Default, Clone, Copy, PartialEq, PartialOrd, Eq, Ord)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
|
||||||
|
pub enum TokenType {
|
||||||
|
Comment(MultiLine),
|
||||||
|
Function,
|
||||||
|
Keyword,
|
||||||
|
Literal,
|
||||||
|
Hyperlink,
|
||||||
|
Numeric(Float),
|
||||||
|
Punctuation(char),
|
||||||
|
Special,
|
||||||
|
Str(char),
|
||||||
|
Type,
|
||||||
|
Whitespace(char),
|
||||||
|
#[default]
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
impl std::fmt::Debug for TokenType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let mut name = String::new();
|
||||||
|
match &self {
|
||||||
|
TokenType::Comment(multiline) => {
|
||||||
|
name.push_str("Comment");
|
||||||
|
{
|
||||||
|
if *multiline {
|
||||||
|
name.push_str(" MultiLine");
|
||||||
|
} else {
|
||||||
|
name.push_str(" SingleLine");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenType::Function => name.push_str("Function"),
|
||||||
|
TokenType::Keyword => name.push_str("Keyword"),
|
||||||
|
TokenType::Literal => name.push_str("Literal"),
|
||||||
|
TokenType::Hyperlink => name.push_str("Hyperlink"),
|
||||||
|
TokenType::Numeric(float) => {
|
||||||
|
name.push_str("Numeric");
|
||||||
|
if *float {
|
||||||
|
name.push_str(" Float");
|
||||||
|
} else {
|
||||||
|
name.push_str(" Integer");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TokenType::Punctuation(_) => name.push_str("Punctuation"),
|
||||||
|
TokenType::Special => name.push_str("Special"),
|
||||||
|
TokenType::Str(quote) => {
|
||||||
|
name.push_str("Str ");
|
||||||
|
name.push(*quote);
|
||||||
|
}
|
||||||
|
TokenType::Type => name.push_str("Type"),
|
||||||
|
TokenType::Whitespace(c) => {
|
||||||
|
name.push_str("Whitespace");
|
||||||
|
match c {
|
||||||
|
' ' => name.push_str(" Space"),
|
||||||
|
'\t' => name.push_str(" Tab"),
|
||||||
|
'\n' => name.push_str(" New Line"),
|
||||||
|
_ => (),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
TokenType::Unknown => name.push_str("Unknown"),
|
||||||
|
};
|
||||||
|
write!(f, "{name}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl From<char> for TokenType {
|
||||||
|
fn from(c: char) -> Self {
|
||||||
|
match c {
|
||||||
|
c if c.is_whitespace() => TokenType::Whitespace(c),
|
||||||
|
c if QUOTES.contains(&c) => TokenType::Str(c),
|
||||||
|
c if c.is_numeric() => TokenType::Numeric(false),
|
||||||
|
c if c.is_alphabetic() || SEPARATORS.contains(&c) => TokenType::Literal,
|
||||||
|
c if c.is_ascii_punctuation() => TokenType::Punctuation(c),
|
||||||
|
_ => TokenType::Unknown,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, PartialEq)]
|
||||||
|
/// Rules for highlighting.
|
||||||
|
pub struct Syntax {
|
||||||
|
pub language: &'static str,
|
||||||
|
pub case_sensitive: bool,
|
||||||
|
pub comment: &'static str,
|
||||||
|
pub comment_multiline: [&'static str; 2],
|
||||||
|
pub hyperlinks: BTreeSet<&'static str>,
|
||||||
|
pub keywords: BTreeSet<&'static str>,
|
||||||
|
pub types: BTreeSet<&'static str>,
|
||||||
|
pub special: BTreeSet<&'static str>,
|
||||||
|
}
|
||||||
|
impl Default for Syntax {
|
||||||
|
fn default() -> Self {
|
||||||
|
Syntax::dsa()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Hash for Syntax {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||||
|
self.language.hash(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl Syntax {
|
||||||
|
pub fn new(language: &'static str) -> Self {
|
||||||
|
Syntax {
|
||||||
|
language,
|
||||||
|
..Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_case_sensitive(self, case_sensitive: bool) -> Self {
|
||||||
|
Syntax {
|
||||||
|
case_sensitive,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_comment(self, comment: &'static str) -> Self {
|
||||||
|
Syntax { comment, ..self }
|
||||||
|
}
|
||||||
|
pub fn with_comment_multiline(self, comment_multiline: [&'static str; 2]) -> Self {
|
||||||
|
Syntax {
|
||||||
|
comment_multiline,
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_hyperlinks<T: Into<BTreeSet<&'static str>>>(self, hyperlinks: T) -> Self {
|
||||||
|
Syntax {
|
||||||
|
hyperlinks: hyperlinks.into(),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_keywords<T: Into<BTreeSet<&'static str>>>(self, keywords: T) -> Self {
|
||||||
|
Syntax {
|
||||||
|
keywords: keywords.into(),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_types<T: Into<BTreeSet<&'static str>>>(self, types: T) -> Self {
|
||||||
|
Syntax {
|
||||||
|
types: types.into(),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn with_special<T: Into<BTreeSet<&'static str>>>(self, special: T) -> Self {
|
||||||
|
Syntax {
|
||||||
|
special: special.into(),
|
||||||
|
..self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn language(&self) -> &str {
|
||||||
|
self.language
|
||||||
|
}
|
||||||
|
pub fn comment(&self) -> &str {
|
||||||
|
self.comment
|
||||||
|
}
|
||||||
|
pub fn is_hyperlink(&self, word: &str) -> bool {
|
||||||
|
self.hyperlinks.contains(word.to_ascii_lowercase().as_str())
|
||||||
|
}
|
||||||
|
pub fn is_keyword(&self, word: &str) -> bool {
|
||||||
|
if self.case_sensitive {
|
||||||
|
self.keywords.contains(&word)
|
||||||
|
} else {
|
||||||
|
self.keywords.contains(word.to_ascii_lowercase().as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_type(&self, word: &str) -> bool {
|
||||||
|
if self.case_sensitive {
|
||||||
|
self.types.contains(&word)
|
||||||
|
} else {
|
||||||
|
self.types.contains(word.to_ascii_lowercase().as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pub fn is_special(&self, word: &str) -> bool {
|
||||||
|
if self.case_sensitive {
|
||||||
|
self.special.contains(&word)
|
||||||
|
} else {
|
||||||
|
self.special.contains(word.to_ascii_lowercase().as_str())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Syntax {
|
||||||
|
pub fn simple(comment: &'static str) -> Self {
|
||||||
|
Syntax {
|
||||||
|
language: "",
|
||||||
|
case_sensitive: false,
|
||||||
|
comment,
|
||||||
|
comment_multiline: [comment; 2],
|
||||||
|
hyperlinks: BTreeSet::new(),
|
||||||
|
keywords: BTreeSet::new(),
|
||||||
|
types: BTreeSet::new(),
|
||||||
|
special: BTreeSet::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,147 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
|
pub mod theme;
|
||||||
|
|
||||||
|
use super::syntax::TokenType;
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
use egui::Color32;
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
pub const ERROR_COLOR: Color32 = Color32::from_rgb(255, 0, 255);
|
||||||
|
|
||||||
|
/// Array of default themes.
|
||||||
|
pub const DEFAULT_THEMES: [ColorTheme; 1] = [ColorTheme::THEME];
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
fn color_from_hex(hex: &str) -> Option<Color32> {
|
||||||
|
if hex == "none" {
|
||||||
|
return Some(Color32::from_rgba_premultiplied(255, 0, 255, 0));
|
||||||
|
}
|
||||||
|
let rgb = (1..hex.len())
|
||||||
|
.step_by(2)
|
||||||
|
.filter_map(|i| u8::from_str_radix(&hex[i..i + 2], 16).ok())
|
||||||
|
.collect::<Vec<u8>>();
|
||||||
|
let color = Color32::from_rgb(*rgb.first()?, *rgb.get(1)?, *rgb.get(2)?);
|
||||||
|
Some(color)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Hash, Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
/// Colors in hexadecimal notation as used in HTML and CSS.
|
||||||
|
pub struct ColorTheme {
|
||||||
|
pub name: &'static str,
|
||||||
|
pub dark: bool,
|
||||||
|
pub bg: &'static str,
|
||||||
|
pub cursor: &'static str,
|
||||||
|
pub selection: &'static str,
|
||||||
|
pub comments: &'static str,
|
||||||
|
pub functions: &'static str,
|
||||||
|
pub keywords: &'static str,
|
||||||
|
pub literals: &'static str,
|
||||||
|
pub numerics: &'static str,
|
||||||
|
pub punctuation: &'static str,
|
||||||
|
pub strs: &'static str,
|
||||||
|
pub types: &'static str,
|
||||||
|
pub special: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ColorTheme {
|
||||||
|
fn default() -> Self {
|
||||||
|
ColorTheme::THEME
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ColorTheme {
|
||||||
|
pub fn name(&self) -> &str {
|
||||||
|
self.name
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_dark(&self) -> bool {
|
||||||
|
self.dark
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
pub fn bg(&self) -> Color32 {
|
||||||
|
color_from_hex(self.bg).unwrap_or(ERROR_COLOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
pub fn cursor(&self) -> Color32 {
|
||||||
|
color_from_hex(self.cursor).unwrap_or(ERROR_COLOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
pub fn selection(&self) -> Color32 {
|
||||||
|
color_from_hex(self.selection).unwrap_or(ERROR_COLOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
pub fn modify_style(&self, ui: &mut egui::Ui, fontsize: f32) {
|
||||||
|
let style = ui.style_mut();
|
||||||
|
style.visuals.widgets.noninteractive.bg_fill = self.bg();
|
||||||
|
style.visuals.window_fill = self.bg();
|
||||||
|
style.visuals.selection.stroke.color = self.cursor();
|
||||||
|
style.visuals.selection.bg_fill = self.selection();
|
||||||
|
style.visuals.extreme_bg_color = self.bg();
|
||||||
|
style.override_font_id = Some(egui::FontId::monospace(fontsize));
|
||||||
|
style.visuals.text_cursor.stroke.width = fontsize * 0.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub const fn type_color_str(&self, ty: TokenType) -> &'static str {
|
||||||
|
match ty {
|
||||||
|
TokenType::Comment(_) => self.comments,
|
||||||
|
TokenType::Function => self.functions,
|
||||||
|
TokenType::Keyword => self.keywords,
|
||||||
|
TokenType::Literal => self.literals,
|
||||||
|
TokenType::Hyperlink => self.special,
|
||||||
|
TokenType::Numeric(_) => self.numerics,
|
||||||
|
TokenType::Punctuation(_) => self.punctuation,
|
||||||
|
TokenType::Special => self.special,
|
||||||
|
TokenType::Str(_) => self.strs,
|
||||||
|
TokenType::Type => self.types,
|
||||||
|
TokenType::Whitespace(_) | TokenType::Unknown => self.comments,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "egui")]
|
||||||
|
pub fn type_color(&self, ty: TokenType) -> Color32 {
|
||||||
|
match ty {
|
||||||
|
TokenType::Comment(_) => color_from_hex(self.comments),
|
||||||
|
TokenType::Function => color_from_hex(self.functions),
|
||||||
|
TokenType::Keyword => color_from_hex(self.keywords),
|
||||||
|
TokenType::Literal => color_from_hex(self.literals),
|
||||||
|
TokenType::Hyperlink => color_from_hex(self.special),
|
||||||
|
TokenType::Numeric(_) => color_from_hex(self.numerics),
|
||||||
|
TokenType::Punctuation(_) => color_from_hex(self.punctuation),
|
||||||
|
TokenType::Special => color_from_hex(self.special),
|
||||||
|
TokenType::Str(_) => color_from_hex(self.strs),
|
||||||
|
TokenType::Type => color_from_hex(self.types),
|
||||||
|
TokenType::Whitespace(_) | TokenType::Unknown => {
|
||||||
|
color_from_hex(self.comments)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.unwrap_or(ERROR_COLOR)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn monocolor(
|
||||||
|
dark: bool,
|
||||||
|
bg: &'static str,
|
||||||
|
fg: &'static str,
|
||||||
|
cursor: &'static str,
|
||||||
|
selection: &'static str,
|
||||||
|
) -> Self {
|
||||||
|
ColorTheme {
|
||||||
|
name: "monocolor",
|
||||||
|
dark,
|
||||||
|
bg,
|
||||||
|
cursor,
|
||||||
|
selection,
|
||||||
|
literals: fg,
|
||||||
|
numerics: fg,
|
||||||
|
keywords: fg,
|
||||||
|
functions: fg,
|
||||||
|
punctuation: fg,
|
||||||
|
types: fg,
|
||||||
|
strs: fg,
|
||||||
|
comments: fg,
|
||||||
|
special: fg,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
use super::ColorTheme;
|
||||||
|
|
||||||
|
impl ColorTheme {
|
||||||
|
/// Author : Jakub Bartodziej <kubabartodziej@gmail.com>
|
||||||
|
/// Theme uses the gruvbox dark palette with standard contrast <https://github.com/morhetz/gruvbox>
|
||||||
|
pub const THEME: ColorTheme = ColorTheme {
|
||||||
|
name: "Theme",
|
||||||
|
dark: true,
|
||||||
|
bg: "#1b1b1b",
|
||||||
|
cursor: "#de5252", // fg4
|
||||||
|
selection: "#28323B", // bg2
|
||||||
|
comments: "#444444", // gray1
|
||||||
|
functions: "#7CCCC7", // green1
|
||||||
|
keywords: "#6C81E0", // red1
|
||||||
|
literals: "#A3ABFF", // fg1
|
||||||
|
numerics: "#8A46CF", // purple1
|
||||||
|
punctuation: "#99C9C9", // orange1
|
||||||
|
strs: "#618c84", // aqua1
|
||||||
|
types: "#B8B9D4", // yellow1
|
||||||
|
special: "#de5252", // blue1
|
||||||
|
};
|
||||||
|
}
|
||||||
+6
-1
@@ -10,9 +10,14 @@ path = "src/lib.rs"
|
|||||||
[dependencies]
|
[dependencies]
|
||||||
common = { path = "../common" }
|
common = { path = "../common" }
|
||||||
assembler = { path = "../assembler" }
|
assembler = { path = "../assembler" }
|
||||||
dsa_editor = { path = "../dsa_editor" }
|
dsa_editor = { git = "https://github.com/zxq5-dev/egui_code_editor", package = "egui_code_editor", rev = "5eb313e" }
|
||||||
eframe = "0.31.1"
|
eframe = "0.31.1"
|
||||||
egui = "0.31.1"
|
egui = "0.31.1"
|
||||||
rfd = "0.15.3"
|
rfd = "0.15.3"
|
||||||
dirs = "6.0.0"
|
dirs = "6.0.0"
|
||||||
|
discord-presence = { version = "1.6.0", optional = true }
|
||||||
|
toml = { version = "0.8.23", optional = true }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
discord-rpc = ["dep:discord-presence"]
|
||||||
|
config = ["dep:toml"]
|
||||||
|
|||||||
@@ -0,0 +1,2 @@
|
|||||||
|
//! Loads configuration information from a TOML file in the current working directory.
|
||||||
|
//! Currently doesn't do much but this may be expanded.
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
#[cfg(feature = "discord-rpc")]
|
||||||
|
pub mod rpc;
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
//! Just for fun I thought I would add a Discord RPC client to the emulator.
|
||||||
|
//!
|
||||||
|
//! This will display information like the current value of PCX, architecture name and
|
||||||
|
//! GitHub repo links to show off the ISA. Perhaps in the future if we cross-compile to
|
||||||
|
//! WASM we could include a link to run this software in the browser.
|
||||||
|
//!
|
||||||
|
//!
|
||||||
|
//! # Configuration
|
||||||
|
//!
|
||||||
|
//! This may be disabled like so in your `.dsarc.toml` file:
|
||||||
|
//!
|
||||||
|
//! ```toml
|
||||||
|
//! [misc]
|
||||||
|
//! use_discord_rpc = false
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
|
//! Alternatively, you can hide this in your Discord settings.
|
||||||
|
|
||||||
|
use discord_presence::{Client, DiscordError};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub enum DiscordRpcError {
|
||||||
|
Client(DiscordError),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for DiscordRpcError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
Self::Client(why) => write!(f, "discord RPC error: {why}"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for DiscordRpcError {}
|
||||||
|
|
||||||
|
impl From<DiscordError> for DiscordRpcError {
|
||||||
|
fn from(err: DiscordError) -> Self {
|
||||||
|
Self::Client(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sets up the Discord RPC client.
|
||||||
|
#[expect(clippy::unreadable_literal)]
|
||||||
|
pub fn start_rpc() -> Result<Client, DiscordRpcError> {
|
||||||
|
let mut client = discord_presence::Client::new(1384303074088190042);
|
||||||
|
|
||||||
|
_ = client.on_ready(|ctx| {
|
||||||
|
eprintln!("The discord RPC client is ready. Got event {:?}", ctx.event);
|
||||||
|
});
|
||||||
|
|
||||||
|
client.start();
|
||||||
|
|
||||||
|
client.set_activity(|act| act)?;
|
||||||
|
|
||||||
|
Ok(client)
|
||||||
|
}
|
||||||
@@ -1,2 +1,5 @@
|
|||||||
|
#[cfg(feature = "config")]
|
||||||
|
pub mod config;
|
||||||
|
pub mod misc;
|
||||||
pub mod system;
|
pub mod system;
|
||||||
pub mod ui;
|
pub mod ui;
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ impl MemoryUnit for MainStore {
|
|||||||
bytes[1] = block.data[(offset + 1) as usize];
|
bytes[1] = block.data[(offset + 1) as usize];
|
||||||
bytes[2] = block.data[(offset + 2) as usize];
|
bytes[2] = block.data[(offset + 2) as usize];
|
||||||
bytes[3] = block.data[(offset + 3) as usize];
|
bytes[3] = block.data[(offset + 3) as usize];
|
||||||
u32::from_be_bytes(bytes)
|
u32::from_le_bytes(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_range(&mut self, addr: u32, size: u32) -> Vec<u8> {
|
fn read_range(&mut self, addr: u32, size: u32) -> Vec<u8> {
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ use crate::emulator::system::{
|
|||||||
model::{IODevice, RegFile},
|
model::{IODevice, RegFile},
|
||||||
};
|
};
|
||||||
|
|
||||||
use common::instructions::{Instruction, Interrupt, Register, errors::InstructionDecodeError};
|
use common::instructions::{
|
||||||
|
Instruction, Interrupt, Register, errors::InstructionDecodeError,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Processor {
|
pub struct Processor {
|
||||||
pub memory: Box<dyn MemoryUnit>,
|
pub memory: Box<dyn MemoryUnit>,
|
||||||
@@ -168,71 +170,90 @@ impl Executable for Instruction {
|
|||||||
*cpu.reg(a.dr) = cpu.get(a.sr1);
|
*cpu.reg(a.dr) = cpu.get(a.sr1);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copies from SrcReg to a.drReg, sign extending the value to take up a full word.
|
// Copies from SrcReg to a.drReg, sign extending the value to take up a full
|
||||||
|
// word.
|
||||||
Self::MovSigned(a) => {
|
Self::MovSigned(a) => {
|
||||||
*cpu.reg(a.dr) = sign_extend(cpu.get(a.sr1));
|
*cpu.reg(a.dr) = sign_extend(cpu.get(a.sr1));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads a byte from memory address (base + offset) into a.drReg. The effective address must be byte-aligned.
|
// Loads a byte from memory address (base + offset) into a.drReg. The
|
||||||
|
// effective address must be byte-aligned.
|
||||||
Self::LoadByte(a) => {
|
Self::LoadByte(a) => {
|
||||||
*cpu.reg(a.r2) =
|
*cpu.reg(a.r2) = u32::from(
|
||||||
u32::from(cpu.memory.read_byte(cpu.get(a.r1) + u32::from(a.immediate)));
|
cpu.memory.read_byte(cpu.get(a.r1) + u32::from(a.immediate)),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads a sign-extended byte from memory address (base + offset) into a.drReg. The effective address must be byte-aligned.
|
// Loads a sign-extended byte from memory address (base + offset) into
|
||||||
|
// a.drReg. The effective address must be byte-aligned.
|
||||||
Self::LoadByteSigned(a) => {
|
Self::LoadByteSigned(a) => {
|
||||||
*cpu.reg(a.r2) = sign_extend(u32::from(
|
*cpu.reg(a.r2) = sign_extend(u32::from(
|
||||||
cpu.memory.read_byte(cpu.get(a.r1) + u32::from(a.immediate)),
|
cpu.memory.read_byte(cpu.get(a.r1) + u32::from(a.immediate)),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads a half-word from memory address (base + offset) into a.drReg. The effective address must be 2-byte-aligned.
|
// Loads a half-word from memory address (base + offset) into a.drReg. The
|
||||||
|
// effective address must be 2-byte-aligned.
|
||||||
Self::LoadHalfword(a) => {
|
Self::LoadHalfword(a) => {
|
||||||
// we read an entire word, then right shift so we only get the first half of the word
|
// we read an entire word, then right shift so we only get the first half
|
||||||
*cpu.reg(a.r2) = cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate)) >> 16;
|
// of the word
|
||||||
}
|
|
||||||
|
|
||||||
// Loads a sign-extended half-word from memory address (base + offset) into a.drReg. The effective address must be 2-byte-aligned.
|
|
||||||
Self::LoadHalfwordSigned(a) => {
|
|
||||||
*cpu.reg(a.r2) =
|
*cpu.reg(a.r2) =
|
||||||
sign_extend(cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate)) >> 16);
|
cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate)) >> 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads a word from memory address (base + offset) into a.drReg. The effective address must be 4-byte-aligned.
|
// Loads a sign-extended half-word from memory address (base + offset) into
|
||||||
|
// a.drReg. The effective address must be 2-byte-aligned.
|
||||||
|
Self::LoadHalfwordSigned(a) => {
|
||||||
|
*cpu.reg(a.r2) = sign_extend(
|
||||||
|
cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate)) >> 16,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads a word from memory address (base + offset) into a.drReg. The
|
||||||
|
// effective address must be 4-byte-aligned.
|
||||||
Self::LoadWord(a) => {
|
Self::LoadWord(a) => {
|
||||||
*cpu.reg(a.r2) = cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate));
|
*cpu.reg(a.r2) =
|
||||||
|
cpu.memory.read_word(cpu.get(a.r1) + u32::from(a.immediate));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stores a byte from SrcReg in memory address (base + offset) The effective address must be byte-aligned.
|
// Stores a byte from SrcReg in memory address (base + offset) The effective
|
||||||
|
// address must be byte-aligned.
|
||||||
Self::StoreByte(a) => {
|
Self::StoreByte(a) => {
|
||||||
cpu.memory
|
cpu.memory.write_byte(
|
||||||
.write_byte(cpu.get(a.r1) + u32::from(a.immediate), cpu.get(a.r2) as u8);
|
cpu.get(a.r1) + u32::from(a.immediate),
|
||||||
|
cpu.get(a.r2) as u8,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stores a half-word from SrcReg in memory address (base + offset) The effective address must be 2-byte-aligned.
|
// Stores a half-word from SrcReg in memory address (base + offset) The
|
||||||
|
// effective address must be 2-byte-aligned.
|
||||||
Self::StoreHalfword(a) => {
|
Self::StoreHalfword(a) => {
|
||||||
// split the value into bytes and then write two bytes
|
// split the value into bytes and then write two bytes
|
||||||
let bytes = (cpu.get(a.r1) as u16).to_be_bytes();
|
let bytes = (cpu.get(a.r1) as u16).to_le_bytes();
|
||||||
cpu.memory
|
cpu.memory
|
||||||
.write_byte(cpu.get(a.r1) + u32::from(a.immediate), bytes[0]);
|
.write_byte(cpu.get(a.r1) + u32::from(a.immediate), bytes[0]);
|
||||||
cpu.memory
|
cpu.memory
|
||||||
.write_byte(cpu.get(a.r1) + u32::from(a.immediate) + 1, bytes[1]);
|
.write_byte(cpu.get(a.r1) + u32::from(a.immediate) + 1, bytes[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stores a word from SrcReg in memory address (base + offset) The effective address must be 4-byte-aligned.
|
// Stores a word from SrcReg in memory address (base + offset) The effective
|
||||||
|
// address must be 4-byte-aligned.
|
||||||
Self::StoreWord(a) => {
|
Self::StoreWord(a) => {
|
||||||
cpu.memory
|
cpu.memory
|
||||||
.write_word(cpu.get(a.r1) + u32::from(a.immediate), cpu.get(a.r2));
|
.write_word(cpu.get(a.r1) + u32::from(a.immediate), cpu.get(a.r2));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads a 16-bit literal value into reg, setting the bottom 16 bits of the word. To populate the upper 16 bits, see LUI.
|
// Loads a 16-bit literal value into reg, setting the bottom 16 bits of the
|
||||||
|
// word. To populate the upper 16 bits, see LUI.
|
||||||
Self::LoadLowerImmediate(a) => {
|
Self::LoadLowerImmediate(a) => {
|
||||||
*cpu.reg(a.r1) = u32::from(a.immediate);
|
*cpu.reg(a.r1) = u32::from(a.immediate);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads a 16-bit literal value into reg, setting the top 16 bits of the word. To populate the lower 16 bits, see LLI.
|
// Loads a 16-bit literal value into reg, setting the top 16 bits of the word.
|
||||||
|
// To populate the lower 16 bits, see LLI.
|
||||||
Self::LoadUpperImmediate(a) => {
|
Self::LoadUpperImmediate(a) => {
|
||||||
*cpu.reg(a.r1) = (cpu.get(a.r1) & 0x0000_FFFF) | u32::from(a.immediate) << 16;
|
*cpu.reg(a.r1) =
|
||||||
|
(cpu.get(a.r1) & 0x0000_FFFF) | u32::from(a.immediate) << 16;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unconditionally jumps to the calculated address or direct address
|
// Unconditionally jumps to the calculated address or direct address
|
||||||
@@ -259,7 +280,8 @@ impl Executable for Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jumps to the calculated address or direct address if greater than flag or equal flag set.
|
// Jumps to the calculated address or direct address if greater than flag or
|
||||||
|
// equal flag set.
|
||||||
Self::JumpGe(a) => {
|
Self::JumpGe(a) => {
|
||||||
if cpu.get_flag(Flag::GreaterThan) || cpu.get_flag(Flag::Equal) {
|
if cpu.get_flag(Flag::GreaterThan) || cpu.get_flag(Flag::Equal) {
|
||||||
cpu.jump(a.r1, a.immediate);
|
cpu.jump(a.r1, a.immediate);
|
||||||
@@ -273,7 +295,8 @@ impl Executable for Instruction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Jumps to the calculated address or direct address if less than flag or equal flag set.
|
// Jumps to the calculated address or direct address if less than flag or
|
||||||
|
// equal flag set.
|
||||||
Self::JumpLe(a) => {
|
Self::JumpLe(a) => {
|
||||||
if cpu.get_flag(Flag::LessThan) || cpu.get_flag(Flag::Equal) {
|
if cpu.get_flag(Flag::LessThan) || cpu.get_flag(Flag::Equal) {
|
||||||
cpu.jump(a.r1, a.immediate);
|
cpu.jump(a.r1, a.immediate);
|
||||||
@@ -286,20 +309,24 @@ impl Executable for Instruction {
|
|||||||
// Decrements the value in the given register
|
// Decrements the value in the given register
|
||||||
Self::Decrement(a) => *cpu.reg(a.sr1) = dec(cpu.get(a.sr1)),
|
Self::Decrement(a) => *cpu.reg(a.sr1) = dec(cpu.get(a.sr1)),
|
||||||
|
|
||||||
// Left shifts the value in Reg by the given amount (either a register, or a literal value)
|
// Left shifts the value in Reg by the given amount (either a register, or a
|
||||||
|
// literal value)
|
||||||
Self::ShiftLeft(a) => {
|
Self::ShiftLeft(a) => {
|
||||||
let regval = cpu.get(a.sr2);
|
let regval = cpu.get(a.sr2);
|
||||||
let val = cpu.get(a.sr1);
|
let val = cpu.get(a.sr1);
|
||||||
|
|
||||||
*cpu.reg(a.sr1) = shl(val, if regval != 0 { regval as u8 } else { a.shamt });
|
*cpu.reg(a.sr1) =
|
||||||
|
shl(val, if regval != 0 { regval as u8 } else { a.shamt });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right shifts the value in Reg by the given amount (either a register, or a literal value).
|
// Right shifts the value in Reg by the given amount (either a register, or a
|
||||||
|
// literal value).
|
||||||
Self::ShiftRight(a) => {
|
Self::ShiftRight(a) => {
|
||||||
let regval = cpu.get(a.sr2);
|
let regval = cpu.get(a.sr2);
|
||||||
let val = cpu.get(a.sr1);
|
let val = cpu.get(a.sr1);
|
||||||
|
|
||||||
*cpu.reg(a.sr1) = shr(val, if regval != 0 { regval as u8 } else { a.shamt });
|
*cpu.reg(a.sr1) =
|
||||||
|
shr(val, if regval != 0 { regval as u8 } else { a.shamt });
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adds the value of Src2 to Src1 and writes the result to a.dr
|
// Adds the value of Src2 to Src1 and writes the result to a.dr
|
||||||
@@ -333,7 +360,8 @@ impl Executable for Instruction {
|
|||||||
// Performs bitwise XNOR on Src1 and Src2 storing the result in a.dr
|
// Performs bitwise XNOR on Src1 and Src2 storing the result in a.dr
|
||||||
Self::Xnor(a) => *cpu.reg(a.dr) = xnor(cpu.get(a.sr1), cpu.get(a.sr2)),
|
Self::Xnor(a) => *cpu.reg(a.dr) = xnor(cpu.get(a.sr1), cpu.get(a.sr2)),
|
||||||
|
|
||||||
// Compares the value of Reg1 to the value in Reg2. The results of the comparisons are set in the Status register.
|
// Compares the value of Reg1 to the value in Reg2. The results of the
|
||||||
|
// comparisons are set in the Status register.
|
||||||
Self::Compare(a) => {
|
Self::Compare(a) => {
|
||||||
cpu.cmp(cpu.get(a.sr1), cpu.get(a.sr2));
|
cpu.cmp(cpu.get(a.sr1), cpu.get(a.sr2));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ impl Component for Editor {
|
|||||||
|
|
||||||
if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) {
|
if ui.input(|i| i.key_pressed(Key::S) && i.modifiers.ctrl) {
|
||||||
self.save();
|
self.save();
|
||||||
};
|
}
|
||||||
|
|
||||||
self.render_toolbar(state, ui, ctx);
|
self.render_toolbar(state, ui, ctx);
|
||||||
|
|
||||||
@@ -87,17 +87,23 @@ impl Editor {
|
|||||||
fn filename(&self) -> &str {
|
fn filename(&self) -> &str {
|
||||||
self.path
|
self.path
|
||||||
.file_name()
|
.file_name()
|
||||||
.unwrap_or(OsStr::new("Unnamed!"))
|
.unwrap_or_else(|| OsStr::new("Unnamed!"))
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.map_or_else(
|
||||||
|
|| unreachable!("File name should be valid UTF-8."),
|
||||||
|
|ext| ext,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn extension(&self) -> &str {
|
fn extension(&self) -> &str {
|
||||||
self.path
|
self.path
|
||||||
.extension()
|
.extension()
|
||||||
.unwrap_or(OsStr::new("Unknown!"))
|
.unwrap_or_else(|| OsStr::new("Unknown!"))
|
||||||
.to_str()
|
.to_str()
|
||||||
.unwrap()
|
.map_or_else(
|
||||||
|
|| unreachable!("File name should be valid UTF-8."),
|
||||||
|
|ext| ext,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn save(&mut self) {
|
fn save(&mut self) {
|
||||||
@@ -172,7 +178,7 @@ impl Editor {
|
|||||||
bytes[i] = byte;
|
bytes[i] = byte;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let value = u32::from_be_bytes(bytes);
|
let value = u32::from_le_bytes(bytes);
|
||||||
|
|
||||||
// Address column
|
// Address column
|
||||||
ui.with_layout(
|
ui.with_layout(
|
||||||
@@ -210,10 +216,10 @@ impl Editor {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Instruction column
|
// Instruction column
|
||||||
let instruction = match Instruction::decode(value) {
|
let instruction = Instruction::decode(value).map_or_else(
|
||||||
Ok(instruction) => instruction.to_string(),
|
|_| format!("{value:10}"),
|
||||||
Err(_) => format!("{value:10}"),
|
|instruction| instruction.to_string(),
|
||||||
};
|
);
|
||||||
|
|
||||||
ui.label(
|
ui.label(
|
||||||
egui::RichText::new(instruction)
|
egui::RichText::new(instruction)
|
||||||
@@ -230,19 +236,25 @@ impl Editor {
|
|||||||
fn render_editor(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
fn render_editor(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
||||||
let available_width = ui.available_width();
|
let available_width = ui.available_width();
|
||||||
let syntax = match self.extension() {
|
let syntax = match self.extension() {
|
||||||
"dsa" => Syntax::dsa(),
|
"dsa" => Some(Syntax::new("dsa")),
|
||||||
_ => Syntax::dsa(),
|
_ => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
CodeEditor::default()
|
let ed = CodeEditor::default()
|
||||||
.id_source("editor")
|
.id_source("editor")
|
||||||
.with_fontsize(12.0)
|
.with_fontsize(12.0)
|
||||||
.with_rows(0)
|
.with_rows(0)
|
||||||
.with_theme(ColorTheme::default())
|
.with_theme(ColorTheme::default())
|
||||||
.with_syntax(syntax)
|
|
||||||
.with_numlines(true)
|
.with_numlines(true)
|
||||||
.desired_width(available_width - 450.0)
|
.desired_width(available_width - 450.0);
|
||||||
.show(ui, &mut self.text);
|
|
||||||
|
let mut editor = ed.clone();
|
||||||
|
|
||||||
|
if let Some(syntax) = syntax {
|
||||||
|
editor = ed.with_syntax(syntax);
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.show(ui, &mut self.text);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
fn render_toolbar(&mut self, _state: &mut State, ui: &mut Ui, _ctx: &Context) {
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
include print "../resources/dsa/print.dsa"
|
// include print "../resources/dsa/print.dsa"
|
||||||
// Fibonacci sequence calculator in DSA assembly
|
// Fibonacci sequence calculator in DSA assembly
|
||||||
// Calculates the first 8 Fibonacci numbers: 0, 1, 1, 2, 3, 5, 8, 13
|
// Calculates the first 8 Fibonacci numbers: 0, 1, 1, 2, 3, 5, 8, 13
|
||||||
dw fib_count: 6 // How many more numbers to calculate after F(0) and F(1)
|
dw fib_count: 6 // How many more numbers to calculate after F(0) and F(1)
|
||||||
|
|
||||||
init:
|
init:
|
||||||
// Initialize the first two Fibonacci numbers
|
// Initialize the first two Fibonacci numbers
|
||||||
lli 0, rg0 // F(0) = 0
|
lli rg0, 0 // F(0) = 0
|
||||||
lli 1, rg1 // F(1) = 1
|
lli rg1, 1 // F(1) = 1
|
||||||
|
push rg0
|
||||||
// Load loop counter
|
// Load loop counter
|
||||||
ldw fib_count, zero, rg2 // Load number of iterations remaining
|
ldw fib_count, zero, rg2 // Load number of iterations remaining
|
||||||
|
|
||||||
@@ -21,7 +21,7 @@ fibonacci_loop:
|
|||||||
|
|
||||||
// Decrement loop counter
|
// Decrement loop counter
|
||||||
dec rg2 // rg2 = rg2 - 1
|
dec rg2 // rg2 = rg2 - 1
|
||||||
|
|
||||||
// Check if we should continue looping
|
// Check if we should continue looping
|
||||||
cmp rg2, zero // Compare counter with 0
|
cmp rg2, zero // Compare counter with 0
|
||||||
jgt fibonacci_loop // Jump back if counter > 0
|
jgt fibonacci_loop // Jump back if counter > 0
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ init:
|
|||||||
|
|
||||||
start:
|
start:
|
||||||
ldb length, rg0
|
ldb length, rg0
|
||||||
lwi string, rg1
|
lwi rg1, string
|
||||||
lwi display, rg2
|
lwi rg2, display
|
||||||
|
|
||||||
loop:
|
loop:
|
||||||
// read from string and write to display
|
// read from string and write to display
|
||||||
|
|||||||
Reference in New Issue
Block a user