forked from nonsensical-dev/zenyx-engine
Basic repl
This commit is contained in:
commit
ecbfce64ed
20 changed files with 565 additions and 0 deletions
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
|
@ -0,0 +1,2 @@
|
|||
[alias]
|
||||
xtask = "run --quiet --package xtask --"
|
1
.gitattributes
vendored
Normal file
1
.gitattributes
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
*.obj filter=lfs diff=lfs merge=lfs -text
|
29
.github/workflows/rust.yml
vendored
Normal file
29
.github/workflows/rust.yml
vendored
Normal file
|
@ -0,0 +1,29 @@
|
|||
name: Rust
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main", "master" ]
|
||||
pull_request:
|
||||
branches: [ "main", "master" ]
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Build
|
||||
run: cargo build --verbose
|
||||
|
||||
- name: Run tests
|
||||
run: cargo test --verbose
|
||||
|
||||
- name: Check formatting
|
||||
run: cargo fmt -- --check
|
||||
|
||||
- name: Clippy
|
||||
run: cargo clippy -- -D warnings
|
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
|
@ -0,0 +1,4 @@
|
|||
/target
|
||||
.idea
|
||||
Cargo.lock
|
||||
*.log
|
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
|
@ -0,0 +1,21 @@
|
|||
# MIT License
|
||||
|
||||
## Copyright (c) 2024 Caznix
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
**THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.**
|
0
README.md
Normal file
0
README.md
Normal file
7
cargo.toml
Normal file
7
cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
|||
[workspace]
|
||||
resolver = "2"
|
||||
members = [
|
||||
"engine",
|
||||
"editor",
|
||||
"xtask"
|
||||
]
|
7
editor/Cargo.toml
Normal file
7
editor/Cargo.toml
Normal file
|
@ -0,0 +1,7 @@
|
|||
[package]
|
||||
name = "editor"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
floem = "0.2.0"
|
18
editor/src/main.rs
Normal file
18
editor/src/main.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use floem::{prelude::*, style::Style};
|
||||
|
||||
fn main() {
|
||||
floem::launch(counter_view);
|
||||
}
|
||||
|
||||
fn counter_view() -> impl IntoView {
|
||||
let mut counter = RwSignal::new(20);
|
||||
v_stack({
|
||||
label("test")
|
||||
}).style(Style::d);
|
||||
h_stack((
|
||||
button("Increment").action(move || counter += 1),
|
||||
label(move || format!("Value: {counter}")),
|
||||
button("Decrement").action(move || counter -= 1),
|
||||
))
|
||||
.style(|s| s.size_full().items_center().justify_center().gap(10))
|
||||
}
|
1
engine/.gitignore
vendored
Normal file
1
engine/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
|||
/target
|
16
engine/Cargo.toml
Normal file
16
engine/Cargo.toml
Normal file
|
@ -0,0 +1,16 @@
|
|||
[package]
|
||||
name = "zenyx"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
chrono = "0.4.38"
|
||||
colored = "2.1.0"
|
||||
lazy_static = "1.5.0"
|
||||
#log = "0.4.22"
|
||||
log2 = "0.1.14"
|
||||
parking_lot = "0.12.3"
|
||||
reedline = "0.37.0"
|
||||
regex = "1.11.1"
|
||||
thiserror = "2.0.3"
|
||||
tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] }
|
36
engine/src/core/commands.rs
Normal file
36
engine/src/core/commands.rs
Normal file
|
@ -0,0 +1,36 @@
|
|||
use std::process::Command;
|
||||
|
||||
use log2::{debug, info};
|
||||
|
||||
use crate::core::repl::COMMAND_LIST;
|
||||
|
||||
pub fn say_hello() {
|
||||
println!("Hello from your new command!");
|
||||
}
|
||||
|
||||
pub fn echo(args: Vec<String>) {
|
||||
debug!("{}", args.join(" "));
|
||||
println!("{}", args.join(" "))
|
||||
}
|
||||
|
||||
pub fn exit() {
|
||||
debug!("Exiting...");
|
||||
std::process::exit(0)
|
||||
}
|
||||
pub fn clear() {
|
||||
info!("Clearing screen..., running command");
|
||||
let _result = if cfg!(target_os = "windows") {
|
||||
debug!("target_os is windows");
|
||||
Command::new("cmd").args(["/c", "cls"]).spawn()
|
||||
} else {
|
||||
debug!("target_os was unix");
|
||||
// "clear" or "tput reset"
|
||||
Command::new("tput").arg("reset").spawn()
|
||||
};
|
||||
}
|
||||
pub fn cmds() {
|
||||
println!("Commands:");
|
||||
for cmd in COMMAND_LIST.commands.read().iter() {
|
||||
println!("{:#}", cmd);
|
||||
}
|
||||
}
|
3
engine/src/core/mod.rs
Normal file
3
engine/src/core/mod.rs
Normal file
|
@ -0,0 +1,3 @@
|
|||
pub mod commands;
|
||||
pub mod repl;
|
||||
pub mod splash;
|
248
engine/src/core/repl.rs
Normal file
248
engine/src/core/repl.rs
Normal file
|
@ -0,0 +1,248 @@
|
|||
use super::commands;
|
||||
use chrono::Local;
|
||||
use lazy_static::lazy_static;
|
||||
use log2::{debug, error, info};
|
||||
use parking_lot::RwLock;
|
||||
use reedline::{Prompt, Reedline, Signal};
|
||||
use regex::Regex;
|
||||
use std::{borrow::Borrow, collections::HashMap, sync::Arc};
|
||||
|
||||
struct ZPrompt {
|
||||
left_text: String,
|
||||
right_text: String,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
enum Callable {
|
||||
Simple(fn()),
|
||||
WithArgs(fn(Vec<String>)),
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub struct Command {
|
||||
pub name: &'static str,
|
||||
pub description: Option<&'static str>,
|
||||
function: Callable,
|
||||
pub arg_count: u8,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
pub fn execute(&self, args: Option<Vec<String>>) {
|
||||
//debug!("Executing command: {}", self.name);
|
||||
match &self.function {
|
||||
Callable::Simple(f) => {
|
||||
if let Some(args) = args {
|
||||
error!(
|
||||
"Command expected 0 arguments but {} args were given. Ignoring..",
|
||||
args.len()
|
||||
);
|
||||
}
|
||||
f()
|
||||
}
|
||||
Callable::WithArgs(f) => match args {
|
||||
Some(args) => f(args),
|
||||
None => error!("Command expected arguments but received 0"),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Command {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"Name: {}\n\t{}",
|
||||
self.name,
|
||||
self.description.unwrap_or("No description")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
lazy_static! {
|
||||
pub static ref COMMAND_LIST: Arc<CommandList> = Arc::new(CommandList::new());
|
||||
}
|
||||
|
||||
pub struct CommandList {
|
||||
pub commands: RwLock<Vec<Command>>,
|
||||
pub aliases: RwLock<HashMap<String, String>>,
|
||||
}
|
||||
|
||||
impl CommandList {
|
||||
fn new() -> Self {
|
||||
CommandList {
|
||||
commands: RwLock::new(Vec::new()),
|
||||
aliases: RwLock::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
fn add_command(
|
||||
&self,
|
||||
name: &'static str,
|
||||
description: Option<&'static str>,
|
||||
func: Callable,
|
||||
arg_count: Option<u8>,
|
||||
) {
|
||||
debug!("Adding command: {}", name);
|
||||
let mut commands = self.commands.write();
|
||||
|
||||
commands.push(Command {
|
||||
name,
|
||||
description,
|
||||
function: func,
|
||||
arg_count: arg_count.unwrap_or(0),
|
||||
});
|
||||
}
|
||||
fn add_alias(&self, name: String, alias: String) {
|
||||
//println!("Input alias: {}", alias);
|
||||
if self.aliases.read().contains_key(&alias) {
|
||||
error!("Alias: '{}' already exists", alias);
|
||||
return;
|
||||
}
|
||||
let mut commands = self.commands.write();
|
||||
if let Some(command) = commands.iter_mut().find(|cmd| cmd.name == name) {
|
||||
info!("Adding alias: {} for cmd: {}", alias, command.name);
|
||||
self.aliases
|
||||
.write()
|
||||
.insert(alias.to_string(), name.to_string());
|
||||
} else {
|
||||
error!("Command: '{}' was not found", name);
|
||||
}
|
||||
}
|
||||
|
||||
fn execute_command(&self, mut name: String, args: Option<Vec<String>>) {
|
||||
//info!("received input command: {}", name);
|
||||
let commands = self.commands.borrow();
|
||||
if self.aliases.read().contains_key(&name) {
|
||||
name = self
|
||||
.aliases
|
||||
.read()
|
||||
.get_key_value(&name)
|
||||
.unwrap()
|
||||
.1
|
||||
.to_string();
|
||||
debug!("changed to {}", name);
|
||||
}
|
||||
if let Some(command) = commands.read().iter().find(|cmd| cmd.name == name) { match (command.arg_count, args.as_ref()) {
|
||||
(expected, Some(args_vec)) if args_vec.len() != expected as usize => {
|
||||
eprintln!(
|
||||
"Command: '{}' expected {} arguments but received {}",
|
||||
name,
|
||||
expected,
|
||||
args_vec.len()
|
||||
);
|
||||
}
|
||||
(_, _) => command.execute(args),
|
||||
} }
|
||||
}
|
||||
}
|
||||
|
||||
impl Prompt for ZPrompt {
|
||||
fn render_prompt_left(&self) -> std::borrow::Cow<str> {
|
||||
std::borrow::Cow::Borrowed(&self.left_text)
|
||||
}
|
||||
|
||||
fn render_prompt_right(&self) -> std::borrow::Cow<str> {
|
||||
std::borrow::Cow::Borrowed(&self.right_text)
|
||||
}
|
||||
|
||||
fn render_prompt_history_search_indicator(
|
||||
&self,
|
||||
_history_search: reedline::PromptHistorySearch,
|
||||
) -> std::borrow::Cow<str> {
|
||||
std::borrow::Cow::Borrowed("")
|
||||
}
|
||||
|
||||
fn render_prompt_indicator(
|
||||
&self,
|
||||
prompt_mode: reedline::PromptEditMode,
|
||||
) -> std::borrow::Cow<str> {
|
||||
match prompt_mode {
|
||||
reedline::PromptEditMode::Default => std::borrow::Cow::Borrowed(">>"),
|
||||
reedline::PromptEditMode::Emacs => {
|
||||
let timestamp = Local::now().format("[%H:%M:%S.%3f/SHELL] >>\t").to_string();
|
||||
std::borrow::Cow::Owned(timestamp)
|
||||
}
|
||||
reedline::PromptEditMode::Vi(_) => std::borrow::Cow::Borrowed("vi>>"),
|
||||
reedline::PromptEditMode::Custom(_) => std::borrow::Cow::Borrowed("custom>>"),
|
||||
}
|
||||
}
|
||||
|
||||
fn render_prompt_multiline_indicator(&self) -> std::borrow::Cow<str> {
|
||||
std::borrow::Cow::Borrowed("><")
|
||||
}
|
||||
}
|
||||
|
||||
fn setup() {
|
||||
COMMAND_LIST.add_command(
|
||||
"hello",
|
||||
Some("test"),
|
||||
Callable::Simple(commands::say_hello),
|
||||
None,
|
||||
);
|
||||
COMMAND_LIST.add_command("exit", None, Callable::Simple(commands::exit), None);
|
||||
COMMAND_LIST.add_command("clear", None, Callable::Simple(commands::clear), None);
|
||||
COMMAND_LIST.add_command("echo", None, Callable::WithArgs(commands::echo), Some(1));
|
||||
COMMAND_LIST.add_command("cmds", None, Callable::Simple(commands::cmds), None);
|
||||
COMMAND_LIST.add_alias("cmds".to_string(), "help".to_string());
|
||||
COMMAND_LIST.add_alias("cmds".to_string(), "cmd_list".to_string());
|
||||
COMMAND_LIST.add_alias("hello".to_string(), "exit".to_string());
|
||||
COMMAND_LIST.add_alias("clear".to_string(), "exit".to_string());
|
||||
}
|
||||
|
||||
pub async fn handle_repl() {
|
||||
let mut line_editor = Reedline::create();
|
||||
setup();
|
||||
|
||||
loop {
|
||||
let sig = line_editor.read_line(&ZPrompt {
|
||||
left_text: String::new(),
|
||||
right_text: "<<".to_string(),
|
||||
});
|
||||
|
||||
match sig {
|
||||
Ok(Signal::Success(buffer)) => {
|
||||
if buffer == "exit" {
|
||||
std::process::exit(0);
|
||||
} else {
|
||||
evaluate_command(&buffer);
|
||||
}
|
||||
}
|
||||
Ok(Signal::CtrlC) => {
|
||||
println!("\nCONTROL+C RECEIVED, TERMINATING");
|
||||
std::process::exit(0);
|
||||
}
|
||||
err => {
|
||||
eprintln!("Error: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn evaluate_command(input: &str) {
|
||||
if input.trim().is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let pattern = Regex::new(r"[;|\n]").unwrap();
|
||||
let commands: Vec<&str> = pattern.split(input).collect();
|
||||
|
||||
for command in commands {
|
||||
let command = command.trim();
|
||||
if command.is_empty() {
|
||||
println!("Empty command, skipping.");
|
||||
continue;
|
||||
}
|
||||
|
||||
let tokens: Vec<&str> = command.split_whitespace().collect();
|
||||
if tokens.is_empty() {
|
||||
return;
|
||||
}
|
||||
|
||||
let cmd_name = tokens[0];
|
||||
let args: Vec<String> = tokens[1..].iter().map(|&s| s.to_string()).collect();
|
||||
|
||||
COMMAND_LIST.execute_command(
|
||||
cmd_name.to_string(),
|
||||
if args.is_empty() { None } else { Some(args) },
|
||||
);
|
||||
}
|
||||
}
|
26
engine/src/core/splash.rs
Normal file
26
engine/src/core/splash.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
use colored::Colorize;
|
||||
|
||||
pub fn print_splash() {
|
||||
println!(
|
||||
r#"
|
||||
&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&&&&&
|
||||
&& &&&&&&&&&
|
||||
&& &&&&&&&&&
|
||||
&&&&&&&&&&&& &&&&&&&&&&&
|
||||
&&&&&&&&&&&&& &&&&&&&&&&&&
|
||||
&&&&&&&&&&&&& &&&&&&&&&&&&&
|
||||
&&&&&&&&&&&& &&&&&&&&&&&&&
|
||||
&&&&&&&&&&& &&&&&&&&&&&&
|
||||
&&&&&&&&& &&
|
||||
&&&&&&&&& &&
|
||||
&&&&&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&&&&&&&
|
||||
&&&&&&&&&&&
|
||||
|
||||
Version: {}
|
||||
"#,
|
||||
env!("CARGO_PKG_VERSION").yellow().italic().underline()
|
||||
);
|
||||
}
|
21
engine/src/main.rs
Normal file
21
engine/src/main.rs
Normal file
|
@ -0,0 +1,21 @@
|
|||
use std::io;
|
||||
|
||||
use log2::info;
|
||||
|
||||
pub mod core;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), io::Error> {
|
||||
let _log2 = log2::open("z.log").tee(true).level("trace").start();
|
||||
info!("Initalizing Engine");
|
||||
let shell_thread = tokio::task::spawn(async {
|
||||
info!("Shell thread started");
|
||||
core::repl::handle_repl().await;
|
||||
}
|
||||
);
|
||||
|
||||
core::splash::print_splash();
|
||||
info!("Engine Initalized");
|
||||
shell_thread.await?;
|
||||
Ok(())
|
||||
}
|
32
xtask/Cargo.toml
Normal file
32
xtask/Cargo.toml
Normal file
|
@ -0,0 +1,32 @@
|
|||
[package]
|
||||
name = "xtask"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.20", features = ["derive"] }
|
||||
|
||||
[profile.dev]
|
||||
rpath = false
|
||||
panic = "abort"
|
||||
lto = "off"
|
||||
opt-level = 0
|
||||
debug = false
|
||||
overflow-checks = false
|
||||
incremental = true
|
||||
codegen-units = 256
|
||||
|
||||
strip = "symbols"
|
||||
debug-assertions = false
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 0
|
||||
debug = false
|
||||
overflow-checks = false
|
||||
incremental = true
|
||||
codegen-units = 256
|
||||
|
||||
strip = "symbols"
|
||||
debug-assertions = false
|
||||
|
1
xtask/src/editor.rs
Normal file
1
xtask/src/editor.rs
Normal file
|
@ -0,0 +1 @@
|
|||
pub fn build_editor() {}
|
22
xtask/src/engine.rs
Normal file
22
xtask/src/engine.rs
Normal file
|
@ -0,0 +1,22 @@
|
|||
use std::process::Stdio;
|
||||
|
||||
|
||||
pub fn build_engine() {
|
||||
|
||||
}
|
||||
|
||||
pub fn build_core() {
|
||||
let threads = format!("-j{}",std::thread::available_parallelism().unwrap().get());
|
||||
let mut run = std::process::Command::new("cargo")
|
||||
.arg("run")
|
||||
|
||||
.arg(threads)
|
||||
.arg("--bin")
|
||||
.arg("zenyx")
|
||||
.stdin(Stdio::inherit())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit())
|
||||
.spawn()
|
||||
.unwrap();
|
||||
run.wait().unwrap();
|
||||
}
|
70
xtask/src/main.rs
Normal file
70
xtask/src/main.rs
Normal file
|
@ -0,0 +1,70 @@
|
|||
|
||||
use clap::{CommandFactory, Parser, Subcommand, ValueEnum};
|
||||
pub mod engine;
|
||||
pub mod editor;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(version, about, long_about = None,disable_version_flag = true,disable_help_flag = true)]
|
||||
struct Cli {
|
||||
#[arg(short,long)]
|
||||
release: bool,
|
||||
#[command(subcommand)]
|
||||
command: Option<Commands>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum Commands {
|
||||
Run {
|
||||
#[arg()]
|
||||
task: Task
|
||||
},
|
||||
Config,
|
||||
|
||||
}
|
||||
#[derive(Clone,ValueEnum)]
|
||||
enum Task {
|
||||
Engine, // Builds both editor and core
|
||||
Editor, // Builds editor only
|
||||
Core, // Builds engine core only
|
||||
Help,
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn main() {
|
||||
let cli = Cli::parse();
|
||||
|
||||
|
||||
|
||||
|
||||
if cli.release {
|
||||
println!("Running in release mode")
|
||||
}
|
||||
|
||||
match &cli.command {
|
||||
|
||||
None => {
|
||||
Cli::command().print_help().map_err(|e| {
|
||||
println!("Could not run Xtask: {e}");
|
||||
|
||||
}).unwrap();
|
||||
}
|
||||
Some(Commands::Run { task }) => {
|
||||
match task {
|
||||
Task::Engine => engine::build_engine(),
|
||||
Task::Editor => todo!("Editor is not being actively worked on"),
|
||||
Task::Core => {
|
||||
engine::build_core();
|
||||
},
|
||||
Task::Help => {
|
||||
println!("The following options are avalible to run");
|
||||
todo!()
|
||||
},
|
||||
}
|
||||
}
|
||||
Some(Commands::Config) => {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue