From a79c1f399b4200f11133f3be27d5da715557583f Mon Sep 17 00:00:00 2001 From: Doonv <58695417+doonv@users.noreply.github.com> Date: Sat, 23 Dec 2023 15:20:18 +0200 Subject: [PATCH] Commit before trying to use UniqueRc --- README.md | 45 ++++--- examples/custom_functions.rs | 20 ++- src/builtin_parser.rs | 3 +- src/builtin_parser/parser.rs | 3 + src/builtin_parser/runner.rs | 26 +--- src/builtin_parser/runner/environment.rs | 25 ++-- src/builtin_parser/runner/stdlib.rs | 10 +- src/builtin_parser/runner/unique_rc.rs | 27 ++++ src/builtin_parser/runner/value.rs | 152 ++++++++++++++--------- src/ui.rs | 2 +- 10 files changed, 195 insertions(+), 118 deletions(-) create mode 100644 src/builtin_parser/runner/unique_rc.rs diff --git a/README.md b/README.md index dee8666..fba916f 100644 --- a/README.md +++ b/README.md @@ -9,26 +9,32 @@ ## Usage 1. Add the `bevy_dev_console` git package. -```bash -cargo add --git https://github.com/doonv/bevy_dev_console.git -``` + + ```bash + cargo add --git https://github.com/doonv/bevy_dev_console.git + ``` + 2. Import the `prelude`. -```rs -use bevy_dev_console::prelude::*; -``` + + ```rs + use bevy_dev_console::prelude::*; + ``` + 3. Add the plugins. -```rs -App::new() - .add_plugins(( - // Start capturing logs before the default plugins initiate. - ConsoleLogPlugin::default(), - // Add the default plugins without the LogPlugin. - // Not removing the LogPlugin will cause a panic! - DefaultPlugins.build().disable::(), - // Add the dev console plugin itself. - DevConsolePlugin, - )) -``` + + ```rs + App::new() + .add_plugins(( + // Start capturing logs before the default plugins initiate. + ConsoleLogPlugin::default(), + // Add the default plugins without the LogPlugin. + // Not removing the LogPlugin will cause a panic! + DefaultPlugins.build().disable::(), + // Add the dev console plugin itself. + DevConsolePlugin, + )) + ``` + 4. That should be it! You can now press the `` ` `` or `~` key on your keyboard and it should open the console! ## Features @@ -37,7 +43,6 @@ App::new() ## Bevy Compatibility - | bevy | bevy_dev_console | | ------ | ---------------- | -| 0.12.* | 0.1.0 | \ No newline at end of file +| 0.12.* | 0.1.0 | diff --git a/examples/custom_functions.rs b/examples/custom_functions.rs index 2334ac4..5abf6b6 100644 --- a/examples/custom_functions.rs +++ b/examples/custom_functions.rs @@ -1,11 +1,13 @@ //! A simple exmaple +use std::{cell::RefCell, rc::Weak}; + use bevy::{ log::{Level, LogPlugin}, prelude::*, }; use bevy_dev_console::{ - builtin_parser::{Environment, Spanned, Value}, + builtin_parser::{Environment, RunError, Spanned, Value}, prelude::*, register, }; @@ -45,6 +47,21 @@ fn increment_global_counter(world: &mut World) -> f64 { }) } +// Function with reference (Syntax subject to change very soon) +fn increment_number(number: Spanned>>) -> Result<(), RunError> { + let rc = number.value.upgrade().unwrap(); + let mut reference = rc.borrow_mut(); + if let Value::Number(number) = &mut *reference { + *number += 1.0; + Ok(()) + } else { + Err(RunError::Custom { + text: "w".to_string(), + span: number.span, + }) + } +} + // For more example take a look at the standard library. // Register our functions by creating and inserting our own environment @@ -57,6 +74,7 @@ fn custom_environment() -> Environment { fn add; fn print_debug_info; fn increment_global_counter; + fn increment_number; }); environment diff --git a/src/builtin_parser.rs b/src/builtin_parser.rs index 3650a54..269f196 100644 --- a/src/builtin_parser.rs +++ b/src/builtin_parser.rs @@ -15,8 +15,7 @@ pub(crate) mod lexer; pub(crate) mod parser; pub(crate) mod runner; -pub use runner::environment::Environment; -pub use runner::Value; +pub use runner::{environment::Environment, Value, RunError}; /// Wrapper around `T` that stores a [Span] (A location in the source code) #[derive(Debug, Clone)] diff --git a/src/builtin_parser/parser.rs b/src/builtin_parser/parser.rs index cab421d..ae7d21a 100644 --- a/src/builtin_parser/parser.rs +++ b/src/builtin_parser/parser.rs @@ -9,6 +9,9 @@ use super::{ Environment, Spanned, }; +/// An [Abstract Syntax Tree](https://en.wikipedia.org/wiki/Abstract_syntax_tree). +/// +/// This type represents a list of expressions, which is what makes up a command. pub type Ast = Vec>; macro_rules! expect { diff --git a/src/builtin_parser/runner.rs b/src/builtin_parser/runner.rs index da16f31..5bbf5c4 100644 --- a/src/builtin_parser/runner.rs +++ b/src/builtin_parser/runner.rs @@ -19,22 +19,10 @@ pub mod environment; pub mod reflection; pub mod stdlib; pub mod value; +pub mod unique_rc; pub use value::Value; -// enum ReflectMutlect<'a> { -// Ref(&'a mut dyn Reflect), -// Mut(Mut<'a, dyn Reflect>) -// } -// impl<'a> ReflectMutlect<'a> { -// pub fn to_ref(&'a mut self) -> &'a mut dyn Reflect { -// match self { -// ReflectMutlect::Ref(reference) => *reference, -// ReflectMutlect::Mut(reference) => reference.as_reflect_mut() -// } -// } -// } - /// Container for every value needed by evaluation functions. pub struct EvalParams<'world, 'env, 'reg> { world: &'world mut World, @@ -44,7 +32,7 @@ pub struct EvalParams<'world, 'env, 'reg> { #[derive(Debug)] pub enum RunError { - Basic { text: String, span: Span }, + Custom { text: String, span: Span }, VariableNotFound(Span), ExpectedNumberAfterUnaryOperator(Value), InvalidVariantForResource(String, String), @@ -107,11 +95,7 @@ pub fn run(ast: Ast, world: &mut World) { match value { Ok(Value::None) => {} - Ok(value) => match value.try_format( - span, - &world, - ®istrations, - ) { + Ok(value) => match value.try_format(span, &world, ®istrations) { Ok(value) => info!(name: "console_result", "> {value}"), Err(err) => error!("{err:?}"), }, @@ -197,9 +181,7 @@ fn eval_expression( } }) } else { - Err(RunError::EnumVariantNotFound { - name: variable - }) + Err(RunError::EnumVariantNotFound { name: variable }) }? } Expression::StructObject { name, map } => { diff --git a/src/builtin_parser/runner/environment.rs b/src/builtin_parser/runner/environment.rs index 3f34882..abc099e 100644 --- a/src/builtin_parser/runner/environment.rs +++ b/src/builtin_parser/runner/environment.rs @@ -50,6 +50,11 @@ macro_rules! register { /// Get around implementation of Result causing stupid errors pub(super) struct ResultContainer(pub Result); +impl> From for ResultContainer { + fn from(value: T) -> Self { + ResultContainer(Ok(value.into())) + } +} impl From> for Result { fn from(ResultContainer(result): ResultContainer) -> Self { result @@ -60,9 +65,12 @@ impl, E> From> for ResultContainer { ResultContainer(result.map(|v| v.into())) } } +/// A parameter in a [`Function`]. pub trait FunctionParam: Sized { - // TODO: Add `Self` as default when https://github.com/rust-lang/rust/issues/29661 gets merged + /// TODO: Add `Self` as default when https://github.com/rust-lang/rust/issues/29661 gets merged type Item<'world, 'env, 'reg>; + /// Whether this parameter requires a [`Spanned`]. + /// If `false` then `FunctionParam::get`'s `value` will be [`None`], and vice versa. const USES_VALUE: bool; fn get<'world, 'env, 'reg>( @@ -84,16 +92,6 @@ pub trait IntoFunction { fn into_function(self) -> Function; } -macro_rules! replace_expr { - ($_t:ident $sub:expr) => { - $sub - }; -} - -macro_rules! count_idents { - ($($tts:ident)*) => {0usize $(+ replace_expr!($tts 1usize))*}; -} - macro_rules! impl_into_function { ( $($( @@ -180,6 +178,7 @@ impl_into_function!(T1, T2, T3, T4, T5, T6); impl_into_function!(T1, T2, T3, T4, T5, T6, T7); impl_into_function!(T1, T2, T3, T4, T5, T6, T7, T8); +/// A variable inside the [`Environment`]. pub enum Variable { Unmoved(Rc>), Moved, @@ -207,8 +206,8 @@ impl Default for Environment { impl Environment { /// Set a variable. - pub fn set(&mut self, name: String, value: Rc>) { - self.variables.insert(name, Variable::Unmoved(value)); + pub fn set(&mut self, name: impl Into, value: Rc>) { + self.variables.insert(name.into(), Variable::Unmoved(value)); } /// Returns a reference to a function if it exists. diff --git a/src/builtin_parser/runner/stdlib.rs b/src/builtin_parser/runner/stdlib.rs index 1fd5eb1..746d20c 100644 --- a/src/builtin_parser/runner/stdlib.rs +++ b/src/builtin_parser/runner/stdlib.rs @@ -1,10 +1,14 @@ use crate::register; -use bevy::{log::info, ecs::world::World, reflect::TypeRegistration}; +use bevy::{ecs::world::World, log::info, reflect::TypeRegistration}; use std::{cell::Ref, ops::Range}; use super::{Environment, RunError, Spanned, Value}; -fn print(value: Spanned, world: &mut World, registrations: &[&TypeRegistration]) -> Result<(), RunError> { +fn print( + value: Spanned, + world: &mut World, + registrations: &[&TypeRegistration], +) -> Result<(), RunError> { match value.value { Value::String(string) => info!("{string}"), _ => { @@ -50,7 +54,7 @@ fn ref_depth(Spanned { span, value }: Spanned) -> Result { } /// Disposes of a [`Value`]. -fn drop(_v: Value) {} +fn drop(_: Value) {} pub fn register(environment: &mut Environment) { register!(environment => { diff --git a/src/builtin_parser/runner/unique_rc.rs b/src/builtin_parser/runner/unique_rc.rs new file mode 100644 index 0000000..61edc33 --- /dev/null +++ b/src/builtin_parser/runner/unique_rc.rs @@ -0,0 +1,27 @@ +use std::{rc::Rc, ops::{DerefMut, Deref}, cell::RefCell}; + +struct UniqueRc(Rc); +impl UniqueRc { + pub fn into_rc(self) -> Rc { + self.0 + } +} +impl UniqueRc { + pub fn into_inner(self) -> T { + // SAFETY: There is only ever one owner of Rc + unsafe { Rc::try_unwrap(self.0).unwrap_unchecked() } + } +} + +impl Deref for UniqueRc { + type Target = T; + fn deref(&self) -> &Self::Target { + self.0.as_ref() + } +} +impl DerefMut for UniqueRc { + fn deref_mut(&mut self) -> &mut Self::Target { + // SAFETY: There is only ever one owner of Rc + unsafe { Rc::get_mut(&mut self.0).unwrap_unchecked() } + } +} \ No newline at end of file diff --git a/src/builtin_parser/runner/value.rs b/src/builtin_parser/runner/value.rs index 800a4c5..5cc1f4a 100644 --- a/src/builtin_parser/runner/value.rs +++ b/src/builtin_parser/runner/value.rs @@ -1,3 +1,4 @@ +use std::cell::RefMut; use std::collections::HashMap; use std::fmt::Debug; use std::rc::Weak; @@ -6,10 +7,11 @@ use std::{cell::RefCell, rc::Rc}; use crate::builtin_parser::Environment; use super::environment::{FunctionParam, ResultContainer}; -use super::reflection::{mut_dyn_reflect, IntoResource, IntoRegistration}; +use super::reflection::{mut_dyn_reflect, IntoRegistration, IntoResource}; use super::EvalParams; use super::{super::Spanned, RunError}; +use bevy::ecs::system::Resource; use bevy::ecs::world::World; use bevy::reflect::{ DynamicStruct, GetPath, Reflect, ReflectRef, TypeInfo, TypeRegistration, VariantInfo, @@ -62,7 +64,7 @@ impl Value { Value::Boolean(boolean) => Box::new(boolean), Value::String(string) => Box::new(string), Value::Reference(reference) => todo!(), - Value::Object(object) => { + Value::Object(object) | Value::StructObject { map: object, .. } => { let mut dyn_struct = DynamicStruct::default(); for (name, value) in object { @@ -72,16 +74,6 @@ impl Value { Box::new(dyn_struct) } - Value::StructObject { name, map } => { - let mut dyn_struct = DynamicStruct::default(); - - for (name, value) in map { - dyn_struct - .insert_boxed(&name, Rc::try_unwrap(value).unwrap().into_inner().reflect()); - } - - Box::new(dyn_struct) - } Value::Resource(_) => todo!(), } } @@ -130,7 +122,9 @@ impl Value { for (key, value) in map { string += &format!( "\n\t{key}: {},", - value.borrow().try_format(span.clone(), world, registrations)? + value + .borrow() + .try_format(span.clone(), world, registrations)? ); } if !map.is_empty() { @@ -145,13 +139,15 @@ impl Value { } /// A massive function that takes in a type registration and the world and then /// does all the hard work of printing out the type nicely. -fn fancy_debug_print(resource: &IntoResource, world: &World, registrations: &[&TypeRegistration]) -> String { +fn fancy_debug_print( + resource: &IntoResource, + world: &World, + registrations: &[&TypeRegistration], +) -> String { let registration = registrations.into_registration(resource.id); let dyn_reflect = resource.ref_dyn_reflect(world, registration); - let reflect = dyn_reflect - .reflect_path(resource.path.as_str()) - .unwrap(); + let reflect = dyn_reflect.reflect_path(resource.path.as_str()).unwrap(); let mut f = String::new(); let reflect_ref = reflect.reflect_ref(); @@ -233,36 +229,42 @@ fn fancy_debug_print(resource: &IntoResource, world: &World, registrations: &[&T f } -impl From for ResultContainer { - fn from(value: Value) -> Self { - ResultContainer(Ok(value)) - } -} impl From<()> for Value { fn from((): ()) -> Self { Value::None } } -impl From<()> for ResultContainer { - fn from((): ()) -> Self { - ResultContainer(Ok(Value::None)) - } -} impl From for Value { fn from(number: f64) -> Self { Value::Number(number) } } -impl From for ResultContainer { - fn from(number: f64) -> Self { - ResultContainer(Ok(Value::Number(number))) - } -} impl From for Value { fn from(string: String) -> Self { Value::String(string) } } +impl From for Value { + fn from(boolean: bool) -> Self { + Value::Boolean(boolean) + } +} + +impl From>>> for Value { + fn from(hashmap: HashMap>>) -> Self { + Value::Object(hashmap) + } +} +impl From> for Value { + fn from(hashmap: HashMap) -> Self { + Value::Object( + hashmap + .into_iter() + .map(|(k, v)| (k, Rc::new(RefCell::new(v)))) + .collect(), + ) + } +} impl FunctionParam for Spanned { type Item<'world, 'env, 'reg> = Self; @@ -277,8 +279,8 @@ impl FunctionParam for Spanned { Ok(value.unwrap()) } } -impl FunctionParam for Value { - type Item<'world, 'env, 'reg> = Value; +impl> FunctionParam for Spanned { + type Item<'world, 'env, 'reg> = Self; const USES_VALUE: bool = true; fn get<'world, 'env, 'reg>( @@ -287,11 +289,15 @@ impl FunctionParam for Value { _: &mut Option<&'env mut Environment>, _: &'reg [&'reg TypeRegistration], ) -> Result, RunError> { - Ok(value.unwrap().value) + let value = value.unwrap(); + Ok(Spanned { + span: value.span, + value: T::try_from(value.value)?, + }) } } -impl FunctionParam for f64 { - type Item<'world, 'env, 'reg> = f64; +impl FunctionParam for Value { + type Item<'world, 'env, 'reg> = Self; const USES_VALUE: bool = true; fn get<'world, 'env, 'reg>( @@ -300,31 +306,50 @@ impl FunctionParam for f64 { _: &mut Option<&'env mut Environment>, _: &'reg [&'reg TypeRegistration], ) -> Result, RunError> { - if let Value::Number(number) = value.unwrap().value { - Ok(number) - } else { - todo!() - } + Ok(value.unwrap().value) } } -impl FunctionParam for String { - type Item<'world, 'env, 'reg> = Self; - const USES_VALUE: bool = true; +macro_rules! impl_function_param_for_value { + (impl $type:ty: $value_pattern:pat => $return:expr) => { + impl FunctionParam for $type { + type Item<'world, 'env, 'reg> = Self; + const USES_VALUE: bool = true; - fn get<'world, 'env, 'reg>( - value: Option>, - _: &mut Option<&'world mut World>, - _: &mut Option<&'env mut Environment>, - _: &'reg [&'reg TypeRegistration], - ) -> Result, RunError> { - if let Value::String(string) = value.unwrap().value { - Ok(string) - } else { - todo!() + fn get<'world, 'env, 'reg>( + value: Option>, + _: &mut Option<&'world mut World>, + _: &mut Option<&'env mut Environment>, + _: &'reg [&'reg TypeRegistration], + ) -> Result, RunError> { + if let $value_pattern = value.unwrap().value { + Ok($return) + } else { + todo!() + } + } } - } + impl TryFrom for $type { + type Error = RunError; + + fn try_from(value: Value) -> Result { + if let $value_pattern = value { + Ok($return) + } else { + todo!() + } + } + } + }; } +impl_function_param_for_value!(impl f64: Value::Number(number) => number); +impl_function_param_for_value!(impl bool: Value::Boolean(boolean) => boolean); +impl_function_param_for_value!(impl String: Value::String(string) => string); +impl_function_param_for_value!(impl HashMap>>: Value::Object(object) => object); +impl_function_param_for_value!(impl HashMap: Value::Object(object) => { + object.into_iter().map(|(k, v)| (k, Rc::try_unwrap(v).unwrap().into_inner())).collect() +}); +impl_function_param_for_value!(impl Weak>: Value::Reference(reference) => reference); impl FunctionParam for &mut World { type Item<'world, 'env, 'reg> = &'world mut World; @@ -345,6 +370,21 @@ impl FunctionParam for &mut World { } } +// This probably isn't a good idea. But eh who cares, more power to the user. +impl FunctionParam for &mut Environment { + type Item<'world, 'env, 'reg> = &'env mut Environment; + const USES_VALUE: bool = false; + + fn get<'world, 'env, 'reg>( + _: Option>, + _: &mut Option<&'world mut World>, + environment: &mut Option<&'env mut Environment>, + _: &'reg [&'reg TypeRegistration], + ) -> Result, RunError> { + Ok(environment.take().unwrap()) + } +} + impl FunctionParam for &[&TypeRegistration] { type Item<'world, 'env, 'reg> = &'reg [&'reg TypeRegistration]; const USES_VALUE: bool = false; diff --git a/src/ui.rs b/src/ui.rs index 37b6340..b432848 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -24,7 +24,7 @@ fn system_time_to_chrono_utc(t: SystemTime) -> chrono::DateTime { chrono::Utc.timestamp_opt(sec, nsec).unwrap() } -pub fn ui( +pub(crate) fn ui( mut commands: Commands, mut contexts: EguiContexts, mut state: ResMut,