Skip to content

Commit

Permalink
Merge pull request #8 from Zenyx-Engine/improve-repl
Browse files Browse the repository at this point in the history
Improve repl
  • Loading branch information
Caznix authored Dec 7, 2024
2 parents ac94f49 + e4a45ee commit b4f3178
Show file tree
Hide file tree
Showing 22 changed files with 627 additions and 62 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[workspace]
resolver = "2"
members = ["engine", "subcrates/zephyr"]
members = ["engine", "subcrates/zephyr", "plugin_api"]

[profile.dev]
rpath = false
Expand Down
3 changes: 3 additions & 0 deletions engine/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,13 @@ lazy_static = "1.5.0"
log = "0.4.22"
once_cell = "1.20.2"
parking_lot = "0.12.3"
rand = "0.8.5"
regex = "1.11.1"
rustyline = { version = "15.0.0", features = ["derive", "rustyline-derive"] }
thiserror = "2.0.3"
tokio = { version = "1.41.1", features = ["macros", "rt", "rt-multi-thread"] }
wgpu = "23.0.1"
winit = "0.30.5"
zephyr.workspace = true
plugin_api = { path = "../plugin_api" }
horizon-plugin-api = "0.1.13"
2 changes: 1 addition & 1 deletion engine/src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use renderer::App;
use winit::event_loop::{ControlFlow, EventLoop};

pub fn init_renderer() -> Result<()> {
let event_loop = EventLoop::new().unwrap();
let event_loop = EventLoop::new()?;
event_loop.set_control_flow(ControlFlow::Poll);
let mut app = App::default();
Ok(event_loop.run_app(&mut app)?)
Expand Down
10 changes: 5 additions & 5 deletions engine/src/core/renderer/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ impl<'window> WgpuCtx<'window> {
let width = size.width.max(1);
let height = size.height.max(1);

let surface_config = surface.get_default_config(&adapter, width, height).unwrap();
let surface_config = surface
.get_default_config(&adapter, width, height)
.expect("Failed to get default surface configuration");
surface.configure(&device, &surface_config);

Ok(WgpuCtx {
Expand All @@ -55,9 +57,7 @@ impl<'window> WgpuCtx<'window> {

pub fn new_blocking(window: Arc<Window>) -> Result<WgpuCtx<'window>> {
tokio::task::block_in_place(|| {
tokio::runtime::Runtime::new()
.unwrap()
.block_on(async { WgpuCtx::new(window).await })
tokio::runtime::Runtime::new()?.block_on(async { WgpuCtx::new(window).await })
})
}

Expand All @@ -80,7 +80,7 @@ impl<'window> WgpuCtx<'window> {
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
{
let mut rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
let rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &view,
Expand Down
4 changes: 2 additions & 2 deletions engine/src/core/renderer/mod.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
pub mod ctx;
use ctx::WgpuCtx;
use std::sync::Arc;

use ctx::WgpuCtx;
use log::{debug, trace};
use std::sync::Arc;
use winit::application::ApplicationHandler;
use winit::event::WindowEvent;
use winit::event_loop::ActiveEventLoop;
Expand Down
66 changes: 59 additions & 7 deletions engine/src/core/repl/commands.rs
Original file line number Diff line number Diff line change
@@ -1,31 +1,83 @@
use std::{ffi::OsStr, process::Command};

use lazy_static::lazy_static;
use parking_lot::Mutex;

use super::COMMAND_LIST;
use std::process::Command;
use crate::core::repl::exec::evaluate_command;
const MAX_RECURSION_DEPTH: usize = 500; // increasing this value WILL cause a stack overflow. attempt at your own risk -
// Caz

pub(crate) fn say_hello() {
lazy_static! {
static ref RECURSION_DEPTH: Mutex<usize> = parking_lot::Mutex::new(0);
}

pub(crate) fn say_hello() -> anyhow::Result<()> {
println!("Hello, World!");
Ok(())
}

pub(crate) fn echo(args: Vec<String>) {
println!("{}", args.join(" "))
pub(crate) fn echo(args: Vec<String>) -> anyhow::Result<()> {
println!("{}", args.join(" "));
Ok(())
}

pub(crate) fn exit() {
pub(crate) fn exit() -> anyhow::Result<()> {
println!("Exiting...");
std::process::exit(0)
}

pub(crate) fn clear() {
pub(crate) fn clear() -> anyhow::Result<()> {
println!("Clearing screen..., running command");
let _result = if cfg!(target_os = "windows") {
Command::new("cmd").args(["/c", "cls"]).spawn()
} else {
Command::new("clear").spawn()
};
Ok(())
}

pub(crate) fn help() {
pub(crate) fn help() -> anyhow::Result<()> {
println!("Commands:");
for cmd in COMMAND_LIST.commands.read().iter() {
println!("{:#}", cmd);
}
Ok(())
}
pub(crate) fn exec(args: Vec<String>) -> anyhow::Result<()> {
*RECURSION_DEPTH.lock() += 1;
if *RECURSION_DEPTH.lock() > MAX_RECURSION_DEPTH {
eprintln!("Maximum recursion depth reached. Aborting.");
*RECURSION_DEPTH.lock() = 0;
return Ok(());
}
println!("Recursion depth: {}", *RECURSION_DEPTH.lock());
let file_path_str = &args[0];
let file_path = std::path::Path::new(file_path_str);
println!("File path: {:#?}", file_path);

if !file_path.is_file() {
eprintln!(
"Error: File does not exist or is not a valid file: {}",
file_path.display()
);
return Ok(());
}
if file_path.extension() != Some(OsStr::new("zensh")) {
eprintln!(
"Error: File is not a zenshell script: {:#?}",
file_path.extension()
);
//TODO: dont panic on this error
return Ok(());
}
println!("Executing file: {:#?}", file_path);
let file_content = std::fs::read_to_string(file_path)?;
if file_content.is_empty() {
eprintln!("Error: file has no content. Is this a valid zenshell script?");
return Ok(());
}
println!("File contents:\n{file_content}");
evaluate_command(file_content.trim())?;
Ok(())
}
78 changes: 59 additions & 19 deletions engine/src/core/repl/repl.rs → engine/src/core/repl/exec.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
use crate::{
core::repl::{commands, Callable, COMMAND_LIST},
utils::logger::LOGGER,
use std::{
borrow::Cow::{self, Borrowed, Owned},
sync::Arc,
};
use anyhow::Result;

use chrono::Local;
use colored::Colorize;
use log::debug;
use parking_lot::Mutex;
use regex::Regex;
use rustyline::{
error::ReadlineError, highlight::Highlighter, hint::HistoryHinter, history::DefaultHistory,
Cmd, Completer, ConditionalEventHandler, Editor, Event, EventContext, EventHandler, Helper,
Hinter, KeyEvent, RepeatCount, Validator,
};
use std::{
borrow::Cow::{self, Borrowed, Owned},
sync::{Arc, Mutex},

use crate::{
core::repl::{commands, Callable, COMMAND_LIST},
utils::logger::LOGGER,
};

#[derive(Completer, Helper, Hinter, Validator)]
Expand All @@ -34,7 +36,7 @@ impl Highlighter for MyHelper {
}

fn highlight_hint<'h>(&self, hint: &'h str) -> Cow<'h, str> {
Owned(hint.bold().to_string())
Owned(hint.italic().bright_black().to_string())
}
}

Expand All @@ -47,7 +49,11 @@ impl ConditionalEventHandler for BacktickEventHandler {
fn handle(&self, evt: &Event, _: RepeatCount, _: bool, _: &EventContext) -> Option<Cmd> {
if let Some(k) = evt.get(0) {
if *k == KeyEvent::from('`') {
let mut state = self.toggle_state.lock().unwrap();
let mut state = self.toggle_state.lock();
println!(
"Stdout Logging: {}",
if *state { "ON".green() } else { "OFF".red() }
);
if *state {
LOGGER.write_to_stdout();
} else {
Expand Down Expand Up @@ -99,14 +105,47 @@ fn register_commands() {
Callable::Simple(commands::help),
None,
);
COMMAND_LIST.add_command(
"exec",
Some("Executes a .nyx file."),
Callable::WithArgs(commands::exec),
Some(1),
);

// Example of adding aliases for commands
COMMAND_LIST.add_alias("clear".to_string(), "cls".to_string());
}

fn evaluate_command(input: &str) {
fn tokenize(command: &str) -> Vec<String> {
let mut tokens = Vec::new();
let mut current_token = String::new();
let mut inside_string = false;

for char in command.chars() {
if char == '"' || char == '\'' {
inside_string = !inside_string;
} else if char.is_whitespace() && !inside_string {
if !current_token.is_empty() {
tokens.push(current_token);
current_token = String::new();
}
} else {
current_token.push(char);
}
}

// ignore the last token if it's empty. Who are we. Mojang? - Caz
if !current_token.is_empty() {
tokens.push(current_token);
}

tokens
}

pub fn evaluate_command(input: &str) -> anyhow::Result<()> {
if input.trim().is_empty() {
return;
println!("Empty command, skipping. type 'help' for a list of commands.");
return Ok(());
}

let pattern = Regex::new(r"[;|\n]").unwrap();
Expand All @@ -119,22 +158,23 @@ fn evaluate_command(input: &str) {
continue;
}

let tokens: Vec<&str> = command.split_whitespace().collect();
let tokens = tokenize(command);
if tokens.is_empty() {
return;
println!("Empty command, skipping.");
continue;
}

let cmd_name = tokens[0];
let args: Vec<String> = tokens[1..].iter().map(|&s| s.to_string()).collect();
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) },
);
)?;
}
Ok(())
}

pub async fn handle_repl() -> Result<()> {
pub async fn handle_repl() -> anyhow::Result<()> {
let mut rl = Editor::<MyHelper, DefaultHistory>::new()?;
rl.set_helper(Some(MyHelper(HistoryHinter::new())));

Expand All @@ -159,7 +199,7 @@ pub async fn handle_repl() -> Result<()> {
match sig {
Ok(line) => {
rl.add_history_entry(line.as_str())?;
evaluate_command(line.as_str());
evaluate_command(line.as_str())?;
}
Err(ReadlineError::Interrupted) => {
println!("CTRL+C received, exiting...");
Expand Down
Loading

0 comments on commit b4f3178

Please sign in to comment.